More UI features & leaderboards for level time

This commit is contained in:
Edgaru089 2024-04-30 06:57:24 +08:00
parent 437ad8c4b0
commit 36ba0b1c9f
13 changed files with 395 additions and 29 deletions

2
app.c
View File

@ -37,6 +37,7 @@ App *app_NewApp() {
ui_RebuildUI(app->ui); ui_RebuildUI(app->ui);
ui_PushState(app->ui, ui_TitleMenu); ui_PushState(app->ui, ui_TitleMenu);
app->current_level = NULL;
app->switch_level = NULL; app->switch_level = NULL;
app->timescale = 1.0; app->timescale = 1.0;
app->clear_color = RGB(40, 40, 40); app->clear_color = RGB(40, 40, 40);
@ -83,6 +84,7 @@ void app_Advance(App *app, Duration deltaTime) {
physics_Advance(app->physics, delta_game); physics_Advance(app->physics, delta_game);
entity_Advance(app->entity, delta_game); entity_Advance(app->entity, delta_game);
camera_Advance(app->camera, delta_game); camera_Advance(app->camera, delta_game);
app->level_playtime.microseconds += deltaTime.microseconds;
} }
ui_Advance(app->ui, deltaTime); ui_Advance(app->ui, deltaTime);

2
app.h
View File

@ -28,7 +28,9 @@ typedef struct _App {
System_GameTime *time; System_GameTime *time;
System_UI *ui; System_UI *ui;
char *current_level;
char *switch_level; char *switch_level;
Duration level_playtime;
double timescale; double timescale;
uint32_t clear_color; uint32_t clear_color;
bool wantQuit, debugOn; bool wantQuit, debugOn;

View File

@ -47,7 +47,7 @@ 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\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); PUSH_STRING(buf);
for (int i = 0; i < vector_Size(app->ui->state); i++) { for (int i = 0; i < vector_Size(app->ui->state); i++) {

View File

@ -238,6 +238,7 @@ void _app_SwitchLevel(App *app) {
entity_Clear(app->entity); entity_Clear(app->entity);
particle_Clear(app->particle); particle_Clear(app->particle);
app->camera->target = NULL; app->camera->target = NULL;
app->level_playtime.microseconds = 0;
// Read every line // Read every line
char linebuf[512]; char linebuf[512];
@ -253,7 +254,9 @@ void _app_SwitchLevel(App *app) {
} }
free(app->switch_level); if (app->current_level)
free(app->current_level);
app->current_level = app->switch_level;
app->switch_level = NULL; app->switch_level = NULL;
fclose(f); fclose(f);
} }

View File

@ -6,6 +6,7 @@
#include "player.h" #include "player.h"
#include "render_util.h" #include "render_util.h"
#include "types.h" #include "types.h"
#include "ui.h"
#include "util/assert.h" #include "util/assert.h"
#include "util/rand.h" #include "util/rand.h"
#include <stdio.h> #include <stdio.h>
@ -171,7 +172,7 @@ void misc_thinker_ChangeLevel(App *app, Entity *e, Duration deltaTime) {
Box2 worldbox = ABSOLUTE_BOX(e, e->misc->trigger); Box2 worldbox = ABSOLUTE_BOX(e, e->misc->trigger);
if (box2_Intersects(worldbox, playerbox, NULL)) if (box2_Intersects(worldbox, playerbox, NULL))
app_QueueLoadLevel(app, e->misc->change_level); ui_EnterIntermission(app->ui, e->misc->change_level);
} }

View File

@ -107,6 +107,14 @@ Box2 box2_FromCenter(Vec2 center, Vec2 size) {
.y = center.y - size.y / 2.0}}; .y = center.y - size.y / 2.0}};
return box; 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) { Box2 box2_Offset(Box2 box, Vec2 offset) {
box.lefttop = vec2_Add(box.lefttop, offset); box.lefttop = vec2_Add(box.lefttop, offset);

View File

@ -70,6 +70,12 @@ static inline Box2 box2(double x, double y, double width, double height) {
b.size.y = height; b.size.y = height;
return b; return b;
} }
static inline Box2 box2v(Vec2 lefttop, Vec2 size) {
Box2 b;
b.lefttop = lefttop;
b.size = size;
return b;
}
// Intersection test. // Intersection test.
@ -79,6 +85,7 @@ bool box2_Contains(const Box2 box, const Vec2 point);
Vec2 box2_Center(Box2 box); Vec2 box2_Center(Box2 box);
Box2 box2_FromCenter(Vec2 center, Vec2 size); 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_Offset(Box2 box, Vec2 offset);
Box2 box2_OffsetX(Box2 box, double offsetX); Box2 box2_OffsetX(Box2 box, double offsetX);

6
ui.c
View File

@ -12,7 +12,9 @@ const char *ui_StateTitle[ui_StateCount] = {
"Pause Menu", "Pause Menu",
"Jack's Escape (ver. 3.141592) - Title", "Jack's Escape (ver. 3.141592) - Title",
"Select Level", "Select Level",
"Options"}; "Options",
"Level Finished",
"Leaderboards"};
static inline double dabs(double x) { return x > 0 ? x : -x; } static inline double dabs(double x) { return x > 0 ? x : -x; }
@ -32,6 +34,7 @@ void ui_DeleteSystem(System_UI *sys) {
for (int i = 0; i < ui_StateCount; i++) for (int i = 0; i < ui_StateCount; i++)
for (int j = 0; j < vector_Size(sys->parts[i]); j++) { for (int j = 0; j < vector_Size(sys->parts[i]); j++) {
ui_Part *part = (ui_Part *)vector_At(sys->parts[i], j); ui_Part *part = (ui_Part *)vector_At(sys->parts[i], j);
if (part->free)
part->free(sys, part, part->user); part->free(sys, part, part->user);
} }
for (int i = 0; i < ui_StateCount; i++) for (int i = 0; i < ui_StateCount; i++)
@ -102,6 +105,7 @@ void ui_Advance(System_UI *sys, Duration deltaTime) {
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);
} }
if (part->update)
part->update(sys, part, part->user, deltaTime); part->update(sys, part, part->user, deltaTime);
} }
} }

