summaryrefslogtreecommitdiff
path: root/src/layout.c
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2026-03-25 19:59:14 -0700
committer3gg <3gg@shellblade.net>2026-03-25 19:59:14 -0700
commit4152fbecb6ee8360575aa4c24e9cedf822f159dc (patch)
tree9e9b9db0216a37c5867d472a65289502c459691f /src/layout.c
parent7778755c20e779554cd654ecdf7404d37b723fcc (diff)
Implement vertical and horizontal layouts. Use widget position properly when rendering. Toolbar, buttons and edit bars WIPmain
Diffstat (limited to 'src/layout.c')
-rw-r--r--src/layout.c192
1 files changed, 171 insertions, 21 deletions
diff --git a/src/layout.c b/src/layout.c
index 5261eb3..3532928 100644
--- a/src/layout.c
+++ b/src/layout.c
@@ -5,16 +5,59 @@
5 5
6#include <cassert.h> 6#include <cassert.h>
7 7
8#define Min(a, b) ((a) < (b) ? (a) : (b)) 8static void LayoutWidget(uiWidget* widget, int width, int height);
9 9
10static void ResizeTable(uiTable* table, int width, int height) { 10/// Return the area required to fit the text.
11static uiSize GetTextSize(const string* text) {
12 return (uiSize){
13 .width = (int)(g_ui.font->header.glyph_width * string_length(*text)),
14 .height = (int)g_ui.font->header.glyph_height};
15}
16
17static void ResizeButton(uiButton* button, int width, int height) {
18 assert(button);
19
20 // TODO: Define the button's border. But don't store this. Make it a function
21 // shared between layout.c and render.c. Define it in a new common.h?
22
23 const uiSize minSize = GetTextSize(&button->text);
24 uiSize size = minSize;
25 if (button->widget.stretch & uiStretchX) {
26 size.width = Max(size.width, width);
27 }
28 if (button->widget.stretch & uiStretchY) {
29 size.height = Max(size.height, height);
30 }
31 button->widget.rect.width = size.width;
32 button->widget.rect.height = size.height;
33}
34
35static void ResizeLabel(uiLabel* label, int width, int height) {
36 assert(label);
37
38 const uiSize minSize = GetTextSize(&label->text);
39 uiSize size = minSize;
40 if (label->widget.stretch & uiStretchX) {
41 size.width = Max(size.width, width);
42 }
43 if (label->widget.stretch & uiStretchY) {
44 size.height = Max(size.height, height);
45 }
46 label->widget.rect.width = size.width;
47 label->widget.rect.height = size.height;
48}
49
50static void LayoutTable(uiTable* table, int width, int height) {
11 assert(table); 51 assert(table);
12 52
13 if (table->cols == 0) { 53 if ((table->cols == 0) || (table->rows == 0)) {
54 table->widget.rect.width = 0;
55 table->widget.rect.height = 0;
14 return; 56 return;
15 } 57 }
16 58
17 table->height = height; 59 table->widget.rect.width = width;
60 table->widget.rect.height = height;
18 61
19 // Compute the number of rows that are visible at once. 62 // Compute the number of rows that are visible at once.
20 table->num_visible_rows = 63 table->num_visible_rows =
@@ -121,11 +164,10 @@ static void ResizeTable(uiTable* table, int width, int height) {
121 } 164 }
122 165
123 // Set scrollbar layout. 166 // Set scrollbar layout.
124 scrollbar->width = ScrollbarWidth; 167 scrollbar->width = ScrollbarWidth;
125 scrollbar->height = table->height; 168 scrollbar->height = height;
126 scrollbar->handle_height = 169 scrollbar->handle_height = (int)((double)table->num_visible_rows /
127 (int)((double)table->num_visible_rows / (double)table->rows * 170 (double)table->rows * (double)height);
128 (double)table->height);
129 uiTableScroll(table, table->offset); 171 uiTableScroll(table, table->offset);
130 } else { // Scroll bar not visible. 172 } else { // Scroll bar not visible.
131 scrollbar->width = 0; 173 scrollbar->width = 0;
@@ -135,31 +177,139 @@ static void ResizeTable(uiTable* table, int width, int height) {
135 } 177 }
136} 178}
137 179
138static void ResizeWidget(uiWidget* widget, int width, int height) { 180static void Layout(uiLayout* layout, int width, int height) {
139 assert(widget); 181 assert(layout);
140 182
141 widget->rect.width = width; 183 layout->widget.rect.width = width;
142 widget->rect.height = height; 184 layout->widget.rect.height = height;
185
186 // Resizing a layout can get complicated depending on how much flexibility we
187 // want to support. To start simple:
188 // 1. Let the layout stretch to occupy the given size.
189 // 2. For each child, check whether the child has a fixed width/height or
190 // if it wants to grow.
191 // 3. Fixed-size widgets get their requested size.
192 // 4. Variably-sized widgets get the remainder of the space uniformly
193 // distributed among them.
194
195 // First resize fixed-size widgets and compute free area, if any, to determine
196 // the size of stretchable widgets along the layout direction. Then resize
197 // stretchable widgets by uniformly distributing the free area.
198 switch (layout->direction) {
199 case uiVertical: {
200 // Resize fixed-size children and compute free area.
201 int free_area = height;
202 int stretchable_count = 0; // Number of stretchable widgets.
203 list_foreach(layout->widget.children, child, {
204 if (child->stretch & uiStretchY) {
205 stretchable_count++;
206 } else {
207 LayoutWidget(child, width, free_area);
208 free_area -= child->rect.height;
209 }
210 });
211 if (stretchable_count > 0) {
212 // Resize stretchable children.
213 const int stretchable_widget_size = free_area / stretchable_count;
214 list_foreach(layout->widget.children, child, {
215 if (child->stretch != uiStretchNone) {
216 LayoutWidget(child, width, stretchable_widget_size);
217 } else {
218 LayoutWidget(child, width, height);
219 }
220 });
221 }
222 // Now position all widgets inside the layout.
223 int y = 0;
224 list_foreach(layout->widget.children, child, {
225 child->rect.y = y;
226 y += child->rect.height;
227 });
228 // Layout's width is max of its children.
229 layout->widget.rect.width = 0;
230 list_foreach(layout->widget.children, child, {
231 layout->widget.rect.width =
232 Max(layout->widget.rect.width, child->rect.width);
233 });
234 break;
235 }
236 case uiHorizontal: {
237 // Resize fixed-size children and compute free area.
238 int free_area = width;
239 int stretchable_count = 0; // Number of stretchable widgets.
240 list_foreach(layout->widget.children, child, {
241 if (child->stretch & uiStretchX) {
242 stretchable_count++;
243 } else {
244 LayoutWidget(child, free_area, height);
245 free_area -= child->rect.width;
246 }
247 });
248 if (stretchable_count > 0) {
249 // Resize stretchable children.
250 const int stretchable_size = free_area / stretchable_count;
251 list_foreach(layout->widget.children, child, {
252 if (child->stretch != uiStretchNone) {
253 LayoutWidget(child, stretchable_size, height);
254 }
255 });
256 }
257 // Now position all widgets inside the layout.
258 int x = 0;
259 list_foreach(layout->widget.children, child, {
260 child->rect.x = x;
261 x += child->rect.width;
262 });
263 // Layout's height is max of its children.
264 layout->widget.rect.height = 0;
265 list_foreach(layout->widget.children, child, {
266 layout->widget.rect.height =
267 Max(layout->widget.rect.height, child->rect.height);
268 });
269 break;
270 }
271 }
272}
273
274static void ResizeFrame(uiFrame* frame, int width, int height) {
275 assert(frame);
276
277 frame->widget.rect.width = width;
278 frame->widget.rect.height = height;
279
280 list_foreach_mut(
281 frame->widget.children, child, { LayoutWidget(child, width, height); });
282}
283
284void uiLayOut(uiFrame* frame, int width, int height) {
285 assert(frame);
286 LayoutWidget(&frame->widget, width, height);
287}
288
289static void LayoutWidget(uiWidget* widget, int width, int height) {
290 assert(widget);
143 291
144 switch (widget->type) { 292 switch (widget->type) {
293 case uiTypeLayout:
294 Layout((uiLayout*)widget, width, height);
295 break;
145 case uiTypeButton: 296 case uiTypeButton:
297 ResizeButton((uiButton*)widget, width, height);
146 break; 298 break;
147 case uiTypeFrame: 299 case uiTypeFrame:
148 list_foreach_mut( 300 ResizeFrame((uiFrame*)widget, width, height);
149 widget->children, child, { ResizeWidget(child, width, height); });
150 break; 301 break;
151 case uiTypeLabel: 302 case uiTypeLabel:
303 ResizeLabel((uiLabel*)widget, width, height);
304 break;
305 case uiTypeEdit:
306 // TODO: ResizeEdit()
152 break; 307 break;
153 case uiTypeTable: 308 case uiTypeTable:
154 ResizeTable((uiTable*)widget, width, height); 309 LayoutTable((uiTable*)widget, width, height);
155 break; 310 break;
156 case uiTypeMax: 311 case uiTypeMax:
157 TRAP(); 312 TRAP();
158 break; 313 break;
159 } 314 }
160} 315}
161
162void uiResizeFrame(uiFrame* frame, int width, int height) {
163 assert(frame);
164 ResizeWidget(&frame->widget, width, height);
165}