From 4152fbecb6ee8360575aa4c24e9cedf822f159dc Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Wed, 25 Mar 2026 19:59:14 -0700 Subject: Implement vertical and horizontal layouts. Use widget position properly when rendering. Toolbar, buttons and edit bars WIP --- src/widget/button.c | 9 +++-- src/widget/frame.c | 9 ++++- src/widget/label.c | 13 +++---- src/widget/layout.c | 25 ++++++++++++ src/widget/table.c | 13 ++++--- src/widget/widget.c | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++- src/widget/widget.h | 14 ++++++- 7 files changed, 172 insertions(+), 20 deletions(-) create mode 100644 src/widget/layout.c (limited to 'src/widget') diff --git a/src/widget/button.c b/src/widget/button.c index f2313fd..d8de266 100644 --- a/src/widget/button.c +++ b/src/widget/button.c @@ -2,18 +2,21 @@ #include "widget.h" -uiButton* uiMakeButton(const char* text) { +uiButton* uiMakeButton(uiPtr parent, const char* text, const uiParams* params) { assert(text); + assert(params); uiButton* button = UI_NEW(uiButton); *button = (uiButton){ .widget = (uiWidget){ - .type = uiTypeButton, - .rect = {0}, + .type = uiTypeButton, + .rect = {0}, + .stretch = params->stretch, }, .text = string_new(text), }; + WidgetSetParent(uiMakeButtonPtr(button), parent); return button; } diff --git a/src/widget/frame.c b/src/widget/frame.c index e1078be..7640e42 100644 --- a/src/widget/frame.c +++ b/src/widget/frame.c @@ -3,8 +3,13 @@ #include "widget.h" uiFrame* uiMakeFrame(void) { - uiFrame* frame = UI_NEW(uiFrame); - frame->widget.type = uiTypeFrame; + uiFrame* frame = UI_NEW(uiFrame); + + *frame = (uiFrame){ + .widget = + (uiWidget){.type = uiTypeFrame, .stretch = uiStretchX | uiStretchY} + }; + return frame; } diff --git a/src/widget/label.c b/src/widget/label.c index 30ca0ec..5c0c00a 100644 --- a/src/widget/label.c +++ b/src/widget/label.c @@ -3,22 +3,21 @@ #include "uiLibrary.h" #include "widget.h" -uiLabel* uiMakeLabel(const char* text) { +uiLabel* uiMakeLabel(uiPtr parent, const char* text) { assert(text); uiLabel* label = UI_NEW(uiLabel); *label = (uiLabel){ .widget = - (uiWidget){ - .type = uiTypeLabel, + (uiWidget){.type = uiTypeLabel, .rect = - (uiRect){ - .width = - (int)strlen(text) * g_ui.font->header.glyph_width, - .height = g_ui.font->header.glyph_height}}, + (uiRect){.width = (int)strlen(text) * + g_ui.font->header.glyph_width, + .height = g_ui.font->header.glyph_height}}, .text = string_new(text), }; + WidgetSetParent(uiMakeLabelPtr(label), parent); return label; } diff --git a/src/widget/layout.c b/src/widget/layout.c new file mode 100644 index 0000000..c529c56 --- /dev/null +++ b/src/widget/layout.c @@ -0,0 +1,25 @@ +#include "widget.h" + +static uiStretch StretchFromDirection(uiLayoutDirection direction) { + switch (direction) { + case uiHorizontal: + return uiStretchX; + case uiVertical: + return uiStretchY; + } + assert(false); + return uiStretchNone; +} + +uiLayout* uiMakeLayout(uiPtr parent, uiLayoutDirection direction) { + uiLayout* layout = UI_NEW(uiLayout); + + *layout = (uiLayout){ + .widget = (uiWidget){.type = uiTypeLayout, + .stretch = StretchFromDirection(direction)}, + .direction = direction, + }; + WidgetSetParent(uiMakeLayoutPtr(layout), parent); + + return layout; +} diff --git a/src/widget/table.c b/src/widget/table.c index e7d412e..d9a6440 100644 --- a/src/widget/table.c +++ b/src/widget/table.c @@ -3,11 +3,12 @@ #define Min(a, b) ((a) < (b) ? (a) : (b)) #define Max(a, b) ((a) > (b) ? (a) : (b)) -uiTable* uiMakeTable(int rows, int cols, const char** header) { +uiTable* uiMakeTable(uiPtr parent, int rows, int cols, const char** header) { uiTable* table = UI_NEW(uiTable); *table = (uiTable){ - .widget = (uiWidget){.type = uiTypeTable}, + .widget = + (uiWidget){.type = uiTypeTable, .stretch = (uiStretchX | uiStretchY)}, .rows = rows, .cols = cols, .widths = (cols > 0) ? calloc(cols, sizeof(int)) : 0, @@ -15,6 +16,7 @@ uiTable* uiMakeTable(int rows, int cols, const char** header) { .cells = (rows * cols > 0) ? calloc(rows, sizeof(uiCell*)) : 0, .flags = {0}, }; + WidgetSetParent(uiMakeTablePtr(table), parent); if (header) { for (int col = 0; col < cols; ++col) { @@ -91,13 +93,14 @@ void SyncScrollbarToTable(uiTable* table) { assert(table); ScrollbarScroll( &table->scrollbar, (int)((double)table->offset / (double)table->rows * - (double)table->height)); + (double)table->widget.rect.height)); } void SyncTableToScrollbar(uiTable* table) { assert(table); - table->offset = (int)((double)table->scrollbar.handle_y / - (double)table->height * (double)table->rows); + table->offset = + (int)((double)table->scrollbar.handle_y / + (double)table->widget.rect.height * (double)table->rows); } const uiCell* TableGetCell(const uiTable* table, int row, int col) { diff --git a/src/widget/widget.c b/src/widget/widget.c index ebcaf10..2c525cc 100644 --- a/src/widget/widget.c +++ b/src/widget/widget.c @@ -2,6 +2,8 @@ #include +#include + // ----------------------------------------------------------------------------- // Widget. @@ -30,14 +32,19 @@ void DestroyWidget(uiWidget** ppWidget) { UI_DEL(ppWidget); } -void uiWidgetSetParent(uiPtr child_, uiPtr parent_) { +void WidgetSetParent(uiPtr child_, uiPtr parent_) { uiWidget* child = child_.widget; uiWidget* parent = parent_.widget; assert(child); assert(parent); - list_add(parent->children, child); + if (!uiIsNullptr(child->parent)) { + list_remove(child->parent.widget->children, child); + } + + list_push(parent->children, child); + child->parent = parent_; } // ----------------------------------------------------------------------------- @@ -58,6 +65,11 @@ uiPtr uiMakeLabelPtr(uiLabel* label) { return (uiPtr){.type = uiTypeLabel, .label = label}; } +uiPtr uiMakeLayoutPtr(uiLayout* layout) { + assert(layout); + return (uiPtr){.type = uiTypeLayout, .layout = layout}; +} + uiPtr uiMakeTablePtr(uiTable* table) { assert(table); return (uiPtr){.type = uiTypeTable, .table = table}; @@ -72,6 +84,8 @@ uiPtr uiMakeWidgetPtr(uiWidget* widget) { return uiMakeFramePtr((uiFrame*)widget); case uiTypeLabel: return uiMakeLabelPtr((uiLabel*)widget); + case uiTypeLayout: + return uiMakeLayoutPtr((uiLayout*)widget); case uiTypeTable: return uiMakeTablePtr((uiTable*)widget); default: @@ -103,8 +117,99 @@ uiLabel* uiGetLabelPtr(uiPtr ptr) { return ptr.label; } +uiLayout* uiGetLayoutPtr(uiPtr ptr) { + assert(ptr.type == uiTypeLayout); + assert(ptr.layout); + return ptr.layout; +} + +uiEdit* uiGetEditPtr(uiPtr ptr) { + assert(ptr.type == uiTypeEdit); + assert(ptr.edit); + return ptr.edit; +} + uiTable* uiGetTablePtr(uiPtr ptr) { assert(ptr.type == uiTypeTable); assert(ptr.table); return ptr.table; } + +typedef struct PrintState { + mstring pad; + mstring rect; +} PrintState; + +static void RectToString(uiRect rect, mstring* out) { + assert(out); + out->length = snprintf( + out->str, sizeof(out->str), "rect{(x:%d, y:%d), (w:%d, h:%d)", rect.x, + rect.y, rect.width, rect.height); +} + +static void uiPrintRec(uiPtr ptr, PrintState* state) { + if (uiIsNullptr(ptr)) { + return; + } + RectToString(ptr.widget->rect, &state->rect); + switch (ptr.type) { + case uiTypeButton: { + const uiButton* button = uiGetButtonPtr(ptr); + printf( + "%sbutton{rect=%s, text=\"%s\"}\n", mstring_cstr(&state->pad), + mstring_cstr(&state->rect), string_cstr(&button->text)); + break; + } + case uiTypeLabel: { + const uiLabel* label = uiGetLabelPtr(ptr); + printf( + "%sbutton{rect=%s, text=\"%s\"}\n", mstring_cstr(&state->pad), + mstring_cstr(&state->rect), string_cstr(&label->text)); + break; + } + case uiTypeLayout: { + const uiLayout* layout = uiGetLayoutPtr(ptr); + const char* direction = ""; + switch (layout->direction) { + case uiHorizontal: + direction = "horizontal"; + break; + case uiVertical: + direction = "vertical"; + break; + } + printf( + "%s%s_layout{rect=%s}\n", mstring_cstr(&state->pad), direction, + mstring_cstr(&state->rect)); + break; + } + case uiTypeFrame: { + printf( + "%sframe{rect=%s}\n", mstring_cstr(&state->pad), + mstring_cstr(&state->rect)); + break; + } + case uiTypeTable: { + const uiTable* table = uiGetTablePtr(ptr); + printf( + "%stable{rect=%s}\n", mstring_cstr(&state->pad), + mstring_cstr(&state->rect)); + break; + } + default: + printf("%swidget\n", mstring_cstr(&state->pad)); + break; + } + const mstring pad = state->pad; + state->pad = mstring_concat(state->pad, mstring_make(" ")); + list_foreach(ptr.widget->children, child, { + uiPrintRec(uiMakeWidgetPtr(child), state); + }); + state->pad = pad; +} + +void uiPrint(uiPtr ptr) { + PrintState state = + (PrintState){.pad = mstring_make_empty(), .rect = mstring_make_empty()}; + uiPrintRec(ptr, &state); +} diff --git a/src/widget/widget.h b/src/widget/widget.h index db11164..7482d38 100644 --- a/src/widget/widget.h +++ b/src/widget/widget.h @@ -13,9 +13,16 @@ DEF_LIST(Widget, uiWidget*) typedef struct uiWidget { uiWidgetType type; uiRect rect; + uiStretch stretch; + uiPtr parent; Widget_list children; } uiWidget; +typedef struct uiLayout { + uiWidget widget; + uiLayoutDirection direction; +} uiLayout; + typedef struct uiButton { uiWidget widget; string text; @@ -30,6 +37,11 @@ typedef struct uiLabel { string text; } uiLabel; +typedef struct uiEdit { + uiWidget widget; + string text; +} uiEdit; + typedef struct uiScrollbar { int width; int height; // Total height: handle plus scrollable area. @@ -45,7 +57,6 @@ typedef struct uiTable { uiWidget widget; int rows; int cols; - int height; // Height in pixels. int* widths; // Width, in pixels, for each column. uiCell* header; // If non-null, row of 'cols' header cells. uiCell** cells; // Array of 'rows' rows, each of 'cols' cells. @@ -57,6 +68,7 @@ typedef struct uiTable { } flags; } uiTable; +void WidgetSetParent(uiPtr child, uiPtr parent); void DestroyWidget(uiWidget** ppWidget); /// Set the scrollbar handle's y-coordinate, which is clipped to the scrollbar's -- cgit v1.2.3