From 36ba0b1c9f1c65564cdb661cb5333ffc15ffb2c3 Mon Sep 17 00:00:00 2001 From: Edgaru089 Date: Tue, 30 Apr 2024 06:57:24 +0800 Subject: [PATCH] More UI features & leaderboards for level time --- app.c | 12 ++--- app.h | 2 + app_debug.c | 2 +- app_file.c | 11 +++-- mapper_misc.c | 3 +- types.c | 8 ++++ types.h | 7 +++ ui.c | 10 +++-- ui.h | 54 +++++++++++++++++++++-- ui_boxbuilder.c | 99 ++++++++++++++++++++++++++++++++++++++++++ ui_build.c | 113 +++++++++++++++++++++++++++++++++++++++++++++--- ui_label.c | 58 +++++++++++++++++++++++++ ui_render.cpp | 45 ++++++++++++++++--- 13 files changed, 395 insertions(+), 29 deletions(-) create mode 100644 ui_boxbuilder.c create mode 100644 ui_label.c diff --git a/app.c b/app.c index b4779ee..cdb3212 100644 --- a/app.c +++ b/app.c @@ -37,11 +37,12 @@ App *app_NewApp() { ui_RebuildUI(app->ui); ui_PushState(app->ui, ui_TitleMenu); - app->switch_level = NULL; - app->timescale = 1.0; - app->clear_color = RGB(40, 40, 40); - app->wantQuit = false; - app->paused = false; + app->current_level = NULL; + app->switch_level = NULL; + app->timescale = 1.0; + app->clear_color = RGB(40, 40, 40); + app->wantQuit = false; + app->paused = false; app_QueueLoadLevel(app, "title.txt"); @@ -83,6 +84,7 @@ void app_Advance(App *app, Duration deltaTime) { physics_Advance(app->physics, delta_game); entity_Advance(app->entity, delta_game); camera_Advance(app->camera, delta_game); + app->level_playtime.microseconds += deltaTime.microseconds; } ui_Advance(app->ui, deltaTime); diff --git a/app.h b/app.h index 206affe..868c094 100644 --- a/app.h +++ b/app.h @@ -28,7 +28,9 @@ typedef struct _App { System_GameTime *time; System_UI *ui; + char *current_level; char *switch_level; + Duration level_playtime; double timescale; uint32_t clear_color; bool wantQuit, debugOn; diff --git a/app_debug.c b/app_debug.c index ae631cc..e3b6c79 100644 --- a/app_debug.c +++ b/app_debug.c @@ -47,7 +47,7 @@ void app_DebugText(App *app, vector_Vector *vec_string) { PUSH_STRING(buf); snprintf( buf, sizeof(buf) - 1, - "Particle count[0]: %d\n\nUI Stack:\n", tree_Count(app->particle->parts[0])); + "Particle count[0]: %d\n\nCurrent Level: %s\nPlaytime: %.4lfs\n\nUI Stack:\n", tree_Count(app->particle->parts[0]), (app->current_level ? app->current_level : "NULL"), duration_Seconds(app->level_playtime)); PUSH_STRING(buf); for (int i = 0; i < vector_Size(app->ui->state); i++) { diff --git a/app_file.c b/app_file.c index c77c81b..d2ad8d9 100644 --- a/app_file.c +++ b/app_file.c @@ -172,7 +172,7 @@ static void _app_LevelCommand(App *app, char *cmd) { } CMD("PUT_CAMERA") { - Vec2 center = readvec2(); + Vec2 center = readvec2(); app->camera->cam = box2_FromCenter(center, app->camera->screen.size); } @@ -237,7 +237,8 @@ void _app_SwitchLevel(App *app) { // Clear the current level entity_Clear(app->entity); particle_Clear(app->particle); - app->camera->target = NULL; + app->camera->target = NULL; + app->level_playtime.microseconds = 0; // Read every line char linebuf[512]; @@ -253,7 +254,9 @@ void _app_SwitchLevel(App *app) { } - free(app->switch_level); - app->switch_level = NULL; + if (app->current_level) + free(app->current_level); + app->current_level = app->switch_level; + app->switch_level = NULL; fclose(f); } diff --git a/mapper_misc.c b/mapper_misc.c index 781c75d..0b01b4a 100644 --- a/mapper_misc.c +++ b/mapper_misc.c @@ -6,6 +6,7 @@ #include "player.h" #include "render_util.h" #include "types.h" +#include "ui.h" #include "util/assert.h" #include "util/rand.h" #include @@ -171,7 +172,7 @@ void misc_thinker_ChangeLevel(App *app, Entity *e, Duration deltaTime) { Box2 worldbox = ABSOLUTE_BOX(e, e->misc->trigger); if (box2_Intersects(worldbox, playerbox, NULL)) - app_QueueLoadLevel(app, e->misc->change_level); + ui_EnterIntermission(app->ui, e->misc->change_level); } diff --git a/types.c b/types.c index fa294f1..0b31a30 100644 --- a/types.c +++ b/types.c @@ -107,6 +107,14 @@ Box2 box2_FromCenter(Vec2 center, Vec2 size) { .y = center.y - size.y / 2.0}}; return box; } +Box2 box2_FromPivot(Vec2 position, Vec2 pivot, Vec2 size) { + Box2 box = { + .size = size, + .lefttop = { + .x = position.x - size.x * pivot.x, + .y = position.y - size.y * pivot.y}}; + return box; +} Box2 box2_Offset(Box2 box, Vec2 offset) { box.lefttop = vec2_Add(box.lefttop, offset); diff --git a/types.h b/types.h index 865d56f..ab9e6a3 100644 --- a/types.h +++ b/types.h @@ -70,6 +70,12 @@ static inline Box2 box2(double x, double y, double width, double height) { b.size.y = height; return b; } +static inline Box2 box2v(Vec2 lefttop, Vec2 size) { + Box2 b; + b.lefttop = lefttop; + b.size = size; + return b; +} // Intersection test. @@ -79,6 +85,7 @@ bool box2_Contains(const Box2 box, const Vec2 point); Vec2 box2_Center(Box2 box); Box2 box2_FromCenter(Vec2 center, Vec2 size); +Box2 box2_FromPivot(Vec2 position, Vec2 pivot, Vec2 size); Box2 box2_Offset(Box2 box, Vec2 offset); Box2 box2_OffsetX(Box2 box, double offsetX); diff --git a/ui.c b/ui.c index 66104b3..fb1af77 100644 --- a/ui.c +++ b/ui.c @@ -12,7 +12,9 @@ const char *ui_StateTitle[ui_StateCount] = { "Pause Menu", "Jack's Escape (ver. 3.141592) - Title", "Select Level", - "Options"}; + "Options", + "Level Finished", + "Leaderboards"}; static inline double dabs(double x) { return x > 0 ? x : -x; } @@ -32,7 +34,8 @@ void ui_DeleteSystem(System_UI *sys) { for (int i = 0; i < ui_StateCount; i++) for (int j = 0; j < vector_Size(sys->parts[i]); j++) { ui_Part *part = (ui_Part *)vector_At(sys->parts[i], j); - part->free(sys, part, part->user); + if (part->free) + part->free(sys, part, part->user); } for (int i = 0; i < ui_StateCount; i++) vector_Destroy(sys->parts[i]); @@ -102,7 +105,8 @@ void ui_Advance(System_UI *sys, Duration deltaTime) { part->progress = dmax(part->progress - UI_BUTTON_HOVER_SPEED * duration_Seconds(deltaTime), 0.0); } - part->update(sys, part, part->user, deltaTime); + if (part->update) + part->update(sys, part, part->user, deltaTime); } } diff --git a/ui.h b/ui.h index 03b14a9..085b751 100644 --- a/ui.h +++ b/ui.h @@ -17,6 +17,8 @@ typedef enum { ui_TitleMenu, ui_SceneSelect, ui_Options, + ui_InterMission, + ui_Leaderboards, ui_StateCount // Keep at last } ui_State; @@ -34,7 +36,7 @@ typedef struct _ui_Part { uintptr_t user; // user data void (*draw)(System_UI *sys, struct _ui_Part *part, uintptr_t user); // called when the UI is to be drawn void (*update)(System_UI *sys, struct _ui_Part *part, uintptr_t user, Duration deltatime); // called when the element is to be updated - void (*free)(System_UI *sys, struct _ui_Part *part, uintptr_t user); // called when the element is to be deleted + void (*free)(System_UI *sys, struct _ui_Part *part, uintptr_t user); // called when the element is to be deleted, dont free part! } ui_Part; // UI system with its own state machine. @@ -57,12 +59,17 @@ void ui_PushPart(System_UI *sys, ui_State layer, ui_Part part); void ui_Advance(System_UI *sys, Duration deltaTime); void ui_Render(System_UI *sys); -void ui_PushState(System_UI *sys, ui_State push_state); -void ui_PopState(System_UI *sys); - +void ui_PushState(System_UI *sys, ui_State push_state); +void ui_PopState(System_UI *sys); ui_State ui_CurrentState(System_UI *sys); +// Called when the level is finished +// If next_level is NULL, there are no next level and the +// button returns to title screen +void ui_EnterIntermission(System_UI *sys, const char *next_level); + + #define UI_PADDING (16) #define UI_BACKGROUND (0) @@ -96,6 +103,45 @@ ui_Part ui_Button_NewLeftAligned(Box2 box, const char *label, ui_ActionCallback // ui_Part ui_Button_NewColored(Box2 box, const char *label, uint32_t bg, uint32_t hover, uint32_t clicked, ui_ActionCallback callback, uintptr_t data); +typedef struct { + char *label; // Allocated & copied, NULL means no label + int align; // <0: left aligned; ==0: center aligned; >0: right aligned +} ui_Label; +ui_Part ui_Label_New(Box2 box, const char *label, int alignment); +void ui_Label_SetLabel(ui_Part *part, const char *label); + +// Creates a new box fill with the given color +ui_Part ui_Fill_New(Box2 box, uint32_t color); + + +// Box organizer +typedef struct { + vector_Vector *rows; // vector of vector_Vector* of Box2 + vector_Vector *row_counts; // vector of Ints + double width; // Total width, excluding external padding + double line_height; // Line height + Vec2 position; // Position of the entire bounding box on screen + Vec2 pivot; // Pivot or Origin of the position, [0, 1] +} ui_BoxBuilder; + +#define UI_BOXBUILDER_DEFAULT_LINEHEIGHT (48) +#define UI_BOXBUILDER_DEFAULT_WIDTH (400) +#define UI_BOXBUILDER_DEFAULT_PIVOT_X (0.5) +#define UI_BOXBUILDER_DEFAULT_PIVOT_Y (0.0) + +ui_BoxBuilder *ui_BoxBuilder_New(); +void ui_BoxBuilder_Delete(ui_BoxBuilder *builder); + +// Set that this row should have this many columns +void ui_BoxBuilder_SetCount(ui_BoxBuilder *builder, int row, int num_cols); + +// Assemble everything +void ui_BoxBuilder_Assemble(ui_BoxBuilder *builder); + +// Every number starts from 1 +Box2 ui_BoxBuilder_At(ui_BoxBuilder *builder, int row, int col); + + #ifdef __cplusplus } #endif diff --git a/ui_boxbuilder.c b/ui_boxbuilder.c new file mode 100644 index 0000000..e8a82f9 --- /dev/null +++ b/ui_boxbuilder.c @@ -0,0 +1,99 @@ + +#include "types.h" +#include "ui.h" +#include "util/vector.h" + +ui_BoxBuilder *ui_BoxBuilder_New() { + ui_BoxBuilder *builder = zero_malloc(sizeof(ui_BoxBuilder)); + builder->line_height = UI_BOXBUILDER_DEFAULT_LINEHEIGHT; + builder->width = UI_BOXBUILDER_DEFAULT_WIDTH; + builder->pivot = vec2(UI_BOXBUILDER_DEFAULT_PIVOT_X, UI_BOXBUILDER_DEFAULT_PIVOT_Y); + builder->row_counts = vector_Create(sizeof(int)); + // builder->rows = vector_Create(sizeof(vector_Vector *)); + return builder; +} +void ui_BoxBuilder_Delete(ui_BoxBuilder *builder) { + vector_Destroy(builder->row_counts); + for (int i = 0; i < vector_Size(builder->rows); i++) { + vector_Vector *vr = *((vector_Vector **)vector_At(builder->rows, i)); + if (vr) + vector_Destroy(vr); + } + vector_Destroy(builder->rows); + free(builder); +} + +// Set that this row should have this many columns +void ui_BoxBuilder_SetCount(ui_BoxBuilder *builder, int row, int num_cols) { + if (vector_Size(builder->row_counts) < row) { + size_t old_size = vector_Size(builder->row_counts); + vector_Resize(builder->row_counts, row); + // Set the new parts to zero + for (int i = old_size; i < row; i++) + *((int *)vector_At(builder->row_counts, i)) = 0; + } + *((int *)vector_At(builder->row_counts, row - 1)) = num_cols; +} + +// Assemble everything +void ui_BoxBuilder_Assemble(ui_BoxBuilder *builder) { + if (builder->rows) { + // Free the previous vectors + for (int i = 0; i < vector_Size(builder->rows); i++) { + vector_Vector *vr = *((vector_Vector **)vector_At(builder->rows, i)); + if (vr) + vector_Destroy(vr); + } + vector_Clear(builder->rows); + } else + builder->rows = vector_Create(sizeof(vector_Vector *)); + vector_Resize(builder->rows, vector_Size(builder->row_counts)); + + double cursor_y = 0.0; + for (int i = 0; i < vector_Size(builder->row_counts); i++) { + int row_count = *((int *)vector_At(builder->row_counts, i)); + if (row_count == 0) { + *(vector_Vector **)vector_At(builder->rows, i) = NULL; + cursor_y += UI_PADDING; + continue; + } + vector_Vector *row = vector_Create(sizeof(Box2)); + + *(vector_Vector **)vector_At(builder->rows, i) = row; + + // Append every box + double total_width = builder->width - UI_PADDING * (row_count - 1); + double avg_width = total_width / row_count; + for (int i = 0; i < row_count; i++) { + Box2 box = { + .lefttop = {.x = i * (avg_width + UI_PADDING), .y = cursor_y}, + .size = {.x = avg_width, .y = builder->line_height}}; + vector_Push(row, &box); + } + + cursor_y += builder->line_height + UI_PADDING; + } + + int row_num = vector_Size(builder->row_counts); + Vec2 max_size = { + .x = builder->width, + .y = builder->line_height * row_num + UI_PADDING * (row_num - 1)}; + Box2 max_box = box2_FromPivot(builder->position, builder->pivot, max_size); + + // Move every box + for (int i = 0; i < vector_Size(builder->row_counts); i++) { + int row_count = *((int *)vector_At(builder->row_counts, i)); + if (row_count == 0) + continue; + vector_Vector *row = *(vector_Vector **)vector_At(builder->rows, i); + for (int i = 0; i < row_count; i++) { + Box2 *box = (Box2 *)vector_At(row, i); + box->lefttop = vec2_Add(box->lefttop, max_box.lefttop); + } + } +} + +// Every number starts from 1 +Box2 ui_BoxBuilder_At(ui_BoxBuilder *builder, int row, int col) { + return *(Box2 *)vector_At(*(vector_Vector **)vector_At(builder->rows, row - 1), col - 1); +} diff --git a/ui_build.c b/ui_build.c index 0525cf2..ad1124e 100644 --- a/ui_build.c +++ b/ui_build.c @@ -1,4 +1,5 @@ +#include "render_util.h" #include "ui.h" #include "app.h" #include "util/vector.h" @@ -17,7 +18,7 @@ static void _ui_Action_ResumeGame(System_UI *sys, ui_Part *part, uintptr_t data) 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); + ui_Action_PopState(sys, part, 0); } sys->super->paused = false; if (vector_Size(sys->state) == 0) @@ -46,6 +47,42 @@ static void _ui_Action_Quit(System_UI *sys, ui_Part *part, uintptr_t data) { sys->super->wantQuit = true; } +static void _ui_Action_ReplayLevel(System_UI *sys, ui_Part *part, uintptr_t data) { + _ui_Action_SelectLevel(sys, part, (uintptr_t)sys->super->current_level); +} + +static void _ui_Action_EndIntermission(System_UI *sys, ui_Part *part, uintptr_t data) { + if (!data) + // next mission string is NULL, return to title + _ui_Action_ReturnToTitle(sys, part, 0); + else { + // load next level + // should be able to just use this + _ui_Action_SelectLevel(sys, part, data); + free((void *)data); + } +} + + +void ui_EnterIntermission(System_UI *sys, const char *next_level) { + // Let's malloc & copy this next_level string + // And hide it in the last part of this state + // It must be a button, so put the string in the callback data + ui_Part *last_part = vector_Back(sys->parts[ui_InterMission]); + if (((ui_Button *)last_part->user)->callback_data) + free((void *)((ui_Button *)last_part->user)->callback_data); + if (!next_level || strlen(next_level) == 0) + ((ui_Button *)last_part->user)->callback_data = 0; + else + ((ui_Button *)last_part->user)->callback_data = (uintptr_t)copy_malloc(next_level); + ui_PushState(sys, ui_InterMission); + sys->super->paused = true; + + // We need to update the UI elements in this State + // 0 1 2 3 4 5 6 7 8 9 + // "Level Time" "Record Time" +} + #define PUSH_UI(part) \ _p0 = (part); \ @@ -105,19 +142,23 @@ void ui_RebuildUI(System_UI *sys) { p = sys->parts[ui_Paused]; vector_Clear(p); PUSH_UI(ui_Button_New( - box2_FromCenter(vec2(SCREEN_WIDTH / 2.0, 300), vec2(400, 40)), + box2_FromCenter(vec2(SCREEN_WIDTH / 2.0, 250), vec2(400, 40)), "Resume Game", &_ui_Action_ResumeGame, 0)) PUSH_UI(ui_Button_New( - box2_FromCenter(vec2(SCREEN_WIDTH / 2.0, 300 + (40 + UI_PADDING)), vec2(400, 40)), + box2_FromCenter(vec2(SCREEN_WIDTH / 2.0, 250 + (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)), + box2_FromCenter(vec2(SCREEN_WIDTH / 2.0, 250 + 2 * (40 + UI_PADDING)), vec2(400, 40)), + "Leaderboard for This Level", + &ui_Action_PushState, (uintptr_t)ui_Leaderboards)) + PUSH_UI(ui_Button_New( + box2_FromCenter(vec2(SCREEN_WIDTH / 2.0, 250 + 3 * (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)), + box2_FromCenter(vec2(SCREEN_WIDTH / 2.0, 250 + 4 * (40 + UI_PADDING) + UI_PADDING), vec2(400, 40)), "Return to Title", &_ui_Action_ReturnToTitle, 0)) @@ -129,4 +170,66 @@ void ui_RebuildUI(System_UI *sys) { box2_FromCenter(vec2(SCREEN_WIDTH / 2.0, 300), vec2(600, 50)), "Return", &ui_Action_PopState, 0)) + + + // InterMission + p = sys->parts[ui_InterMission]; + vector_Clear(p); + ui_BoxBuilder *builder = ui_BoxBuilder_New(); + builder->line_height = 36; + builder->pivot = vec2(0.5, 0.625); + builder->position = vec2(SCREEN_WIDTH / 2.0, SCREEN_HEIGHT / 2.0); + ui_BoxBuilder_SetCount(builder, 1, 1); + ui_BoxBuilder_SetCount(builder, 2, 1); + ui_BoxBuilder_SetCount(builder, 4, 2); + ui_BoxBuilder_SetCount(builder, 5, 1); + ui_BoxBuilder_SetCount(builder, 7, 1); + ui_BoxBuilder_Assemble(builder); + + PUSH_UI(ui_Label_New( + ui_BoxBuilder_At(builder, 1, 1), + "Time in This Level", -1)) + PUSH_UI(ui_Label_New( + ui_BoxBuilder_At(builder, 1, 1), + "00:00.00", 1)) + PUSH_UI(ui_Label_New( + ui_BoxBuilder_At(builder, 2, 1), + "Record Time", -1)) + PUSH_UI(ui_Label_New( + ui_BoxBuilder_At(builder, 2, 1), + "00:00.00", 1)) + PUSH_UI(ui_Fill_New( + box2v( + vec2_Add( + ui_BoxBuilder_At(builder, 4, 1).lefttop, + vec2(0, -UI_PADDING)), + vec2(builder->width, 1.0)), + RGB(128, 128, 128))); + PUSH_UI(ui_Button_New( + ui_BoxBuilder_At(builder, 4, 1), + "Replay Level", + &_ui_Action_ReplayLevel, 0)) + PUSH_UI(ui_Button_New( + ui_BoxBuilder_At(builder, 4, 2), + "Back to Title", + &_ui_Action_ReturnToTitle, 0)) + PUSH_UI(ui_Button_New( + ui_BoxBuilder_At(builder, 5, 1), + "View Leaderboards for Level", + &ui_Action_PushState, (uintptr_t)ui_Leaderboards)) + PUSH_UI(ui_Fill_New( + box2v( + vec2_Add( + ui_BoxBuilder_At(builder, 7, 1).lefttop, + vec2(0, -UI_PADDING)), + vec2(builder->width, 1.0)), + RGB(128, 128, 128))); + PUSH_UI(ui_Button_New( + ui_BoxBuilder_At(builder, 7, 1), + "Proceed to Next Level", + &_ui_Action_EndIntermission, 0)) + + + p = sys->parts[ui_Leaderboards]; + vector_Clear(p); } diff --git a/ui_label.c b/ui_label.c new file mode 100644 index 0000000..acb0704 --- /dev/null +++ b/ui_label.c @@ -0,0 +1,58 @@ + +#include "types.h" +#include "ui.h" +#include + + +void _ui_Fill_Draw(System_UI *sys, ui_Part *part, uintptr_t user); // Defined in ui_render.cpp +void _ui_Label_Draw(System_UI *sys, ui_Part *part, uintptr_t user); // Defined in ui_render.cpp +void _ui_Label_Free(System_UI *sys, ui_Part *part, uintptr_t user) { + ui_Label *label = (ui_Label *)part->user; + if (label->label) + free(label->label); +} + +ui_Part ui_Label_New(Box2 box, const char *label_text, int alignment) { + ui_Label *label = zero_malloc(sizeof(ui_Label)); + if (label_text && strlen(label_text) > 0) + label->label = copy_malloc(label_text); + label->align = alignment; + + ui_Part part = { + .box = box, + .progress = .0f, + .hovered = false, + .user = (uintptr_t)label, + .draw = &_ui_Label_Draw, + .update = NULL, + .free = &_ui_Label_Free}; + return part; +} +void ui_Label_SetLabel(ui_Part *part, const char *label_text) { + ui_Label *label = (ui_Label *)part->user; + size_t len; + if (label_text && (len = strlen(label_text)) > 0) { + if (label->label) { + label->label = realloc(label->label, len + 1); + memcpy(label->label, label_text, len + 1); + } else + label->label = copy_malloc(label_text); + } else { + if (label->label) { + free(label->label); + label->label = NULL; + } + } +} + +ui_Part ui_Fill_New(Box2 box, uint32_t color) { + ui_Part part = { + .box = box, + .progress = .0f, + .hovered = false, + .user = (uintptr_t)color, + .draw = &_ui_Fill_Draw, + .update = NULL, + .free = NULL}; + return part; +} diff --git a/ui_render.cpp b/ui_render.cpp index 39add37..c3fbc19 100644 --- a/ui_render.cpp +++ b/ui_render.cpp @@ -34,7 +34,8 @@ extern "C" void ui_Render(System_UI *sys) { if (sys->bg.size.x > sys->bg_target.size.x * 0.9) for (int i = 0; i < vector_Size(sys->parts[layer]); i++) { ui_Part *part = (ui_Part *)vector_At(sys->parts[layer], i); - part->draw(sys, part, part->user); + if (part->draw) + part->draw(sys, part, part->user); } } @@ -66,14 +67,46 @@ extern "C" void _ui_Button_Draw(System_UI *sys, ui_Part *part, void *user) { setfillcolor(color_final); settextcolor(RGB(255, 255, 255)); - Box2 padded_box = part->box; - padded_box.lefttop.x += UI_PADDING; - padded_box.size.x -= 2 * UI_PADDING; - solidrectangle( (int)round(part->box.lefttop.x), (int)round(part->box.lefttop.y), (int)round(part->box.lefttop.x + part->box.size.x), (int)round(part->box.lefttop.y + part->box.size.y)); - render_DrawTextEx(b->label, padded_box, (b->left_aligned ? DT_LEFT : DT_CENTER) | DT_VCENTER | DT_SINGLELINE); + if (b->left_aligned) { + Box2 padded_box = part->box; + padded_box.lefttop.x += UI_PADDING; + padded_box.size.x -= 2 * UI_PADDING; + + render_DrawTextEx(b->label, padded_box, DT_LEFT | DT_VCENTER | DT_SINGLELINE); + } else + render_DrawTextEx(b->label, part->box, DT_CENTER | DT_VCENTER | DT_SINGLELINE); +} + +extern "C" void _ui_Label_Draw(System_UI *sys, ui_Part *part, uintptr_t user) { + ui_Label *label = (ui_Label *)user; + if (!label->label || strlen(label->label) == 0) + return; + + Box2 padded_box = part->box; + padded_box.lefttop.x += UI_PADDING; + padded_box.size.x -= 2 * UI_PADDING; + + setbkcolor(0); + settextcolor(RGB(255, 255, 255)); + + if (label->align < 0) + render_DrawTextEx(label->label, padded_box, DT_LEFT | DT_VCENTER | DT_SINGLELINE); + else if (label->align > 0) + render_DrawTextEx(label->label, padded_box, DT_RIGHT | DT_VCENTER | DT_SINGLELINE); + else // align == 0 + render_DrawTextEx(label->label, padded_box, DT_CENTER | DT_VCENTER | DT_SINGLELINE); +} + +extern "C" void _ui_Fill_Draw(System_UI *sys, ui_Part *part, uintptr_t user) { + setfillcolor(part->user); + solidrectangle( + (int)round(part->box.lefttop.x), + (int)round(part->box.lefttop.y), + (int)round(part->box.lefttop.x + part->box.size.x), + (int)round(part->box.lefttop.y + part->box.size.y)); }