summaryrefslogtreecommitdiff
path: root/src/ui
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-03-30 15:03:04 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-03-30 15:03:04 +0300
commit8ab2c6c2a27f888602ea05afaf05a75ecca9bf02 (patch)
treef6160a88fa05ae2fc6b34c04d96f5cc637f40ba4 /src/ui
parent23f70040368e8d5577f89b2bacf303b5dac8561f (diff)
Widget: Arrangement debugging; min size
There is a problem with repeated arrangements: the previous set sizes affect subsequent outcomes. This results in Preferences not being able to reduce in size, only to expand. It should be possible to reset sizes back to zero/minimum size before starting an arrangement, but LabelWidget needs to cooperate by using `minSize` to set its default size. `minSize` is preferable to fixedSize because then the widget can go through the usual arranging logic. To be continued at a later time...
Diffstat (limited to 'src/ui')
-rw-r--r--src/ui/bindingswidget.c1
-rw-r--r--src/ui/certimportwidget.c4
-rw-r--r--src/ui/documentwidget.c2
-rw-r--r--src/ui/inputwidget.c2
-rw-r--r--src/ui/lookupwidget.c4
-rw-r--r--src/ui/translation.c5
-rw-r--r--src/ui/util.c74
-rw-r--r--src/ui/util.h4
-rw-r--r--src/ui/widget.c222
-rw-r--r--src/ui/widget.h5
-rw-r--r--src/ui/window.c15
11 files changed, 255 insertions, 83 deletions
diff --git a/src/ui/bindingswidget.c b/src/ui/bindingswidget.c
index c73982b3..dc23dd6e 100644
--- a/src/ui/bindingswidget.c
+++ b/src/ui/bindingswidget.c
@@ -110,6 +110,7 @@ static void updateItems_BindingsWidget_(iBindingsWidget *d) {
110void init_BindingsWidget(iBindingsWidget *d) { 110void init_BindingsWidget(iBindingsWidget *d) {
111 iWidget *w = as_Widget(d); 111 iWidget *w = as_Widget(d);
112 init_Widget(w); 112 init_Widget(w);
113 setId_Widget(w, "bindings");
113 setFlags_Widget(w, resizeChildren_WidgetFlag, iTrue); 114 setFlags_Widget(w, resizeChildren_WidgetFlag, iTrue);
114 d->activePos = iInvalidPos; 115 d->activePos = iInvalidPos;
115 d->contextPos = iInvalidPos; 116 d->contextPos = iInvalidPos;
diff --git a/src/ui/certimportwidget.c b/src/ui/certimportwidget.c
index 43e8ff97..a4245603 100644
--- a/src/ui/certimportwidget.c
+++ b/src/ui/certimportwidget.c
@@ -148,8 +148,8 @@ void init_CertImportWidget(iCertImportWidget *d) {
148 } 148 }
149 addChild_Widget(w, iClob(page)); 149 addChild_Widget(w, iClob(page));
150 arrange_Widget(w); 150 arrange_Widget(w);
151 setSize_Widget(as_Widget(d->crtLabel), init_I2(width_Widget(w) - 6.5 * gap_UI, gap_UI * 12)); 151 setFixedSize_Widget(as_Widget(d->crtLabel), init_I2(width_Widget(w) - 6.5 * gap_UI, gap_UI * 12));
152 setSize_Widget(as_Widget(d->keyLabel), init_I2(width_Widget(w) - 6.5 * gap_UI, gap_UI * 12)); 152 setFixedSize_Widget(as_Widget(d->keyLabel), init_I2(width_Widget(w) - 6.5 * gap_UI, gap_UI * 12));
153 /* Buttons. */ 153 /* Buttons. */
154 addChild_Widget(w, iClob(makePadding_Widget(gap_UI))); 154 addChild_Widget(w, iClob(makePadding_Widget(gap_UI)));
155 iWidget *buttons = makeDialogButtons_Widget( 155 iWidget *buttons = makeDialogButtons_Widget(
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index 07b359da..9a50b611 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -1752,7 +1752,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
1752 delete_Array(items); 1752 delete_Array(items);
1753 /* Enforce a minimum size. */ 1753 /* Enforce a minimum size. */
1754 iWidget *sizer = new_Widget(); 1754 iWidget *sizer = new_Widget();
1755 setSize_Widget(sizer, init_I2(gap_UI * 65, 1)); 1755 setFixedSize_Widget(sizer, init_I2(gap_UI * 65, 1));
1756 addChildFlags_Widget(dlg, iClob(sizer), frameless_WidgetFlag); 1756 addChildFlags_Widget(dlg, iClob(sizer), frameless_WidgetFlag);
1757 setFlags_Widget(dlg, centerHorizontal_WidgetFlag, iFalse); 1757 setFlags_Widget(dlg, centerHorizontal_WidgetFlag, iFalse);
1758 if (deviceType_App() != phone_AppDeviceType) { 1758 if (deviceType_App() != phone_AppDeviceType) {
diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c
index 5593b5b9..0856db93 100644
--- a/src/ui/inputwidget.c
+++ b/src/ui/inputwidget.c
@@ -248,7 +248,7 @@ void setMaxLen_InputWidget(iInputWidget *d, size_t maxLen) {
248 /* Set a fixed size. */ 248 /* Set a fixed size. */
249 iBlock *content = new_Block(maxLen); 249 iBlock *content = new_Block(maxLen);
250 fill_Block(content, 'M'); 250 fill_Block(content, 'M');
251 setSize_Widget( 251 setFixedSize_Widget(
252 as_Widget(d), 252 as_Widget(d),
253 add_I2(measure_Text(d->font, cstr_Block(content)), init_I2(6 * gap_UI, 2 * gap_UI))); 253 add_I2(measure_Text(d->font, cstr_Block(content)), init_I2(6 * gap_UI, 2 * gap_UI)));
254 delete_Block(content); 254 delete_Block(content);
diff --git a/src/ui/lookupwidget.c b/src/ui/lookupwidget.c
index c711d61f..f3fd54d2 100644
--- a/src/ui/lookupwidget.c
+++ b/src/ui/lookupwidget.c
@@ -657,8 +657,8 @@ static iBool processEvent_LookupWidget_(iLookupWidget *d, const SDL_Event *ev) {
657 const iWindow *window = get_Window(); 657 const iWindow *window = get_Window();
658 const iInt2 rootSize = rootSize_Window(window); 658 const iInt2 rootSize = rootSize_Window(window);
659 const iRect navBarBounds = bounds_Widget(findWidget_App("navbar")); 659 const iRect navBarBounds = bounds_Widget(findWidget_App("navbar"));
660 setSize_Widget(w, init_I2(width_Widget(findWidget_App("url")), 660 setFixedSize_Widget(w, init_I2(width_Widget(findWidget_App("url")),
661 (rootSize.y - bottom_Rect(navBarBounds)) / 2)); 661 (rootSize.y - bottom_Rect(navBarBounds)) / 2));
662 setPos_Widget(w, bottomLeft_Rect(bounds_Widget(findWidget_App("url")))); 662 setPos_Widget(w, bottomLeft_Rect(bounds_Widget(findWidget_App("url"))));
663#if defined (iPlatformAppleMobile) 663#if defined (iPlatformAppleMobile)
664 /* Adjust height based on keyboard size. */ { 664 /* Adjust height based on keyboard size. */ {
diff --git a/src/ui/translation.c b/src/ui/translation.c
index 86607fc6..4102fcb9 100644
--- a/src/ui/translation.c
+++ b/src/ui/translation.c
@@ -442,8 +442,9 @@ iBool handleCommand_Translation(iTranslation *d, const char *cmd) {
442 setFlags_Widget(as_Widget(acceptButton), disabled_WidgetFlag, iTrue); 442 setFlags_Widget(as_Widget(acceptButton), disabled_WidgetFlag, iTrue);
443 iTranslationProgressWidget *prog = new_TranslationProgressWidget(); 443 iTranslationProgressWidget *prog = new_TranslationProgressWidget();
444 setPos_Widget(as_Widget(prog), langs->rect.pos); 444 setPos_Widget(as_Widget(prog), langs->rect.pos);
445 setSize_Widget(as_Widget(prog), langs->rect.size); 445 setFixedSize_Widget(as_Widget(prog), init_I2(width_Rect(innerBounds_Widget(d->dlg)),
446 addChild_Widget(d->dlg, iClob(prog)); 446 langs->rect.size.y));
447 addChildFlags_Widget(d->dlg, iClob(prog), 0);
447 submit_Translation(d); 448 submit_Translation(d);
448 } 449 }
449 return iTrue; 450 return iTrue;
diff --git a/src/ui/util.c b/src/ui/util.c
index 41b8f65c..564cf785 100644
--- a/src/ui/util.c
+++ b/src/ui/util.c
@@ -438,7 +438,7 @@ iInt2 delta_Click(const iClick *d) {
438iWidget *makePadding_Widget(int size) { 438iWidget *makePadding_Widget(int size) {
439 iWidget *pad = new_Widget(); 439 iWidget *pad = new_Widget();
440 setId_Widget(pad, "padding"); 440 setId_Widget(pad, "padding");
441 setSize_Widget(pad, init1_I2(size)); 441 setFixedSize_Widget(pad, init1_I2(size));
442 return pad; 442 return pad;
443} 443}
444 444
@@ -463,7 +463,7 @@ iWidget *makeHDiv_Widget(void) {
463 463
464iWidget *addAction_Widget(iWidget *parent, int key, int kmods, const char *command) { 464iWidget *addAction_Widget(iWidget *parent, int key, int kmods, const char *command) {
465 iLabelWidget *action = newKeyMods_LabelWidget("", key, kmods, command); 465 iLabelWidget *action = newKeyMods_LabelWidget("", key, kmods, command);
466 setSize_Widget(as_Widget(action), zero_I2()); 466 setFixedSize_Widget(as_Widget(action), zero_I2());
467 addChildFlags_Widget(parent, iClob(action), hidden_WidgetFlag); 467 addChildFlags_Widget(parent, iClob(action), hidden_WidgetFlag);
468 return as_Widget(action); 468 return as_Widget(action);
469} 469}
@@ -856,16 +856,23 @@ iWidget *removeTabPage_Widget(iWidget *tabs, size_t index) {
856} 856}
857 857
858void resizeToLargestPage_Widget(iWidget *tabs) { 858void resizeToLargestPage_Widget(iWidget *tabs) {
859// puts("RESIZE TO LARGEST PAGE ...");
860 iWidget *pages = findChild_Widget(tabs, "tabs.pages");
861 iForEach(ObjectList, i, children_Widget(pages)) {
862 setMinSize_Widget(i.object, zero_I2());
863// resetSize_Widget(i.object);
864 }
859 arrange_Widget(tabs); 865 arrange_Widget(tabs);
860 iInt2 largest = zero_I2(); 866 iInt2 largest = zero_I2();
861 iWidget *pages = findChild_Widget(tabs, "tabs.pages"); 867 iConstForEach(ObjectList, j, children_Widget(pages)) {
862 iConstForEach(ObjectList, i, children_Widget(pages)) { 868 const iWidget *page = constAs_Widget(j.object);
863 largest = max_I2(largest, ((const iWidget *) i.object)->rect.size); 869 largest = max_I2(largest, page->rect.size);
864 } 870 }
865 iForEach(ObjectList, j, children_Widget(pages)) { 871 iForEach(ObjectList, k, children_Widget(pages)) {
866 setSize_Widget(j.object, largest); 872 setMinSize_Widget(k.object, largest);
867 } 873 }
868 setSize_Widget(tabs, addY_I2(largest, height_Widget(findChild_Widget(tabs, "tabs.buttons")))); 874 setFixedSize_Widget(tabs, addY_I2(largest, height_Widget(findChild_Widget(tabs, "tabs.buttons"))));
875// puts("... DONE WITH RESIZE TO LARGEST PAGE");
869} 876}
870 877
871iLabelWidget *tabButtonForPage_Widget_(iWidget *tabs, const iWidget *page) { 878iLabelWidget *tabButtonForPage_Widget_(iWidget *tabs, const iWidget *page) {
@@ -1012,7 +1019,7 @@ static void updateSheetPanelMetrics_(iWidget *sheet) {
1012 setSize_Widget(pad, init1_I2(naviHeight)); 1019 setSize_Widget(pad, init1_I2(naviHeight));
1013 } 1020 }
1014#endif 1021#endif
1015 setSize_Widget(navi, init_I2(-1, naviHeight)); 1022 setFixedSize_Widget(navi, init_I2(-1, naviHeight));
1016} 1023}
1017 1024
1018static iBool slidePanelHandler_(iWidget *d, const char *cmd) { 1025static iBool slidePanelHandler_(iWidget *d, const char *cmd) {
@@ -1786,12 +1793,16 @@ static iBool toggleHandler_(iWidget *d, const char *cmd) {
1786 isSelected_Widget(d) ? 1 : 0)); 1793 isSelected_Widget(d) ? 1 : 0));
1787 return iTrue; 1794 return iTrue;
1788 } 1795 }
1796 else if (equal_Command(cmd, "lang.changed")) {
1797 /* TODO: Measure labels again. */
1798 }
1789 return iFalse; 1799 return iFalse;
1790} 1800}
1791 1801
1792iWidget *makeToggle_Widget(const char *id) { 1802iWidget *makeToggle_Widget(const char *id) {
1793 iWidget *toggle = as_Widget(new_LabelWidget("${toggle.yes}", "toggle")); /* "YES" for sizing */ 1803 iWidget *toggle = as_Widget(new_LabelWidget("${toggle.yes}", "toggle")); /* "YES" for sizing */
1794 setId_Widget(toggle, id); 1804 setId_Widget(toggle, id);
1805 /* TODO: Measure both labels and use the larger of the two. */
1795 updateTextCStr_LabelWidget((iLabelWidget *) toggle, "${toggle.no}"); /* actual initial value */ 1806 updateTextCStr_LabelWidget((iLabelWidget *) toggle, "${toggle.no}"); /* actual initial value */
1796 setFlags_Widget(toggle, fixedWidth_WidgetFlag, iTrue); 1807 setFlags_Widget(toggle, fixedWidth_WidgetFlag, iTrue);
1797 setCommandHandler_Widget(toggle, toggleHandler_); 1808 setCommandHandler_Widget(toggle, toggleHandler_);
@@ -1879,6 +1890,36 @@ static int cmp_MenuItem_(const void *e1, const void *e2) {
1879} 1890}
1880#endif 1891#endif
1881 1892
1893void updatePreferencesLayout_Widget(iWidget *prefs) {
1894 if (!prefs) return;
1895 /* Doing manual layout here because the widget arranging logic isn't sophisticated enough. */
1896 /* TODO: Make the arranging more sophisticated to automate this. */
1897 static const char *inputIds[] = {
1898 "prefs.searchurl",
1899 "prefs.downloads",
1900 "prefs.ca.file",
1901 "prefs.ca.path",
1902 "prefs.proxy.gemini",
1903 "prefs.proxy.gopher",
1904 "prefs.proxy.http"
1905 };
1906 iWidget *tabs = findChild_Widget(prefs, "prefs.tabs");
1907 /* Input fields expand to the right edge. */
1908 /* TODO: Add an arrangement flag for this. */
1909 iForIndices(i, inputIds) {
1910 iInputWidget *input = findChild_Widget(tabs, inputIds[i]);
1911 if (input) {
1912 as_Widget(input)->rect.size.x = 0;
1913 }
1914 }
1915 as_Widget(findChild_Widget(prefs, "bindings"))->rect.size.x = 0;
1916 resizeToLargestPage_Widget(tabs);
1917 arrange_Widget(prefs);
1918 iForIndices(i, inputIds) {
1919 expandInputFieldWidth_(findChild_Widget(tabs, inputIds[i]));
1920 }
1921}
1922
1882iWidget *makePreferences_Widget(void) { 1923iWidget *makePreferences_Widget(void) {
1883 iWidget *dlg = makeSheet_Widget("prefs"); 1924 iWidget *dlg = makeSheet_Widget("prefs");
1884 addChildFlags_Widget(dlg, 1925 addChildFlags_Widget(dlg,
@@ -2021,7 +2062,7 @@ iWidget *makePreferences_Widget(void) {
2021 addChildFlags_Widget(values, iClob(sats), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag); 2062 addChildFlags_Widget(values, iClob(sats), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag);
2022 } 2063 }
2023 /* Layout. */ { 2064 /* Layout. */ {
2024 appendTwoColumnPage_(tabs, "${heading.prefs.style}", '4', &headings, &values); 2065 setId_Widget(appendTwoColumnPage_(tabs, "${heading.prefs.style}", '4', &headings, &values), "prefs.page.style");
2025 makeTwoColumnHeading_("${heading.prefs.fonts}", headings, values); 2066 makeTwoColumnHeading_("${heading.prefs.fonts}", headings, values);
2026 /* Fonts. */ { 2067 /* Fonts. */ {
2027 addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.headingfont}"))); 2068 addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.headingfont}")));
@@ -2096,22 +2137,13 @@ iWidget *makePreferences_Widget(void) {
2096 appendFramelessTabPage_(tabs, iClob(bind), "${heading.prefs.keys}", '6', KMOD_PRIMARY); 2137 appendFramelessTabPage_(tabs, iClob(bind), "${heading.prefs.keys}", '6', KMOD_PRIMARY);
2097 } 2138 }
2098 addChild_Widget(dlg, iClob(makePadding_Widget(gap_UI))); 2139 addChild_Widget(dlg, iClob(makePadding_Widget(gap_UI)));
2099 resizeToLargestPage_Widget(tabs); 2140 updatePreferencesLayout_Widget(dlg);
2100 arrange_Widget(dlg);
2101 /* Set input field sizes. */ {
2102 expandInputFieldWidth_(findChild_Widget(tabs, "prefs.searchurl"));
2103 expandInputFieldWidth_(findChild_Widget(tabs, "prefs.downloads"));
2104 expandInputFieldWidth_(findChild_Widget(tabs, "prefs.ca.file"));
2105 expandInputFieldWidth_(findChild_Widget(tabs, "prefs.ca.path"));
2106 expandInputFieldWidth_(findChild_Widget(tabs, "prefs.proxy.gemini"));
2107 expandInputFieldWidth_(findChild_Widget(tabs, "prefs.proxy.gopher"));
2108 expandInputFieldWidth_(findChild_Widget(tabs, "prefs.proxy.http"));
2109 }
2110 addChild_Widget(dlg, 2141 addChild_Widget(dlg,
2111 iClob(makeDialogButtons_Widget( 2142 iClob(makeDialogButtons_Widget(
2112 (iMenuItem[]){ { "${dismiss}", SDLK_ESCAPE, 0, "prefs.dismiss" } }, 1))); 2143 (iMenuItem[]){ { "${dismiss}", SDLK_ESCAPE, 0, "prefs.dismiss" } }, 1)));
2113 addChild_Widget(get_Window()->root, iClob(dlg)); 2144 addChild_Widget(get_Window()->root, iClob(dlg));
2114 finalizeSheet_Widget(dlg); 2145 finalizeSheet_Widget(dlg);
2146 //printTree_Widget(dlg);
2115 return dlg; 2147 return dlg;
2116} 2148}
2117 2149
diff --git a/src/ui/util.h b/src/ui/util.h
index c6320e29..02b0df38 100644
--- a/src/ui/util.h
+++ b/src/ui/util.h
@@ -228,7 +228,9 @@ iWidget * makeMessage_Widget (const char *title, const char *msg);
228iWidget * makeQuestion_Widget (const char *title, const char *msg, 228iWidget * makeQuestion_Widget (const char *title, const char *msg,
229 const iMenuItem *items, size_t numItems); 229 const iMenuItem *items, size_t numItems);
230 230
231iWidget * makePreferences_Widget (void); 231iWidget * makePreferences_Widget (void);
232void updatePreferencesLayout_Widget (iWidget *prefs);
233
232iWidget * makeBookmarkEditor_Widget (void); 234iWidget * makeBookmarkEditor_Widget (void);
233iWidget * makeBookmarkCreation_Widget (const iString *url, const iString *title, iChar icon); 235iWidget * makeBookmarkCreation_Widget (const iString *url, const iString *title, iChar icon);
234iWidget * makeIdentityCreation_Widget (void); 236iWidget * makeIdentityCreation_Widget (void);
diff --git a/src/ui/widget.c b/src/ui/widget.c
index 91f6c12d..baabf673 100644
--- a/src/ui/widget.c
+++ b/src/ui/widget.c
@@ -86,6 +86,7 @@ void init_Widget(iWidget *d) {
86 init_String(&d->id); 86 init_String(&d->id);
87 d->flags = 0; 87 d->flags = 0;
88 d->rect = zero_Rect(); 88 d->rect = zero_Rect();
89 d->minSize = zero_I2();
89 d->bgColor = none_ColorId; 90 d->bgColor = none_ColorId;
90 d->frameColor = none_ColorId; 91 d->frameColor = none_ColorId;
91 init_Anim(&d->visualOffset, 0.0f); 92 init_Anim(&d->visualOffset, 0.0f);
@@ -188,20 +189,25 @@ void setPos_Widget(iWidget *d, iInt2 pos) {
188 setFlags_Widget(d, fixedPosition_WidgetFlag, iTrue); 189 setFlags_Widget(d, fixedPosition_WidgetFlag, iTrue);
189} 190}
190 191
191void setSize_Widget(iWidget *d, iInt2 size) { 192void setFixedSize_Widget(iWidget *d, iInt2 fixedSize) {
192 int flags = fixedSize_WidgetFlag; 193 int flags = fixedSize_WidgetFlag;
193 if (size.x < 0) { 194 if (fixedSize.x < 0) {
194 size.x = d->rect.size.x; 195 fixedSize.x = d->rect.size.x;
195 flags &= ~fixedWidth_WidgetFlag; 196 flags &= ~fixedWidth_WidgetFlag;
196 } 197 }
197 if (size.y < 0) { 198 if (fixedSize.y < 0) {
198 size.y = d->rect.size.y; 199 fixedSize.y = d->rect.size.y;
199 flags &= ~fixedHeight_WidgetFlag; 200 flags &= ~fixedHeight_WidgetFlag;
200 } 201 }
201 d->rect.size = size; 202 d->rect.size = fixedSize;
202 setFlags_Widget(d, flags, iTrue); 203 setFlags_Widget(d, flags, iTrue);
203} 204}
204 205
206void setMinSize_Widget(iWidget *d, iInt2 minSize) {
207 d->minSize = minSize;
208 /* rearranging needed to apply this */
209}
210
205void setPadding_Widget(iWidget *d, int left, int top, int right, int bottom) { 211void setPadding_Widget(iWidget *d, int left, int top, int right, int bottom) {
206 d->padding[0] = left; 212 d->padding[0] = left;
207 d->padding[1] = top; 213 d->padding[1] = top;
@@ -268,33 +274,79 @@ static int widestChild_Widget_(const iWidget *d) {
268 return width; 274 return width;
269} 275}
270 276
277static void arrange_Widget_(iWidget *);
278static const iBool tracing_ = iFalse;
279
280#define TRACE(d, ...) if (tracing_) { printf_Widget_(d, __VA_ARGS__); }
281
282static int depth_Widget_(const iWidget *d) {
283 int depth = 0;
284 for (const iWidget *w = d->parent; w; w = w->parent) {
285 depth++;
286 }
287 return depth;
288}
289
290static void printf_Widget_(const iWidget *d, const char *format, ...) {
291 va_list args;
292 va_start(args, format);
293 iString *msg = new_String();
294 for (size_t i = 0; i < depth_Widget_(d); ++i) {
295 appendCStr_String(msg, "| ");
296 }
297 appendFormat_String(msg, "[%p] %s(%s) ", d, class_Widget(d)->name, cstr_String(id_Widget(d)));
298 while (size_String(msg) < 44 + depth_Widget_(d) * 4) {
299 appendCStr_String(msg, " ");
300 }
301 iBlock *msg2 = new_Block(0);
302 vprintf_Block(msg2, format, args);
303 va_end(args);
304 printf("%s%s\n", cstr_String(msg), cstr_Block(msg2));
305 delete_Block(msg2);
306 delete_String(msg);
307}
308
271static void setWidth_Widget_(iWidget *d, int width) { 309static void setWidth_Widget_(iWidget *d, int width) {
272 iAssert(width >= 0); 310 iAssert(width >= 0);
311 TRACE(d, "attempt to set width to %d (current: %d, min width: %d)", width, d->rect.size.x, d->minSize.x);
312 width = iMax(width, d->minSize.x);
273 if (~d->flags & fixedWidth_WidgetFlag || d->flags & collapse_WidgetFlag) { 313 if (~d->flags & fixedWidth_WidgetFlag || d->flags & collapse_WidgetFlag) {
274 if (d->rect.size.x != width) { 314 if (d->rect.size.x != width) {
275 d->rect.size.x = width; 315 d->rect.size.x = width;
316 TRACE(d, "width has changed to %d", width);
276 if (class_Widget(d)->sizeChanged) { 317 if (class_Widget(d)->sizeChanged) {
277 const int oldHeight = d->rect.size.y; 318 const int oldHeight = d->rect.size.y;
278 class_Widget(d)->sizeChanged(d); 319 class_Widget(d)->sizeChanged(d);
279 if (d->rect.size.y != oldHeight) { 320 if (d->rect.size.y != oldHeight) {
321 TRACE(d, "sizeChanged() cuased height change to %d; redoing parent", d->rect.size.y);
280 /* Widget updated its height. */ 322 /* Widget updated its height. */
281 arrange_Widget(d->parent); 323 arrange_Widget_(d->parent);
324 TRACE(d, "parent layout redone");
282 } 325 }
283 } 326 }
284 } 327 }
285 } 328 }
329 else {
330 TRACE(d, "changing width not allowed; flags: %x", d->flags);
331 }
286} 332}
287 333
288static void setHeight_Widget_(iWidget *d, int height) { 334static void setHeight_Widget_(iWidget *d, int height) {
289 iAssert(height >= 0); 335 iAssert(height >= 0);
336 TRACE(d, "attempt to set height to %d (current: %d, min height: %d)", height, d->rect.size.y, d->minSize.y);
337 height = iMax(height, d->minSize.y);
290 if (~d->flags & fixedHeight_WidgetFlag || d->flags & collapse_WidgetFlag) { 338 if (~d->flags & fixedHeight_WidgetFlag || d->flags & collapse_WidgetFlag) {
291 if (d->rect.size.y != height) { 339 if (d->rect.size.y != height) {
292 d->rect.size.y = height; 340 d->rect.size.y = height;
341 TRACE(d, "height has changed to %d", height);
293 if (class_Widget(d)->sizeChanged) { 342 if (class_Widget(d)->sizeChanged) {
294 class_Widget(d)->sizeChanged(d); 343 class_Widget(d)->sizeChanged(d);
295 } 344 }
296 } 345 }
297 } 346 }
347 else {
348 TRACE(d, "changing height not allowed; flags: %x", d->flags);
349 }
298} 350}
299 351
300iLocalDef iBool isCollapsed_Widget_(const iWidget *d) { 352iLocalDef iBool isCollapsed_Widget_(const iWidget *d) {
@@ -336,29 +388,63 @@ static void centerHorizontal_Widget_(iWidget *d) {
336 : rootSize_Window(get_Window()).x) - 388 : rootSize_Window(get_Window()).x) -
337 width_Rect(d->rect)) / 389 width_Rect(d->rect)) /
338 2; 390 2;
391 TRACE(d, "center horizontally: %d", d->rect.pos.x);
339} 392}
340 393
341void arrange_Widget(iWidget *d) { 394static void boundsOfChildren_Widget_(const iWidget *d, iRect *bounds_out) {
395 *bounds_out = zero_Rect();
396 iConstForEach(ObjectList, i, d->children) {
397 const iWidget *child = constAs_Widget(i.object);
398 if (isCollapsed_Widget_(child)) {
399 continue;
400 }
401 iRect childRect = child->rect;
402 if (child->flags & ignoreForParentWidth_WidgetFlag) {
403 childRect.size.x = 0;
404 }
405 if (isEmpty_Rect(*bounds_out)) {
406 *bounds_out = childRect;
407 }
408 else {
409 *bounds_out = union_Rect(*bounds_out, childRect);
410 }
411 }
412}
413
414static void arrange_Widget_(iWidget *d) {
415 TRACE(d, "arranging...");
342 if (isCollapsed_Widget_(d)) { 416 if (isCollapsed_Widget_(d)) {
417 TRACE(d, "collapsed => END");
343 setFlags_Widget(d, wasCollapsed_WidgetFlag, iTrue); 418 setFlags_Widget(d, wasCollapsed_WidgetFlag, iTrue);
344 return; 419 return;
345 } 420 }
346 if (d->flags & moveToParentLeftEdge_WidgetFlag) { 421 if (d->flags & moveToParentLeftEdge_WidgetFlag) {
347 d->rect.pos.x = d->padding[0]; /* FIXME: Shouldn't this be d->parent->padding[0]? */ 422 d->rect.pos.x = d->padding[0]; /* FIXME: Shouldn't this be d->parent->padding[0]? */
423 TRACE(d, "move to parent left edge: %d", d->rect.pos.x);
348 } 424 }
349 else if (d->flags & moveToParentRightEdge_WidgetFlag) { 425 else if (d->flags & moveToParentRightEdge_WidgetFlag) {
350 d->rect.pos.x = width_Rect(innerRect_Widget_(d->parent)) - width_Rect(d->rect); 426 d->rect.pos.x = width_Rect(innerRect_Widget_(d->parent)) - width_Rect(d->rect);
427 TRACE(d, "move to parent right edge: %d", d->rect.pos.x);
351 } 428 }
352 else if (d->flags & moveToParentBottomEdge_WidgetFlag) { 429 else if (d->flags & moveToParentBottomEdge_WidgetFlag) {
353 d->rect.pos.y = height_Rect(innerRect_Widget_(d->parent)) - height_Rect(d->rect); 430 d->rect.pos.y = height_Rect(innerRect_Widget_(d->parent)) - height_Rect(d->rect);
431 TRACE(d, "move to parent bottom edge: %d", d->rect.pos.y);
354 } 432 }
355 else if (d->flags & centerHorizontal_WidgetFlag) { 433 else if (d->flags & centerHorizontal_WidgetFlag) {
356 centerHorizontal_Widget_(d); 434 centerHorizontal_Widget_(d);
357 } 435 }
358 if (d->flags & resizeToParentWidth_WidgetFlag) { 436 if (d->flags & resizeToParentWidth_WidgetFlag) {
359 setWidth_Widget_(d, width_Rect(innerRect_Widget_(d->parent))); 437 iRect childBounds = zero_Rect();
438 if (flags_Widget(d->parent) & arrangeWidth_WidgetFlag) {
439 /* Can't go narrower than what the children require, though. */
440 boundsOfChildren_Widget_(d, &childBounds);
441 }
442 TRACE(d, "resize to parent width; child bounds width %d", childBounds.size.x, childBounds.size.y);
443 setWidth_Widget_(d, iMaxi(width_Rect(innerRect_Widget_(d->parent)),
444 width_Rect(childBounds)));
360 } 445 }
361 if (d->flags & resizeToParentHeight_WidgetFlag) { 446 if (d->flags & resizeToParentHeight_WidgetFlag) {
447 TRACE(d, "resize to parent height");
362 setHeight_Widget_(d, height_Rect(innerRect_Widget_(d->parent))); 448 setHeight_Widget_(d, height_Rect(innerRect_Widget_(d->parent)));
363 } 449 }
364 if (d->flags & safePadding_WidgetFlag) { 450 if (d->flags & safePadding_WidgetFlag) {
@@ -370,46 +456,44 @@ void arrange_Widget(iWidget *d) {
370 } 456 }
371 /* The rest of the arrangement depends on child widgets. */ 457 /* The rest of the arrangement depends on child widgets. */
372 if (!d->children) { 458 if (!d->children) {
459 TRACE(d, "no children => END");
373 return; 460 return;
374 } 461 }
375 const size_t childCount = numArrangedChildren_Widget_(d); 462 const size_t childCount = numArrangedChildren_Widget_(d);
376 /* There may still be unarranged children that need arranging internally. */ 463 TRACE(d, "%d arranged children", childCount);
377 if (childCount == 0) {
378 iForEach(ObjectList, i, d->children) {
379 iWidget *child = as_Widget(i.object);
380 if (isArranged_Widget_(child)) {
381 arrange_Widget(child);
382 }
383 }
384 return;
385 }
386 /* Resize children to fill the parent widget. */ 464 /* Resize children to fill the parent widget. */
387 if (d->flags & resizeChildren_WidgetFlag) { 465 if (d->flags & resizeChildren_WidgetFlag) {
388 const iInt2 dirs = init_I2((d->flags & resizeWidthOfChildren_WidgetFlag) != 0, 466 const iInt2 dirs = init_I2((d->flags & resizeWidthOfChildren_WidgetFlag) != 0,
389 (d->flags & resizeHeightOfChildren_WidgetFlag) != 0); 467 (d->flags & resizeHeightOfChildren_WidgetFlag) != 0);
468 TRACE(d, "resize children, x:%d y:%d", dirs.x, dirs.y);
390 /* Collapse hidden children. */ 469 /* Collapse hidden children. */
391 iBool collapseChanged = iFalse; 470 iBool collapseChanged = iFalse;
392 iForEach(ObjectList, c, d->children) { 471 iForEach(ObjectList, c, d->children) {
393 iWidget *child = as_Widget(c.object); 472 iWidget *child = as_Widget(c.object);
394 if (!isCollapsed_Widget_(child) && child->flags & wasCollapsed_WidgetFlag) { 473 if (!isCollapsed_Widget_(child) && child->flags & wasCollapsed_WidgetFlag) {
395 setFlags_Widget(child, wasCollapsed_WidgetFlag, iFalse); 474 setFlags_Widget(child, wasCollapsed_WidgetFlag, iFalse);
475 TRACE(d, "child %p is uncollapsed", child);
396 /* Undo collapse and determine the normal size again. */ 476 /* Undo collapse and determine the normal size again. */
397 arrange_Widget(d); 477 arrange_Widget_(d);
398 collapseChanged = iTrue; 478 collapseChanged = iTrue;
399 } 479 }
400 else if (isCollapsed_Widget_(child) && ~child->flags & wasCollapsed_WidgetFlag) { 480 else if (isCollapsed_Widget_(child) && ~child->flags & wasCollapsed_WidgetFlag) {
401 setFlags_Widget(child, wasCollapsed_WidgetFlag, iTrue); 481 setFlags_Widget(child, wasCollapsed_WidgetFlag, iTrue);
402 collapseChanged = iTrue; 482 collapseChanged = iTrue;
483 TRACE(d, "child %p flagged as collapsed", child);
403 } 484 }
404 } 485 }
405 if (collapseChanged) { 486 if (collapseChanged) {
406 arrange_Widget(d); /* Redo with the new child sizes. */ 487 TRACE(d, "redoing arrangement due to changes in child collapse state");
488 arrange_Widget_(d); /* Redo with the new child sizes. */
407 return; 489 return;
408 } 490 }
409 const int expCount = numExpandingChildren_Widget_(d); 491 const int expCount = numExpandingChildren_Widget_(d);
492 TRACE(d, "%d expanding children", expCount);
410 /* Only resize the expanding children, not touching the others. */ 493 /* Only resize the expanding children, not touching the others. */
411 if (expCount > 0) { 494 if (expCount > 0) {
412 iInt2 avail = innerRect_Widget_(d).size; 495 iInt2 avail = innerRect_Widget_(d).size;
496 TRACE(d, "inner size: %dx%d", avail.x, avail.y);
413 iConstForEach(ObjectList, i, d->children) { 497 iConstForEach(ObjectList, i, d->children) {
414 const iWidget *child = constAs_Widget(i.object); 498 const iWidget *child = constAs_Widget(i.object);
415 if (!isArranged_Widget_(child)) { 499 if (!isArranged_Widget_(child)) {
@@ -420,9 +504,11 @@ void arrange_Widget(iWidget *d) {
420 } 504 }
421 } 505 }
422 avail = divi_I2(max_I2(zero_I2(), avail), expCount); 506 avail = divi_I2(max_I2(zero_I2(), avail), expCount);
507 TRACE(d, "changing child sizes (expand mode)...");
423 iForEach(ObjectList, j, d->children) { 508 iForEach(ObjectList, j, d->children) {
424 iWidget *child = as_Widget(j.object); 509 iWidget *child = as_Widget(j.object);
425 if (!isArranged_Widget_(child)) { 510 if (!isArranged_Widget_(child)) {
511 TRACE(d, "child %p is not arranged", child);
426 continue; 512 continue;
427 } 513 }
428 if (child->flags & expand_WidgetFlag) { 514 if (child->flags & expand_WidgetFlag) {
@@ -445,6 +531,7 @@ void arrange_Widget(iWidget *d) {
445 } 531 }
446 } 532 }
447 } 533 }
534 TRACE(d, "...done changing child sizes (expand mode)");
448 } 535 }
449 else { 536 else {
450 /* Evenly size all children. */ 537 /* Evenly size all children. */
@@ -458,6 +545,7 @@ void arrange_Widget(iWidget *d) {
458 childSize.y /= childCount; 545 childSize.y /= childCount;
459 unpaddedChildSize.y /= childCount; 546 unpaddedChildSize.y /= childCount;
460 } 547 }
548 TRACE(d, "begin changing child sizes (EVEN mode)...");
461 iForEach(ObjectList, i, d->children) { 549 iForEach(ObjectList, i, d->children) {
462 iWidget *child = as_Widget(i.object); 550 iWidget *child = as_Widget(i.object);
463 if (isArranged_Widget_(child) && ~child->flags & parentCannotResize_WidgetFlag) { 551 if (isArranged_Widget_(child) && ~child->flags & parentCannotResize_WidgetFlag) {
@@ -468,34 +556,52 @@ void arrange_Widget(iWidget *d) {
468 setHeight_Widget_(child, child->flags & unpadded_WidgetFlag ? unpaddedChildSize.y : childSize.y); 556 setHeight_Widget_(child, child->flags & unpadded_WidgetFlag ? unpaddedChildSize.y : childSize.y);
469 } 557 }
470 } 558 }
559 else {
560 TRACE(d, "child %p cannot be resized (parentCannotResize: %d)", child,
561 (child->flags & parentCannotResize_WidgetFlag) != 0);
562 }
471 } 563 }
564 TRACE(d, "...done changing child sizes (EVEN mode)");
472 } 565 }
473 } 566 }
474 if (d->flags & resizeChildrenToWidestChild_WidgetFlag) { 567 if (d->flags & resizeChildrenToWidestChild_WidgetFlag) {
475 const int widest = widestChild_Widget_(d); 568 const int widest = widestChild_Widget_(d);
569 TRACE(d, "resizing children to widest child (%d)...", widest);
476 iForEach(ObjectList, i, d->children) { 570 iForEach(ObjectList, i, d->children) {
477 iWidget *child = as_Widget(i.object); 571 iWidget *child = as_Widget(i.object);
478 if (isArranged_Widget_(child) && ~child->flags & parentCannotResize_WidgetFlag) { 572 if (isArranged_Widget_(child) && ~child->flags & parentCannotResize_WidgetFlag) {
479 setWidth_Widget_(child, widest); 573 setWidth_Widget_(child, widest);
480 } 574 }
575 else {
576 TRACE(d, "child %p cannot be resized (parentCannotResize: %d)", child,
577 (child->flags & parentCannotResize_WidgetFlag) != 0);
578 }
481 } 579 }
580 TRACE(d, "...done resizing children to widest child");
482 } 581 }
483 iInt2 pos = initv_I2(d->padding); 582 iInt2 pos = initv_I2(d->padding);
583 TRACE(d, "begin positioning children from %d,%d (flags:%s%s)...", pos.x, pos.y,
584 d->flags & arrangeHorizontal_WidgetFlag ? " horiz" : "",
585 d->flags & arrangeVertical_WidgetFlag ? " vert" : "");
484 iForEach(ObjectList, i, d->children) { 586 iForEach(ObjectList, i, d->children) {
485 iWidget *child = as_Widget(i.object); 587 iWidget *child = as_Widget(i.object);
486 arrange_Widget(child); 588 arrange_Widget_(child);
487 if (!isArranged_Widget_(child)) { 589 if (!isArranged_Widget_(child)) {
590 TRACE(d, "child %p arranging prohibited", child);
488 continue; 591 continue;
489 } 592 }
490 if (child->flags & centerHorizontal_WidgetFlag) { 593 if (child->flags & centerHorizontal_WidgetFlag) {
594 TRACE(d, "child %p is centered, skipping", child);
491 continue; 595 continue;
492 } 596 }
493 if (d->flags & (arrangeHorizontal_WidgetFlag | arrangeVertical_WidgetFlag)) { 597 if (d->flags & (arrangeHorizontal_WidgetFlag | arrangeVertical_WidgetFlag)) {
494 if (child->flags & 598 if (child->flags &
495 (moveToParentLeftEdge_WidgetFlag | moveToParentRightEdge_WidgetFlag)) { 599 (moveToParentLeftEdge_WidgetFlag | moveToParentRightEdge_WidgetFlag)) {
600 TRACE(d, "child %p is attached an edge, skipping", child);
496 continue; /* Not part of the sequential arrangement .*/ 601 continue; /* Not part of the sequential arrangement .*/
497 } 602 }
498 child->rect.pos = pos; 603 child->rect.pos = pos;
604 TRACE(d, "child %p set position to %d,%d", child, pos.x, pos.y);
499 if (d->flags & arrangeHorizontal_WidgetFlag) { 605 if (d->flags & arrangeHorizontal_WidgetFlag) {
500 pos.x += child->rect.size.x; 606 pos.x += child->rect.size.x;
501 } 607 }
@@ -506,30 +612,20 @@ void arrange_Widget(iWidget *d) {
506 else if ((d->flags & resizeChildren_WidgetFlag) == resizeChildren_WidgetFlag && 612 else if ((d->flags & resizeChildren_WidgetFlag) == resizeChildren_WidgetFlag &&
507 ~child->flags & moveToParentBottomEdge_WidgetFlag) { 613 ~child->flags & moveToParentBottomEdge_WidgetFlag) {
508 child->rect.pos = pos; 614 child->rect.pos = pos;
615 TRACE(d, "child %p set position to %d,%d (not sequential, children being resized)", child, pos.x, pos.y);
509 } 616 }
510 else if (d->flags & resizeWidthOfChildren_WidgetFlag) { 617 else if (d->flags & resizeWidthOfChildren_WidgetFlag) {
511 child->rect.pos.x = pos.x; 618 child->rect.pos.x = pos.x;
619 TRACE(d, "child %p set X to %d (not sequential, children being resized)", child, pos.x);
512 } 620 }
513 } 621 }
622 TRACE(d, "...done positioning children");
514 /* Update the size of the widget according to the arrangement. */ 623 /* Update the size of the widget according to the arrangement. */
515 if (d->flags & arrangeSize_WidgetFlag) { 624 if (d->flags & arrangeSize_WidgetFlag) {
516 iRect bounds = zero_Rect(); 625 iRect bounds;
517 iConstForEach(ObjectList, i, d->children) { 626 boundsOfChildren_Widget_(d, &bounds);
518 const iWidget *child = constAs_Widget(i.object); 627 TRACE(d, "begin arranging own size; bounds of children: %d,%d %dx%d",
519 if (isCollapsed_Widget_(child)) { 628 bounds.pos.x, bounds.pos.y, bounds.size.x, bounds.size.y);
520 continue;
521 }
522 iRect childRect = child->rect;
523 if (child->flags & ignoreForParentWidth_WidgetFlag) {
524 childRect.size.x = 0;
525 }
526 if (isEmpty_Rect(bounds)) {
527 bounds = childRect;
528 }
529 else {
530 bounds = union_Rect(bounds, childRect);
531 }
532 }
533 adjustEdges_Rect(&bounds, -d->padding[1], d->padding[2], d->padding[3], -d->padding[0]); 629 adjustEdges_Rect(&bounds, -d->padding[1], d->padding[2], d->padding[3], -d->padding[0]);
534 if (d->flags & arrangeWidth_WidgetFlag) { 630 if (d->flags & arrangeWidth_WidgetFlag) {
535 setWidth_Widget_(d, bounds.size.x); 631 setWidth_Widget_(d, bounds.size.x);
@@ -539,14 +635,15 @@ void arrange_Widget(iWidget *d) {
539 if (child->flags & 635 if (child->flags &
540 (resizeToParentWidth_WidgetFlag | 636 (resizeToParentWidth_WidgetFlag |
541 moveToParentLeftEdge_WidgetFlag | 637 moveToParentLeftEdge_WidgetFlag |
542 moveToParentBottomEdge_WidgetFlag |
543 moveToParentRightEdge_WidgetFlag)) { 638 moveToParentRightEdge_WidgetFlag)) {
544 arrange_Widget(child); 639 TRACE(d, "rearranging child %p because its size or position depends on parent width", child);
640 arrange_Widget_(child);
545 } 641 }
546 } 642 }
547 if (d->flags & moveToParentRightEdge_WidgetFlag) { 643 if (d->flags & moveToParentRightEdge_WidgetFlag) {
548 /* TODO: Fix this: not DRY. See beginning of method. */ 644 /* TODO: Fix this: not DRY. See beginning of method. */
549 d->rect.pos.x = width_Rect(innerRect_Widget_(d->parent)) - width_Rect(d->rect); 645 d->rect.pos.x = width_Rect(innerRect_Widget_(d->parent)) - width_Rect(d->rect);
646 TRACE(d, "after width change moving to right edge of parent, set X to %d", d, d->rect.pos.x);
550 } 647 }
551 } 648 }
552 if (d->flags & arrangeHeight_WidgetFlag) { 649 if (d->flags & arrangeHeight_WidgetFlag) {
@@ -554,8 +651,10 @@ void arrange_Widget(iWidget *d) {
554 /* Parent size changed, must update the children.*/ 651 /* Parent size changed, must update the children.*/
555 iForEach(ObjectList, j, d->children) { 652 iForEach(ObjectList, j, d->children) {
556 iWidget *child = as_Widget(j.object); 653 iWidget *child = as_Widget(j.object);
557 if (child->flags & resizeToParentHeight_WidgetFlag) { 654 if (child->flags & (resizeToParentHeight_WidgetFlag |
558 arrange_Widget(child); 655 moveToParentBottomEdge_WidgetFlag)) {
656 TRACE(d, "rearranging child %p because its size or position depends on parent height", child);
657 arrange_Widget_(child);
559 } 658 }
560 } 659 }
561 } 660 }
@@ -566,9 +665,31 @@ void arrange_Widget(iWidget *d) {
566 if (d->flags & centerHorizontal_WidgetFlag) { 665 if (d->flags & centerHorizontal_WidgetFlag) {
567 centerHorizontal_Widget_(d); 666 centerHorizontal_Widget_(d);
568 } 667 }
668 TRACE(d, "...done arranging own size");
669 }
670 TRACE(d, "END");
671}
672
673void resetSize_Widget(iWidget *d) {
674 if (~d->flags & fixedWidth_WidgetFlag) {
675 d->rect.size.x = d->minSize.x;
676 }
677 if (~d->flags & fixedHeight_WidgetFlag) {
678 d->rect.size.y = d->minSize.y;
679 }
680 iForEach(ObjectList, i, children_Widget(d)) {
681 iWidget *child = as_Widget(i.object);
682 if (isArranged_Widget_(child)) {
683 resetSize_Widget(child);
684 }
569 } 685 }
570} 686}
571 687
688void arrange_Widget(iWidget *d) {
689 //resetSize_Widget_(d); /* back to initial default sizes */
690 arrange_Widget_(d);
691}
692
572static void applyVisualOffset_Widget_(const iWidget *d, iInt2 *pos) { 693static void applyVisualOffset_Widget_(const iWidget *d, iInt2 *pos) {
573 if (d->flags & (visualOffset_WidgetFlag | dragged_WidgetFlag)) { 694 if (d->flags & (visualOffset_WidgetFlag | dragged_WidgetFlag)) {
574 const int off = iRound(value_Anim(&d->visualOffset)); 695 const int off = iRound(value_Anim(&d->visualOffset));
@@ -1354,6 +1475,7 @@ void refresh_Widget(const iAnyObject *d) {
1354 postRefresh_App(); 1475 postRefresh_App();
1355} 1476}
1356 1477
1478/* Debug utility for inspecting widget trees. */
1357#include "labelwidget.h" 1479#include "labelwidget.h"
1358static void printTree_Widget_(const iWidget *d, int indent) { 1480static void printTree_Widget_(const iWidget *d, int indent) {
1359 for (int i = 0; i < indent; ++i) { 1481 for (int i = 0; i < indent; ++i) {
@@ -1365,9 +1487,17 @@ static void printTree_Widget_(const iWidget *d, int indent) {
1365 cstr_String(text_LabelWidget((const iLabelWidget *) d)), 1487 cstr_String(text_LabelWidget((const iLabelWidget *) d)),
1366 cstr_String(command_LabelWidget((const iLabelWidget *) d))); 1488 cstr_String(command_LabelWidget((const iLabelWidget *) d)));
1367 } 1489 }
1368 printf("size:%dx%d [%d..%d %d:%d] flags:%08llx%s\n", d->rect.size.x, d->rect.size.y, 1490 printf("size:%dx%d {min:%dx%d} [%d..%d %d:%d] flags:%08llx%s%s%s%s%s\n",
1369 d->padding[0], d->padding[2], d->padding[1], d->padding[3], 1491 d->rect.size.x, d->rect.size.y,
1370 (long long unsigned int) d->flags, d->flags & tight_WidgetFlag ? " tight" : ""); 1492 d->minSize.x, d->minSize.y,
1493 d->padding[0], d->padding[2],
1494 d->padding[1], d->padding[3],
1495 (long long unsigned int) d->flags,
1496 d->flags & expand_WidgetFlag ? " exp" : "",
1497 d->flags & tight_WidgetFlag ? " tight" : "",
1498 d->flags & fixedWidth_WidgetFlag ? " fixW" : "",
1499 d->flags & fixedHeight_WidgetFlag ? " fixH" : "",
1500 d->flags & resizeToParentWidth_WidgetFlag ? " rsPrnW" : "");
1371 iConstForEach(ObjectList, i, d->children) { 1501 iConstForEach(ObjectList, i, d->children) {
1372 printTree_Widget_(i.object, indent + 1); 1502 printTree_Widget_(i.object, indent + 1);
1373 } 1503 }
diff --git a/src/ui/widget.h b/src/ui/widget.h
index bde13272..4c5b7d85 100644
--- a/src/ui/widget.h
+++ b/src/ui/widget.h
@@ -129,6 +129,7 @@ struct Impl_Widget {
129 iString id; 129 iString id;
130 int64_t flags; 130 int64_t flags;
131 iRect rect; 131 iRect rect;
132 iInt2 minSize;
132 int padding[4]; /* left, top, right, bottom */ 133 int padding[4]; /* left, top, right, bottom */
133 iAnim visualOffset; 134 iAnim visualOffset;
134 int bgColor; 135 int bgColor;
@@ -221,7 +222,8 @@ iBool hasParent_Widget (const iWidget *d, const iWidget *someParent
221void setId_Widget (iWidget *, const char *id); 222void setId_Widget (iWidget *, const char *id);
222void setFlags_Widget (iWidget *, int64_t flags, iBool set); 223void setFlags_Widget (iWidget *, int64_t flags, iBool set);
223void setPos_Widget (iWidget *, iInt2 pos); 224void setPos_Widget (iWidget *, iInt2 pos);
224void setSize_Widget (iWidget *, iInt2 size); 225void setFixedSize_Widget (iWidget *, iInt2 fixedSize);
226void setMinSize_Widget (iWidget *, iInt2 minSize);
225void setPadding_Widget (iWidget *, int left, int top, int right, int bottom); 227void setPadding_Widget (iWidget *, int left, int top, int right, int bottom);
226iLocalDef void setPadding1_Widget (iWidget *d, int padding) { setPadding_Widget(d, padding, padding, padding, padding); } 228iLocalDef void setPadding1_Widget (iWidget *d, int padding) { setPadding_Widget(d, padding, padding, padding, padding); }
227void setVisualOffset_Widget (iWidget *, int value, uint32_t span, int animFlags); 229void setVisualOffset_Widget (iWidget *, int value, uint32_t span, int animFlags);
@@ -238,6 +240,7 @@ iAny * removeChild_Widget (iWidget *, iAnyObject *child); /* returns a
238iAny * child_Widget (iWidget *, size_t index); /* O(n) */ 240iAny * child_Widget (iWidget *, size_t index); /* O(n) */
239size_t childIndex_Widget (const iWidget *, const iAnyObject *child); /* O(n) */ 241size_t childIndex_Widget (const iWidget *, const iAnyObject *child); /* O(n) */
240void arrange_Widget (iWidget *); 242void arrange_Widget (iWidget *);
243void resetSize_Widget (iWidget *);
241iBool dispatchEvent_Widget (iWidget *, const SDL_Event *); 244iBool dispatchEvent_Widget (iWidget *, const SDL_Event *);
242iBool processEvent_Widget (iWidget *, const SDL_Event *); 245iBool processEvent_Widget (iWidget *, const SDL_Event *);
243void postCommand_Widget (const iAnyObject *, const char *cmd, ...); 246void postCommand_Widget (const iAnyObject *, const char *cmd, ...);
diff --git a/src/ui/window.c b/src/ui/window.c
index a3675ae5..1b6c5e89 100644
--- a/src/ui/window.c
+++ b/src/ui/window.c
@@ -875,10 +875,10 @@ static void updateMetrics_Window_(iWindow *d) {
875 iWidget *appMax = findChild_Widget(winBar, "winbar.max"); 875 iWidget *appMax = findChild_Widget(winBar, "winbar.max");
876 iWidget *appClose = findChild_Widget(winBar, "winbar.close"); 876 iWidget *appClose = findChild_Widget(winBar, "winbar.close");
877 setPadding_Widget(winBar, 0, gap_UI / 3, 0, 0); 877 setPadding_Widget(winBar, 0, gap_UI / 3, 0, 0);
878 setSize_Widget(appMin, init_I2(gap_UI * 11.5f, height_Widget(appTitle))); 878 setFixedSize_Widget(appMin, init_I2(gap_UI * 11.5f, height_Widget(appTitle)));
879 setSize_Widget(appMax, appMin->rect.size); 879 setFixedSize_Widget(appMax, appMin->rect.size);
880 setSize_Widget(appClose, appMin->rect.size); 880 setFixedSize_Widget(appClose, appMin->rect.size);
881 setSize_Widget(appIcon, init_I2(appIconSize_(), appMin->rect.size.y)); 881 setFixedSize_Widget(appIcon, init_I2(appIconSize_(), appMin->rect.size.y));
882 } 882 }
883 iWidget *navBar = findChild_Widget(d->root, "navbar"); 883 iWidget *navBar = findChild_Widget(d->root, "navbar");
884 iWidget *lock = findChild_Widget(navBar, "navbar.lock"); 884 iWidget *lock = findChild_Widget(navBar, "navbar.lock");
@@ -888,7 +888,7 @@ static void updateMetrics_Window_(iWindow *d) {
888 setPadding_Widget(as_Widget(url), 0, gap_UI, 0, gap_UI); 888 setPadding_Widget(as_Widget(url), 0, gap_UI, 0, gap_UI);
889 navBar->rect.size.y = 0; /* recalculate height based on children (FIXME: shouldn't be needed) */ 889 navBar->rect.size.y = 0; /* recalculate height based on children (FIXME: shouldn't be needed) */
890 updateSize_LabelWidget((iLabelWidget *) lock); 890 updateSize_LabelWidget((iLabelWidget *) lock);
891 setSize_Widget(embedPad, init_I2(width_Widget(lock) + gap_UI / 2, 1)); 891 setFixedSize_Widget(embedPad, init_I2(width_Widget(lock) + gap_UI / 2, 1));
892 setContentPadding_InputWidget((iInputWidget *) url, width_Widget(lock) * 0.75, 892 setContentPadding_InputWidget((iInputWidget *) url, width_Widget(lock) * 0.75,
893 width_Widget(lock) * 0.75); 893 width_Widget(lock) * 0.75);
894 rightEmbed->rect.pos.y = gap_UI; 894 rightEmbed->rect.pos.y = gap_UI;
@@ -1251,6 +1251,7 @@ static void updateRootSize_Window_(iWindow *d, iBool notifyAlways) {
1251 const iInt2 oldSize = *size; 1251 const iInt2 oldSize = *size;
1252 SDL_GetRendererOutputSize(d->render, &size->x, &size->y); 1252 SDL_GetRendererOutputSize(d->render, &size->x, &size->y);
1253 size->y -= d->keyboardHeight; 1253 size->y -= d->keyboardHeight;
1254 d->root->minSize = *size;
1254 if (notifyAlways || !isEqual_I2(oldSize, *size)) { 1255 if (notifyAlways || !isEqual_I2(oldSize, *size)) {
1255 const iBool isHoriz = (d->place.lastNotifiedSize.x != size->x); 1256 const iBool isHoriz = (d->place.lastNotifiedSize.x != size->x);
1256 const iBool isVert = (d->place.lastNotifiedSize.y != size->y); 1257 const iBool isVert = (d->place.lastNotifiedSize.y != size->y);
@@ -1465,7 +1466,7 @@ void init_Window(iWindow *d, iRect rect) {
1465 setupWindow_iOS(d); 1466 setupWindow_iOS(d);
1466#endif 1467#endif
1467 d->root = new_Widget(); 1468 d->root = new_Widget();
1468 setFlags_Widget(d->root, focusRoot_WidgetFlag, iTrue); 1469 setFlags_Widget(d->root, fixedSize_WidgetFlag | focusRoot_WidgetFlag, iTrue);
1469 d->presentTime = 0.0; 1470 d->presentTime = 0.0;
1470 d->frameTime = SDL_GetTicks(); 1471 d->frameTime = SDL_GetTicks();
1471 d->loadAnimTimer = 0; 1472 d->loadAnimTimer = 0;
@@ -1840,7 +1841,9 @@ iBool processEvent_Window(iWindow *d, const SDL_Event *ev) {
1840 insertMacMenus_(); 1841 insertMacMenus_();
1841#endif 1842#endif
1842 invalidate_Window_(d); 1843 invalidate_Window_(d);
1844 updatePreferencesLayout_Widget(findChild_Widget(d->root, "prefs"));
1843 arrange_Widget(d->root); 1845 arrange_Widget(d->root);
1846 printTree_Widget(findChild_Widget(d->root, "prefs"));
1844 } 1847 }
1845 if (oldHover != hover_Widget()) { 1848 if (oldHover != hover_Widget()) {
1846 postRefresh_App(); 1849 postRefresh_App();