diff options
| author | 3gg <3gg@shellblade.net> | 2026-03-25 19:59:14 -0700 |
|---|---|---|
| committer | 3gg <3gg@shellblade.net> | 2026-03-25 19:59:14 -0700 |
| commit | 4152fbecb6ee8360575aa4c24e9cedf822f159dc (patch) | |
| tree | 9e9b9db0216a37c5867d472a65289502c459691f /src/widget | |
| parent | 7778755c20e779554cd654ecdf7404d37b723fcc (diff) | |
Implement vertical and horizontal layouts. Use widget position properly when rendering. Toolbar, buttons and edit bars WIPmain
Diffstat (limited to 'src/widget')
| -rw-r--r-- | src/widget/button.c | 9 | ||||
| -rw-r--r-- | src/widget/frame.c | 9 | ||||
| -rw-r--r-- | src/widget/label.c | 13 | ||||
| -rw-r--r-- | src/widget/layout.c | 25 | ||||
| -rw-r--r-- | src/widget/table.c | 13 | ||||
| -rw-r--r-- | src/widget/widget.c | 109 | ||||
| -rw-r--r-- | src/widget/widget.h | 14 |
7 files changed, 172 insertions, 20 deletions
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 @@ | |||
| 2 | 2 | ||
| 3 | #include "widget.h" | 3 | #include "widget.h" |
| 4 | 4 | ||
| 5 | uiButton* uiMakeButton(const char* text) { | 5 | uiButton* uiMakeButton(uiPtr parent, const char* text, const uiParams* params) { |
| 6 | assert(text); | 6 | assert(text); |
| 7 | assert(params); | ||
| 7 | 8 | ||
| 8 | uiButton* button = UI_NEW(uiButton); | 9 | uiButton* button = UI_NEW(uiButton); |
| 9 | 10 | ||
| 10 | *button = (uiButton){ | 11 | *button = (uiButton){ |
| 11 | .widget = | 12 | .widget = |
| 12 | (uiWidget){ | 13 | (uiWidget){ |
| 13 | .type = uiTypeButton, | 14 | .type = uiTypeButton, |
| 14 | .rect = {0}, | 15 | .rect = {0}, |
| 16 | .stretch = params->stretch, | ||
| 15 | }, | 17 | }, |
| 16 | .text = string_new(text), | 18 | .text = string_new(text), |
| 17 | }; | 19 | }; |
| 20 | WidgetSetParent(uiMakeButtonPtr(button), parent); | ||
| 18 | return button; | 21 | return button; |
| 19 | } | 22 | } |
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 @@ | |||
| 3 | #include "widget.h" | 3 | #include "widget.h" |
| 4 | 4 | ||
| 5 | uiFrame* uiMakeFrame(void) { | 5 | uiFrame* uiMakeFrame(void) { |
| 6 | uiFrame* frame = UI_NEW(uiFrame); | 6 | uiFrame* frame = UI_NEW(uiFrame); |
| 7 | frame->widget.type = uiTypeFrame; | 7 | |
| 8 | *frame = (uiFrame){ | ||
| 9 | .widget = | ||
| 10 | (uiWidget){.type = uiTypeFrame, .stretch = uiStretchX | uiStretchY} | ||
| 11 | }; | ||
| 12 | |||
| 8 | return frame; | 13 | return frame; |
| 9 | } | 14 | } |
| 10 | 15 | ||
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 @@ | |||
| 3 | #include "uiLibrary.h" | 3 | #include "uiLibrary.h" |
| 4 | #include "widget.h" | 4 | #include "widget.h" |
| 5 | 5 | ||
| 6 | uiLabel* uiMakeLabel(const char* text) { | 6 | uiLabel* uiMakeLabel(uiPtr parent, const char* text) { |
| 7 | assert(text); | 7 | assert(text); |
| 8 | 8 | ||
| 9 | uiLabel* label = UI_NEW(uiLabel); | 9 | uiLabel* label = UI_NEW(uiLabel); |
| 10 | 10 | ||
| 11 | *label = (uiLabel){ | 11 | *label = (uiLabel){ |
| 12 | .widget = | 12 | .widget = |
| 13 | (uiWidget){ | 13 | (uiWidget){.type = uiTypeLabel, |
| 14 | .type = uiTypeLabel, | ||
| 15 | .rect = | 14 | .rect = |
| 16 | (uiRect){ | 15 | (uiRect){.width = (int)strlen(text) * |
| 17 | .width = | 16 | g_ui.font->header.glyph_width, |
| 18 | (int)strlen(text) * g_ui.font->header.glyph_width, | 17 | .height = g_ui.font->header.glyph_height}}, |
| 19 | .height = g_ui.font->header.glyph_height}}, | ||
| 20 | .text = string_new(text), | 18 | .text = string_new(text), |
| 21 | }; | 19 | }; |
| 20 | WidgetSetParent(uiMakeLabelPtr(label), parent); | ||
| 22 | return label; | 21 | return label; |
| 23 | } | 22 | } |
| 24 | 23 | ||
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 @@ | |||
| 1 | #include "widget.h" | ||
| 2 | |||
| 3 | static uiStretch StretchFromDirection(uiLayoutDirection direction) { | ||
| 4 | switch (direction) { | ||
| 5 | case uiHorizontal: | ||
| 6 | return uiStretchX; | ||
| 7 | case uiVertical: | ||
| 8 | return uiStretchY; | ||
| 9 | } | ||
| 10 | assert(false); | ||
| 11 | return uiStretchNone; | ||
| 12 | } | ||
| 13 | |||
| 14 | uiLayout* uiMakeLayout(uiPtr parent, uiLayoutDirection direction) { | ||
| 15 | uiLayout* layout = UI_NEW(uiLayout); | ||
| 16 | |||
| 17 | *layout = (uiLayout){ | ||
| 18 | .widget = (uiWidget){.type = uiTypeLayout, | ||
| 19 | .stretch = StretchFromDirection(direction)}, | ||
| 20 | .direction = direction, | ||
| 21 | }; | ||
| 22 | WidgetSetParent(uiMakeLayoutPtr(layout), parent); | ||
| 23 | |||
| 24 | return layout; | ||
| 25 | } | ||
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 @@ | |||
| 3 | #define Min(a, b) ((a) < (b) ? (a) : (b)) | 3 | #define Min(a, b) ((a) < (b) ? (a) : (b)) |
| 4 | #define Max(a, b) ((a) > (b) ? (a) : (b)) | 4 | #define Max(a, b) ((a) > (b) ? (a) : (b)) |
| 5 | 5 | ||
| 6 | uiTable* uiMakeTable(int rows, int cols, const char** header) { | 6 | uiTable* uiMakeTable(uiPtr parent, int rows, int cols, const char** header) { |
| 7 | uiTable* table = UI_NEW(uiTable); | 7 | uiTable* table = UI_NEW(uiTable); |
| 8 | 8 | ||
| 9 | *table = (uiTable){ | 9 | *table = (uiTable){ |
| 10 | .widget = (uiWidget){.type = uiTypeTable}, | 10 | .widget = |
| 11 | (uiWidget){.type = uiTypeTable, .stretch = (uiStretchX | uiStretchY)}, | ||
| 11 | .rows = rows, | 12 | .rows = rows, |
| 12 | .cols = cols, | 13 | .cols = cols, |
| 13 | .widths = (cols > 0) ? calloc(cols, sizeof(int)) : 0, | 14 | .widths = (cols > 0) ? calloc(cols, sizeof(int)) : 0, |
| @@ -15,6 +16,7 @@ uiTable* uiMakeTable(int rows, int cols, const char** header) { | |||
| 15 | .cells = (rows * cols > 0) ? calloc(rows, sizeof(uiCell*)) : 0, | 16 | .cells = (rows * cols > 0) ? calloc(rows, sizeof(uiCell*)) : 0, |
| 16 | .flags = {0}, | 17 | .flags = {0}, |
| 17 | }; | 18 | }; |
| 19 | WidgetSetParent(uiMakeTablePtr(table), parent); | ||
| 18 | 20 | ||
| 19 | if (header) { | 21 | if (header) { |
| 20 | for (int col = 0; col < cols; ++col) { | 22 | for (int col = 0; col < cols; ++col) { |
| @@ -91,13 +93,14 @@ void SyncScrollbarToTable(uiTable* table) { | |||
| 91 | assert(table); | 93 | assert(table); |
| 92 | ScrollbarScroll( | 94 | ScrollbarScroll( |
| 93 | &table->scrollbar, (int)((double)table->offset / (double)table->rows * | 95 | &table->scrollbar, (int)((double)table->offset / (double)table->rows * |
| 94 | (double)table->height)); | 96 | (double)table->widget.rect.height)); |
| 95 | } | 97 | } |
| 96 | 98 | ||
| 97 | void SyncTableToScrollbar(uiTable* table) { | 99 | void SyncTableToScrollbar(uiTable* table) { |
| 98 | assert(table); | 100 | assert(table); |
| 99 | table->offset = (int)((double)table->scrollbar.handle_y / | 101 | table->offset = |
| 100 | (double)table->height * (double)table->rows); | 102 | (int)((double)table->scrollbar.handle_y / |
| 103 | (double)table->widget.rect.height * (double)table->rows); | ||
| 101 | } | 104 | } |
| 102 | 105 | ||
| 103 | const uiCell* TableGetCell(const uiTable* table, int row, int col) { | 106 | 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 @@ | |||
| 2 | 2 | ||
| 3 | #include <cassert.h> | 3 | #include <cassert.h> |
| 4 | 4 | ||
| 5 | #include <stdio.h> | ||
| 6 | |||
| 5 | // ----------------------------------------------------------------------------- | 7 | // ----------------------------------------------------------------------------- |
| 6 | // Widget. | 8 | // Widget. |
| 7 | 9 | ||
| @@ -30,14 +32,19 @@ void DestroyWidget(uiWidget** ppWidget) { | |||
| 30 | UI_DEL(ppWidget); | 32 | UI_DEL(ppWidget); |
| 31 | } | 33 | } |
| 32 | 34 | ||
| 33 | void uiWidgetSetParent(uiPtr child_, uiPtr parent_) { | 35 | void WidgetSetParent(uiPtr child_, uiPtr parent_) { |
| 34 | uiWidget* child = child_.widget; | 36 | uiWidget* child = child_.widget; |
| 35 | uiWidget* parent = parent_.widget; | 37 | uiWidget* parent = parent_.widget; |
| 36 | 38 | ||
| 37 | assert(child); | 39 | assert(child); |
| 38 | assert(parent); | 40 | assert(parent); |
| 39 | 41 | ||
| 40 | list_add(parent->children, child); | 42 | if (!uiIsNullptr(child->parent)) { |
| 43 | list_remove(child->parent.widget->children, child); | ||
| 44 | } | ||
| 45 | |||
| 46 | list_push(parent->children, child); | ||
| 47 | child->parent = parent_; | ||
| 41 | } | 48 | } |
| 42 | 49 | ||
| 43 | // ----------------------------------------------------------------------------- | 50 | // ----------------------------------------------------------------------------- |
| @@ -58,6 +65,11 @@ uiPtr uiMakeLabelPtr(uiLabel* label) { | |||
| 58 | return (uiPtr){.type = uiTypeLabel, .label = label}; | 65 | return (uiPtr){.type = uiTypeLabel, .label = label}; |
| 59 | } | 66 | } |
| 60 | 67 | ||
| 68 | uiPtr uiMakeLayoutPtr(uiLayout* layout) { | ||
| 69 | assert(layout); | ||
| 70 | return (uiPtr){.type = uiTypeLayout, .layout = layout}; | ||
| 71 | } | ||
| 72 | |||
| 61 | uiPtr uiMakeTablePtr(uiTable* table) { | 73 | uiPtr uiMakeTablePtr(uiTable* table) { |
| 62 | assert(table); | 74 | assert(table); |
| 63 | return (uiPtr){.type = uiTypeTable, .table = table}; | 75 | return (uiPtr){.type = uiTypeTable, .table = table}; |
| @@ -72,6 +84,8 @@ uiPtr uiMakeWidgetPtr(uiWidget* widget) { | |||
| 72 | return uiMakeFramePtr((uiFrame*)widget); | 84 | return uiMakeFramePtr((uiFrame*)widget); |
| 73 | case uiTypeLabel: | 85 | case uiTypeLabel: |
| 74 | return uiMakeLabelPtr((uiLabel*)widget); | 86 | return uiMakeLabelPtr((uiLabel*)widget); |
| 87 | case uiTypeLayout: | ||
| 88 | return uiMakeLayoutPtr((uiLayout*)widget); | ||
| 75 | case uiTypeTable: | 89 | case uiTypeTable: |
| 76 | return uiMakeTablePtr((uiTable*)widget); | 90 | return uiMakeTablePtr((uiTable*)widget); |
| 77 | default: | 91 | default: |
| @@ -103,8 +117,99 @@ uiLabel* uiGetLabelPtr(uiPtr ptr) { | |||
| 103 | return ptr.label; | 117 | return ptr.label; |
| 104 | } | 118 | } |
| 105 | 119 | ||
| 120 | uiLayout* uiGetLayoutPtr(uiPtr ptr) { | ||
| 121 | assert(ptr.type == uiTypeLayout); | ||
| 122 | assert(ptr.layout); | ||
| 123 | return ptr.layout; | ||
| 124 | } | ||
| 125 | |||
| 126 | uiEdit* uiGetEditPtr(uiPtr ptr) { | ||
| 127 | assert(ptr.type == uiTypeEdit); | ||
| 128 | assert(ptr.edit); | ||
| 129 | return ptr.edit; | ||
| 130 | } | ||
| 131 | |||
| 106 | uiTable* uiGetTablePtr(uiPtr ptr) { | 132 | uiTable* uiGetTablePtr(uiPtr ptr) { |
| 107 | assert(ptr.type == uiTypeTable); | 133 | assert(ptr.type == uiTypeTable); |
| 108 | assert(ptr.table); | 134 | assert(ptr.table); |
| 109 | return ptr.table; | 135 | return ptr.table; |
| 110 | } | 136 | } |
| 137 | |||
| 138 | typedef struct PrintState { | ||
| 139 | mstring pad; | ||
| 140 | mstring rect; | ||
| 141 | } PrintState; | ||
| 142 | |||
| 143 | static void RectToString(uiRect rect, mstring* out) { | ||
| 144 | assert(out); | ||
| 145 | out->length = snprintf( | ||
| 146 | out->str, sizeof(out->str), "rect{(x:%d, y:%d), (w:%d, h:%d)", rect.x, | ||
| 147 | rect.y, rect.width, rect.height); | ||
| 148 | } | ||
| 149 | |||
| 150 | static void uiPrintRec(uiPtr ptr, PrintState* state) { | ||
| 151 | if (uiIsNullptr(ptr)) { | ||
| 152 | return; | ||
| 153 | } | ||
| 154 | RectToString(ptr.widget->rect, &state->rect); | ||
| 155 | switch (ptr.type) { | ||
| 156 | case uiTypeButton: { | ||
| 157 | const uiButton* button = uiGetButtonPtr(ptr); | ||
| 158 | printf( | ||
| 159 | "%sbutton{rect=%s, text=\"%s\"}\n", mstring_cstr(&state->pad), | ||
| 160 | mstring_cstr(&state->rect), string_cstr(&button->text)); | ||
| 161 | break; | ||
| 162 | } | ||
| 163 | case uiTypeLabel: { | ||
| 164 | const uiLabel* label = uiGetLabelPtr(ptr); | ||
| 165 | printf( | ||
| 166 | "%sbutton{rect=%s, text=\"%s\"}\n", mstring_cstr(&state->pad), | ||
| 167 | mstring_cstr(&state->rect), string_cstr(&label->text)); | ||
| 168 | break; | ||
| 169 | } | ||
| 170 | case uiTypeLayout: { | ||
| 171 | const uiLayout* layout = uiGetLayoutPtr(ptr); | ||
| 172 | const char* direction = ""; | ||
| 173 | switch (layout->direction) { | ||
| 174 | case uiHorizontal: | ||
| 175 | direction = "horizontal"; | ||
| 176 | break; | ||
| 177 | case uiVertical: | ||
| 178 | direction = "vertical"; | ||
| 179 | break; | ||
| 180 | } | ||
| 181 | printf( | ||
| 182 | "%s%s_layout{rect=%s}\n", mstring_cstr(&state->pad), direction, | ||
| 183 | mstring_cstr(&state->rect)); | ||
| 184 | break; | ||
| 185 | } | ||
| 186 | case uiTypeFrame: { | ||
| 187 | printf( | ||
| 188 | "%sframe{rect=%s}\n", mstring_cstr(&state->pad), | ||
| 189 | mstring_cstr(&state->rect)); | ||
| 190 | break; | ||
| 191 | } | ||
| 192 | case uiTypeTable: { | ||
| 193 | const uiTable* table = uiGetTablePtr(ptr); | ||
| 194 | printf( | ||
| 195 | "%stable{rect=%s}\n", mstring_cstr(&state->pad), | ||
| 196 | mstring_cstr(&state->rect)); | ||
| 197 | break; | ||
| 198 | } | ||
| 199 | default: | ||
| 200 | printf("%swidget\n", mstring_cstr(&state->pad)); | ||
| 201 | break; | ||
| 202 | } | ||
| 203 | const mstring pad = state->pad; | ||
| 204 | state->pad = mstring_concat(state->pad, mstring_make(" ")); | ||
| 205 | list_foreach(ptr.widget->children, child, { | ||
| 206 | uiPrintRec(uiMakeWidgetPtr(child), state); | ||
| 207 | }); | ||
| 208 | state->pad = pad; | ||
| 209 | } | ||
| 210 | |||
| 211 | void uiPrint(uiPtr ptr) { | ||
| 212 | PrintState state = | ||
| 213 | (PrintState){.pad = mstring_make_empty(), .rect = mstring_make_empty()}; | ||
| 214 | uiPrintRec(ptr, &state); | ||
| 215 | } | ||
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*) | |||
| 13 | typedef struct uiWidget { | 13 | typedef struct uiWidget { |
| 14 | uiWidgetType type; | 14 | uiWidgetType type; |
| 15 | uiRect rect; | 15 | uiRect rect; |
| 16 | uiStretch stretch; | ||
| 17 | uiPtr parent; | ||
| 16 | Widget_list children; | 18 | Widget_list children; |
| 17 | } uiWidget; | 19 | } uiWidget; |
| 18 | 20 | ||
| 21 | typedef struct uiLayout { | ||
| 22 | uiWidget widget; | ||
| 23 | uiLayoutDirection direction; | ||
| 24 | } uiLayout; | ||
| 25 | |||
| 19 | typedef struct uiButton { | 26 | typedef struct uiButton { |
| 20 | uiWidget widget; | 27 | uiWidget widget; |
| 21 | string text; | 28 | string text; |
| @@ -30,6 +37,11 @@ typedef struct uiLabel { | |||
| 30 | string text; | 37 | string text; |
| 31 | } uiLabel; | 38 | } uiLabel; |
| 32 | 39 | ||
| 40 | typedef struct uiEdit { | ||
| 41 | uiWidget widget; | ||
| 42 | string text; | ||
| 43 | } uiEdit; | ||
| 44 | |||
| 33 | typedef struct uiScrollbar { | 45 | typedef struct uiScrollbar { |
| 34 | int width; | 46 | int width; |
| 35 | int height; // Total height: handle plus scrollable area. | 47 | int height; // Total height: handle plus scrollable area. |
| @@ -45,7 +57,6 @@ typedef struct uiTable { | |||
| 45 | uiWidget widget; | 57 | uiWidget widget; |
| 46 | int rows; | 58 | int rows; |
| 47 | int cols; | 59 | int cols; |
| 48 | int height; // Height in pixels. | ||
| 49 | int* widths; // Width, in pixels, for each column. | 60 | int* widths; // Width, in pixels, for each column. |
| 50 | uiCell* header; // If non-null, row of 'cols' header cells. | 61 | uiCell* header; // If non-null, row of 'cols' header cells. |
| 51 | uiCell** cells; // Array of 'rows' rows, each of 'cols' cells. | 62 | uiCell** cells; // Array of 'rows' rows, each of 'cols' cells. |
| @@ -57,6 +68,7 @@ typedef struct uiTable { | |||
| 57 | } flags; | 68 | } flags; |
| 58 | } uiTable; | 69 | } uiTable; |
| 59 | 70 | ||
| 71 | void WidgetSetParent(uiPtr child, uiPtr parent); | ||
| 60 | void DestroyWidget(uiWidget** ppWidget); | 72 | void DestroyWidget(uiWidget** ppWidget); |
| 61 | 73 | ||
| 62 | /// Set the scrollbar handle's y-coordinate, which is clipped to the scrollbar's | 74 | /// Set the scrollbar handle's y-coordinate, which is clipped to the scrollbar's |
