summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-04-30 12:17:07 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-04-30 12:17:07 +0300
commit1495d08163a4bfcd5db5f6c3b06f031032c5461f (patch)
tree7538e5ca6f054afd292ca9c4b7b7ff81a3d1222f
parentfed2b149aeb5c5cd692421602e0fe77ceffb8b28 (diff)
Revising and fixing widget layout
Some of the logic for arranging widgets was invalid, leading to problems with the navbar: - cannot resize children if own size depends on their size - expanding children won't expand unless resizing all children
-rw-r--r--src/ui/lookupwidget.c5
-rw-r--r--src/ui/root.c130
-rw-r--r--src/ui/util.c4
-rw-r--r--src/ui/widget.c161
-rw-r--r--src/ui/widget.h4
-rw-r--r--src/ui/window.c5
6 files changed, 182 insertions, 127 deletions
diff --git a/src/ui/lookupwidget.c b/src/ui/lookupwidget.c
index 7a0fe237..b06523f9 100644
--- a/src/ui/lookupwidget.c
+++ b/src/ui/lookupwidget.c
@@ -655,11 +655,10 @@ static iBool processEvent_LookupWidget_(iLookupWidget *d, const SDL_Event *ev) {
655 equal_Rangecc(range_Command(cmd, "id"), "navbar"))) { 655 equal_Rangecc(range_Command(cmd, "id"), "navbar"))) {
656 /* Position the lookup popup under the URL bar. */ { 656 /* Position the lookup popup under the URL bar. */ {
657 iRoot *root = w->root; 657 iRoot *root = w->root;
658 const iInt2 rootSize = size_Root(root);
659 const iRect navBarBounds = bounds_Widget(findChild_Widget(root->widget, "navbar")); 658 const iRect navBarBounds = bounds_Widget(findChild_Widget(root->widget, "navbar"));
660 iWidget *url = findChild_Widget(root->widget, "url"); 659 iWidget *url = findChild_Widget(root->widget, "url");
661 setFixedSize_Widget(w, init_I2(width_Widget(url), 660 setFixedSize_Widget(w, init_I2(width_Widget(url),
662 (rootSize.y - bottom_Rect(navBarBounds)) / 2)); 661 (bottom_Rect(rect_Root(root)) - bottom_Rect(navBarBounds)) / 2));
663 setPos_Widget(w, windowToLocal_Widget(w, bottomLeft_Rect(bounds_Widget(url)))); 662 setPos_Widget(w, windowToLocal_Widget(w, bottomLeft_Rect(bounds_Widget(url))));
664#if defined (iPlatformAppleMobile) 663#if defined (iPlatformAppleMobile)
665 /* Adjust height based on keyboard size. */ { 664 /* Adjust height based on keyboard size. */ {
@@ -667,7 +666,7 @@ static iBool processEvent_LookupWidget_(iLookupWidget *d, const SDL_Event *ev) {
667 if (deviceType_App() == phone_AppDeviceType) { 666 if (deviceType_App() == phone_AppDeviceType) {
668 float l, r; 667 float l, r;
669 safeAreaInsets_iOS(&l, NULL, &r, NULL); 668 safeAreaInsets_iOS(&l, NULL, &r, NULL);
670 w->rect.size.x = rootSize.x - l - r; 669 w->rect.size.x = size_Root(root).x - l - r;
671 w->rect.pos.x = l; 670 w->rect.pos.x = l;
672 /* TODO: Need to use windowToLocal_Widget? */ 671 /* TODO: Need to use windowToLocal_Widget? */
673 } 672 }
diff --git a/src/ui/root.c b/src/ui/root.c
index a97c8d49..287b641e 100644
--- a/src/ui/root.c
+++ b/src/ui/root.c
@@ -541,59 +541,63 @@ iBool isNarrow_Root(const iRoot *d) {
541 return width_Rect(safeRect_Root(d)) / gap_UI < 140; 541 return width_Rect(safeRect_Root(d)) / gap_UI < 140;
542} 542}
543 543
544static iBool handleNavBarCommands_(iWidget *navBar, const char *cmd) { 544static void updateNavBarSize_(iWidget *navBar) {
545 if (equal_Command(cmd, "window.resized") || equal_Command(cmd, "metrics.changed")) { 545 const iBool isPhone = deviceType_App() == phone_AppDeviceType;
546 const iBool isPhone = deviceType_App() == phone_AppDeviceType; 546 const iBool isNarrow = !isPhone && isNarrow_Root(navBar->root);
547 const iBool isNarrow = !isPhone && isNarrow_Root(navBar->root); 547 /* Adjust navbar padding. */ {
548 /* Adjust navbar padding. */ { 548 int hPad = isPhone && isPortrait_App() ? 0 : (isPhone || isNarrow) ? gap_UI / 2
549 int hPad = isPhone && isPortrait_App() ? 0 : (isPhone || isNarrow) ? gap_UI / 2 549 : gap_UI * 3 / 2;
550 : gap_UI * 3 / 2; 550 int vPad = gap_UI * 3 / 2;
551 int vPad = gap_UI * 3 / 2; 551 int topPad = !findWidget_Root("winbar") ? gap_UI / 2 : 0;
552 int topPad = !findWidget_App("winbar") ? gap_UI / 2 : 0; 552 setPadding_Widget(navBar, hPad, vPad / 3 + topPad, hPad, vPad / 2);
553 setPadding_Widget(navBar, hPad, vPad / 3 + topPad, hPad, vPad / 2); 553 }
554 } 554 /* Button sizing. */
555 /* Button sizing. */ 555 if (isNarrow ^ ((flags_Widget(navBar) & tight_WidgetFlag) != 0)) {
556 if (isNarrow ^ ((flags_Widget(navBar) & tight_WidgetFlag) != 0)) { 556 setFlags_Widget(navBar, tight_WidgetFlag, isNarrow);
557 setFlags_Widget(navBar, tight_WidgetFlag, isNarrow); 557 iForEach(ObjectList, i, navBar->children) {
558 iForEach(ObjectList, i, navBar->children) { 558 iWidget *child = as_Widget(i.object);
559 iWidget *child = as_Widget(i.object); 559 setFlags_Widget(child, tight_WidgetFlag, isNarrow);
560 setFlags_Widget(child, tight_WidgetFlag, isNarrow); 560 if (isInstance_Object(i.object, &Class_LabelWidget)) {
561 if (isInstance_Object(i.object, &Class_LabelWidget)) { 561 iLabelWidget *label = i.object;
562 iLabelWidget *label = i.object; 562 updateSize_LabelWidget(label);
563 updateSize_LabelWidget(label);
564 }
565 } 563 }
566 /* Note that InputWidget uses the `tight` flag to adjust its inner padding. */
567 /* TODO: Is this redundant? See `updateMetrics_Window_()`. */
568 const int embedButtonWidth = width_Widget(findChild_Widget(navBar, "navbar.lock"));
569 setContentPadding_InputWidget(findChild_Widget(navBar, "url"),
570 embedButtonWidth * 0.75f,
571 embedButtonWidth * 0.75f);
572 } 564 }
573 if (isPhone) { 565 /* Note that InputWidget uses the `tight` flag to adjust its inner padding. */
574 static const char *buttons[] = { "navbar.back", "navbar.forward", "navbar.sidebar", 566 /* TODO: Is this redundant? See `updateMetrics_Window_()`. */
575 "navbar.ident", "navbar.home", "navbar.menu" }; 567 const int embedButtonWidth = width_Widget(findChild_Widget(navBar, "navbar.lock"));
576 iWidget *toolBar = findWidget_App("toolbar"); 568 setContentPadding_InputWidget(findChild_Widget(navBar, "url"),
577 setVisualOffset_Widget(toolBar, 0, 0, 0); 569 embedButtonWidth * 0.75f,
578 setFlags_Widget(toolBar, hidden_WidgetFlag, isLandscape_App()); 570 embedButtonWidth * 0.75f);
579 iForIndices(i, buttons) { 571 }
580 iLabelWidget *btn = findChild_Widget(navBar, buttons[i]); 572 if (isPhone) {
581 setFlags_Widget(as_Widget(btn), hidden_WidgetFlag, isPortrait_App()); 573 static const char *buttons[] = { "navbar.back", "navbar.forward", "navbar.sidebar",
582 if (isLandscape_App()) { 574 "navbar.ident", "navbar.home", "navbar.menu" };
583 /* Collapsing sets size to zero and the label doesn't know when to update 575 iWidget *toolBar = findWidget_App("toolbar");
584 its own size automatically. */ 576 setVisualOffset_Widget(toolBar, 0, 0, 0);
585 updateSize_LabelWidget(btn); 577 setFlags_Widget(toolBar, hidden_WidgetFlag, isLandscape_App());
586 } 578 iForIndices(i, buttons) {
579 iLabelWidget *btn = findChild_Widget(navBar, buttons[i]);
580 setFlags_Widget(as_Widget(btn), hidden_WidgetFlag, isPortrait_App());
581 if (isLandscape_App()) {
582 /* Collapsing sets size to zero and the label doesn't know when to update
583 its own size automatically. */
584 updateSize_LabelWidget(btn);
587 } 585 }
588 arrange_Widget(navBar->root->widget);
589 } 586 }
590 /* Resize the URL input field. */ { 587 arrange_Widget(navBar->root->widget);
591 iWidget *urlBar = findChild_Widget(navBar, "url"); 588 }
592 urlBar->rect.size.x = iMini(navBarAvailableSpace_(navBar), 167 * gap_UI); 589 /* Resize the URL input field. */ {
593 arrange_Widget(navBar); 590 iWidget *urlBar = findChild_Widget(navBar, "url");
594 } 591 urlBar->rect.size.x = iMini(navBarAvailableSpace_(navBar), 167 * gap_UI);
595 refresh_Widget(navBar); 592 arrange_Widget(navBar);
596 postCommand_Widget(navBar, "layout.changed id:navbar"); 593 }
594 refresh_Widget(navBar);
595 postCommand_Widget(navBar, "layout.changed id:navbar");
596}
597
598static iBool handleNavBarCommands_(iWidget *navBar, const char *cmd) {
599 if (equal_Command(cmd, "window.resized") || equal_Command(cmd, "metrics.changed")) {
600 updateNavBarSize_(navBar);
597 return iFalse; 601 return iFalse;
598 } 602 }
599 else if (equal_Command(cmd, "window.reload.update")) { 603 else if (equal_Command(cmd, "window.reload.update")) {
@@ -740,16 +744,18 @@ static iBool handleSearchBarCommands_(iWidget *searchBar, const char *cmd) {
740 else if (equal_Command(cmd, "focus.gained")) { 744 else if (equal_Command(cmd, "focus.gained")) {
741 if (pointer_Command(cmd) == findChild_Widget(searchBar, "find.input")) { 745 if (pointer_Command(cmd) == findChild_Widget(searchBar, "find.input")) {
742 if (!isVisible_Widget(searchBar)) { 746 if (!isVisible_Widget(searchBar)) {
743 setFlags_Widget(searchBar, hidden_WidgetFlag | disabled_WidgetFlag, iFalse); 747// setFlags_Widget(searchBar, hidden_WidgetFlag | disabled_WidgetFlag, iFalse);
744 arrange_Widget(root_Widget(searchBar)); 748// arrange_Widget(root_Widget(searchBar));
745 postRefresh_App(); 749// postRefresh_App();
750 showCollapsed_Widget(searchBar, iTrue);
746 } 751 }
747 } 752 }
748 } 753 }
749 else if (equal_Command(cmd, "find.close")) { 754 else if (equal_Command(cmd, "find.close")) {
750 if (isVisible_Widget(searchBar)) { 755 if (isVisible_Widget(searchBar)) {
751 setFlags_Widget(searchBar, hidden_WidgetFlag | disabled_WidgetFlag, iTrue); 756 //setFlags_Widget(searchBar, hidden_WidgetFlag | disabled_WidgetFlag, iTrue);
752 arrange_Widget(searchBar->parent); 757 //arrange_Widget(searchBar->parent);
758 showCollapsed_Widget(searchBar, iFalse);
753 if (isFocused_Widget(findChild_Widget(searchBar, "find.input"))) { 759 if (isFocused_Widget(findChild_Widget(searchBar, "find.input"))) {
754 setFocus_Widget(NULL); 760 setFocus_Widget(NULL);
755 } 761 }
@@ -886,6 +892,7 @@ void updateMetrics_Root(iRoot *d) {
886 892
887void createUserInterface_Root(iRoot *d) { 893void createUserInterface_Root(iRoot *d) {
888 iWidget *root = d->widget = new_Widget(); 894 iWidget *root = d->widget = new_Widget();
895 root->rect.size = get_Window()->size;
889 iAssert(root->root == d); 896 iAssert(root->root == d);
890 setId_Widget(root, "root"); 897 setId_Widget(root, "root");
891 /* Children of root cover the entire window. */ 898 /* Children of root cover the entire window. */
@@ -944,13 +951,14 @@ void createUserInterface_Root(iRoot *d) {
944 setBackgroundColor_Widget(winBar, uiBackground_ColorId); 951 setBackgroundColor_Widget(winBar, uiBackground_ColorId);
945 } 952 }
946#endif 953#endif
954 iWidget *navBar;
947 /* Navigation bar. */ { 955 /* Navigation bar. */ {
948 iWidget *navBar = new_Widget(); 956 navBar = new_Widget();
949 setId_Widget(navBar, "navbar"); 957 setId_Widget(navBar, "navbar");
950 setFlags_Widget(navBar, 958 setFlags_Widget(navBar,
951 hittable_WidgetFlag | /* context menu */ 959 hittable_WidgetFlag | /* context menu */
952 arrangeHeight_WidgetFlag | 960 arrangeHeight_WidgetFlag |
953 resizeChildren_WidgetFlag | 961 resizeWidthOfChildren_WidgetFlag |
954 arrangeHorizontal_WidgetFlag | 962 arrangeHorizontal_WidgetFlag |
955 drawBackgroundToHorizontalSafeArea_WidgetFlag | 963 drawBackgroundToHorizontalSafeArea_WidgetFlag |
956 drawBackgroundToVerticalSafeArea_WidgetFlag, 964 drawBackgroundToVerticalSafeArea_WidgetFlag,
@@ -958,7 +966,8 @@ void createUserInterface_Root(iRoot *d) {
958 addChild_Widget(div, iClob(navBar)); 966 addChild_Widget(div, iClob(navBar));
959 setBackgroundColor_Widget(navBar, uiBackground_ColorId); 967 setBackgroundColor_Widget(navBar, uiBackground_ColorId);
960 setCommandHandler_Widget(navBar, handleNavBarCommands_); 968 setCommandHandler_Widget(navBar, handleNavBarCommands_);
961 setId_Widget(addChildFlags_Widget(navBar, iClob(newIcon_LabelWidget(backArrow_Icon, 0, 0, "navigate.back")), collapse_WidgetFlag), "navbar.back"); 969 iWidget *navBack;
970 setId_Widget(navBack = addChildFlags_Widget(navBar, iClob(newIcon_LabelWidget(backArrow_Icon, 0, 0, "navigate.back")), collapse_WidgetFlag), "navbar.back");
962 setId_Widget(addChildFlags_Widget(navBar, iClob(newIcon_LabelWidget(forwardArrow_Icon, 0, 0, "navigate.forward")), collapse_WidgetFlag), "navbar.forward"); 971 setId_Widget(addChildFlags_Widget(navBar, iClob(newIcon_LabelWidget(forwardArrow_Icon, 0, 0, "navigate.forward")), collapse_WidgetFlag), "navbar.forward");
963 /* Mobile devices have a button for easier access to the left sidebar. */ 972 /* Mobile devices have a button for easier access to the left sidebar. */
964 if (deviceType_App() != desktop_AppDeviceType) { 973 if (deviceType_App() != desktop_AppDeviceType) {
@@ -999,7 +1008,7 @@ void createUserInterface_Root(iRoot *d) {
999 setId_Widget(rightEmbed, "url.rightembed"); 1008 setId_Widget(rightEmbed, "url.rightembed");
1000 addChildFlags_Widget(as_Widget(url), 1009 addChildFlags_Widget(as_Widget(url),
1001 iClob(rightEmbed), 1010 iClob(rightEmbed),
1002 arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag | 1011 arrangeHorizontal_WidgetFlag | arrangeWidth_WidgetFlag |
1003 resizeHeightOfChildren_WidgetFlag | 1012 resizeHeightOfChildren_WidgetFlag |
1004 moveToParentRightEdge_WidgetFlag); 1013 moveToParentRightEdge_WidgetFlag);
1005 /* Feeds refresh indicator is inside the input field. */ { 1014 /* Feeds refresh indicator is inside the input field. */ {
@@ -1132,8 +1141,8 @@ void createUserInterface_Root(iRoot *d) {
1132 iWidget *searchBar = new_Widget(); 1141 iWidget *searchBar = new_Widget();
1133 setId_Widget(searchBar, "search"); 1142 setId_Widget(searchBar, "search");
1134 setFlags_Widget(searchBar, 1143 setFlags_Widget(searchBar,
1135 hidden_WidgetFlag | disabled_WidgetFlag | collapse_WidgetFlag | 1144 hidden_WidgetFlag | disabledWhenHidden_WidgetFlag | collapse_WidgetFlag |
1136 arrangeHeight_WidgetFlag | resizeChildren_WidgetFlag | 1145 arrangeHeight_WidgetFlag | resizeWidthOfChildren_WidgetFlag |
1137 arrangeHorizontal_WidgetFlag, 1146 arrangeHorizontal_WidgetFlag,
1138 iTrue); 1147 iTrue);
1139 if (deviceType_App() == desktop_AppDeviceType) { 1148 if (deviceType_App() == desktop_AppDeviceType) {
@@ -1270,6 +1279,7 @@ void createUserInterface_Root(iRoot *d) {
1270 addAction_Widget(root, SDLK_j, KMOD_PRIMARY, "splitmenu.open"); 1279 addAction_Widget(root, SDLK_j, KMOD_PRIMARY, "splitmenu.open");
1271 } 1280 }
1272 updateMetrics_Root(d); 1281 updateMetrics_Root(d);
1282 updateNavBarSize_(navBar);
1273} 1283}
1274 1284
1275void showToolbars_Root(iRoot *d, iBool show) { 1285void showToolbars_Root(iRoot *d, iBool show) {
diff --git a/src/ui/util.c b/src/ui/util.c
index 0a0c37a9..7bc611e0 100644
--- a/src/ui/util.c
+++ b/src/ui/util.c
@@ -2061,8 +2061,8 @@ static iWidget *appendTwoColumnPage_(iWidget *tabs, const char *title, int short
2061 iWidget **values) { 2061 iWidget **values) {
2062 /* TODO: Use `makeTwoColumnWidget_()`, see above. */ 2062 /* TODO: Use `makeTwoColumnWidget_()`, see above. */
2063 iWidget *page = new_Widget(); 2063 iWidget *page = new_Widget();
2064 setFlags_Widget(page, arrangeVertical_WidgetFlag | arrangeSize_WidgetFlag | 2064 setBackgroundColor_Widget(page, red_ColorId);
2065 resizeHeightOfChildren_WidgetFlag, iTrue); 2065 setFlags_Widget(page, arrangeVertical_WidgetFlag | arrangeSize_WidgetFlag, iTrue);
2066 addChildFlags_Widget(page, iClob(new_Widget()), expand_WidgetFlag); 2066 addChildFlags_Widget(page, iClob(new_Widget()), expand_WidgetFlag);
2067 setPadding_Widget(page, 0, gap_UI, 0, gap_UI); 2067 setPadding_Widget(page, 0, gap_UI, 0, gap_UI);
2068 iWidget *columns = new_Widget(); 2068 iWidget *columns = new_Widget();
diff --git a/src/ui/widget.c b/src/ui/widget.c
index e4d92b35..62112e86 100644
--- a/src/ui/widget.c
+++ b/src/ui/widget.c
@@ -232,11 +232,29 @@ void setRoot_Widget(iWidget *d, iRoot *root) {
232 } 232 }
233} 233}
234 234
235iLocalDef iBool isCollapsed_Widget_(const iWidget *d) {
236 return (d->flags & (hidden_WidgetFlag | collapse_WidgetFlag)) ==
237 (hidden_WidgetFlag | collapse_WidgetFlag);
238}
239
240iLocalDef iBool isArrangedPos_Widget_(const iWidget *d) {
241 return (d->flags & fixedPosition_WidgetFlag) == 0;
242}
243
244iLocalDef iBool isArrangedSize_Widget_(const iWidget *d) {
245 return !isCollapsed_Widget_(d) && isArrangedPos_Widget_(d) &&
246 !(d->flags & parentCannotResize_WidgetFlag);
247}
248
249iLocalDef iBool doesAffectSizing_Widget_(const iWidget *d) {
250 return !isCollapsed_Widget_(d) && isArrangedPos_Widget_(d);
251}
252
235static int numExpandingChildren_Widget_(const iWidget *d) { 253static int numExpandingChildren_Widget_(const iWidget *d) {
236 int count = 0; 254 int count = 0;
237 iConstForEach(ObjectList, i, d->children) { 255 iConstForEach(ObjectList, i, d->children) {
238 const iWidget *child = constAs_Widget(i.object); 256 const iWidget *child = constAs_Widget(i.object);
239 if (flags_Widget(child) & expand_WidgetFlag) { 257 if (flags_Widget(child) & expand_WidgetFlag && doesAffectSizing_Widget_(child)) {
240 count++; 258 count++;
241 } 259 }
242 } 260 }
@@ -253,7 +271,7 @@ static int widestChild_Widget_(const iWidget *d) {
253} 271}
254 272
255static void arrange_Widget_(iWidget *); 273static void arrange_Widget_(iWidget *);
256static const iBool tracing_ = iFalse; 274static const iBool tracing_ = 0;
257 275
258#define TRACE(d, ...) if (tracing_) { printf_Widget_(d, __VA_ARGS__); } 276#define TRACE(d, ...) if (tracing_) { printf_Widget_(d, __VA_ARGS__); }
259 277
@@ -288,7 +306,7 @@ static void setWidth_Widget_(iWidget *d, int width) {
288 iAssert(width >= 0); 306 iAssert(width >= 0);
289 TRACE(d, "attempt to set width to %d (current: %d, min width: %d)", width, d->rect.size.x, d->minSize.x); 307 TRACE(d, "attempt to set width to %d (current: %d, min width: %d)", width, d->rect.size.x, d->minSize.x);
290 width = iMax(width, d->minSize.x); 308 width = iMax(width, d->minSize.x);
291 if (~d->flags & fixedWidth_WidgetFlag || d->flags & collapse_WidgetFlag) { 309 if (~d->flags & fixedWidth_WidgetFlag) { //} || d->flags & collapse_WidgetFlag) {
292 if (d->rect.size.x != width) { 310 if (d->rect.size.x != width) {
293 d->rect.size.x = width; 311 d->rect.size.x = width;
294 TRACE(d, "width has changed to %d", width); 312 TRACE(d, "width has changed to %d", width);
@@ -313,7 +331,7 @@ static void setHeight_Widget_(iWidget *d, int height) {
313 iAssert(height >= 0); 331 iAssert(height >= 0);
314 TRACE(d, "attempt to set height to %d (current: %d, min height: %d)", height, d->rect.size.y, d->minSize.y); 332 TRACE(d, "attempt to set height to %d (current: %d, min height: %d)", height, d->rect.size.y, d->minSize.y);
315 height = iMax(height, d->minSize.y); 333 height = iMax(height, d->minSize.y);
316 if (~d->flags & fixedHeight_WidgetFlag || d->flags & collapse_WidgetFlag) { 334 if (~d->flags & fixedHeight_WidgetFlag) { //} || d->flags & collapse_WidgetFlag) {
317 if (d->rect.size.y != height) { 335 if (d->rect.size.y != height) {
318 d->rect.size.y = height; 336 d->rect.size.y = height;
319 TRACE(d, "height has changed to %d", height); 337 TRACE(d, "height has changed to %d", height);
@@ -327,11 +345,6 @@ static void setHeight_Widget_(iWidget *d, int height) {
327 } 345 }
328} 346}
329 347
330iLocalDef iBool isCollapsed_Widget_(const iWidget *d) {
331 return (d->flags & (hidden_WidgetFlag | collapse_WidgetFlag)) ==
332 (hidden_WidgetFlag | collapse_WidgetFlag);
333}
334
335iLocalDef iRect innerRect_Widget_(const iWidget *d) { 348iLocalDef iRect innerRect_Widget_(const iWidget *d) {
336 return init_Rect(d->padding[0], 349 return init_Rect(d->padding[0],
337 d->padding[1], 350 d->padding[1],
@@ -347,14 +360,15 @@ iRect innerBounds_Widget(const iWidget *d) {
347 return ib; 360 return ib;
348} 361}
349 362
350iLocalDef iBool isArranged_Widget_(const iWidget *d) { 363//iLocalDef iBool isArranged_Widget_(const iWidget *d) {
351 return !isCollapsed_Widget_(d) && ~d->flags & fixedPosition_WidgetFlag; 364// return !isCollapsed_Widget_(d) && ~d->flags & fixedPosition_WidgetFlag;
352} 365//}
366
353 367
354static size_t numArrangedChildren_Widget_(const iWidget *d) { 368static size_t numArrangedChildren_Widget_(const iWidget *d) {
355 size_t count = 0; 369 size_t count = 0;
356 iConstForEach(ObjectList, i, d->children) { 370 iConstForEach(ObjectList, i, d->children) {
357 if (isArranged_Widget_(i.object)) { 371 if (isArrangedPos_Widget_(i.object)) {
358 count++; 372 count++;
359 } 373 }
360 } 374 }
@@ -387,15 +401,20 @@ static void boundsOfChildren_Widget_(const iWidget *d, iRect *bounds_out) {
387 *bounds_out = union_Rect(*bounds_out, childRect); 401 *bounds_out = union_Rect(*bounds_out, childRect);
388 } 402 }
389 } 403 }
404#if !defined (NDEBUG)
405 if (tracing_) {
406 if (bounds_out->size.x && bounds_out->size.y == 0) {
407 printf("SUSPECT CHILD BOUNDS?\n");
408 puts ("---------------------");
409 printTree_Widget(d);
410 puts ("---------------------");
411 }
412 }
413#endif
390} 414}
391 415
392static void arrange_Widget_(iWidget *d) { 416static void arrange_Widget_(iWidget *d) {
393 TRACE(d, "arranging..."); 417 TRACE(d, "arranging...");
394 if (isCollapsed_Widget_(d)) {
395 TRACE(d, "collapsed => END");
396 setFlags_Widget(d, wasCollapsed_WidgetFlag, iTrue);
397 return;
398 }
399 if (d->flags & moveToParentLeftEdge_WidgetFlag) { 418 if (d->flags & moveToParentLeftEdge_WidgetFlag) {
400 d->rect.pos.x = d->padding[0]; /* FIXME: Shouldn't this be d->parent->padding[0]? */ 419 d->rect.pos.x = d->padding[0]; /* FIXME: Shouldn't this be d->parent->padding[0]? */
401 TRACE(d, "move to parent left edge: %d", d->rect.pos.x); 420 TRACE(d, "move to parent left edge: %d", d->rect.pos.x);
@@ -439,57 +458,42 @@ static void arrange_Widget_(iWidget *d) {
439 } 458 }
440 const size_t childCount = numArrangedChildren_Widget_(d); 459 const size_t childCount = numArrangedChildren_Widget_(d);
441 TRACE(d, "%d arranged children", childCount); 460 TRACE(d, "%d arranged children", childCount);
461 const int expCount = numExpandingChildren_Widget_(d);
462 TRACE(d, "%d expanding children", expCount);
442 /* Resize children to fill the parent widget. */ 463 /* Resize children to fill the parent widget. */
443 if (d->flags & resizeChildren_WidgetFlag) { 464 if (d->flags & resizeChildren_WidgetFlag) {
444 const iInt2 dirs = init_I2((d->flags & resizeWidthOfChildren_WidgetFlag) != 0, 465 const iInt2 dirs = init_I2((d->flags & resizeWidthOfChildren_WidgetFlag) != 0,
445 (d->flags & resizeHeightOfChildren_WidgetFlag) != 0); 466 (d->flags & resizeHeightOfChildren_WidgetFlag) != 0);
446 TRACE(d, "resize children, x:%d y:%d", dirs.x, dirs.y); 467#if !defined (NDEBUG)
447 /* Collapse hidden children. */ 468 /* Check for conflicting flags. */
448 iBool collapseChanged = iFalse; 469 if (dirs.x) iAssert(~d->flags & arrangeWidth_WidgetFlag);
449 iForEach(ObjectList, c, d->children) { 470 if (dirs.y) iAssert(~d->flags & arrangeHeight_WidgetFlag);
450 iWidget *child = as_Widget(c.object); 471#endif
451 if (!isCollapsed_Widget_(child) && child->flags & wasCollapsed_WidgetFlag) { 472 TRACE(d, "resize children, x:%d y:%d (own size: %dx%d)", dirs.x, dirs.y,
452 setFlags_Widget(child, wasCollapsed_WidgetFlag, iFalse); 473 d->rect.size.x, d->rect.size.y);
453 TRACE(d, "child %p is uncollapsed", child);
454 /* Undo collapse and determine the normal size again. */
455 arrange_Widget_(d);
456 collapseChanged = iTrue;
457 }
458 else if (isCollapsed_Widget_(child) && ~child->flags & wasCollapsed_WidgetFlag) {
459 setFlags_Widget(child, wasCollapsed_WidgetFlag, iTrue);
460 collapseChanged = iTrue;
461 TRACE(d, "child %p flagged as collapsed", child);
462 }
463 }
464 if (collapseChanged) {
465 TRACE(d, "redoing arrangement due to changes in child collapse state");
466 arrange_Widget_(d); /* Redo with the new child sizes. */
467 return;
468 }
469 const int expCount = numExpandingChildren_Widget_(d);
470 TRACE(d, "%d expanding children", expCount);
471 /* Only resize the expanding children, not touching the others. */
472 if (expCount > 0) { 474 if (expCount > 0) {
475 /* There are expanding children, so all non-expanding children will retain their
476 current size. */
473 iInt2 avail = innerRect_Widget_(d).size; 477 iInt2 avail = innerRect_Widget_(d).size;
474 TRACE(d, "inner size: %dx%d", avail.x, avail.y); 478 TRACE(d, "inner size: %dx%d", avail.x, avail.y);
475 iConstForEach(ObjectList, i, d->children) { 479 iConstForEach(ObjectList, i, d->children) {
476 const iWidget *child = constAs_Widget(i.object); 480 const iWidget *child = constAs_Widget(i.object);
477 if (!isArranged_Widget_(child)) { 481 if (doesAffectSizing_Widget_(child)) {
478 continue; 482 if (~child->flags & expand_WidgetFlag) {
479 } 483 subv_I2(&avail, child->rect.size);
480 if (~child->flags & expand_WidgetFlag) { 484 }
481 subv_I2(&avail, child->rect.size);
482 } 485 }
483 } 486 }
484 avail = divi_I2(max_I2(zero_I2(), avail), expCount); 487 avail = divi_I2(max_I2(zero_I2(), avail), expCount);
485 TRACE(d, "changing child sizes (expand mode)..."); 488 TRACE(d, "changing child sizes...");
486 iForEach(ObjectList, j, d->children) { 489 iForEach(ObjectList, j, d->children) {
487 iWidget *child = as_Widget(j.object); 490 iWidget *child = as_Widget(j.object);
488 if (!isArranged_Widget_(child)) { 491 if (!isArrangedSize_Widget_(child)) {
489 TRACE(d, "child %p is not arranged", child); 492 TRACE(d, "child %p size is not arranged", child);
490 continue; 493 continue;
491 } 494 }
492 if (child->flags & expand_WidgetFlag) { 495 if (~child->flags & expand_WidgetFlag) {
496#if 0
493 if (d->flags & arrangeHorizontal_WidgetFlag) { 497 if (d->flags & arrangeHorizontal_WidgetFlag) {
494 if (dirs.x) setWidth_Widget_(child, avail.x); 498 if (dirs.x) setWidth_Widget_(child, avail.x);
495 if (dirs.y) setHeight_Widget_(child, height_Rect(innerRect_Widget_(d))); 499 if (dirs.y) setHeight_Widget_(child, height_Rect(innerRect_Widget_(d)));
@@ -500,6 +504,7 @@ static void arrange_Widget_(iWidget *d) {
500 } 504 }
501 } 505 }
502 else { 506 else {
507#endif
503 /* Fill the off axis, though. */ 508 /* Fill the off axis, though. */
504 if (d->flags & arrangeHorizontal_WidgetFlag) { 509 if (d->flags & arrangeHorizontal_WidgetFlag) {
505 if (dirs.y) setHeight_Widget_(child, height_Rect(innerRect_Widget_(d))); 510 if (dirs.y) setHeight_Widget_(child, height_Rect(innerRect_Widget_(d)));
@@ -509,7 +514,7 @@ static void arrange_Widget_(iWidget *d) {
509 } 514 }
510 } 515 }
511 } 516 }
512 TRACE(d, "...done changing child sizes (expand mode)"); 517 TRACE(d, "...done changing child sizes");
513 } 518 }
514 else { 519 else {
515 /* Evenly size all children. */ 520 /* Evenly size all children. */
@@ -526,7 +531,7 @@ static void arrange_Widget_(iWidget *d) {
526 TRACE(d, "begin changing child sizes (EVEN mode)..."); 531 TRACE(d, "begin changing child sizes (EVEN mode)...");
527 iForEach(ObjectList, i, d->children) { 532 iForEach(ObjectList, i, d->children) {
528 iWidget *child = as_Widget(i.object); 533 iWidget *child = as_Widget(i.object);
529 if (isArranged_Widget_(child) && ~child->flags & parentCannotResize_WidgetFlag) { 534 if (isArrangedSize_Widget_(child)) {
530 if (dirs.x) { 535 if (dirs.x) {
531 setWidth_Widget_(child, child->flags & unpadded_WidgetFlag ? unpaddedChildSize.x : childSize.x); 536 setWidth_Widget_(child, child->flags & unpadded_WidgetFlag ? unpaddedChildSize.x : childSize.x);
532 } 537 }
@@ -535,19 +540,55 @@ static void arrange_Widget_(iWidget *d) {
535 } 540 }
536 } 541 }
537 else { 542 else {
538 TRACE(d, "child %p cannot be resized (parentCannotResize: %d)", child, 543 TRACE(d, "child %p cannot be resized (collapsed: %d, arrangedPos: %d, parentCannotResize: %d)", child,
544 isCollapsed_Widget_(child),
545 isArrangedPos_Widget_(child),
539 (child->flags & parentCannotResize_WidgetFlag) != 0); 546 (child->flags & parentCannotResize_WidgetFlag) != 0);
540 } 547 }
541 } 548 }
542 TRACE(d, "...done changing child sizes (EVEN mode)"); 549 TRACE(d, "...done changing child sizes (EVEN mode)");
543 } 550 }
544 } 551 }
552 /* Resize the expanding children to fill the remaining available space. */
553 if (expCount > 0 && (d->flags & (arrangeHorizontal_WidgetFlag | arrangeVertical_WidgetFlag))) {
554 TRACE(d, "%d expanding children, resizing them %s...", expCount,
555 d->flags & arrangeHorizontal_WidgetFlag ? "horizontally" : "vertically");
556 const iRect innerRect = innerRect_Widget_(d);
557 iInt2 avail = innerRect.size;
558 iConstForEach(ObjectList, i, d->children) {
559 const iWidget *child = constAs_Widget(i.object);
560 if (doesAffectSizing_Widget_(child)) {
561 if (~child->flags & expand_WidgetFlag) {
562 subv_I2(&avail, child->rect.size);
563 }
564 }
565 }
566 avail = divi_I2(max_I2(zero_I2(), avail), expCount);
567 TRACE(d, "available for expansion (per child): %d\n", d->flags & arrangeHorizontal_WidgetFlag ? avail.x : avail.y);
568 iForEach(ObjectList, j, d->children) {
569 iWidget *child = as_Widget(j.object);
570 if (!isArrangedSize_Widget_(child)) {
571 TRACE(d, "child %p size is not arranged", child);
572 continue;
573 }
574 if (child->flags & expand_WidgetFlag) {
575 if (d->flags & arrangeHorizontal_WidgetFlag) {
576 setWidth_Widget_(child, avail.x);
577 setHeight_Widget_(child, height_Rect(innerRect));
578 }
579 else if (d->flags & arrangeVertical_WidgetFlag) {
580 setWidth_Widget_(child, width_Rect(innerRect));
581 setHeight_Widget_(child, avail.y);
582 }
583 }
584 }
585 }
545 if (d->flags & resizeChildrenToWidestChild_WidgetFlag) { 586 if (d->flags & resizeChildrenToWidestChild_WidgetFlag) {
546 const int widest = widestChild_Widget_(d); 587 const int widest = widestChild_Widget_(d);
547 TRACE(d, "resizing children to widest child (%d)...", widest); 588 TRACE(d, "resizing children to widest child (%d)...", widest);
548 iForEach(ObjectList, i, d->children) { 589 iForEach(ObjectList, i, d->children) {
549 iWidget *child = as_Widget(i.object); 590 iWidget *child = as_Widget(i.object);
550 if (isArranged_Widget_(child) && ~child->flags & parentCannotResize_WidgetFlag) { 591 if (isArrangedSize_Widget_(child)) {
551 setWidth_Widget_(child, widest); 592 setWidth_Widget_(child, widest);
552 } 593 }
553 else { 594 else {
@@ -564,7 +605,7 @@ static void arrange_Widget_(iWidget *d) {
564 iForEach(ObjectList, i, d->children) { 605 iForEach(ObjectList, i, d->children) {
565 iWidget *child = as_Widget(i.object); 606 iWidget *child = as_Widget(i.object);
566 arrange_Widget_(child); 607 arrange_Widget_(child);
567 if (!isArranged_Widget_(child)) { 608 if (isCollapsed_Widget_(child) || !isArrangedPos_Widget_(child)) {
568 TRACE(d, "child %p arranging prohibited", child); 609 TRACE(d, "child %p arranging prohibited", child);
569 continue; 610 continue;
570 } 611 }
@@ -648,6 +689,7 @@ static void arrange_Widget_(iWidget *d) {
648 TRACE(d, "END"); 689 TRACE(d, "END");
649} 690}
650 691
692#if 0
651void resetSize_Widget(iWidget *d) { 693void resetSize_Widget(iWidget *d) {
652 if (~d->flags & fixedWidth_WidgetFlag) { 694 if (~d->flags & fixedWidth_WidgetFlag) {
653 d->rect.size.x = d->minSize.x; 695 d->rect.size.x = d->minSize.x;
@@ -657,11 +699,12 @@ void resetSize_Widget(iWidget *d) {
657 } 699 }
658 iForEach(ObjectList, i, children_Widget(d)) { 700 iForEach(ObjectList, i, children_Widget(d)) {
659 iWidget *child = as_Widget(i.object); 701 iWidget *child = as_Widget(i.object);
660 if (isArranged_Widget_(child)) { 702 if (isArrangedSize_Widget_(child)) {
661 resetSize_Widget(child); 703 resetSize_Widget(child);
662 } 704 }
663 } 705 }
664} 706}
707#endif
665 708
666void arrange_Widget(iWidget *d) { 709void arrange_Widget(iWidget *d) {
667 //resetSize_Widget_(d); /* back to initial default sizes */ 710 //resetSize_Widget_(d); /* back to initial default sizes */
diff --git a/src/ui/widget.h b/src/ui/widget.h
index c3c9609e..36797210 100644
--- a/src/ui/widget.h
+++ b/src/ui/widget.h
@@ -87,7 +87,8 @@ enum iWidgetFlag {
87}; 87};
88 88
89/* 64-bit extended flags */ 89/* 64-bit extended flags */
90#define wasCollapsed_WidgetFlag iBit64(32) 90//#define wasCollapsed_WidgetFlag iBit64(32)
91#define disabledWhenHidden_WidgetFlag iBit64(32)
91#define centerHorizontal_WidgetFlag iBit64(33) 92#define centerHorizontal_WidgetFlag iBit64(33)
92#define moveToParentLeftEdge_WidgetFlag iBit64(34) 93#define moveToParentLeftEdge_WidgetFlag iBit64(34)
93#define moveToParentRightEdge_WidgetFlag iBit64(35) 94#define moveToParentRightEdge_WidgetFlag iBit64(35)
@@ -116,7 +117,6 @@ enum iWidgetFlag {
116#define parentCannotResizeHeight_WidgetFlag iBit64(58) 117#define parentCannotResizeHeight_WidgetFlag iBit64(58)
117#define ignoreForParentWidth_WidgetFlag iBit64(59) 118#define ignoreForParentWidth_WidgetFlag iBit64(59)
118#define noFadeBackground_WidgetFlag iBit64(60) 119#define noFadeBackground_WidgetFlag iBit64(60)
119#define disabledWhenHidden_WidgetFlag iBit64(61)
120 120
121enum iWidgetAddPos { 121enum iWidgetAddPos {
122 back_WidgetAddPos, 122 back_WidgetAddPos,
diff --git a/src/ui/window.c b/src/ui/window.c
index 9ac9b918..704e6f2a 100644
--- a/src/ui/window.c
+++ b/src/ui/window.c
@@ -471,6 +471,7 @@ void init_Window(iWindow *d, iRect rect) {
471 d->frameTime = SDL_GetTicks(); 471 d->frameTime = SDL_GetTicks();
472 d->loadAnimTimer = 0; 472 d->loadAnimTimer = 0;
473 init_Text(d->render); 473 init_Text(d->render);
474 SDL_GetRendererOutputSize(d->render, &d->size.x, &d->size.y);
474 setupUserInterface_Window(d); 475 setupUserInterface_Window(d);
475 postCommand_App("~bindings.changed"); /* update from bindings */ 476 postCommand_App("~bindings.changed"); /* update from bindings */
476 updateSize_Window_(d, iFalse); 477 updateSize_Window_(d, iFalse);
@@ -1175,6 +1176,7 @@ void setSplitMode_Window(iWindow *d, int splitMode) {
1175 iAssert(d->roots[1] == NULL); 1176 iAssert(d->roots[1] == NULL);
1176 d->roots[1] = new_Root(); 1177 d->roots[1] = new_Root();
1177 setCurrent_Root(d->roots[1]); 1178 setCurrent_Root(d->roots[1]);
1179 d->keyRoot = d->roots[1];
1178 createUserInterface_Root(d->roots[1]); 1180 createUserInterface_Root(d->roots[1]);
1179 /* If the old root has multiple tabs, move the current one to the new split. */ { 1181 /* If the old root has multiple tabs, move the current one to the new split. */ {
1180 iWidget *docTabs0 = findChild_Widget(d->roots[0]->widget, "doctabs"); 1182 iWidget *docTabs0 = findChild_Widget(d->roots[0]->widget, "doctabs");
@@ -1186,7 +1188,8 @@ void setSplitMode_Window(iWindow *d, int splitMode) {
1186 iRelease(removeTabPage_Widget(docTabs1, 0)); /* delete the default tab */ 1188 iRelease(removeTabPage_Widget(docTabs1, 0)); /* delete the default tab */
1187 setRoot_Widget(as_Widget(moved), d->roots[1]); 1189 setRoot_Widget(as_Widget(moved), d->roots[1]);
1188 prependTabPage_Widget(docTabs1, iClob(moved), "", 0, 0); 1190 prependTabPage_Widget(docTabs1, iClob(moved), "", 0, 0);
1189 showTabPage_Widget(docTabs1, as_Widget(moved)); 1191 //showTabPage_Widget(docTabs1, as_Widget(moved));
1192 postCommandf_App("tabs.switch page:%p", moved);
1190 } 1193 }
1191 else { 1194 else {
1192 postCommand_Root(d->roots[1], "navigate.home"); 1195 postCommand_Root(d->roots[1], "navigate.home");