summaryrefslogtreecommitdiff
path: root/src/ui
diff options
context:
space:
mode:
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.c9
-rw-r--r--src/ui/inputwidget.c2
-rw-r--r--src/ui/listwidget.c13
-rw-r--r--src/ui/lookupwidget.c4
-rw-r--r--src/ui/paint.c5
-rw-r--r--src/ui/translation.c5
-rw-r--r--src/ui/util.c85
-rw-r--r--src/ui/util.h4
-rw-r--r--src/ui/visbuf.c64
-rw-r--r--src/ui/visbuf.h2
-rw-r--r--src/ui/widget.c218
-rw-r--r--src/ui/widget.h6
-rw-r--r--src/ui/window.c15
15 files changed, 323 insertions, 114 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 3b5ddb7c..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) {
@@ -2040,10 +2040,9 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
2040 fetchNextUnfetchedImage_DocumentWidget_(d)) { 2040 fetchNextUnfetchedImage_DocumentWidget_(d)) {
2041 return iTrue; 2041 return iTrue;
2042 } 2042 }
2043 const float amount = argLabel_Command(cmd, "full") ? 1.0f : 0.5f; 2043 const float amount = argLabel_Command(cmd, "full") != 0 ? 1.0f : 0.5f;
2044 smoothScroll_DocumentWidget_(d, 2044 smoothScroll_DocumentWidget_(d,
2045 dir * (amount * height_Rect(documentBounds_DocumentWidget_(d)) - 2045 dir * amount * height_Rect(documentBounds_DocumentWidget_(d)),
2046 0 * lineHeight_Text(paragraph_FontId)),
2047 smoothDuration_DocumentWidget_); 2046 smoothDuration_DocumentWidget_);
2048 return iTrue; 2047 return iTrue;
2049 } 2048 }
@@ -3391,7 +3390,7 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) {
3391 const iRangei vis = visibleRange_DocumentWidget_(d); 3390 const iRangei vis = visibleRange_DocumentWidget_(d);
3392 const iRangei full = { 0, size_GmDocument(d->doc).y }; 3391 const iRangei full = { 0, size_GmDocument(d->doc).y };
3393 reposition_VisBuf(visBuf, vis); 3392 reposition_VisBuf(visBuf, vis);
3394 iRangei invalidRange[3]; 3393 iRangei invalidRange[iElemCount(d->visBuf->buffers)];
3395 invalidRanges_VisBuf(visBuf, full, invalidRange); 3394 invalidRanges_VisBuf(visBuf, full, invalidRange);
3396 /* Redraw the invalid ranges. */ { 3395 /* Redraw the invalid ranges. */ {
3397 iPaint *p = &ctx.paint; 3396 iPaint *p = &ctx.paint;
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/listwidget.c b/src/ui/listwidget.c
index 942c46dd..95a59090 100644
--- a/src/ui/listwidget.c
+++ b/src/ui/listwidget.c
@@ -361,17 +361,18 @@ static void draw_ListWidget_(const iListWidget *d) {
361 /* TODO: This seems to draw two items per each shift of the visible region, even though 361 /* TODO: This seems to draw two items per each shift of the visible region, even though
362 one should be enough. Probably an off-by-one error in the calculation of the 362 one should be enough. Probably an off-by-one error in the calculation of the
363 invalid range. */ 363 invalid range. */
364 iAssert(d->visBuf->buffers[0].texture); 364 iForIndices(i, d->visBuf->buffers) {
365 iAssert(d->visBuf->buffers[1].texture); 365 iAssert(d->visBuf->buffers[i].texture);
366 iAssert(d->visBuf->buffers[2].texture); 366 }
367 const int bg[3] = { w->bgColor, w->bgColor, w->bgColor }; 367 const int bg[iElemCount(d->visBuf->buffers)] = {
368// const int bg[3] = { red_ColorId, magenta_ColorId, blue_ColorId }; 368 w->bgColor, w->bgColor, w->bgColor, w->bgColor
369 };
369 const int bottom = numItems_ListWidget(d) * d->itemHeight; 370 const int bottom = numItems_ListWidget(d) * d->itemHeight;
370 const iRangei vis = { d->scrollY / d->itemHeight * d->itemHeight, 371 const iRangei vis = { d->scrollY / d->itemHeight * d->itemHeight,
371 ((d->scrollY + bounds.size.y) / d->itemHeight + 1) * d->itemHeight }; 372 ((d->scrollY + bounds.size.y) / d->itemHeight + 1) * d->itemHeight };
372 reposition_VisBuf(d->visBuf, vis); 373 reposition_VisBuf(d->visBuf, vis);
373 /* Check which parts are invalid. */ 374 /* Check which parts are invalid. */
374 iRangei invalidRange[3]; 375 iRangei invalidRange[iElemCount(d->visBuf->buffers)];
375 invalidRanges_VisBuf(d->visBuf, (iRangei){ 0, bottom }, invalidRange); 376 invalidRanges_VisBuf(d->visBuf, (iRangei){ 0, bottom }, invalidRange);
376 iForIndices(i, d->visBuf->buffers) { 377 iForIndices(i, d->visBuf->buffers) {
377 iVisBufTexture *buf = &d->visBuf->buffers[i]; 378 iVisBufTexture *buf = &d->visBuf->buffers[i];
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/paint.c b/src/ui/paint.c
index 36e3f703..857bf20c 100644
--- a/src/ui/paint.c
+++ b/src/ui/paint.c
@@ -67,6 +67,11 @@ void setClip_Paint(iPaint *d, iRect rect) {
67 rect.pos.y -= off; 67 rect.pos.y -= off;
68 rect.size.y = iMax(0, rect.size.y + off); 68 rect.size.y = iMax(0, rect.size.y + off);
69 } 69 }
70 if (rect.pos.x < 0) {
71 const int off = rect.pos.x;
72 rect.pos.x -= off;
73 rect.size.x = iMax(0, rect.size.x + off);
74 }
70 SDL_RenderSetClipRect(renderer_Paint_(d), (const SDL_Rect *) &rect); 75 SDL_RenderSetClipRect(renderer_Paint_(d), (const SDL_Rect *) &rect);
71} 76}
72 77
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 60618181..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_);
@@ -1837,8 +1848,9 @@ static iWidget *appendTwoColumnPage_(iWidget *tabs, const char *title, int short
1837} 1848}
1838 1849
1839static void makeTwoColumnHeading_(const char *title, iWidget *headings, iWidget *values) { 1850static void makeTwoColumnHeading_(const char *title, iWidget *headings, iWidget *values) {
1840 addChild_Widget(headings, 1851 addChildFlags_Widget(headings,
1841 iClob(makeHeading_Widget(format_CStr(uiHeading_ColorEscape "%s", title)))); 1852 iClob(makeHeading_Widget(format_CStr(uiHeading_ColorEscape "%s", title))),
1853 ignoreForParentWidth_WidgetFlag);
1842 addChild_Widget(values, iClob(makeHeading_Widget(""))); 1854 addChild_Widget(values, iClob(makeHeading_Widget("")));
1843} 1855}
1844 1856
@@ -1878,6 +1890,36 @@ static int cmp_MenuItem_(const void *e1, const void *e2) {
1878} 1890}
1879#endif 1891#endif
1880 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
1881iWidget *makePreferences_Widget(void) { 1923iWidget *makePreferences_Widget(void) {
1882 iWidget *dlg = makeSheet_Widget("prefs"); 1924 iWidget *dlg = makeSheet_Widget("prefs");
1883 addChildFlags_Widget(dlg, 1925 addChildFlags_Widget(dlg,
@@ -1984,9 +2026,6 @@ iWidget *makePreferences_Widget(void) {
1984 setId_Widget(addChild_Widget(values, iClob(new_InputWidget(8))), "prefs.uiscale"); 2026 setId_Widget(addChild_Widget(values, iClob(new_InputWidget(8))), "prefs.uiscale");
1985 addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.retainwindow}"))); 2027 addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.retainwindow}")));
1986 addChild_Widget(values, iClob(makeToggle_Widget("prefs.retainwindow"))); 2028 addChild_Widget(values, iClob(makeToggle_Widget("prefs.retainwindow")));
1987 makeTwoColumnHeading_("${heading.prefs.widelayout}", headings, values);
1988 addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.sideicon}")));
1989 addChild_Widget(values, iClob(makeToggle_Widget("prefs.sideicon")));
1990 } 2029 }
1991 /* Colors. */ { 2030 /* Colors. */ {
1992 appendTwoColumnPage_(tabs, "${heading.prefs.colors}", '3', &headings, &values); 2031 appendTwoColumnPage_(tabs, "${heading.prefs.colors}", '3', &headings, &values);
@@ -2023,7 +2062,7 @@ iWidget *makePreferences_Widget(void) {
2023 addChildFlags_Widget(values, iClob(sats), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag); 2062 addChildFlags_Widget(values, iClob(sats), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag);
2024 } 2063 }
2025 /* Layout. */ { 2064 /* Layout. */ {
2026 appendTwoColumnPage_(tabs, "${heading.prefs.style}", '4', &headings, &values); 2065 setId_Widget(appendTwoColumnPage_(tabs, "${heading.prefs.style}", '4', &headings, &values), "prefs.page.style");
2027 makeTwoColumnHeading_("${heading.prefs.fonts}", headings, values); 2066 makeTwoColumnHeading_("${heading.prefs.fonts}", headings, values);
2028 /* Fonts. */ { 2067 /* Fonts. */ {
2029 addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.headingfont}"))); 2068 addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.headingfont}")));
@@ -2063,6 +2102,9 @@ iWidget *makePreferences_Widget(void) {
2063 addChildFlags_Widget(values, iClob(quote), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag); 2102 addChildFlags_Widget(values, iClob(quote), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag);
2064 addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.biglede}"))); 2103 addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.biglede}")));
2065 addChild_Widget(values, iClob(makeToggle_Widget("prefs.biglede"))); 2104 addChild_Widget(values, iClob(makeToggle_Widget("prefs.biglede")));
2105 makeTwoColumnHeading_("${heading.prefs.widelayout}", headings, values);
2106 addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.sideicon}")));
2107 addChild_Widget(values, iClob(makeToggle_Widget("prefs.sideicon")));
2066 } 2108 }
2067 /* Network. */ { 2109 /* Network. */ {
2068 appendTwoColumnPage_(tabs, "${heading.prefs.network}", '5', &headings, &values); 2110 appendTwoColumnPage_(tabs, "${heading.prefs.network}", '5', &headings, &values);
@@ -2095,22 +2137,13 @@ iWidget *makePreferences_Widget(void) {
2095 appendFramelessTabPage_(tabs, iClob(bind), "${heading.prefs.keys}", '6', KMOD_PRIMARY); 2137 appendFramelessTabPage_(tabs, iClob(bind), "${heading.prefs.keys}", '6', KMOD_PRIMARY);
2096 } 2138 }
2097 addChild_Widget(dlg, iClob(makePadding_Widget(gap_UI))); 2139 addChild_Widget(dlg, iClob(makePadding_Widget(gap_UI)));
2098 resizeToLargestPage_Widget(tabs); 2140 updatePreferencesLayout_Widget(dlg);
2099 arrange_Widget(dlg);
2100 /* Set input field sizes. */ {
2101 expandInputFieldWidth_(findChild_Widget(tabs, "prefs.searchurl"));
2102 expandInputFieldWidth_(findChild_Widget(tabs, "prefs.downloads"));
2103 expandInputFieldWidth_(findChild_Widget(tabs, "prefs.ca.file"));
2104 expandInputFieldWidth_(findChild_Widget(tabs, "prefs.ca.path"));
2105 expandInputFieldWidth_(findChild_Widget(tabs, "prefs.proxy.gemini"));
2106 expandInputFieldWidth_(findChild_Widget(tabs, "prefs.proxy.gopher"));
2107 expandInputFieldWidth_(findChild_Widget(tabs, "prefs.proxy.http"));
2108 }
2109 addChild_Widget(dlg, 2141 addChild_Widget(dlg,
2110 iClob(makeDialogButtons_Widget( 2142 iClob(makeDialogButtons_Widget(
2111 (iMenuItem[]){ { "${dismiss}", SDLK_ESCAPE, 0, "prefs.dismiss" } }, 1))); 2143 (iMenuItem[]){ { "${dismiss}", SDLK_ESCAPE, 0, "prefs.dismiss" } }, 1)));
2112 addChild_Widget(get_Window()->root, iClob(dlg)); 2144 addChild_Widget(get_Window()->root, iClob(dlg));
2113 finalizeSheet_Widget(dlg); 2145 finalizeSheet_Widget(dlg);
2146 //printTree_Widget(dlg);
2114 return dlg; 2147 return dlg;
2115} 2148}
2116 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/visbuf.c b/src/ui/visbuf.c
index 0f9e1092..198ad574 100644
--- a/src/ui/visbuf.c
+++ b/src/ui/visbuf.c
@@ -75,12 +75,13 @@ void dealloc_VisBuf(iVisBuf *d) {
75void reposition_VisBuf(iVisBuf *d, const iRangei vis) { 75void reposition_VisBuf(iVisBuf *d, const iRangei vis) {
76 d->vis = vis; 76 d->vis = vis;
77 iRangei good = { 0, 0 }; 77 iRangei good = { 0, 0 };
78 size_t avail[3], numAvail = 0; 78 size_t avail[iElemCount(d->buffers)], numAvail = 0;
79 /* Check which buffers are available for reuse. */ { 79 /* Check which buffers are available for reuse. */ {
80 iForIndices(i, d->buffers) { 80 iForIndices(i, d->buffers) {
81 iVisBufTexture *buf = d->buffers + i; 81 iVisBufTexture *buf = d->buffers + i;
82 const iRangei region = { buf->origin, buf->origin + d->texSize.y }; 82 const iRangei region = { buf->origin, buf->origin + d->texSize.y };
83 if (region.start >= vis.end || region.end <= vis.start) { 83 if (isEmpty_Rangei(buf->validRange) ||
84 buf->validRange.start >= vis.end || buf->validRange.end <= vis.start) {
84 avail[numAvail++] = i; 85 avail[numAvail++] = i;
85 iZap(buf->validRange); 86 iZap(buf->validRange);
86 } 87 }
@@ -89,25 +90,50 @@ void reposition_VisBuf(iVisBuf *d, const iRangei vis) {
89 } 90 }
90 } 91 }
91 } 92 }
92 if (numAvail == iElemCount(d->buffers)) { 93 iBool wasChanged = iFalse;
93 /* All buffers are outside the visible range, do a reset. */ 94 iBool doReset = (numAvail == iElemCount(d->buffers));
94 d->buffers[0].origin = vis.start; 95 /* Try to extend to cover the visible range. */
95 d->buffers[1].origin = vis.start + d->texSize.y; 96 while (!doReset && vis.start < good.start) {
96 d->buffers[2].origin = vis.start + 2 * d->texSize.y; 97 if (numAvail == 0) {
98 doReset = iTrue;
99 break;
100 }
101 good.start -= d->texSize.y;
102 d->buffers[avail[--numAvail]].origin = good.start;
103 wasChanged = iTrue;
97 } 104 }
98 else { 105 while (!doReset && vis.end > good.end) {
99 /* Extend to cover the visible range. */ 106 if (numAvail == 0) {
100 while (vis.start < good.start) { 107 doReset = iTrue;
101 iAssert(numAvail > 0); 108 break;
102 d->buffers[avail[--numAvail]].origin = good.start - d->texSize.y;
103 good.start -= d->texSize.y;
104 } 109 }
105 while (vis.end > good.end) { 110 d->buffers[avail[--numAvail]].origin = good.end;
106 iAssert(numAvail > 0); 111 good.end += d->texSize.y;
107 d->buffers[avail[--numAvail]].origin = good.end; 112 wasChanged = iTrue;
108 good.end += d->texSize.y; 113 }
114 if (doReset) {
115// puts("VisBuf reset!");
116// fflush(stdout);
117 wasChanged = iTrue;
118 int pos = -1;
119 iForIndices(i, d->buffers) {
120 iZap(d->buffers[i].validRange);
121 d->buffers[i].origin = vis.start + pos++ * d->texSize.y;
109 } 122 }
110 } 123 }
124#if 0
125 if (wasChanged) {
126 printf("\nVISIBLE RANGE: %d ... %d\n", vis.start, vis.end);
127 iForIndices(i, d->buffers) {
128 const iVisBufTexture *bt = &d->buffers[i];
129 printf(" %zu: buf %5d ... %5d valid %5d ... %5d\n", i, bt->origin,
130 bt->origin + d->texSize.y,
131 bt->validRange.start,
132 bt->validRange.end);
133 }
134 fflush(stdout);
135 }
136#endif
111} 137}
112 138
113void invalidRanges_VisBuf(const iVisBuf *d, const iRangei full, iRangei *out_invalidRanges) { 139void invalidRanges_VisBuf(const iVisBuf *d, const iRangei full, iRangei *out_invalidRanges) {
diff --git a/src/ui/visbuf.h b/src/ui/visbuf.h
index 4b0908f9..b285e90b 100644
--- a/src/ui/visbuf.h
+++ b/src/ui/visbuf.h
@@ -38,7 +38,7 @@ struct Impl_VisBufTexture {
38struct Impl_VisBuf { 38struct Impl_VisBuf {
39 iInt2 texSize; 39 iInt2 texSize;
40 iRangei vis; 40 iRangei vis;
41 iVisBufTexture buffers[3]; 41 iVisBufTexture buffers[4];
42}; 42};
43 43
44iDeclareTypeConstruction(VisBuf) 44iDeclareTypeConstruction(VisBuf)
diff --git a/src/ui/widget.c b/src/ui/widget.c
index e19a9482..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,26 +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 if (isEmpty_Rect(bounds)) {
523 bounds = child->rect;
524 }
525 else {
526 bounds = union_Rect(bounds, child->rect);
527 }
528 }
529 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]);
530 if (d->flags & arrangeWidth_WidgetFlag) { 630 if (d->flags & arrangeWidth_WidgetFlag) {
531 setWidth_Widget_(d, bounds.size.x); 631 setWidth_Widget_(d, bounds.size.x);
@@ -535,14 +635,15 @@ void arrange_Widget(iWidget *d) {
535 if (child->flags & 635 if (child->flags &
536 (resizeToParentWidth_WidgetFlag | 636 (resizeToParentWidth_WidgetFlag |
537 moveToParentLeftEdge_WidgetFlag | 637 moveToParentLeftEdge_WidgetFlag |
538 moveToParentBottomEdge_WidgetFlag |
539 moveToParentRightEdge_WidgetFlag)) { 638 moveToParentRightEdge_WidgetFlag)) {
540 arrange_Widget(child); 639 TRACE(d, "rearranging child %p because its size or position depends on parent width", child);
640 arrange_Widget_(child);
541 } 641 }
542 } 642 }
543 if (d->flags & moveToParentRightEdge_WidgetFlag) { 643 if (d->flags & moveToParentRightEdge_WidgetFlag) {
544 /* TODO: Fix this: not DRY. See beginning of method. */ 644 /* TODO: Fix this: not DRY. See beginning of method. */
545 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);
546 } 647 }
547 } 648 }
548 if (d->flags & arrangeHeight_WidgetFlag) { 649 if (d->flags & arrangeHeight_WidgetFlag) {
@@ -550,8 +651,10 @@ void arrange_Widget(iWidget *d) {
550 /* Parent size changed, must update the children.*/ 651 /* Parent size changed, must update the children.*/
551 iForEach(ObjectList, j, d->children) { 652 iForEach(ObjectList, j, d->children) {
552 iWidget *child = as_Widget(j.object); 653 iWidget *child = as_Widget(j.object);
553 if (child->flags & resizeToParentHeight_WidgetFlag) { 654 if (child->flags & (resizeToParentHeight_WidgetFlag |
554 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);
555 } 658 }
556 } 659 }
557 } 660 }
@@ -562,9 +665,31 @@ void arrange_Widget(iWidget *d) {
562 if (d->flags & centerHorizontal_WidgetFlag) { 665 if (d->flags & centerHorizontal_WidgetFlag) {
563 centerHorizontal_Widget_(d); 666 centerHorizontal_Widget_(d);
564 } 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 }
565 } 685 }
566} 686}
567 687
688void arrange_Widget(iWidget *d) {
689 //resetSize_Widget_(d); /* back to initial default sizes */
690 arrange_Widget_(d);
691}
692
568static void applyVisualOffset_Widget_(const iWidget *d, iInt2 *pos) { 693static void applyVisualOffset_Widget_(const iWidget *d, iInt2 *pos) {
569 if (d->flags & (visualOffset_WidgetFlag | dragged_WidgetFlag)) { 694 if (d->flags & (visualOffset_WidgetFlag | dragged_WidgetFlag)) {
570 const int off = iRound(value_Anim(&d->visualOffset)); 695 const int off = iRound(value_Anim(&d->visualOffset));
@@ -1350,6 +1475,7 @@ void refresh_Widget(const iAnyObject *d) {
1350 postRefresh_App(); 1475 postRefresh_App();
1351} 1476}
1352 1477
1478/* Debug utility for inspecting widget trees. */
1353#include "labelwidget.h" 1479#include "labelwidget.h"
1354static void printTree_Widget_(const iWidget *d, int indent) { 1480static void printTree_Widget_(const iWidget *d, int indent) {
1355 for (int i = 0; i < indent; ++i) { 1481 for (int i = 0; i < indent; ++i) {
@@ -1361,9 +1487,17 @@ static void printTree_Widget_(const iWidget *d, int indent) {
1361 cstr_String(text_LabelWidget((const iLabelWidget *) d)), 1487 cstr_String(text_LabelWidget((const iLabelWidget *) d)),
1362 cstr_String(command_LabelWidget((const iLabelWidget *) d))); 1488 cstr_String(command_LabelWidget((const iLabelWidget *) d)));
1363 } 1489 }
1364 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",
1365 d->padding[0], d->padding[2], d->padding[1], d->padding[3], 1491 d->rect.size.x, d->rect.size.y,
1366 (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" : "");
1367 iConstForEach(ObjectList, i, d->children) { 1501 iConstForEach(ObjectList, i, d->children) {
1368 printTree_Widget_(i.object, indent + 1); 1502 printTree_Widget_(i.object, indent + 1);
1369 } 1503 }
diff --git a/src/ui/widget.h b/src/ui/widget.h
index ad7ce168..4c5b7d85 100644
--- a/src/ui/widget.h
+++ b/src/ui/widget.h
@@ -112,6 +112,7 @@ enum iWidgetFlag {
112#define safePadding_WidgetFlag iBit64(56) /* padded using safe area insets */ 112#define safePadding_WidgetFlag iBit64(56) /* padded using safe area insets */
113#define moveToParentBottomEdge_WidgetFlag iBit64(57) 113#define moveToParentBottomEdge_WidgetFlag iBit64(57)
114#define parentCannotResizeHeight_WidgetFlag iBit64(58) 114#define parentCannotResizeHeight_WidgetFlag iBit64(58)
115#define ignoreForParentWidth_WidgetFlag iBit64(59)
115 116
116enum iWidgetAddPos { 117enum iWidgetAddPos {
117 back_WidgetAddPos, 118 back_WidgetAddPos,
@@ -128,6 +129,7 @@ struct Impl_Widget {
128 iString id; 129 iString id;
129 int64_t flags; 130 int64_t flags;
130 iRect rect; 131 iRect rect;
132 iInt2 minSize;
131 int padding[4]; /* left, top, right, bottom */ 133 int padding[4]; /* left, top, right, bottom */
132 iAnim visualOffset; 134 iAnim visualOffset;
133 int bgColor; 135 int bgColor;
@@ -220,7 +222,8 @@ iBool hasParent_Widget (const iWidget *d, const iWidget *someParent
220void setId_Widget (iWidget *, const char *id); 222void setId_Widget (iWidget *, const char *id);
221void setFlags_Widget (iWidget *, int64_t flags, iBool set); 223void setFlags_Widget (iWidget *, int64_t flags, iBool set);
222void setPos_Widget (iWidget *, iInt2 pos); 224void setPos_Widget (iWidget *, iInt2 pos);
223void setSize_Widget (iWidget *, iInt2 size); 225void setFixedSize_Widget (iWidget *, iInt2 fixedSize);
226void setMinSize_Widget (iWidget *, iInt2 minSize);
224void setPadding_Widget (iWidget *, int left, int top, int right, int bottom); 227void setPadding_Widget (iWidget *, int left, int top, int right, int bottom);
225iLocalDef 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); }
226void setVisualOffset_Widget (iWidget *, int value, uint32_t span, int animFlags); 229void setVisualOffset_Widget (iWidget *, int value, uint32_t span, int animFlags);
@@ -237,6 +240,7 @@ iAny * removeChild_Widget (iWidget *, iAnyObject *child); /* returns a
237iAny * child_Widget (iWidget *, size_t index); /* O(n) */ 240iAny * child_Widget (iWidget *, size_t index); /* O(n) */
238size_t childIndex_Widget (const iWidget *, const iAnyObject *child); /* O(n) */ 241size_t childIndex_Widget (const iWidget *, const iAnyObject *child); /* O(n) */
239void arrange_Widget (iWidget *); 242void arrange_Widget (iWidget *);
243void resetSize_Widget (iWidget *);
240iBool dispatchEvent_Widget (iWidget *, const SDL_Event *); 244iBool dispatchEvent_Widget (iWidget *, const SDL_Event *);
241iBool processEvent_Widget (iWidget *, const SDL_Event *); 245iBool processEvent_Widget (iWidget *, const SDL_Event *);
242void 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();