Skip to content

Commit 9f2ac21

Browse files
committed
updated lib and doc
1 parent a9cac8f commit 9f2ac21

File tree

8 files changed

+111
-35
lines changed

8 files changed

+111
-35
lines changed

README.md

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,22 @@ It’s designed for games and lightweight applications that need a clean, polish
2525
leanUI provides a small, cohesive set of widgets:
2626

2727
- **Buttons** — with hover and press animations
28+
29+
![buttons](docs/buttons.png)
30+
2831
- **Labels / value fields** — for clean key–value layouts
2932
- **Toggles** — animated switches inspired by iOS
3033

31-
![animated toggle](docs/toggle.png)
34+
![toggle](docs/toggle.png)
3235

3336
- **Segmented controls** — for mode switching
37+
38+
![segmented](docs/segmented.png)
39+
3440
- **Sliders**
3541

42+
![slider](docs/slider.png)
43+
3644
Each widget is built with consistent spacing, alignment, and interaction patterns.
3745
The goal is not to cover everything, but to make a minimal set look and feel right out of the box.
3846

@@ -61,7 +69,29 @@ Very simple and effective (no rows, no columns)
6169
```c
6270
#include "lean_ui.h"
6371

64-
void frame(float delta_time)
72+
void frame(ui_context* ctx, float delta_time)
6573
{
66-
74+
ui_begin_frame(ctx, 1.f/60.f);
75+
ui_begin_window(ctx, "Alright let's play!", 800, 100, 600, 1200, window_resizable);
76+
77+
const char* list[] = {"One", "Two", "Three", "Four"};
78+
static uint32_t selected = 2;
79+
ui_segmented(ctx, list, 4, &selected);
80+
81+
ui_separator(ctx);
82+
83+
static float quantity = 5.f;
84+
ui_slider(ctx, "Distance", 0.f, 100.f, 1.f, &quantity, "%3.2fkm");
85+
86+
if (ui_button(ctx, "Left Button", align_left))
87+
{
88+
// process
89+
}
90+
91+
ui_end_window(ctx);
92+
ui_end_frame(ctx);
6793
}
94+
95+
96+
```
97+

docs/buttons.png

239 KB
Loading

docs/example.png

-447 Bytes
Loading

docs/segmented.png

105 KB
Loading

docs/slider.png

153 KB
Loading

lean_ui.c

Lines changed: 57 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,6 @@
1414
// Structures
1515
//-----------------------------------------------------------------------------------------------------------------------------
1616

17-
typedef struct
18-
{
19-
float x, y, width, height;
20-
} ui_rect;
21-
2217
typedef struct {float x, y;} ui_vec2;
2318

2419
typedef struct
@@ -27,6 +22,9 @@ typedef struct
2722
uint32_t id;
2823
ui_vec2 pos;
2924
float width, height;
25+
float min_width, min_height;
26+
uint32_t options;
27+
bool closed;
3028
} ui_window;
3129

3230
typedef struct
@@ -69,6 +67,7 @@ struct ui_context
6967
ui_window windows[MAX_WINDOWS];
7068
uint32_t num_windows;
7169
ui_window* current_window;
70+
ui_window* resizing_window;
7271
void* dragging_object;
7372
ui_animation animation;
7473
ui_hover hover;
@@ -77,7 +76,7 @@ struct ui_context
7776
float font_height;
7877
float row_height;
7978
float padding;
80-
float corners_radius;
79+
float corner;
8180
ui_colors colors;
8281
ui_renderer_fnc_t renderer;
8382
char string_buffer[STRING_BUFFER_SIZE];
@@ -88,6 +87,7 @@ struct ui_context
8887
//-----------------------------------------------------------------------------------------------------------------------------
8988