50
ui.h
View File

@ -17,6 +17,8 @@ typedef enum {
ui_TitleMenu, ui_TitleMenu,
ui_SceneSelect, ui_SceneSelect,
ui_Options, ui_Options,
ui_InterMission,
ui_Leaderboards,
ui_StateCount // Keep at last ui_StateCount // Keep at last
} ui_State; } ui_State;
@ -34,7 +36,7 @@ typedef struct _ui_Part {
uintptr_t user; // user data 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 (*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 (*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_Part;
// UI system with its own state machine. // UI system with its own state machine.
@ -59,10 +61,15 @@ void ui_Render(System_UI *sys);
void ui_PushState(System_UI *sys, ui_State push_state); void ui_PushState(System_UI *sys, ui_State push_state);
void ui_PopState(System_UI *sys); void ui_PopState(System_UI *sys);
ui_State ui_CurrentState(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_PADDING (16)
#define UI_BACKGROUND (0) #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); // 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 #ifdef __cplusplus
} }
#endif #endif

99
ui_boxbuilder.c Normal file
View File

@ -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);
}

View File

@ -1,4 +1,5 @@
#include "render_util.h"
#include "ui.h" #include "ui.h"
#include "app.h" #include "app.h"
#include "util/vector.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) { 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) { while (vector_Size(sys->state) > 0 && ui_CurrentState(sys) != ui_TitleMenu) {
INFO("popping \"%s\"", ui_StateTitle[ui_CurrentState(sys)]); INFO("popping \"%s\"", ui_StateTitle[ui_CurrentState(sys)]);
ui_Action_PopState(sys, part, data); ui_Action_PopState(sys, part, 0);
} }
sys->super->paused = false; sys->super->paused = false;
if (vector_Size(sys->state) == 0) 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; 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" <level_time> "Record Time" <record_time> <seperator> <replay_level> <return_to_title> <leaderboards> <sep> <next_level>
}
#define PUSH_UI(part) \ #define PUSH_UI(part) \
_p0 = (part); \ _p0 = (part); \
@ -105,19 +142,23 @@ void ui_RebuildUI(System_UI *sys) {
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, 40)), box2_FromCenter(vec2(SCREEN_WIDTH / 2.0, 250), vec2(400, 40)),
"Resume Game", "Resume Game",
&_ui_Action_ResumeGame, 0)) &_ui_Action_ResumeGame, 0))
PUSH_UI(ui_Button_New( 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", "Reselect Level",
&ui_Action_PushState, (uintptr_t)ui_SceneSelect)) &ui_Action_PushState, (uintptr_t)ui_SceneSelect))
PUSH_UI(ui_Button_New( 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", "Options",
&ui_Action_PushState, (uintptr_t)ui_Options)) &ui_Action_PushState, (uintptr_t)ui_Options))
PUSH_UI(ui_Button_New( 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", "Return to Title",
&_ui_Action_ReturnToTitle, 0)) &_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)), box2_FromCenter(vec2(SCREEN_WIDTH / 2.0, 300), vec2(600, 50)),
"Return", "Return",
&ui_Action_PopState, 0)) &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);
} }

58
ui_label.c Normal file
View File

@ -0,0 +1,58 @@
#include "types.h"
#include "ui.h"
#include <string.h>
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;
}

View File

@ -34,6 +34,7 @@ extern "C" void ui_Render(System_UI *sys) {
if (sys->bg.size.x > sys->bg_target.size.x * 0.9) if (sys->bg.size.x > sys->bg_target.size.x * 0.9)
for (int i = 0; i < vector_Size(sys->parts[layer]); i++) { for (int i = 0; i < vector_Size(sys->parts[layer]); i++) {
ui_Part *part = (ui_Part *)vector_At(sys->parts[layer], i); ui_Part *part = (ui_Part *)vector_At(sys->parts[layer], i);
if (part->draw)
part->draw(sys, part, part->user); 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); 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, 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));
} }