diff options
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 | } | ||