9089
static inline ui_vec2 ui_vec2_sub(ui_vec2 a, ui_vec2 b) {return (ui_vec2){a.x - b.x, a.y - b.y};}
90+
static inline ui_vec2 ui_vec2_add(ui_vec2 a, ui_vec2 b) {return (ui_vec2){a.x + b.x, a.y + b.y};}
9191
static inline bool in_rect(const ui_rect *rect, ui_vec2 pos)
9292
{
9393
return ((pos.x>=rect->x) && (pos.x<=rect->x+rect->width) && (pos.y>=rect->y) && (pos.y<=rect->y+rect->height));
@@ -164,6 +164,8 @@ ui_context* ui_init(const ui_def* def)
164164
{
165165
assert(def->renderer_callbacks.draw_box && def->renderer_callbacks.draw_text &&
166166
def->renderer_callbacks.set_clip_rect && def->renderer_callbacks.text_width);
167+
168+
assert(((uintptr_t)def->preallocated_buffer)%sizeof(uintptr_t) == 0);
167169

168170
ui_context* ctx = (ui_context*)def->preallocated_buffer;
169171
*ctx = (ui_context)
@@ -172,7 +174,7 @@ ui_context* ui_init(const ui_def* def)
172174
.font_height = def->font_height,
173175
.renderer = def->renderer_callbacks,
174176
.padding = fmaxf(def->font_height/4.f, 2.f),
175-
.corners_radius = fmaxf(def->font_height/2.f, 2.f),
177+
.corner = fmaxf(def->font_height/2.f, 2.f),
176178
.colors =
177179
{
178180
.window_bg = 0xFFF7F0E9,
@@ -215,7 +217,7 @@ void ui_begin_frame(ui_context* ctx, float delta_time)
215217
}
216218

217219
//-----------------------------------------------------------------------------------------------------------------------------
218-
void ui_begin_window(ui_context* ctx, const char* name, float x, float y, float width, float height)
220+
void ui_begin_window(ui_context* ctx, const char* name, float x, float y, float width, float height, uint32_t options)
219221
{
220222
assert(ctx->current_window == NULL);
221223

@@ -236,32 +238,49 @@ void ui_begin_window(ui_context* ctx, const char* name, float x, float y, float
236238
.name = name,
237239
.id = id,
238240
.pos = {.x = x, .y = y},
239-
.width = fmaxf(width, ctx->renderer.text_width(name, ctx->renderer.user) + ctx->padding * 2.f),
240-
.height = fmaxf(height, ctx->font_height + ctx->padding * 2.f)
241+
.min_width = ctx->renderer.text_width(name, ctx->renderer.user) + ctx->padding * 2.f,
242+
.min_height = ctx->row_height * 2.f,
243+
.width = width,
244+
.height = height,
245+
.options = options,
246+
.closed = false
241247
};
242248
}
243249

244250
ui_window* w = ctx->current_window;
251+
252+
// resize
253+
ui_rect handle_rect = {w->pos.x + w->width - ctx->corner, w->pos.y + w->height - ctx->corner, ctx->corner, ctx->corner};
254+
if (ctx->mouse_button == button_pressed && in_rect(&handle_rect, ctx->mouse_pos) && (w->options&window_resizable))
255+
{
256+
ctx->resizing_window = w;
257+
ctx->dragging_offset = ui_vec2_sub(ui_vec2_add(w->pos, (ui_vec2) {w->width, w->height}), ctx->mouse_pos);
258+
}
259+
260+
if (ctx->mouse_down && ctx->resizing_window == w)
261+
{
262+
w->width = fmaxf(ctx->mouse_pos.x + ctx->dragging_offset.x - w->pos.x, w->min_width);
263+
w->height = fmaxf(ctx->mouse_pos.y + ctx->dragging_offset.y - w->pos.y, w->min_height);
264+
}
265+
245266
ui_rect title_rect = {w->pos.x+ctx->padding, w->pos.y+ctx->padding, w->width-ctx->padding*2.f, ctx->row_height};
246267

247268
// move the window if click on the title bar
248-
if (ctx->mouse_button == button_pressed && in_rect(&title_rect, ctx->mouse_pos))
269+
if (ctx->mouse_button == button_pressed && in_rect(&title_rect, ctx->mouse_pos) &&
270+
!(w->options&window_pinned) && ctx->resizing_window != w)
249271
{
250272
ctx->dragging_object = w;
251273
ctx->dragging_offset = ui_vec2_sub(ctx->mouse_pos, w->pos);
252274
}
253275

254-
if (ctx->mouse_button == button_released && ctx->dragging_object == w)
255-
ctx->dragging_object = NULL;
256-
257276
if (ctx->mouse_down && ctx->dragging_object == w)
258277
w->pos = ui_vec2_sub(ctx->mouse_pos, ctx->dragging_offset);
259278

260279
// border
261-
ctx->renderer.draw_box(w->pos.x, w->pos.y, w->width, w->height, ctx->corners_radius, ctx->colors.window_border, ctx->renderer.user);
280+
ctx->renderer.draw_box(w->pos.x, w->pos.y, w->width, w->height, ctx->corner, ctx->colors.window_border, ctx->renderer.user);
262281

263282
// title bg
264-
ctx->renderer.draw_box(title_rect.x, title_rect.y, title_rect.width, title_rect.height, ctx->corners_radius, ctx->colors.title_bg, ctx->renderer.user);
283+
ctx->renderer.draw_box(title_rect.x, title_rect.y, title_rect.width, title_rect.height, ctx->corner, ctx->colors.title_bg, ctx->renderer.user);
265284

266285
ctx->layout = (ui_rect)
267286
{
@@ -281,6 +300,10 @@ void ui_begin_window(ui_context* ctx, const char* name, float x, float y, float
281300
// title
282301
draw_align_text(ctx, &title_rect, w->name, ctx->colors.title_text, align_center);
283302

303+
// draw resize handle
304+
if (w->options&window_resizable)
305+
ctx->renderer.draw_box(handle_rect.x, handle_rect.y, handle_rect.width, handle_rect.height, 0.f, ctx->colors.separator, ctx->renderer.user);
306+
284307
// clip rect
285308
uint16_t clip_minx = (uint16_t) fmaxf(ctx->layout.x, 0.f);
286309
uint16_t clip_miny = (uint16_t) fmaxf(ctx->layout.y, 0.f);
@@ -413,7 +436,7 @@ void ui_segmented(ui_context* ctx, const char** entries, uint32_t num_entries, u
413436
};
414437

415438
ctx->renderer.draw_box(seg_rect.x, seg_rect.y + ctx->padding, ctx->layout.width, seg_rect.height,
416-
ctx->corners_radius, ctx->colors.widget_bg, ctx->renderer.user);
439+
ctx->corner, ctx->colors.widget_bg, ctx->renderer.user);
417440

418441
if (*selected < num_entries)
419442
{
@@ -528,9 +551,6 @@ void ui_slider(ui_context* ctx, const char* label, float min_value, float max_va
528551
ctx->dragging_object = NULL;
529552
}
530553

531-
if (ctx->mouse_button == button_released && ctx->dragging_object == value)
532-
ctx->dragging_object = NULL;
533-
534554
// click-on-track update
535555
if (ctx->animation.widget == value)
536556
thumb_x = lerp_float(ctx->animation.value_key0, ctx->animation.value_key1, ctx->animation.t);
@@ -595,22 +615,29 @@ bool ui_button(ui_context* ctx, const char* label, enum ui_text_alignment alignm
595615

596616
// border
597617
ctx->renderer.draw_box(button_rect.x, button_rect.y, button_rect.width, button_rect.height,
598-
ctx->corners_radius, ctx->colors.separator, ctx->renderer.user);
618+
ctx->corner, ctx->colors.separator, ctx->renderer.user);
599619

600620
expand_rect(&button_rect, -1.f);
601621

602622
ctx->renderer.draw_box(button_rect.x, button_rect.y, button_rect.width, button_rect.height,
603-
ctx->corners_radius, button_color, ctx->renderer.user);
623+
ctx->corner, button_color, ctx->renderer.user);
604624

605625
ctx->renderer.draw_text(text_pos.x, text_pos.y, label, ctx->colors.text, ctx->renderer.user);
606626

607627
return clicked;
608628
}
609629

630+
//-----------------------------------------------------------------------------------------------------------------------------
631+
const ui_rect* ui_get_layout(const ui_context* ctx)
632+
{
633+
return &ctx->layout;
634+
}
635+
610636
//-----------------------------------------------------------------------------------------------------------------------------
611637
void ui_end_window(ui_context* ctx)
612638
{
613639
assert(ctx->current_window != NULL);
640+
ctx->current_window->min_height = ctx->layout.y - ctx->current_window->pos.y + ctx->row_height * 2.f;
614641
ctx->current_window = NULL;
615642
ctx->renderer.set_clip_rect(0, 0, UINT16_MAX, UINT16_MAX, ctx->renderer.user);
616643
}
@@ -620,12 +647,18 @@ void ui_end_frame(ui_context* ctx)
620647
{
621648
assert(ctx->current_window == NULL);
622649

623-
ctx->mouse_button = button_idle;
624-
625650
if (ctx->animation.t >= 1.f)
626651
{
627652
ctx->animation.widget = NULL;
628653
ctx->animation.t = 1.f;
629654
}
655+
656+
if (ctx->mouse_button == button_released)
657+
{
658+
ctx->resizing_window = NULL;
659+
ctx->dragging_object = NULL;
660+
}
661+
662+
ctx->mouse_button = button_idle;
630663
}
631664

lean_ui.h

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,24 +23,30 @@ enum ui_text_alignment
2323
align_right
2424
};
2525

26+
enum ui_window_option
27+
{
28+
window_pinned = 1<<0,
29+
window_resizable = 1<<1
30+
};
31+
2632
typedef struct
2733
{
2834
void (*draw_box)(float x, float y, float width, float height, float radius, uint32_t srgb_color, void* user);
29-
30-
// draw a text top-left aligned with x,y
31-
void (*draw_text)(float x, float y, const char* text, uint32_t srgb_color, void* user);
35+
void (*draw_text)(float x, float y, const char* text, uint32_t srgb_color, void* user); // draw a text top-left aligned with x,y
3236
void (*set_clip_rect)(uint16_t min_x, uint16_t min_y, uint16_t max_x, uint16_t max_y, void* user);
3337
float (*text_width)(const char* text, void* user);
3438
void* user;
3539
} ui_renderer_fnc_t;
3640

3741
typedef struct
3842
{
39-
void* preallocated_buffer;
43+
void* preallocated_buffer; // must be aligned on 8 bytes
4044
ui_renderer_fnc_t renderer_callbacks;
4145
float font_height;
4246
} ui_def;
4347

48+
typedef struct {float x, y, width, height;} ui_rect;
49+
4450
typedef struct ui_context ui_context;
4551

4652
//-----------------------------------------------------------------------------------------------------------------------------
@@ -78,7 +84,8 @@ void ui_begin_frame(ui_context* ctx, float delta_time);
7884
// Begins a new window
7985
// [name] unique name, hashed under the hood
8086
// [x, y, width, height] initial position and size in pixels can be changed by the user
81-
void ui_begin_window(ui_context* ctx, const char* name, float x, float y, float width, float height);
87+
// [options] combination of options from enum ui_window_option
88+
void ui_begin_window(ui_context* ctx, const char* name, float x, float y, float width, float height, uint32_t options);
8289

8390
//-----------------------------------------------------------------------------------------------------------------------------
8491
// Displays text according to the alignment
@@ -128,16 +135,20 @@ void ui_slider(ui_context* ctx, const char* label, float min_value, float max_va
128135
// returns true if the button was pressed this frame
129136
bool ui_button(ui_context* ctx, const char* label, enum ui_text_alignment alignment);
130137

138+
//-----------------------------------------------------------------------------------------------------------------------------
139+
// Returns the current layout rect, useful for custom rendering
140+
const ui_rect* ui_get_layout(const ui_context* ctx);
141+
131142
//-----------------------------------------------------------------------------------------------------------------------------
132143
// Ends the current window. Must match ui_begin_window()
133144
void ui_end_window(ui_context* ctx);
134145

135146
//-----------------------------------------------------------------------------------------------------------------------------
136-
//
147+
// Ends the current frame. Must match ui_begin_frame(). Until next frame no more calls to lean_ui.
137148
void ui_end_frame(ui_context* ctx);
138149

139150
#ifdef __cplusplus
140151
}
141152
#endif
142153

143-
#endif
154+
#endif

test/test.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ int main(void)
5151

5252
ui_update_mouse_button(ctx, button_pressed);
5353
ui_update_mouse_pos(ctx, 187.f, 847.f);
54-
ui_begin_window(ctx, "Alright let's play!", 800, 100, 600, 1200);
54+
ui_begin_frame(ctx, 1.f/60.f);
55+
ui_begin_window(ctx, "Alright let's play!", 800, 100, 600, 1200, window_resizable);
5556

5657
ui_text(ctx, align_left, "to the left");
5758
ui_text(ctx, align_right, "right");
@@ -86,6 +87,7 @@ int main(void)
8687
ui_button(ctx, "Right Button", align_right);
8788

8889
ui_end_window(ctx);
90+
ui_end_frame(ctx);
8991

9092
free(def.preallocated_buffer);
9193

0 commit comments

Comments
 (0)