diff options
| author | 3gg <3gg@shellblade.net> | 2024-07-13 10:52:24 -0700 |
|---|---|---|
| committer | 3gg <3gg@shellblade.net> | 2024-07-13 10:52:24 -0700 |
| commit | a4294e4a94189dffb1fdf99c9a60d87d77272926 (patch) | |
| tree | 2e92f7c95116861bc39f4dae1d0ab5d388550000 /src/layout.c | |
| parent | cf9579d7546c04dbc708bd8719e3f935a28088bd (diff) | |
Restructure project.
Diffstat (limited to 'src/layout.c')
| -rw-r--r-- | src/layout.c | 146 |
1 files changed, 146 insertions, 0 deletions
diff --git a/src/layout.c b/src/layout.c new file mode 100644 index 0000000..9d4b556 --- /dev/null +++ b/src/layout.c | |||
| @@ -0,0 +1,146 @@ | |||
| 1 | #include <ui.h> | ||
| 2 | |||
| 3 | #include "uiLibrary.h" | ||
| 4 | #include "widget/table.h" | ||
| 5 | #include "widget/widget.h" | ||
| 6 | |||
| 7 | #include <cassert.h> | ||
| 8 | |||
| 9 | static void ResizeTable(uiTable* table, int width, int height) { | ||
| 10 | assert(table); | ||
| 11 | |||
| 12 | if (table->cols == 0) { | ||
| 13 | return; | ||
| 14 | } | ||
| 15 | |||
| 16 | // Determine if there is vertical overflow. This determines whether we need to | ||
| 17 | // render a scroll bar, in which case room must be made for it. | ||
| 18 | table->flags.vertical_overflow = | ||
| 19 | (table->rows * g_ui.font->header.glyph_height) > | ||
| 20 | table->widget.rect.height; | ||
| 21 | |||
| 22 | // Surface width: W. | ||
| 23 | // Columns: N | ||
| 24 | // | ||
| 25 | // First, find the minimum width of each column based on their contents. | ||
| 26 | // | ||
| 27 | // If the sum of column widths < N, then distribute the extra space first | ||
| 28 | // among the smallest columns and building up towards the larger. | ||
| 29 | // | ||
| 30 | // If the sum of column widths > N, subtract from the largest column first and | ||
| 31 | // move towards the smaller ones to distribute the space as evenly as | ||
| 32 | // possible. | ||
| 33 | |||
| 34 | // Find the minimum width for each column. | ||
| 35 | int* widths = table->widths; | ||
| 36 | // Header. | ||
| 37 | for (int col = 0; col < table->cols; ++col) { | ||
| 38 | const uiCell* cell = &table->header[col]; | ||
| 39 | const uiLabel* label = (uiLabel*)cell->child; | ||
| 40 | const int length = (int)string_length(label->text); | ||
| 41 | |||
| 42 | widths[col] = length; | ||
| 43 | } | ||
| 44 | // Table contents. | ||
| 45 | for (int row = 0; row < table->rows; ++row) { | ||
| 46 | for (int col = 0; col < table->cols; ++col) { | ||
| 47 | const uiCell* cell = GetCell(table, row, col); | ||
| 48 | if (cell->child) { | ||
| 49 | const uiLabel* label = (uiLabel*)cell->child; | ||
| 50 | const int length = (int)string_length(label->text); | ||
| 51 | |||
| 52 | widths[col] = length > widths[col] ? length : widths[col]; | ||
| 53 | } | ||
| 54 | } | ||
| 55 | } | ||
| 56 | // Multiply string lengths times glyph width to compute pixel size. | ||
| 57 | for (int col = 0; col < table->cols; ++col) { | ||
| 58 | widths[col] *= g_ui.font->header.glyph_width; | ||
| 59 | } | ||
| 60 | |||
| 61 | // Find the sum of widths. | ||
| 62 | int used_width = 0; | ||
| 63 | for (int col = 0; col < table->cols; ++col) { | ||
| 64 | used_width += widths[col]; | ||
| 65 | } | ||
| 66 | |||
| 67 | // Pad if available width is larger than sum of widths. | ||
| 68 | if (used_width < width) { | ||
| 69 | // Divide evenly among columns. | ||
| 70 | // const int extra = width - used_width; | ||
| 71 | // const int pad = extra / table->cols; | ||
| 72 | // const int mod = extra % table->cols; | ||
| 73 | // for (int col = 0; col < table->cols; ++col) { | ||
| 74 | // table->widths[col] += pad + (col < mod ? 1 : 0); | ||
| 75 | // } | ||
| 76 | |||
| 77 | int extra = width - used_width; | ||
| 78 | while (extra > 0) { | ||
| 79 | // Find smallest column. | ||
| 80 | int smallest = 0; | ||
| 81 | for (int col = 1; col < table->cols; ++col) { | ||
| 82 | if (widths[col] < widths[smallest]) { | ||
| 83 | smallest = col; | ||
| 84 | } | ||
| 85 | } | ||
| 86 | // Pad it and subtract from the budget. | ||
| 87 | widths[smallest] += 1; | ||
| 88 | extra--; | ||
| 89 | } | ||
| 90 | } | ||
| 91 | // Shrink if available width is smaller than the sum of widths. | ||
| 92 | else if (used_width > width) { | ||
| 93 | int deficit = used_width - width; | ||
| 94 | while (deficit > 0) { | ||
| 95 | // Find largest column. | ||
| 96 | int largest = 0; | ||
| 97 | for (int col = 1; col < table->cols; ++col) { | ||
| 98 | if (widths[col] > widths[largest]) { | ||
| 99 | largest = col; | ||
| 100 | } | ||
| 101 | } | ||
| 102 | // Shrink it and subtract from the deficit. | ||
| 103 | widths[largest] -= 1; | ||
| 104 | deficit--; | ||
| 105 | } | ||
| 106 | } | ||
| 107 | |||
| 108 | // Now make room for the scroll bar, if necessary. | ||
| 109 | if (table->flags.vertical_overflow) { | ||
| 110 | const int offset = ScrollBarWidth / table->cols; | ||
| 111 | const int remainder = ScrollBarWidth % table->cols; | ||
| 112 | for (int col = 0; col < table->cols; ++col) { | ||
| 113 | table->widths[col] -= offset + (col < remainder ? 1 : 0); | ||
| 114 | assert(table->widths[col] >= 0); | ||
| 115 | } | ||
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 119 | static void ResizeWidget(uiWidget* widget, int width, int height) { | ||
| 120 | assert(widget); | ||
| 121 | |||
| 122 | widget->rect.width = width; | ||
| 123 | widget->rect.height = height; | ||
| 124 | |||
| 125 | switch (widget->type) { | ||
| 126 | case uiTypeButton: | ||
| 127 | break; | ||
| 128 | case uiTypeFrame: | ||
| 129 | list_foreach_mut( | ||
| 130 | widget->children, child, { ResizeWidget(child, width, height); }); | ||
| 131 | break; | ||
| 132 | case uiTypeLabel: | ||
| 133 | break; | ||
| 134 | case uiTypeTable: | ||
| 135 | ResizeTable((uiTable*)widget, width, height); | ||
| 136 | break; | ||
| 137 | case uiTypeMax: | ||
| 138 | TRAP(); | ||
| 139 | break; | ||
| 140 | } | ||
| 141 | } | ||
| 142 | |||
| 143 | void uiResizeFrame(uiFrame* frame, int width, int height) { | ||
| 144 | assert(frame); | ||
| 145 | ResizeWidget(&frame->widget, width, height); | ||
| 146 | } | ||
