diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-03-30 15:03:04 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-03-30 15:03:04 +0300 |
commit | 8ab2c6c2a27f888602ea05afaf05a75ecca9bf02 (patch) | |
tree | f6160a88fa05ae2fc6b34c04d96f5cc637f40ba4 | |
parent | 23f70040368e8d5577f89b2bacf303b5dac8561f (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...
-rw-r--r-- | src/ui/bindingswidget.c | 1 | ||||
-rw-r--r-- | src/ui/certimportwidget.c | 4 | ||||
-rw-r--r-- | src/ui/documentwidget.c | 2 | ||||
-rw-r--r-- | src/ui/inputwidget.c | 2 | ||||
-rw-r--r-- | src/ui/lookupwidget.c | 4 | ||||
-rw-r--r-- | src/ui/translation.c | 5 | ||||
-rw-r--r-- | src/ui/util.c | 74 | ||||
-rw-r--r-- | src/ui/util.h | 4 | ||||
-rw-r--r-- | src/ui/widget.c | 222 | ||||
-rw-r--r-- | src/ui/widget.h | 5 | ||||
-rw-r--r-- | src/ui/window.c | 15 |
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) { | |||
110 | void init_BindingsWidget(iBindingsWidget *d) { | 110 | void 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) { | |||
438 | iWidget *makePadding_Widget(int size) { | 438 | iWidget *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 | ||
464 | iWidget *addAction_Widget(iWidget *parent, int key, int kmods, const char *command) { | 464 | iWidget *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 | ||
858 | void resizeToLargestPage_Widget(iWidget *tabs) { | 858 | void 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 | ||
871 | iLabelWidget *tabButtonForPage_Widget_(iWidget *tabs, const iWidget *page) { | 878 | iLabelWidget *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 | ||
1018 | static iBool slidePanelHandler_(iWidget *d, const char *cmd) { | 1025 | static 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 | ||
1792 | iWidget *makeToggle_Widget(const char *id) { | 1802 | iWidget *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 | ||
1893 | void 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 | |||
1882 | iWidget *makePreferences_Widget(void) { | 1923 | iWidget *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); | |||
228 | iWidget * makeQuestion_Widget (const char *title, const char *msg, | 228 | iWidget * makeQuestion_Widget (const char *title, const char *msg, |
229 | const iMenuItem *items, size_t numItems); | 229 | const iMenuItem *items, size_t numItems); |
230 | 230 | ||
231 | iWidget * makePreferences_Widget (void); | 231 | iWidget * makePreferences_Widget (void); |
232 | void updatePreferencesLayout_Widget (iWidget *prefs); | ||
233 | |||
232 | iWidget * makeBookmarkEditor_Widget (void); | 234 | iWidget * makeBookmarkEditor_Widget (void); |
233 | iWidget * makeBookmarkCreation_Widget (const iString *url, const iString *title, iChar icon); | 235 | iWidget * makeBookmarkCreation_Widget (const iString *url, const iString *title, iChar icon); |
234 | iWidget * makeIdentityCreation_Widget (void); | 236 | iWidget * 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 | ||
191 | void setSize_Widget(iWidget *d, iInt2 size) { | 192 | void 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 | ||
206 | void setMinSize_Widget(iWidget *d, iInt2 minSize) { | ||
207 | d->minSize = minSize; | ||
208 | /* rearranging needed to apply this */ | ||
209 | } | ||
210 | |||
205 | void setPadding_Widget(iWidget *d, int left, int top, int right, int bottom) { | 211 | void 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 | ||
277 | static void arrange_Widget_(iWidget *); | ||
278 | static const iBool tracing_ = iFalse; | ||
279 | |||
280 | #define TRACE(d, ...) if (tracing_) { printf_Widget_(d, __VA_ARGS__); } | ||
281 | |||
282 | static 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 | |||
290 | static 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 | |||
271 | static void setWidth_Widget_(iWidget *d, int width) { | 309 | static 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 | ||
288 | static void setHeight_Widget_(iWidget *d, int height) { | 334 | static 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 | ||
300 | iLocalDef iBool isCollapsed_Widget_(const iWidget *d) { | 352 | iLocalDef 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 | ||
341 | void arrange_Widget(iWidget *d) { | 394 | static 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 | |||
414 | static 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 | |||
673 | void 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 | ||
688 | void arrange_Widget(iWidget *d) { | ||
689 | //resetSize_Widget_(d); /* back to initial default sizes */ | ||
690 | arrange_Widget_(d); | ||
691 | } | ||
692 | |||
572 | static void applyVisualOffset_Widget_(const iWidget *d, iInt2 *pos) { | 693 | static 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" |
1358 | static void printTree_Widget_(const iWidget *d, int indent) { | 1480 | static 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 | |||
221 | void setId_Widget (iWidget *, const char *id); | 222 | void setId_Widget (iWidget *, const char *id); |
222 | void setFlags_Widget (iWidget *, int64_t flags, iBool set); | 223 | void setFlags_Widget (iWidget *, int64_t flags, iBool set); |
223 | void setPos_Widget (iWidget *, iInt2 pos); | 224 | void setPos_Widget (iWidget *, iInt2 pos); |
224 | void setSize_Widget (iWidget *, iInt2 size); | 225 | void setFixedSize_Widget (iWidget *, iInt2 fixedSize); |
226 | void setMinSize_Widget (iWidget *, iInt2 minSize); | ||
225 | void setPadding_Widget (iWidget *, int left, int top, int right, int bottom); | 227 | void setPadding_Widget (iWidget *, int left, int top, int right, int bottom); |
226 | iLocalDef void setPadding1_Widget (iWidget *d, int padding) { setPadding_Widget(d, padding, padding, padding, padding); } | 228 | iLocalDef void setPadding1_Widget (iWidget *d, int padding) { setPadding_Widget(d, padding, padding, padding, padding); } |
227 | void setVisualOffset_Widget (iWidget *, int value, uint32_t span, int animFlags); | 229 | void setVisualOffset_Widget (iWidget *, int value, uint32_t span, int animFlags); |
@@ -238,6 +240,7 @@ iAny * removeChild_Widget (iWidget *, iAnyObject *child); /* returns a | |||
238 | iAny * child_Widget (iWidget *, size_t index); /* O(n) */ | 240 | iAny * child_Widget (iWidget *, size_t index); /* O(n) */ |
239 | size_t childIndex_Widget (const iWidget *, const iAnyObject *child); /* O(n) */ | 241 | size_t childIndex_Widget (const iWidget *, const iAnyObject *child); /* O(n) */ |
240 | void arrange_Widget (iWidget *); | 242 | void arrange_Widget (iWidget *); |
243 | void resetSize_Widget (iWidget *); | ||
241 | iBool dispatchEvent_Widget (iWidget *, const SDL_Event *); | 244 | iBool dispatchEvent_Widget (iWidget *, const SDL_Event *); |
242 | iBool processEvent_Widget (iWidget *, const SDL_Event *); | 245 | iBool processEvent_Widget (iWidget *, const SDL_Event *); |
243 | void postCommand_Widget (const iAnyObject *, const char *cmd, ...); | 246 | void 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(); |