summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-03-16 09:33:49 +0200
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-03-16 09:33:49 +0200
commit49bc5bba5df94f2bcbe12b513255b92599c052c2 (patch)
tree80faf1f591fb7498dc512d5826457b52c56fc5f3
parent63d1b8562c655d5f482846c99ec95ba666587e81 (diff)
Mobile: Dialog layout fixes
Orientation and safe inset changes are now applied to phone dialogs. There is still the occasional unscrollable dialog, but it seems random?
-rw-r--r--src/ui/documentwidget.c4
-rw-r--r--src/ui/util.c85
-rw-r--r--src/ui/widget.c80
-rw-r--r--src/ui/widget.h3
-rw-r--r--src/ui/window.c1
5 files changed, 135 insertions, 38 deletions
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index 6df25433..02358597 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -1653,7 +1653,9 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
1653 setSize_Widget(sizer, init_I2(gap_UI * 90, 1)); 1653 setSize_Widget(sizer, init_I2(gap_UI * 90, 1));
1654 addChildFlags_Widget(dlg, iClob(sizer), frameless_WidgetFlag); 1654 addChildFlags_Widget(dlg, iClob(sizer), frameless_WidgetFlag);
1655 setFlags_Widget(dlg, centerHorizontal_WidgetFlag, iFalse); 1655 setFlags_Widget(dlg, centerHorizontal_WidgetFlag, iFalse);
1656 setPos_Widget(dlg, bottomLeft_Rect(bounds_Widget(findWidget_App("navbar.lock")))); 1656 if (deviceType_App() != phone_AppDeviceType) {
1657 setPos_Widget(dlg, bottomLeft_Rect(bounds_Widget(findWidget_App("navbar.lock"))));
1658 }
1657 arrange_Widget(dlg); 1659 arrange_Widget(dlg);
1658 addAction_Widget(dlg, SDLK_ESCAPE, 0, "message.ok"); 1660 addAction_Widget(dlg, SDLK_ESCAPE, 0, "message.ok");
1659 addAction_Widget(dlg, SDLK_SPACE, 0, "message.ok"); 1661 addAction_Widget(dlg, SDLK_SPACE, 0, "message.ok");
diff --git a/src/ui/util.c b/src/ui/util.c
index 18ed7ba2..c1e27751 100644
--- a/src/ui/util.c
+++ b/src/ui/util.c
@@ -403,6 +403,7 @@ static iBool isCommandIgnoredByMenus_(const char *cmd) {
403 equal_Command(cmd, "document.changed") || 403 equal_Command(cmd, "document.changed") ||
404 equal_Command(cmd, "visited.changed") || 404 equal_Command(cmd, "visited.changed") ||
405 (deviceType_App() == desktop_AppDeviceType && equal_Command(cmd, "window.resized")) || 405 (deviceType_App() == desktop_AppDeviceType && equal_Command(cmd, "window.resized")) ||
406 equal_Command(cmd, "widget.overflow") ||
406 equal_Command(cmd, "window.reload.update") || 407 equal_Command(cmd, "window.reload.update") ||
407 equal_Command(cmd, "window.mouse.exited") || 408 equal_Command(cmd, "window.mouse.exited") ||
408 equal_Command(cmd, "window.mouse.entered") || 409 equal_Command(cmd, "window.mouse.entered") ||
@@ -909,6 +910,23 @@ iWidget *makeSheet_Widget(const char *id) {
909 return sheet; 910 return sheet;
910} 911}
911 912
913static void updateSheetPanelMetrics_(iWidget *sheet) {
914 iWidget *navi = findChild_Widget(sheet, "panel.navi");
915 iWidget *naviPad = child_Widget(navi, 0);
916 int naviHeight = lineHeight_Text(defaultBig_FontId) + 4 * gap_UI;
917#if defined (iPlatformAppleMobile)
918 float left, right, top, bottom;
919 safeAreaInsets_iOS(&left, &top, &right, &bottom);
920 setPadding_Widget(sheet, left, 0, right, 0);
921 navi->rect.pos = init_I2(left, top);
922 iConstForEach(PtrArray, i, findChildren_Widget(sheet, "panel.toppad")) {
923 iWidget *pad = *i.value;
924 setSize_Widget(pad, init1_I2(naviHeight));
925 }
926#endif
927 setSize_Widget(navi, init_I2(-1, naviHeight));
928}
929
912static iBool slidePanelHandler_(iWidget *d, const char *cmd) { 930static iBool slidePanelHandler_(iWidget *d, const char *cmd) {
913 if (equal_Command(cmd, "panel.open")) { 931 if (equal_Command(cmd, "panel.open")) {
914 iWidget *button = pointer_Command(cmd); 932 iWidget *button = pointer_Command(cmd);
@@ -944,19 +962,9 @@ static iBool slidePanelHandler_(iWidget *d, const char *cmd) {
944 return iTrue; 962 return iTrue;
945 } 963 }
946 if (equal_Command(cmd, "window.resized")) { 964 if (equal_Command(cmd, "window.resized")) {
947 iWidget *sheet = parent_Widget(d); 965 updateSheetPanelMetrics_(parent_Widget(d));
948#if defined (iPlatformAppleMobile) 966 }
949 float left, top, right, bottom; 967 return iFalse;
950 safeAreaInsets_iOS(&left, &top, &right, &bottom);
951 /* TODO: incorrect */
952 if (isLandscape_App()) {
953 setPadding_Widget(sheet, left, 0, right, 0);
954 }
955 else {
956 setPadding1_Widget(sheet, 0);
957 }
958#endif
959 } return iFalse;
960} 968}
961 969
962static iBool isTwoColumnPage_(iWidget *d) { 970static iBool isTwoColumnPage_(iWidget *d) {
@@ -1082,19 +1090,6 @@ void finalizeSheet_Widget(iWidget *sheet) {
1082 can be taller than the display. In hindsight, it may have been easier to 1090 can be taller than the display. In hindsight, it may have been easier to
1083 create phone versions of each dialog, but at least this works with any future 1091 create phone versions of each dialog, but at least this works with any future
1084 changes to the UI (..."works"). */ 1092 changes to the UI (..."works"). */
1085 int topSafe = 0;
1086 int navBarHeight = lineHeight_Text(defaultBig_FontId) + 4 * gap_UI;
1087#if defined (iPlatformAppleMobile)
1088 /* Safe area insets. */ {
1089 /* TODO: Must be updated when orientation changes; use a widget flag? */
1090 float l, t, r, b;
1091 safeAreaInsets_iOS(&l, &t, &r, &b);
1092// setPadding_Widget(sheet, l, t, r, b);
1093 setPadding1_Widget(sheet, 0);
1094 topSafe = t;
1095 navBarHeight += t;
1096 }
1097#endif
1098 setFlags_Widget(sheet, 1093 setFlags_Widget(sheet,
1099 keepOnTop_WidgetFlag | 1094 keepOnTop_WidgetFlag |
1100 parentCannotResize_WidgetFlag | 1095 parentCannotResize_WidgetFlag |
@@ -1110,6 +1105,7 @@ void finalizeSheet_Widget(iWidget *sheet) {
1110 resizeWidthOfChildren_WidgetFlag, 1105 resizeWidthOfChildren_WidgetFlag,
1111 iTrue); 1106 iTrue);
1112 setBackgroundColor_Widget(sheet, uiBackground_ColorId); 1107 setBackgroundColor_Widget(sheet, uiBackground_ColorId);
1108 setPadding1_Widget(sheet, 0);
1113 iPtrArray *contents = collect_PtrArray(new_PtrArray()); /* two-column pages */ 1109 iPtrArray *contents = collect_PtrArray(new_PtrArray()); /* two-column pages */
1114 iPtrArray *panelButtons = collect_PtrArray(new_PtrArray()); 1110 iPtrArray *panelButtons = collect_PtrArray(new_PtrArray());
1115 iWidget *tabs = findChild_Widget(sheet, "prefs.tabs"); 1111 iWidget *tabs = findChild_Widget(sheet, "prefs.tabs");
@@ -1166,7 +1162,6 @@ void finalizeSheet_Widget(iWidget *sheet) {
1166 } 1162 }
1167 } 1163 }
1168 const iBool useSlidePanels = (size_PtrArray(contents) == size_PtrArray(panelButtons)); 1164 const iBool useSlidePanels = (size_PtrArray(contents) == size_PtrArray(panelButtons));
1169 topPanel->rect.pos = init_I2(0, navBarHeight);
1170 addChildFlags_Widget(sheet, iClob(topPanel), 1165 addChildFlags_Widget(sheet, iClob(topPanel),
1171 arrangeVertical_WidgetFlag | 1166 arrangeVertical_WidgetFlag |
1172 resizeWidthOfChildren_WidgetFlag | arrangeHeight_WidgetFlag | 1167 resizeWidthOfChildren_WidgetFlag | arrangeHeight_WidgetFlag |
@@ -1182,7 +1177,7 @@ void finalizeSheet_Widget(iWidget *sheet) {
1182 setId_Widget(owner, "panel"); 1177 setId_Widget(owner, "panel");
1183 setUserData_Object(button, owner); 1178 setUserData_Object(button, owner);
1184 setBackgroundColor_Widget(owner, uiBackground_ColorId); 1179 setBackgroundColor_Widget(owner, uiBackground_ColorId);
1185 addChild_Widget(owner, iClob(makePadding_Widget(navBarHeight - topSafe))); 1180 setId_Widget(addChild_Widget(owner, iClob(makePadding_Widget(0))), "panel.toppad");
1186 iLabelWidget *title = addChildFlags_Widget(owner, 1181 iLabelWidget *title = addChildFlags_Widget(owner,
1187 iClob(new_LabelWidget(cstrCollect_String(upper_String(text_LabelWidget(button))), NULL)), alignLeft_WidgetFlag | frameless_WidgetFlag); 1182 iClob(new_LabelWidget(cstrCollect_String(upper_String(text_LabelWidget(button))), NULL)), alignLeft_WidgetFlag | frameless_WidgetFlag);
1188 setFont_LabelWidget(title, uiLabelLargeBold_FontId); 1183 setFont_LabelWidget(title, uiLabelLargeBold_FontId);
@@ -1192,6 +1187,7 @@ void finalizeSheet_Widget(iWidget *sheet) {
1192 focusRoot_WidgetFlag | 1187 focusRoot_WidgetFlag |
1193 hidden_WidgetFlag | 1188 hidden_WidgetFlag |
1194 disabled_WidgetFlag | 1189 disabled_WidgetFlag |
1190 //safePadding_WidgetFlag |
1195 arrangeVertical_WidgetFlag | 1191 arrangeVertical_WidgetFlag |
1196 resizeWidthOfChildren_WidgetFlag | 1192 resizeWidthOfChildren_WidgetFlag |
1197 arrangeHeight_WidgetFlag | 1193 arrangeHeight_WidgetFlag |
@@ -1337,6 +1333,7 @@ void finalizeSheet_Widget(iWidget *sheet) {
1337 chevron_WidgetFlag); 1333 chevron_WidgetFlag);
1338 } 1334 }
1339 else { 1335 else {
1336 setFlags_Widget(topPanel, overflowScrollable_WidgetFlag, iTrue);
1340 /* Update heading style. */ 1337 /* Update heading style. */
1341 setFont_LabelWidget((iLabelWidget *) dialogHeading, uiLabelLargeBold_FontId); 1338 setFont_LabelWidget((iLabelWidget *) dialogHeading, uiLabelLargeBold_FontId);
1342 setFlags_Widget(dialogHeading, alignLeft_WidgetFlag, iTrue); 1339 setFlags_Widget(dialogHeading, alignLeft_WidgetFlag, iTrue);
@@ -1348,11 +1345,16 @@ void finalizeSheet_Widget(iWidget *sheet) {
1348 removeChild_Widget(parent_Widget(input), input); 1345 removeChild_Widget(parent_Widget(input), input);
1349 addChild_Widget(topPanel, iClob(makeValuePadding_(as_Widget(input)))); 1346 addChild_Widget(topPanel, iClob(makeValuePadding_(as_Widget(input))));
1350 } 1347 }
1348 /* Top padding for each panel, to account for the overlaid navbar. */ {
1349 setId_Widget(addChildPos_Widget(topPanel,
1350 iClob(makePadding_Widget(0)), front_WidgetAddPos),
1351 "panel.toppad");
1352 }
1351 /* Navbar. */ { 1353 /* Navbar. */ {
1352 iWidget *navi = new_Widget(); 1354 iWidget *navi = new_Widget();
1353 setSize_Widget(navi, init_I2(-1, navBarHeight)); 1355 setId_Widget(navi, "panel.navi");
1354 setBackgroundColor_Widget(navi, uiBackground_ColorId); 1356 setBackgroundColor_Widget(navi, uiBackground_ColorId);
1355 addChild_Widget(navi, iClob(makePadding_Widget(topSafe))); 1357 addChild_Widget(navi, iClob(makePadding_Widget(0)));
1356 iLabelWidget *back = addChildFlags_Widget(navi, 1358 iLabelWidget *back = addChildFlags_Widget(navi,
1357 iClob(new_LabelWidget(leftAngle_Icon " Back", "panel.close")), 1359 iClob(new_LabelWidget(leftAngle_Icon " Back", "panel.close")),
1358 noBackground_WidgetFlag | frameless_WidgetFlag | 1360 noBackground_WidgetFlag | frameless_WidgetFlag |
@@ -1361,6 +1363,7 @@ void finalizeSheet_Widget(iWidget *sheet) {
1361 setId_Widget(as_Widget(back), "panel.back"); 1363 setId_Widget(as_Widget(back), "panel.back");
1362 setFont_LabelWidget(back, defaultBig_FontId); 1364 setFont_LabelWidget(back, defaultBig_FontId);
1363 if (!isPrefs) { 1365 if (!isPrefs) {
1366 /* Pick up the dialog buttons for the navbar. */
1364 iWidget *buttons = findChild_Widget(sheet, "dialogbuttons"); 1367 iWidget *buttons = findChild_Widget(sheet, "dialogbuttons");
1365 iLabelWidget *cancel = findMenuItem_Widget(buttons, "cancel"); 1368 iLabelWidget *cancel = findMenuItem_Widget(buttons, "cancel");
1366 if (cancel) { 1369 if (cancel) {
@@ -1385,7 +1388,22 @@ void finalizeSheet_Widget(iWidget *sheet) {
1385 addChildFlags_Widget(as_Widget(back), iClob(def), moveToParentRightEdge_WidgetFlag); 1388 addChildFlags_Widget(as_Widget(back), iClob(def), moveToParentRightEdge_WidgetFlag);
1386 updateSize_LabelWidget(def); 1389 updateSize_LabelWidget(def);
1387 } 1390 }
1388 /* TODO: Action buttons should be added in the bottom as extra buttons. */ 1391 /* Action buttons are added in the bottom as extra buttons. */ {
1392 iBool isFirstAction = iTrue;
1393 iForEach(ObjectList, i, children_Widget(buttons)) {
1394 if (isInstance_Object(i.object, &Class_LabelWidget) &&
1395 i.object != cancel && i.object != def) {
1396 iLabelWidget *item = i.object;
1397 setBackgroundColor_Widget(i.object, uiBackgroundSidebar_ColorId);
1398 setFont_LabelWidget(item, defaultBig_FontId);
1399 removeChild_Widget(buttons, item);
1400 addChildFlags_Widget(topPanel, iClob(item), panelButtonFlags |
1401 (isFirstAction ? borderTop_WidgetFlag : 0));
1402 updateSize_LabelWidget(item);
1403 isFirstAction = iFalse;
1404 }
1405 }
1406 }
1389 iRelease(removeChild_Widget(parent_Widget(buttons), buttons)); 1407 iRelease(removeChild_Widget(parent_Widget(buttons), buttons));
1390 /* Styling for remaining elements. */ 1408 /* Styling for remaining elements. */
1391 iForEach(ObjectList, i, children_Widget(topPanel)) { 1409 iForEach(ObjectList, i, children_Widget(topPanel)) {
@@ -1400,10 +1418,14 @@ void finalizeSheet_Widget(iWidget *sheet) {
1400 } 1418 }
1401 } 1419 }
1402 addChildFlags_Widget(sheet, iClob(navi), 1420 addChildFlags_Widget(sheet, iClob(navi),
1421 drawBackgroundToVerticalSafeArea_WidgetFlag |
1403 arrangeHeight_WidgetFlag | resizeWidthOfChildren_WidgetFlag | 1422 arrangeHeight_WidgetFlag | resizeWidthOfChildren_WidgetFlag |
1404 resizeToParentWidth_WidgetFlag | arrangeVertical_WidgetFlag); 1423 resizeToParentWidth_WidgetFlag | arrangeVertical_WidgetFlag);
1405 } 1424 }
1425 updateSheetPanelMetrics_(sheet);
1406 arrange_Widget(sheet->parent); 1426 arrange_Widget(sheet->parent);
1427 postCommand_App("widget.overflow"); /* with the correct dimensions */
1428// printTree_Widget(sheet);
1407 } 1429 }
1408 else { 1430 else {
1409 arrange_Widget(sheet); 1431 arrange_Widget(sheet);
@@ -1613,6 +1635,7 @@ static iBool messageHandler_(iWidget *msg, const char *cmd) {
1613 equal_Command(cmd, "document.autoreload") || 1635 equal_Command(cmd, "document.autoreload") ||
1614 equal_Command(cmd, "document.reload") || 1636 equal_Command(cmd, "document.reload") ||
1615 equal_Command(cmd, "document.request.updated") || 1637 equal_Command(cmd, "document.request.updated") ||
1638 equal_Command(cmd, "widget.overflow") ||
1616 startsWith_CStr(cmd, "window."))) { 1639 startsWith_CStr(cmd, "window."))) {
1617 destroy_Widget(msg); 1640 destroy_Widget(msg);
1618 } 1641 }
diff --git a/src/ui/widget.c b/src/ui/widget.c
index 8641dd61..385eb7e0 100644
--- a/src/ui/widget.c
+++ b/src/ui/widget.c
@@ -102,7 +102,7 @@ static void visualOffsetAnimation_Widget_(void *ptr) {
102 addTicker_App(visualOffsetAnimation_Widget_, ptr); 102 addTicker_App(visualOffsetAnimation_Widget_, ptr);
103 } 103 }
104 else { 104 else {
105 setFlags_Widget(d, visualOffset_WidgetFlag, iFalse); 105 d->flags &= ~visualOffset_WidgetFlag;
106 } 106 }
107} 107}
108 108
@@ -341,7 +341,7 @@ void arrange_Widget(iWidget *d) {
341 return; 341 return;
342 } 342 }
343 if (d->flags & moveToParentLeftEdge_WidgetFlag) { 343 if (d->flags & moveToParentLeftEdge_WidgetFlag) {
344 d->rect.pos.x = d->padding[0]; 344 d->rect.pos.x = d->padding[0]; /* FIXME: Shouldn't this be d->parent->padding[0]? */
345 } 345 }
346 else if (d->flags & moveToParentRightEdge_WidgetFlag) { 346 else if (d->flags & moveToParentRightEdge_WidgetFlag) {
347 d->rect.pos.x = width_Rect(innerRect_Widget_(d->parent)) - width_Rect(d->rect); 347 d->rect.pos.x = width_Rect(innerRect_Widget_(d->parent)) - width_Rect(d->rect);
@@ -355,6 +355,13 @@ void arrange_Widget(iWidget *d) {
355 if (d->flags & resizeToParentHeight_WidgetFlag) { 355 if (d->flags & resizeToParentHeight_WidgetFlag) {
356 setHeight_Widget_(d, height_Rect(innerRect_Widget_(d->parent))); 356 setHeight_Widget_(d, height_Rect(innerRect_Widget_(d->parent)));
357 } 357 }
358 if (d->flags & safePadding_WidgetFlag) {
359#if defined (iPlatformAppleMobile)
360 float left, top, right, bottom;
361 safeAreaInsets_iOS(&left, &top, &right, &bottom);
362 setPadding_Widget(d, left, top, right, bottom);
363#endif
364 }
358 /* The rest of the arrangement depends on child widgets. */ 365 /* The rest of the arrangement depends on child widgets. */
359 if (!d->children) { 366 if (!d->children) {
360 return; 367 return;
@@ -489,6 +496,9 @@ void arrange_Widget(iWidget *d) {
489 else if ((d->flags & resizeChildren_WidgetFlag) == resizeChildren_WidgetFlag) { 496 else if ((d->flags & resizeChildren_WidgetFlag) == resizeChildren_WidgetFlag) {
490 child->rect.pos = pos; 497 child->rect.pos = pos;
491 } 498 }
499 else if (d->flags & resizeWidthOfChildren_WidgetFlag) {
500 child->rect.pos.x = pos.x;
501 }
492 } 502 }
493 /* Update the size of the widget according to the arrangement. */ 503 /* Update the size of the widget according to the arrangement. */
494 if (d->flags & arrangeSize_WidgetFlag) { 504 if (d->flags & arrangeSize_WidgetFlag) {
@@ -667,10 +677,10 @@ iBool dispatchEvent_Widget(iWidget *d, const SDL_Event *ev) {
667 } 677 }
668#endif 678#endif
669#if 0 679#if 0
670 if (ev->type == SDL_MOUSEBUTTONDOWN) { 680 if (ev->type == SDL_MOUSEWHEEL) {
671 printf("[%p] %s:'%s' ate the button %d\n", 681 printf("[%p] %s:'%s' ate the wheel\n",
672 child, class_Widget(child)->name, 682 child, class_Widget(child)->name,
673 cstr_String(id_Widget(child)), ev->button.button); 683 cstr_String(id_Widget(child)));
674 fflush(stdout); 684 fflush(stdout);
675 } 685 }
676#endif 686#endif
@@ -692,6 +702,36 @@ iBool dispatchEvent_Widget(iWidget *d, const SDL_Event *ev) {
692 return iFalse; 702 return iFalse;
693} 703}
694 704
705static iBool scrollOverflow_Widget_(iWidget *d, int delta) {
706 iRect bounds = bounds_Widget(d);
707 const iInt2 rootSize = rootSize_Window(get_Window());
708 const iRect winRect = safeRootRect_Window(get_Window());
709 const int yTop = top_Rect(winRect);
710 const int yBottom = bottom_Rect(winRect);
711 //const int safeBottom = rootSize.y - yBottom;
712 bounds.pos.y += delta;
713 const iRangei range = { bottom_Rect(winRect) - height_Rect(bounds), yTop };
714// printf("range: %d ... %d\n", range.start, range.end);
715 if (range.start >= range.end) {
716 bounds.pos.y = range.end;
717 }
718 else {
719 bounds.pos.y = iClamp(bounds.pos.y, range.start, range.end);
720 }
721// if (delta >= 0) {
722// bounds.pos.y = iMin(bounds.pos.y, yTop);
723// }
724// else {
725// bounds.pos.y = iMax(bounds.pos.y, );
726// }
727 const iInt2 newPos = localCoord_Widget(d->parent, bounds.pos);
728 if (!isEqual_I2(newPos, d->rect.pos)) {
729 d->rect.pos = newPos;
730 refresh_Widget(d);
731 }
732 return height_Rect(bounds) > height_Rect(winRect);
733}
734
695iBool processEvent_Widget(iWidget *d, const SDL_Event *ev) { 735iBool processEvent_Widget(iWidget *d, const SDL_Event *ev) {
696 if (ev->type == SDL_KEYDOWN) { 736 if (ev->type == SDL_KEYDOWN) {
697 if (ev->key.keysym.sym == SDLK_TAB) { 737 if (ev->key.keysym.sym == SDLK_TAB) {
@@ -720,6 +760,14 @@ iBool processEvent_Widget(iWidget *d, const SDL_Event *ev) {
720 } 760 }
721 else if (d->flags & overflowScrollable_WidgetFlag && ev->type == SDL_MOUSEWHEEL && 761 else if (d->flags & overflowScrollable_WidgetFlag && ev->type == SDL_MOUSEWHEEL &&
722 ~d->flags & visualOffset_WidgetFlag) { 762 ~d->flags & visualOffset_WidgetFlag) {
763 int step = ev->wheel.y;
764 if (!isPerPixel_MouseWheelEvent(&ev->wheel)) {
765 step *= lineHeight_Text(uiLabel_FontId);
766 }
767 if (scrollOverflow_Widget_(d, step)) {
768 return iTrue;
769 }
770#if 0
723 iRect bounds = bounds_Widget(d); 771 iRect bounds = bounds_Widget(d);
724 const iInt2 rootSize = rootSize_Window(get_Window()); 772 const iInt2 rootSize = rootSize_Window(get_Window());
725 const iRect winRect = safeRootRect_Window(get_Window()); 773 const iRect winRect = safeRootRect_Window(get_Window());
@@ -736,15 +784,20 @@ iBool processEvent_Widget(iWidget *d, const SDL_Event *ev) {
736 bounds.pos.y = iMin(bounds.pos.y, yTop); 784 bounds.pos.y = iMin(bounds.pos.y, yTop);
737 } 785 }
738 else { 786 else {
739 bounds.pos.y = iMax(bounds.pos.y, rootSize.y + safeBottom - height_Rect(bounds)); 787 bounds.pos.y = iMax(bounds.pos.y, rootSize.y /*+ safeBottom*/ - height_Rect(bounds));
740 } 788 }
741 d->rect.pos = localCoord_Widget(d->parent, bounds.pos); 789 d->rect.pos = localCoord_Widget(d->parent, bounds.pos);
742 refresh_Widget(d); 790 refresh_Widget(d);
743 return iTrue; 791 return iTrue;
744 } 792 }
793#endif
745 } 794 }
746 switch (ev->type) { 795 switch (ev->type) {
747 case SDL_USEREVENT: { 796 case SDL_USEREVENT: {
797 if (d->flags & overflowScrollable_WidgetFlag &&
798 isCommand_UserEvent(ev, "widget.overflow")) {
799 scrollOverflow_Widget_(d, 0); /* check bounds */
800 }
748 if (ev->user.code == command_UserEventCode && d->commandHandler && 801 if (ev->user.code == command_UserEventCode && d->commandHandler &&
749 d->commandHandler(d, ev->user.data1)) { 802 d->commandHandler(d, ev->user.data1)) {
750 return iTrue; 803 return iTrue;
@@ -1041,6 +1094,21 @@ iAny *findChild_Widget(const iWidget *d, const char *id) {
1041 return NULL; 1094 return NULL;
1042} 1095}
1043 1096
1097static void addMatchingToArray_Widget_(const iWidget *d, const char *id, iPtrArray *found) {
1098 if (cmp_String(id_Widget(d), id) == 0) {
1099 pushBack_PtrArray(found, d);
1100 }
1101 iForEach(ObjectList, i, d->children) {
1102 addMatchingToArray_Widget_(i.object, id, found);
1103 }
1104}
1105
1106const iPtrArray *findChildren_Widget(const iWidget *d, const char *id) {
1107 iPtrArray *found = new_PtrArray();
1108 addMatchingToArray_Widget_(d, id, found);
1109 return collect_PtrArray(found);
1110}
1111
1044iAny *findParentClass_Widget(const iWidget *d, const iAnyClass *class) { 1112iAny *findParentClass_Widget(const iWidget *d, const iAnyClass *class) {
1045 if (!d) return NULL; 1113 if (!d) return NULL;
1046 iWidget *i = d->parent; 1114 iWidget *i = d->parent;
diff --git a/src/ui/widget.h b/src/ui/widget.h
index 8dc51a1f..88d75b62 100644
--- a/src/ui/widget.h
+++ b/src/ui/widget.h
@@ -29,6 +29,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
29 29
30#include <the_Foundation/object.h> 30#include <the_Foundation/object.h>
31#include <the_Foundation/objectlist.h> 31#include <the_Foundation/objectlist.h>
32#include <the_Foundation/ptrarray.h>
32#include <the_Foundation/rect.h> 33#include <the_Foundation/rect.h>
33#include <the_Foundation/string.h> 34#include <the_Foundation/string.h>
34#include <SDL_events.h> 35#include <SDL_events.h>
@@ -108,6 +109,7 @@ enum iWidgetFlag {
108#define drawBackgroundToBottom_WidgetFlag iBit64(53) 109#define drawBackgroundToBottom_WidgetFlag iBit64(53)
109#define dragged_WidgetFlag iBit64(54) 110#define dragged_WidgetFlag iBit64(54)
110#define hittable_WidgetFlag iBit64(55) 111#define hittable_WidgetFlag iBit64(55)
112#define safePadding_WidgetFlag iBit64(56) /* padded using safe area insets */
111 113
112enum iWidgetAddPos { 114enum iWidgetAddPos {
113 back_WidgetAddPos, 115 back_WidgetAddPos,
@@ -167,6 +169,7 @@ iInt2 localCoord_Widget (const iWidget *, iInt2 coord);
167iBool contains_Widget (const iWidget *, iInt2 coord); 169iBool contains_Widget (const iWidget *, iInt2 coord);
168iAny * hitChild_Widget (const iWidget *, iInt2 coord); 170iAny * hitChild_Widget (const iWidget *, iInt2 coord);
169iAny * findChild_Widget (const iWidget *, const char *id); 171iAny * findChild_Widget (const iWidget *, const char *id);
172const iPtrArray *findChildren_Widget (const iWidget *, const char *id);
170iAny * findParentClass_Widget(const iWidget *, const iAnyClass *class); 173iAny * findParentClass_Widget(const iWidget *, const iAnyClass *class);
171iAny * findFocusable_Widget(const iWidget *startFrom, enum iWidgetFocusDir focusDir); 174iAny * findFocusable_Widget(const iWidget *startFrom, enum iWidgetFocusDir focusDir);
172size_t childCount_Widget (const iWidget *); 175size_t childCount_Widget (const iWidget *);
diff --git a/src/ui/window.c b/src/ui/window.c
index 2ef67142..6e81c14c 100644
--- a/src/ui/window.c
+++ b/src/ui/window.c
@@ -1210,6 +1210,7 @@ static void updateRootSize_Window_(iWindow *d, iBool notifyAlways) {
1210 size->y, 1210 size->y,
1211 isHoriz, 1211 isHoriz,
1212 isVert); 1212 isVert);
1213 postCommand_App("widget.overflow"); /* check bounds with updated sizes */
1213 postRefresh_App(); 1214 postRefresh_App();
1214 d->place.lastNotifiedSize = *size; 1215 d->place.lastNotifiedSize = *size;
1215 } 1216 }