Beautiful GUIs
This commit is contained in:
parent
317ad22de0
commit
888a7a9962
20
app.c
20
app.c
@ -16,6 +16,11 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef RGB
|
||||||
|
#define RGB(r, g, b) ((r) | ((g) << 8) | ((b) << 16))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
App *app_NewApp() {
|
App *app_NewApp() {
|
||||||
render_LoadBundle("bundles.txt");
|
render_LoadBundle("bundles.txt");
|
||||||
|
|
||||||
@ -30,26 +35,15 @@ App *app_NewApp() {
|
|||||||
app->time = gametime_NewSystem(app);
|
app->time = gametime_NewSystem(app);
|
||||||
app->ui = ui_NewSystem(app);
|
app->ui = ui_NewSystem(app);
|
||||||
ui_RebuildUI(app->ui);
|
ui_RebuildUI(app->ui);
|
||||||
ui_PushState(app->ui, ui_Running);
|
ui_PushState(app->ui, ui_TitleMenu);
|
||||||
|
|
||||||
app->switch_level = NULL;
|
app->switch_level = NULL;
|
||||||
app->timescale = 1.0;
|
app->timescale = 1.0;
|
||||||
app->clear_color = 0;
|
app->clear_color = RGB(40, 40, 40);
|
||||||
app->wantQuit = false;
|
app->wantQuit = false;
|
||||||
app->paused = false;
|
app->paused = false;
|
||||||
|
|
||||||
|
|
||||||
Entity *player = entity_Create(app->entity, "player");
|
|
||||||
ADD_COMPONENT(player, player);
|
|
||||||
ADD_COMPONENT(player, position);
|
|
||||||
player->position->position = vec2(500, 500);
|
|
||||||
player->position->velocity = vec2(0, 0);
|
|
||||||
ADD_COMPONENT(player, hitbox);
|
|
||||||
player->hitbox->box.lefttop = vec2(-20, -80);
|
|
||||||
|
|
||||||
app_QueueLoadLevel(app, "intro.txt");
|
|
||||||
_app_SwitchLevel(app);
|
|
||||||
|
|
||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
12
app_debug.c
12
app_debug.c
@ -1,6 +1,7 @@
|
|||||||
|
|
||||||
#include "app.h"
|
#include "app.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
#include "ui.h"
|
||||||
#include "util/tree.h"
|
#include "util/tree.h"
|
||||||
#include "util/vector.h"
|
#include "util/vector.h"
|
||||||
|
|
||||||
@ -46,9 +47,18 @@ void app_DebugText(App *app, vector_Vector *vec_string) {
|
|||||||
PUSH_STRING(buf);
|
PUSH_STRING(buf);
|
||||||
snprintf(
|
snprintf(
|
||||||
buf, sizeof(buf) - 1,
|
buf, sizeof(buf) - 1,
|
||||||
"Particle count[0]: %d\n", tree_Count(app->particle->parts[0]));
|
"Particle count[0]: %d\n\nUI Stack:\n", tree_Count(app->particle->parts[0]));
|
||||||
PUSH_STRING(buf);
|
PUSH_STRING(buf);
|
||||||
|
|
||||||
|
for (int i = 0; i < vector_Size(app->ui->state); i++) {
|
||||||
|
ui_State *s = vector_At(app->ui->state, i);
|
||||||
|
snprintf(
|
||||||
|
buf, sizeof(buf) - 1,
|
||||||
|
" %s\n", ui_StateTitle[*s]);
|
||||||
|
PUSH_STRING(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
char zero = '\0';
|
char zero = '\0';
|
||||||
vector_Push(vec_string, &zero);
|
vector_Push(vec_string, &zero);
|
||||||
}
|
}
|
||||||
|
@ -227,6 +227,8 @@ void _app_SwitchLevel(App *app) {
|
|||||||
FILE *f = fopen(app->switch_level, "r");
|
FILE *f = fopen(app->switch_level, "r");
|
||||||
if (!f) {
|
if (!f) {
|
||||||
WARN("failed to open file\"%s\"", app->switch_level);
|
WARN("failed to open file\"%s\"", app->switch_level);
|
||||||
|
free(app->switch_level);
|
||||||
|
app->switch_level = NULL;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
4
types.h
4
types.h
@ -11,8 +11,8 @@ extern "C" {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#define SCREEN_WIDTH 1536
|
#define SCREEN_WIDTH 1440
|
||||||
#define SCREEN_HEIGHT 864
|
#define SCREEN_HEIGHT 810
|
||||||
|
|
||||||
#define WARN(fmt, ...) fprintf(stderr, "[WARN][%s] " fmt "\n", __func__, ##__VA_ARGS__)
|
#define WARN(fmt, ...) fprintf(stderr, "[WARN][%s] " fmt "\n", __func__, ##__VA_ARGS__)
|
||||||
#define INFO(fmt, ...) fprintf(stderr, "[%s] " fmt "\n", __func__, ##__VA_ARGS__)
|
#define INFO(fmt, ...) fprintf(stderr, "[%s] " fmt "\n", __func__, ##__VA_ARGS__)
|
||||||
|
26
ui.c
26
ui.c
@ -90,20 +90,20 @@ void ui_Advance(System_UI *sys, Duration deltaTime) {
|
|||||||
sys->bg.lefttop = lt;
|
sys->bg.lefttop = lt;
|
||||||
sys->bg.size = vec2_Minus(rb, lt);
|
sys->bg.size = vec2_Minus(rb, lt);
|
||||||
|
|
||||||
for (int i = 0; i < ui_StateCount; i++)
|
ui_State state = ui_CurrentState(sys);
|
||||||
for (int j = 0; j < vector_Size(sys->parts[i]); j++) {
|
for (int i = 0; i < vector_Size(sys->parts[state]); i++) {
|
||||||
ui_Part *part = (ui_Part *)vector_At(sys->parts[i], j);
|
ui_Part *part = (ui_Part *)vector_At(sys->parts[state], i);
|
||||||
// Hover check
|
// Hover check
|
||||||
if (box2_Contains(part->box, input_MousePosition(sys->super->input))) {
|
if (box2_Contains(part->box, input_MousePosition(sys->super->input))) {
|
||||||
part->hovered = true;
|
part->hovered = true;
|
||||||
part->progress = dmin(part->progress + UI_BUTTON_HOVER_SPEED * duration_Seconds(deltaTime), 1.0);
|
part->progress = dmin(part->progress + UI_BUTTON_HOVER_SPEED * duration_Seconds(deltaTime), 1.0);
|
||||||
} else {
|
} else {
|
||||||
part->hovered = false;
|
part->hovered = false;
|
||||||
part->progress = dmax(part->progress - UI_BUTTON_HOVER_SPEED * duration_Seconds(deltaTime), 0.0);
|
part->progress = dmax(part->progress - UI_BUTTON_HOVER_SPEED * duration_Seconds(deltaTime), 0.0);
|
||||||
}
|
|
||||||
|
|
||||||
part->update(sys, part, part->user, deltaTime);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
part->update(sys, part, part->user, deltaTime);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
2
ui.h
2
ui.h
@ -63,7 +63,7 @@ void ui_PopState(System_UI *sys);
|
|||||||
ui_State ui_CurrentState(System_UI *sys);
|
ui_State ui_CurrentState(System_UI *sys);
|
||||||
|
|
||||||
|
|
||||||
#define UI_PADDING (20)
|
#define UI_PADDING (16)
|
||||||
#define UI_BACKGROUND (0)
|
#define UI_BACKGROUND (0)
|
||||||
|
|
||||||
#define UI_BACKGROUND_FADE_MOVE_SPEED (20.0)
|
#define UI_BACKGROUND_FADE_MOVE_SPEED (20.0)
|
||||||
|
102
ui_build.c
102
ui_build.c
@ -2,12 +2,46 @@
|
|||||||
#include "ui.h"
|
#include "ui.h"
|
||||||
#include "app.h"
|
#include "app.h"
|
||||||
#include "util/vector.h"
|
#include "util/vector.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef RGB
|
||||||
|
#define RGB(r, g, b) ((r) | ((g) << 8) | ((b) << 16))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
static void _ui_Action_ResumeGame(System_UI *sys, ui_Part *part, uintptr_t data) {
|
static void _ui_Action_ResumeGame(System_UI *sys, ui_Part *part, uintptr_t data) {
|
||||||
ui_Action_PopState(sys, part, data);
|
ui_Action_PopState(sys, part, data);
|
||||||
sys->super->paused = false;
|
sys->super->paused = false;
|
||||||
}
|
}
|
||||||
|
static void _ui_Action_ReturnToTitle(System_UI *sys, ui_Part *part, uintptr_t data) {
|
||||||
|
while (vector_Size(sys->state) > 0 && ui_CurrentState(sys) != ui_TitleMenu) {
|
||||||
|
INFO("popping \"%s\"", ui_StateTitle[ui_CurrentState(sys)]);
|
||||||
|
ui_Action_PopState(sys, part, data);
|
||||||
|
}
|
||||||
|
sys->super->paused = false;
|
||||||
|
if (vector_Size(sys->state) == 0)
|
||||||
|
WARN("new state stack is now empty, ohno");
|
||||||
|
|
||||||
|
sys->super->clear_color = RGB(40, 40, 40);
|
||||||
|
// Clear the app
|
||||||
|
// see app_file.c:233
|
||||||
|
entity_Clear(sys->super->entity);
|
||||||
|
particle_Clear(sys->super->particle);
|
||||||
|
sys->super->camera->target = NULL;
|
||||||
|
}
|
||||||
|
static void _ui_Action_SelectLevel(System_UI *sys, ui_Part *part, uintptr_t data) {
|
||||||
|
const char *level = (const char *)data;
|
||||||
|
app_QueueLoadLevel(sys->super, level);
|
||||||
|
ui_Action_PushState(sys, part, (uintptr_t)ui_Running);
|
||||||
|
sys->super->paused = false;
|
||||||
|
}
|
||||||
|
static void _ui_Action_StartFromBeginning(System_UI *sys, ui_Part *part, uintptr_t data) {
|
||||||
|
_ui_Action_SelectLevel(sys, part, (uintptr_t) "intro.txt");
|
||||||
|
}
|
||||||
|
static void _ui_Action_Quit(System_UI *sys, ui_Part *part, uintptr_t data) {
|
||||||
|
sys->super->wantQuit = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#define PUSH_UI(part) \
|
#define PUSH_UI(part) \
|
||||||
@ -18,12 +52,78 @@ void ui_RebuildUI(System_UI *sys) {
|
|||||||
vector_Vector *p;
|
vector_Vector *p;
|
||||||
ui_Part _p0;
|
ui_Part _p0;
|
||||||
|
|
||||||
|
// Title Menu
|
||||||
|
p = sys->parts[ui_TitleMenu];
|
||||||
|
vector_Clear(p);
|
||||||
|
PUSH_UI(ui_Button_New(
|
||||||
|
box2_FromCenter(vec2(SCREEN_WIDTH / 2.0, 250), vec2(500, 50)),
|
||||||
|
"Start from Beginning",
|
||||||
|
&_ui_Action_StartFromBeginning, 0))
|
||||||
|
PUSH_UI(ui_Button_New(
|
||||||
|
box2_FromCenter(vec2(SCREEN_WIDTH / 2.0, 250 + (50 + UI_PADDING)), vec2(500, 50)),
|
||||||
|
"Select Level",
|
||||||
|
&ui_Action_PushState, ui_SceneSelect))
|
||||||
|
PUSH_UI(ui_Button_New(
|
||||||
|
box2_FromCenter(vec2(SCREEN_WIDTH / 2.0 - (500 - UI_PADDING) / 4.0 - UI_PADDING / 2.0, 250 + 2 * (50 + UI_PADDING)), vec2((500 - UI_PADDING) / 2.0, 40)),
|
||||||
|
"Options",
|
||||||
|
&ui_Action_PushState, (uintptr_t)ui_Options))
|
||||||
|
PUSH_UI(ui_Button_New(
|
||||||
|
box2_FromCenter(vec2(SCREEN_WIDTH / 2.0 + (500 - UI_PADDING) / 4.0 + UI_PADDING / 2.0, 250 + 2 * (50 + UI_PADDING)), vec2((500 - UI_PADDING) / 2.0, 40)),
|
||||||
|
"Quit",
|
||||||
|
&_ui_Action_Quit, 0))
|
||||||
|
|
||||||
|
|
||||||
|
// Select Level
|
||||||
|
p = sys->parts[ui_SceneSelect];
|
||||||
|
vector_Clear(p);
|
||||||
|
PUSH_UI(ui_Button_NewLeftAligned(
|
||||||
|
box2_FromCenter(vec2(SCREEN_WIDTH / 2.0, 280), vec2(400, 40)),
|
||||||
|
"Introduction",
|
||||||
|
&_ui_Action_SelectLevel, (uintptr_t) "intro.txt"));
|
||||||
|
PUSH_UI(ui_Button_NewLeftAligned(
|
||||||
|
box2_FromCenter(vec2(SCREEN_WIDTH / 2.0, 280 + (40 + UI_PADDING / 2.0)), vec2(400, 40)),
|
||||||
|
"Scene 1",
|
||||||
|
&_ui_Action_SelectLevel, (uintptr_t) "level1.txt"));
|
||||||
|
PUSH_UI(ui_Button_NewLeftAligned(
|
||||||
|
box2_FromCenter(vec2(SCREEN_WIDTH / 2.0, 280 + 2 * (40 + UI_PADDING / 2.0)), vec2(400, 40)),
|
||||||
|
"Scene 2",
|
||||||
|
&_ui_Action_SelectLevel, (uintptr_t) "level2.txt"));
|
||||||
|
PUSH_UI(ui_Button_NewLeftAligned(
|
||||||
|
box2_FromCenter(vec2(SCREEN_WIDTH / 2.0, 280 + 3 * (40 + UI_PADDING / 2.0)), vec2(400, 40)),
|
||||||
|
"Scene 3",
|
||||||
|
&_ui_Action_SelectLevel, (uintptr_t) "level3.txt"));
|
||||||
|
PUSH_UI(ui_Button_NewLeftAligned(
|
||||||
|
box2_FromCenter(vec2(SCREEN_WIDTH / 2.0 + 100, 280 + 3 * (40 + UI_PADDING / 2.0) + (40 + UI_PADDING)), vec2(200, 40)),
|
||||||
|
"Return",
|
||||||
|
&ui_Action_PopState, 0));
|
||||||
|
|
||||||
|
|
||||||
// Paused
|
// Paused
|
||||||
p = sys->parts[ui_Paused];
|
p = sys->parts[ui_Paused];
|
||||||
vector_Clear(p);
|
vector_Clear(p);
|
||||||
PUSH_UI(ui_Button_New(
|
PUSH_UI(ui_Button_New(
|
||||||
box2_FromCenter(vec2(SCREEN_WIDTH / 2.0, 300), vec2(400, 50)),
|
box2_FromCenter(vec2(SCREEN_WIDTH / 2.0, 300), vec2(400, 40)),
|
||||||
"Resume Game",
|
"Resume Game",
|
||||||
&_ui_Action_ResumeGame, 0))
|
&_ui_Action_ResumeGame, 0))
|
||||||
|
PUSH_UI(ui_Button_New(
|
||||||
|
box2_FromCenter(vec2(SCREEN_WIDTH / 2.0, 300 + (40 + UI_PADDING)), vec2(400, 40)),
|
||||||
|
"Reselect Level",
|
||||||
|
&ui_Action_PushState, (uintptr_t)ui_SceneSelect))
|
||||||
|
PUSH_UI(ui_Button_New(
|
||||||
|
box2_FromCenter(vec2(SCREEN_WIDTH / 2.0, 300 + 2 * (40 + UI_PADDING)), vec2(400, 40)),
|
||||||
|
"Options",
|
||||||
|
&ui_Action_PushState, (uintptr_t)ui_Options))
|
||||||
|
PUSH_UI(ui_Button_New(
|
||||||
|
box2_FromCenter(vec2(SCREEN_WIDTH / 2.0, 300 + 3 * (40 + UI_PADDING) + UI_PADDING), vec2(400, 40)),
|
||||||
|
"Return to Title",
|
||||||
|
&_ui_Action_ReturnToTitle, 0))
|
||||||
|
|
||||||
|
|
||||||
|
// Options
|
||||||
|
p = sys->parts[ui_Options];
|
||||||
|
vector_Clear(p);
|
||||||
|
PUSH_UI(ui_Button_New(
|
||||||
|
box2_FromCenter(vec2(SCREEN_WIDTH / 2.0, 300), vec2(600, 50)),
|
||||||
|
"Return",
|
||||||
|
&ui_Action_PopState, 0))
|
||||||
}
|
}
|
||||||
|
@ -42,4 +42,9 @@ ui_Part ui_Button_New(Box2 box, const char *label, ui_ActionCallback callback, u
|
|||||||
.free = &_ui_Button_Free};
|
.free = &_ui_Button_Free};
|
||||||
return part;
|
return part;
|
||||||
}
|
}
|
||||||
ui_Part ui_Button_NewLeftAligned(Box2 box, const char *label, ui_ActionCallback callback, uintptr_t data);
|
ui_Part ui_Button_NewLeftAligned(Box2 box, const char *label, ui_ActionCallback callback, uintptr_t data) {
|
||||||
|
ui_Part part = ui_Button_New(box, label, callback, data);
|
||||||
|
|
||||||
|
((ui_Button *)part.user)->left_aligned = true;
|
||||||
|
return part;
|
||||||
|
}
|
||||||
|
@ -66,10 +66,14 @@ extern "C" void _ui_Button_Draw(System_UI *sys, ui_Part *part, void *user) {
|
|||||||
setfillcolor(color_final);
|
setfillcolor(color_final);
|
||||||
settextcolor(RGB(255, 255, 255));
|
settextcolor(RGB(255, 255, 255));
|
||||||
|
|
||||||
|
Box2 padded_box = part->box;
|
||||||
|
padded_box.lefttop.x += UI_PADDING;
|
||||||
|
padded_box.size.x -= 2 * UI_PADDING;
|
||||||
|
|
||||||
solidrectangle(
|
solidrectangle(
|
||||||
(int)round(part->box.lefttop.x),
|
(int)round(part->box.lefttop.x),
|
||||||
(int)round(part->box.lefttop.y),
|
(int)round(part->box.lefttop.y),
|
||||||
(int)round(part->box.lefttop.x + part->box.size.x),
|
(int)round(part->box.lefttop.x + part->box.size.x),
|
||||||
(int)round(part->box.lefttop.y + part->box.size.y));
|
(int)round(part->box.lefttop.y + part->box.size.y));
|
||||||
render_DrawTextEx(b->label, part->box, (b->left_aligned ? DT_LEFT : DT_CENTER) | DT_VCENTER | DT_SINGLELINE);
|
render_DrawTextEx(b->label, padded_box, (b->left_aligned ? DT_LEFT : DT_CENTER) | DT_VCENTER | DT_SINGLELINE);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user