summaryrefslogtreecommitdiff
path: root/src/layout.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/layout.c')
-rw-r--r--src/layout.c146
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
9static 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
119static 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
143void uiResizeFrame(uiFrame* frame, int width, int height) {
144 assert(frame);
145 ResizeWidget(&frame->widget, width, height);
146}