summaryrefslogtreecommitdiff
path: root/src/ui
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-04-27 13:25:44 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-04-27 13:25:44 +0300
commit379c1f8befcc30b72f0b50dcbd653704348e4761 (patch)
treea43c0d30f39e592237bf7f3b9c8fca26434128d0 /src/ui
parent3846778c99d9efca609b7cb216cb71c675f036b0 (diff)
UI root sizing is independent of window sizing
Diffstat (limited to 'src/ui')
-rw-r--r--src/ui/documentwidget.c2
-rw-r--r--src/ui/inputwidget.c4
-rw-r--r--src/ui/lookupwidget.c3
-rw-r--r--src/ui/root.c38
-rw-r--r--src/ui/root.h7
-rw-r--r--src/ui/sidebarwidget.c4
-rw-r--r--src/ui/touch.c6
-rw-r--r--src/ui/util.c6
-rw-r--r--src/ui/widget.c38
-rw-r--r--src/ui/window.c38
-rw-r--r--src/ui/window.h6
11 files changed, 72 insertions, 80 deletions
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index ee44933e..e09844c2 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -567,7 +567,7 @@ static int scrollMax_DocumentWidget_(const iDocumentWidget *d) {
567 int sm = size_GmDocument(d->doc).y - height_Rect(bounds_Widget(constAs_Widget(d))) + 567 int sm = size_GmDocument(d->doc).y - height_Rect(bounds_Widget(constAs_Widget(d))) +
568 (hasSiteBanner_GmDocument(d->doc) ? 1 : 2) * d->pageMargin * gap_UI; 568 (hasSiteBanner_GmDocument(d->doc) ? 1 : 2) * d->pageMargin * gap_UI;
569 if (d->phoneToolbar) { 569 if (d->phoneToolbar) {
570 sm += rootSize_Window(get_Window()).y - 570 sm += size_Root(get_Root()).y -
571 top_Rect(boundsWithoutVisualOffset_Widget(d->phoneToolbar)); 571 top_Rect(boundsWithoutVisualOffset_Widget(d->phoneToolbar));
572 } 572 }
573 return sm; 573 return sm;
diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c
index df5eb0df..52cb9805 100644
--- a/src/ui/inputwidget.c
+++ b/src/ui/inputwidget.c
@@ -338,7 +338,7 @@ void setText_InputWidget(iInputWidget *d, const iString *text) {
338 text = enc; 338 text = enc;
339 } 339 }
340 /* Omit the default (Gemini) scheme if there isn't much space. */ 340 /* Omit the default (Gemini) scheme if there isn't much space. */
341 if (isNarrow_Window(get_Window())) { // flags_Widget(as_Widget(d)) & tight_WidgetFlag) { 341 if (isNarrow_Root(get_Root())) { // flags_Widget(as_Widget(d)) & tight_WidgetFlag) {
342 text = omitDefaultScheme_(collect_String(copy_String(text))); 342 text = omitDefaultScheme_(collect_String(copy_String(text)));
343 } 343 }
344 } 344 }
@@ -733,7 +733,7 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) {
733 if (isFocused_Widget(d) && arg_Command(command_UserEvent(ev))) { 733 if (isFocused_Widget(d) && arg_Command(command_UserEvent(ev))) {
734 iRect rect = bounds_Widget(w); 734 iRect rect = bounds_Widget(w);
735 rect.pos.y -= value_Anim(&get_Window()->rootOffset); 735 rect.pos.y -= value_Anim(&get_Window()->rootOffset);
736 const iInt2 visRoot = visibleRootSize_Window(get_Window()); 736 const iInt2 visRoot = visibleSize_Root(get_Root());
737 if (bottom_Rect(rect) > visRoot.y) { 737 if (bottom_Rect(rect) > visRoot.y) {
738 setValue_Anim(&get_Window()->rootOffset, -(bottom_Rect(rect) - visRoot.y), 250); 738 setValue_Anim(&get_Window()->rootOffset, -(bottom_Rect(rect) - visRoot.y), 250);
739 } 739 }
diff --git a/src/ui/lookupwidget.c b/src/ui/lookupwidget.c
index f3fd54d2..7c3ebce2 100644
--- a/src/ui/lookupwidget.c
+++ b/src/ui/lookupwidget.c
@@ -654,8 +654,7 @@ static iBool processEvent_LookupWidget_(iLookupWidget *d, const SDL_Event *ev) {
654 (equal_Command(cmd, "layout.changed") && 654 (equal_Command(cmd, "layout.changed") &&
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 const iWindow *window = get_Window(); 657 const iInt2 rootSize = size_Root(get_Root());
658 const iInt2 rootSize = rootSize_Window(window);
659 const iRect navBarBounds = bounds_Widget(findWidget_App("navbar")); 658 const iRect navBarBounds = bounds_Widget(findWidget_App("navbar"));
660 setFixedSize_Widget(w, init_I2(width_Widget(findWidget_App("url")), 659 setFixedSize_Widget(w, init_I2(width_Widget(findWidget_App("url")),
661 (rootSize.y - bottom_Rect(navBarBounds)) / 2)); 660 (rootSize.y - bottom_Rect(navBarBounds)) / 2));
diff --git a/src/ui/root.c b/src/ui/root.c
index bd7d07a1..375107c9 100644
--- a/src/ui/root.c
+++ b/src/ui/root.c
@@ -522,14 +522,14 @@ static int navBarAvailableSpace_(iWidget *navBar) {
522 return avail; 522 return avail;
523} 523}
524 524
525iBool isNarrow_Window(const iWindow *d) { 525iBool isNarrow_Root(const iRoot *d) {
526 return width_Rect(safeRootRect_Window(d)) / gap_UI < 140; 526 return width_Rect(safeRect_Root(d)) / gap_UI < 140;
527} 527}
528 528
529static iBool handleNavBarCommands_(iWidget *navBar, const char *cmd) { 529static iBool handleNavBarCommands_(iWidget *navBar, const char *cmd) {
530 if (equal_Command(cmd, "window.resized") || equal_Command(cmd, "metrics.changed")) { 530 if (equal_Command(cmd, "window.resized") || equal_Command(cmd, "metrics.changed")) {
531 const iBool isPhone = deviceType_App() == phone_AppDeviceType; 531 const iBool isPhone = deviceType_App() == phone_AppDeviceType;
532 const iBool isNarrow = !isPhone && isNarrow_Window(get_Window()); 532 const iBool isNarrow = !isPhone && isNarrow_Root(get_Root());
533 /* Adjust navbar padding. */ { 533 /* Adjust navbar padding. */ {
534 int hPad = isPhone && isPortrait_App() ? 0 : (isPhone || isNarrow) ? gap_UI / 2 534 int hPad = isPhone && isPortrait_App() ? 0 : (isPhone || isNarrow) ? gap_UI / 2
535 : gap_UI * 3 / 2; 535 : gap_UI * 3 / 2;
@@ -769,7 +769,7 @@ static iBool handleToolBarCommands_(iWidget *toolBar, const char *cmd) {
769 // setFlags_Widget(findChild_Widget(toolBar, "toolbar.view"), noBackground_WidgetFlag, 769 // setFlags_Widget(findChild_Widget(toolBar, "toolbar.view"), noBackground_WidgetFlag,
770 // isVisible); 770 // isVisible);
771 /* If a sidebar hasn't been shown yet, it's height is zero. */ 771 /* If a sidebar hasn't been shown yet, it's height is zero. */
772 const int viewHeight = rootSize_Window(get_Window()).y; 772 const int viewHeight = size_Root(get_Root()).y;
773 if (arg_Command(cmd) >= 0) { 773 if (arg_Command(cmd) >= 0) {
774 postCommandf_App("sidebar.mode arg:%d show:1", arg_Command(cmd)); 774 postCommandf_App("sidebar.mode arg:%d show:1", arg_Command(cmd));
775 if (!isVisible) { 775 if (!isVisible) {
@@ -798,14 +798,14 @@ static iBool handleToolBarCommands_(iWidget *toolBar, const char *cmd) {
798 // setFlags_Widget(findChild_Widget(toolBar, "toolbar.ident"), noBackground_WidgetFlag, 798 // setFlags_Widget(findChild_Widget(toolBar, "toolbar.ident"), noBackground_WidgetFlag,
799 // isVisible); 799 // isVisible);
800 /* If a sidebar hasn't been shown yet, it's height is zero. */ 800 /* If a sidebar hasn't been shown yet, it's height is zero. */
801 const int viewHeight = rootSize_Window(get_Window()).y; 801 const int viewHeight = size_Root(get_Root()).y;
802 if (isVisible) { 802 if (isVisible) {
803 dismissSidebar_(sidebar2, NULL); 803 dismissSidebar_(sidebar2, NULL);
804 } 804 }
805 else { 805 else {
806 postCommand_App("sidebar2.mode arg:3 show:1"); 806 postCommand_App("sidebar2.mode arg:3 show:1");
807 int offset = height_Widget(sidebar2); 807 int offset = height_Widget(sidebar2);
808 if (offset == 0) offset = rootSize_Window(get_Window()).y; 808 if (offset == 0) offset = size_Root(get_Root()).y;
809 setVisualOffset_Widget(sidebar2, offset, 0, 0); 809 setVisualOffset_Widget(sidebar2, offset, 0, 0);
810 setVisualOffset_Widget(sidebar2, 0, 400, easeOut_AnimFlag | softer_AnimFlag); 810 setVisualOffset_Widget(sidebar2, 0, 400, easeOut_AnimFlag | softer_AnimFlag);
811 } 811 }
@@ -1240,7 +1240,7 @@ void showToolbars_Root(iRoot *d, iBool show) {
1240 if (isLandscape_App()) return; 1240 if (isLandscape_App()) return;
1241 iWidget *toolBar = findChild_Widget(d->widget, "toolbar"); 1241 iWidget *toolBar = findChild_Widget(d->widget, "toolbar");
1242 if (!toolBar) return; 1242 if (!toolBar) return;
1243 const int height = rootSize_Window(get_Window()).y - top_Rect(boundsWithoutVisualOffset_Widget(toolBar)); 1243 const int height = size_Root(d).y - top_Rect(boundsWithoutVisualOffset_Widget(toolBar));
1244 if (show && !isVisible_Widget(toolBar)) { 1244 if (show && !isVisible_Widget(toolBar)) {
1245 setFlags_Widget(toolBar, hidden_WidgetFlag, iFalse); 1245 setFlags_Widget(toolBar, hidden_WidgetFlag, iFalse);
1246 setVisualOffset_Widget(toolBar, 0, 200, easeOut_AnimFlag); 1246 setVisualOffset_Widget(toolBar, 0, 200, easeOut_AnimFlag);
@@ -1254,3 +1254,27 @@ void showToolbars_Root(iRoot *d, iBool show) {
1254 } 1254 }
1255} 1255}
1256 1256
1257iInt2 size_Root(const iRoot *d) {
1258 return d && d->widget ? d->widget->rect.size : zero_I2();
1259}
1260
1261iRect rect_Root(const iRoot *d) {
1262 if (d && d->widget) {
1263 return d->widget->rect;
1264 }
1265 return zero_Rect();
1266}
1267
1268iRect safeRect_Root(const iRoot *d) {
1269 iRect rect = { zero_I2(), size_Root(d) };
1270#if defined (iPlatformAppleMobile)
1271 float left, top, right, bottom;
1272 safeAreaInsets_iOS(&left, &top, &right, &bottom);
1273 adjustEdges_Rect(&rect, top, -right, -bottom, left);
1274#endif
1275 return rect;
1276}
1277
1278iInt2 visibleSize_Root(const iRoot *d) {
1279 return addY_I2(size_Root(d), -get_Window()->keyboardHeight);
1280}
diff --git a/src/ui/root.h b/src/ui/root.h
index 4b14b942..65da8d85 100644
--- a/src/ui/root.h
+++ b/src/ui/root.h
@@ -2,6 +2,7 @@
2 2
3#include "widget.h" 3#include "widget.h"
4#include <the_Foundation/ptrset.h> 4#include <the_Foundation/ptrset.h>
5#include <the_Foundation/vec2.h>
5 6
6iDeclareType(Root) 7iDeclareType(Root)
7 8
@@ -30,3 +31,9 @@ void updateMetrics_Root (iRoot *);
30void updatePadding_Root (iRoot *); /* TODO: is part of metrics? */ 31void updatePadding_Root (iRoot *); /* TODO: is part of metrics? */
31void dismissPortraitPhoneSidebars_Root (iRoot *); 32void dismissPortraitPhoneSidebars_Root (iRoot *);
32void showToolbars_Root (iRoot *, iBool show); 33void showToolbars_Root (iRoot *, iBool show);
34
35iInt2 size_Root (const iRoot *);
36iRect rect_Root (const iRoot *);
37iRect safeRect_Root (const iRoot *);
38iInt2 visibleSize_Root (const iRoot *); /* may be obstructed by software keyboard */
39iBool isNarrow_Root (const iRoot *);
diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c
index 8d83eed6..f31dd195 100644
--- a/src/ui/sidebarwidget.c
+++ b/src/ui/sidebarwidget.c
@@ -792,7 +792,7 @@ void setWidth_SidebarWidget(iSidebarWidget *d, float widthAsGaps) {
792 /* Even less space if the other sidebar is visible, too. */ 792 /* Even less space if the other sidebar is visible, too. */
793 const int otherWidth = 793 const int otherWidth =
794 width_Widget(findWidget_App(d->side == left_SideBarSide ? "sidebar2" : "sidebar")); 794 width_Widget(findWidget_App(d->side == left_SideBarSide ? "sidebar2" : "sidebar"));
795 width = iClamp(width, 30 * gap_UI, rootSize_Window(get_Window()).x - 50 * gap_UI - otherWidth); 795 width = iClamp(width, 30 * gap_UI, size_Root(get_Root()).x - 50 * gap_UI - otherWidth);
796 } 796 }
797 d->widthAsGaps = (float) width / (float) gap_UI; 797 d->widthAsGaps = (float) width / (float) gap_UI;
798 if (isVisible_Widget(w)) { 798 if (isVisible_Widget(w)) {
@@ -967,7 +967,7 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev)
967 d, 967 d,
968 ((d->side == left_SideBarSide 968 ((d->side == left_SideBarSide
969 ? local.x 969 ? local.x
970 : (rootSize_Window(get_Window()).x - coord_Command(cmd).x)) + 970 : (size_Root(get_Root()).x - coord_Command(cmd).x)) +
971 resMid) / (float) gap_UI); 971 resMid) / (float) gap_UI);
972 } 972 }
973 return iTrue; 973 return iTrue;
diff --git a/src/ui/touch.c b/src/ui/touch.c
index 75bbf765..6867a34b 100644
--- a/src/ui/touch.c
+++ b/src/ui/touch.c
@@ -335,7 +335,7 @@ static void update_TouchState_(void *ptr) {
335} 335}
336 336
337static iWidget *findOverflowScrollable_Widget_(iWidget *d) { 337static iWidget *findOverflowScrollable_Widget_(iWidget *d) {
338 const iInt2 rootSize = rootSize_Window(get_Window()); 338 const iInt2 rootSize = size_Root(get_Root());
339 for (iWidget *w = d; w; w = parent_Widget(w)) { 339 for (iWidget *w = d; w; w = parent_Widget(w)) {
340 if (flags_Widget(w) & overflowScrollable_WidgetFlag) { 340 if (flags_Widget(w) & overflowScrollable_WidgetFlag) {
341 if (height_Widget(w) > rootSize.y && !hasVisibleChildOnTop_Widget(w)) { 341 if (height_Widget(w) > rootSize.y && !hasVisibleChildOnTop_Widget(w)) {
@@ -437,11 +437,11 @@ iBool processEvent_Touch(const SDL_Event *ev) {
437 return iFalse; 437 return iFalse;
438 } 438 }
439 iTouchState *d = touchState_(); 439 iTouchState *d = touchState_();
440 iWindow *window = get_Window(); 440 iWindow *window = get_Window();
441 if (!isFinished_Anim(&window->rootOffset)) { 441 if (!isFinished_Anim(&window->rootOffset)) {
442 return iFalse; 442 return iFalse;
443 } 443 }
444 const iInt2 rootSize = rootSize_Window(window); 444 const iInt2 rootSize = size_Root(get_Root());
445 const SDL_TouchFingerEvent *fing = &ev->tfinger; 445 const SDL_TouchFingerEvent *fing = &ev->tfinger;
446 const iFloat3 pos = add_F3(init_F3(fing->x * rootSize.x, fing->y * rootSize.y, 0), /* pixels */ 446 const iFloat3 pos = add_F3(init_F3(fing->x * rootSize.x, fing->y * rootSize.y, 0), /* pixels */
447 init_F3(0, -value_Anim(&window->rootOffset), 0)); 447 init_F3(0, -value_Anim(&window->rootOffset), 0));
diff --git a/src/ui/util.c b/src/ui/util.c
index 6b9a43d9..2a72d511 100644
--- a/src/ui/util.c
+++ b/src/ui/util.c
@@ -738,7 +738,7 @@ void openMenu_Widget(iWidget *d, iInt2 coord) {
738} 738}
739 739
740void openMenuFlags_Widget(iWidget *d, iInt2 coord, iBool postCommands) { 740void openMenuFlags_Widget(iWidget *d, iInt2 coord, iBool postCommands) {
741 const iInt2 rootSize = rootSize_Window(get_Window()); 741 const iInt2 rootSize = size_Window(get_Window());
742 const iBool isPortraitPhone = (deviceType_App() == phone_AppDeviceType && isPortrait_App()); 742 const iBool isPortraitPhone = (deviceType_App() == phone_AppDeviceType && isPortrait_App());
743 const iBool isSlidePanel = (flags_Widget(d) & horizontalOffset_WidgetFlag) != 0; 743 const iBool isSlidePanel = (flags_Widget(d) & horizontalOffset_WidgetFlag) != 0;
744 if (postCommands) { 744 if (postCommands) {
@@ -756,7 +756,7 @@ void openMenuFlags_Widget(iWidget *d, iInt2 coord, iBool postCommands) {
756 if (!isSlidePanel) { 756 if (!isSlidePanel) {
757 setFlags_Widget(d, borderTop_WidgetFlag, iTrue); 757 setFlags_Widget(d, borderTop_WidgetFlag, iTrue);
758 } 758 }
759 d->rect.size.x = rootSize_Window(get_Window()).x; 759 d->rect.size.x = size_Window(get_Window()).x;
760 } 760 }
761 /* Update item fonts. */ { 761 /* Update item fonts. */ {
762 iForEach(ObjectList, i, children_Widget(d)) { 762 iForEach(ObjectList, i, children_Widget(d)) {
@@ -1754,7 +1754,7 @@ static void acceptValueInput_(iWidget *dlg) {
1754} 1754}
1755 1755
1756static void updateValueInputWidth_(iWidget *dlg) { 1756static void updateValueInputWidth_(iWidget *dlg) {
1757 const iRect safeRoot = safeRootRect_Window(get_Window()); 1757 const iRect safeRoot = safeRect_Root(get_Root());
1758 const iInt2 rootSize = safeRoot.size; 1758 const iInt2 rootSize = safeRoot.size;
1759 iWidget * title = findChild_Widget(dlg, "valueinput.title"); 1759 iWidget * title = findChild_Widget(dlg, "valueinput.title");
1760 iWidget * prompt = findChild_Widget(dlg, "valueinput.prompt"); 1760 iWidget * prompt = findChild_Widget(dlg, "valueinput.prompt");
diff --git a/src/ui/widget.c b/src/ui/widget.c
index 47522dbe..2d9bf45d 100644
--- a/src/ui/widget.c
+++ b/src/ui/widget.c
@@ -352,7 +352,7 @@ static size_t numArrangedChildren_Widget_(const iWidget *d) {
352 352
353static void centerHorizontal_Widget_(iWidget *d) { 353static void centerHorizontal_Widget_(iWidget *d) {
354 d->rect.pos.x = ((d->parent ? width_Rect(innerRect_Widget_(d->parent)) 354 d->rect.pos.x = ((d->parent ? width_Rect(innerRect_Widget_(d->parent))
355 : rootSize_Window(get_Window()).x) - 355 : size_Root(get_Root()).x) -
356 width_Rect(d->rect)) / 356 width_Rect(d->rect)) /
357 2; 357 2;
358 TRACE(d, "center horizontally: %d", d->rect.pos.x); 358 TRACE(d, "center horizontally: %d", d->rect.pos.x);
@@ -706,7 +706,7 @@ iBool containsExpanded_Widget(const iWidget *d, iInt2 coord, int expand) {
706 const iRect bounds = { 706 const iRect bounds = {
707 zero_I2(), 707 zero_I2(),
708 addY_I2(d->rect.size, 708 addY_I2(d->rect.size,
709 d->flags & drawBackgroundToBottom_WidgetFlag ? rootSize_Window(get_Window()).y : 0) 709 d->flags & drawBackgroundToBottom_WidgetFlag ? size_Root(get_Root()).y : 0)
710 }; 710 };
711 return contains_Rect(expand ? expanded_Rect(bounds, init1_I2(expand)) : bounds, 711 return contains_Rect(expand ? expanded_Rect(bounds, init1_I2(expand)) : bounds,
712 localCoord_Widget(d, coord)); 712 localCoord_Widget(d, coord));
@@ -845,8 +845,8 @@ iBool dispatchEvent_Widget(iWidget *d, const SDL_Event *ev) {
845 845
846static iBool scrollOverflow_Widget_(iWidget *d, int delta) { 846static iBool scrollOverflow_Widget_(iWidget *d, int delta) {
847 iRect bounds = bounds_Widget(d); 847 iRect bounds = bounds_Widget(d);
848 const iInt2 rootSize = rootSize_Window(get_Window()); 848 const iInt2 rootSize = size_Root(get_Root());
849 const iRect winRect = safeRootRect_Window(get_Window()); 849 const iRect winRect = safeRect_Root(get_Root());
850 const int yTop = top_Rect(winRect); 850 const int yTop = top_Rect(winRect);
851 const int yBottom = bottom_Rect(winRect); 851 const int yBottom = bottom_Rect(winRect);
852 //const int safeBottom = rootSize.y - yBottom; 852 //const int safeBottom = rootSize.y - yBottom;
@@ -899,30 +899,6 @@ iBool processEvent_Widget(iWidget *d, const SDL_Event *ev) {
899 if (scrollOverflow_Widget_(d, step)) { 899 if (scrollOverflow_Widget_(d, step)) {
900 return iTrue; 900 return iTrue;
901 } 901 }
902#if 0
903 iRect bounds = bounds_Widget(d);
904 const iInt2 rootSize = rootSize_Window(get_Window());
905 const iRect winRect = safeRootRect_Window(get_Window());
906 const int yTop = top_Rect(winRect);
907 const int yBottom = bottom_Rect(winRect);
908 const int safeBottom = rootSize.y - yBottom;
909 if (height_Rect(bounds) > height_Rect(winRect)) {
910 int step = ev->wheel.y;
911 if (!isPerPixel_MouseWheelEvent(&ev->wheel)) {
912 step *= lineHeight_Text(uiLabel_FontId);
913 }
914 bounds.pos.y += step;
915 if (step > 0) {
916 bounds.pos.y = iMin(bounds.pos.y, yTop);
917 }
918 else {
919 bounds.pos.y = iMax(bounds.pos.y, rootSize.y /*+ safeBottom*/ - height_Rect(bounds));
920 }
921 d->rect.pos = localCoord_Widget(d->parent, bounds.pos);
922 refresh_Widget(d);
923 return iTrue;
924 }
925#endif
926 } 902 }
927 switch (ev->type) { 903 switch (ev->type) {
928 case SDL_USEREVENT: { 904 case SDL_USEREVENT: {
@@ -1003,14 +979,14 @@ void drawBackground_Widget(const iWidget *d) {
1003 break; 979 break;
1004 } 980 }
1005 fillRect_Paint(&p, 981 fillRect_Paint(&p,
1006 initCorners_Rect(zero_I2(), rootSize_Window(get_Window())), 982 rect_Root(get_Root()),
1007 fadeColor); 983 fadeColor);
1008 SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_NONE); 984 SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_NONE);
1009 } 985 }
1010 if (d->bgColor >= 0 || d->frameColor >= 0) { 986 if (d->bgColor >= 0 || d->frameColor >= 0) {
1011 iRect rect = bounds_Widget(d); 987 iRect rect = bounds_Widget(d);
1012 if (d->flags & drawBackgroundToBottom_WidgetFlag) { 988 if (d->flags & drawBackgroundToBottom_WidgetFlag) {
1013 rect.size.y = rootSize_Window(get_Window()).y - top_Rect(rect); 989 rect.size.y = size_Root(get_Root()).y - top_Rect(rect);
1014 } 990 }
1015 iPaint p; 991 iPaint p;
1016 init_Paint(&p); 992 init_Paint(&p);
@@ -1018,7 +994,7 @@ void drawBackground_Widget(const iWidget *d) {
1018#if defined (iPlatformAppleMobile) 994#if defined (iPlatformAppleMobile)
1019 if (d->flags & (drawBackgroundToHorizontalSafeArea_WidgetFlag | 995 if (d->flags & (drawBackgroundToHorizontalSafeArea_WidgetFlag |
1020 drawBackgroundToVerticalSafeArea_WidgetFlag)) { 996 drawBackgroundToVerticalSafeArea_WidgetFlag)) {
1021 const iInt2 rootSize = rootSize_Window(get_Window()); 997 const iInt2 rootSize = size_Root(get_Root());
1022 const iInt2 center = divi_I2(rootSize, 2); 998 const iInt2 center = divi_I2(rootSize, 2);
1023 int top = 0, right = 0, bottom = 0, left = 0; 999 int top = 0, right = 0, bottom = 0, left = 0;
1024 if (d->flags & drawBackgroundToHorizontalSafeArea_WidgetFlag) { 1000 if (d->flags & drawBackgroundToHorizontalSafeArea_WidgetFlag) {
diff --git a/src/ui/window.c b/src/ui/window.c
index 1e98b167..5c10fea6 100644
--- a/src/ui/window.c
+++ b/src/ui/window.c
@@ -170,12 +170,13 @@ static void setupUserInterface_Window(iWindow *d) {
170 setCurrent_Root(NULL); 170 setCurrent_Root(NULL);
171} 171}
172 172
173static void updateRootSize_Window_(iWindow *d, iBool notifyAlways) { 173static void updateSize_Window_(iWindow *d, iBool notifyAlways) {
174 iInt2 *size = &d->root.widget->rect.size; 174 iInt2 *size = &d->size;
175 const iInt2 oldSize = *size; 175 const iInt2 oldSize = *size;
176 SDL_GetRendererOutputSize(d->render, &size->x, &size->y); 176 SDL_GetRendererOutputSize(d->render, &size->x, &size->y);
177 size->y -= d->keyboardHeight; 177 size->y -= d->keyboardHeight;
178 d->root.widget->minSize = *size; 178 d->root.widget->rect.size = *size; /* Reposition roots. */
179 d->root.widget->minSize = *size;
179 if (notifyAlways || !isEqual_I2(oldSize, *size)) { 180 if (notifyAlways || !isEqual_I2(oldSize, *size)) {
180 updatePadding_Root(&d->root); 181 updatePadding_Root(&d->root);
181 const iBool isHoriz = (d->place.lastNotifiedSize.x != size->x); 182 const iBool isHoriz = (d->place.lastNotifiedSize.x != size->x);
@@ -346,6 +347,7 @@ static SDL_Surface *loadImage_(const iBlock *data, int resized) {
346void init_Window(iWindow *d, iRect rect) { 347void init_Window(iWindow *d, iRect rect) {
347 theWindow_ = d; 348 theWindow_ = d;
348 d->win = NULL; 349 d->win = NULL;
350 d->size = zero_I2(); /* will be updated below */
349 init_Root(&d->root); 351 init_Root(&d->root);
350 iZap(d->cursors); 352 iZap(d->cursors);
351 d->place.initialPos = rect.pos; 353 d->place.initialPos = rect.pos;
@@ -426,7 +428,7 @@ void init_Window(iWindow *d, iRect rect) {
426 init_Text(d->render); 428 init_Text(d->render);
427 setupUserInterface_Window(d); 429 setupUserInterface_Window(d);
428 postCommand_App("~bindings.changed"); /* update from bindings */ 430 postCommand_App("~bindings.changed"); /* update from bindings */
429 updateRootSize_Window_(d, iFalse); 431 updateSize_Window_(d, iFalse);
430 /* Load the border shadow texture. */ { 432 /* Load the border shadow texture. */ {
431 SDL_Surface *surf = loadImage_(&imageShadow_Embedded, 0); 433 SDL_Surface *surf = loadImage_(&imageShadow_Embedded, 0);
432 d->borderShadow = SDL_CreateTextureFromSurface(d->render, surf); 434 d->borderShadow = SDL_CreateTextureFromSurface(d->render, surf);
@@ -639,7 +641,7 @@ static iBool handleWindowEvent_Window_(iWindow *d, const SDL_WindowEvent *ev) {
639 case SDL_WINDOWEVENT_RESIZED: 641 case SDL_WINDOWEVENT_RESIZED:
640 updatePadding_Root(&d->root); 642 updatePadding_Root(&d->root);
641 if (d->isMinimized) { 643 if (d->isMinimized) {
642 updateRootSize_Window_(d, iTrue); 644 updateSize_Window_(d, iTrue);
643 return iTrue; 645 return iTrue;
644 } 646 }
645 if (unsnap_Window_(d, NULL)) { 647 if (unsnap_Window_(d, NULL)) {
@@ -650,11 +652,11 @@ static iBool handleWindowEvent_Window_(iWindow *d, const SDL_WindowEvent *ev) {
650 //printf("normal rect set (resize)\n"); fflush(stdout); 652 //printf("normal rect set (resize)\n"); fflush(stdout);
651 } 653 }
652 checkPixelRatioChange_Window_(d); 654 checkPixelRatioChange_Window_(d);
653 updateRootSize_Window_(d, iTrue /* we were already redrawing during the resize */); 655 updateSize_Window_(d, iTrue /* we were already redrawing during the resize */);
654 postRefresh_App(); 656 postRefresh_App();
655 return iTrue; 657 return iTrue;
656 case SDL_WINDOWEVENT_RESTORED: 658 case SDL_WINDOWEVENT_RESTORED:
657 updateRootSize_Window_(d, iTrue); 659 updateSize_Window_(d, iTrue);
658 invalidate_Window_(d); 660 invalidate_Window_(d);
659 d->isMinimized = iFalse; 661 d->isMinimized = iFalse;
660 postRefresh_App(); 662 postRefresh_App();
@@ -847,7 +849,7 @@ void draw_Window(iWindow *d) {
847 iInt2 renderSize; 849 iInt2 renderSize;
848 SDL_GetRendererOutputSize(d->render, &renderSize.x, &renderSize.y); 850 SDL_GetRendererOutputSize(d->render, &renderSize.x, &renderSize.y);
849 if (!isEqual_I2(renderSize, d->root->rect.size)) { 851 if (!isEqual_I2(renderSize, d->root->rect.size)) {
850 updateRootSize_Window_(d, iTrue); 852 updateSize_Window_(d, iTrue);
851 processEvents_App(postedEventsOnly_AppEventMode); 853 processEvents_App(postedEventsOnly_AppEventMode);
852 } 854 }
853 } 855 }
@@ -905,7 +907,7 @@ void draw_Window(iWindow *d) {
905 907
906void resize_Window(iWindow *d, int w, int h) { 908void resize_Window(iWindow *d, int w, int h) {
907 SDL_SetWindowSize(d->win, w, h); 909 SDL_SetWindowSize(d->win, w, h);
908 updateRootSize_Window_(d, iFalse); 910 updateSize_Window_(d, iFalse);
909} 911}
910 912
911void setTitle_Window(iWindow *d, const iString *title) { 913void setTitle_Window(iWindow *d, const iString *title) {
@@ -944,22 +946,8 @@ uint32_t id_Window(const iWindow *d) {
944 return d && d->win ? SDL_GetWindowID(d->win) : 0; 946 return d && d->win ? SDL_GetWindowID(d->win) : 0;
945} 947}
946 948
947iInt2 rootSize_Window(const iWindow *d) { 949iInt2 size_Window(const iWindow *d) {
948 return d && d->root.widget ? d->root.widget->rect.size : zero_I2(); 950 return d ? d->size : zero_I2();
949}
950
951iRect safeRootRect_Window(const iWindow *d) {
952 iRect rect = { zero_I2(), rootSize_Window(d) };
953#if defined (iPlatformAppleMobile)
954 float left, top, right, bottom;
955 safeAreaInsets_iOS(&left, &top, &right, &bottom);
956 adjustEdges_Rect(&rect, top, -right, -bottom, left);
957#endif
958 return rect;
959}
960
961iInt2 visibleRootSize_Window(const iWindow *d) {
962 return addY_I2(rootSize_Window(d), -d->keyboardHeight);
963} 951}
964 952
965iInt2 coord_Window(const iWindow *d, int x, int y) { 953iInt2 coord_Window(const iWindow *d, int x, int y) {
diff --git a/src/ui/window.h b/src/ui/window.h
index f90bd863..a95d9f40 100644
--- a/src/ui/window.h
+++ b/src/ui/window.h
@@ -66,6 +66,7 @@ struct Impl_Window {
66 iBool ignoreClick; 66 iBool ignoreClick;
67 uint32_t focusGainedAt; 67 uint32_t focusGainedAt;
68 SDL_Renderer *render; 68 SDL_Renderer *render;
69 iInt2 size;
69 iRoot root; /* root widget and UI state */ 70 iRoot root; /* root widget and UI state */
70 float pixelRatio; /* conversion between points and pixels, e.g., coords, window size */ 71 float pixelRatio; /* conversion between points and pixels, e.g., coords, window size */
71 float displayScale; /* DPI-based scaling factor of current display, affects uiScale only */ 72 float displayScale; /* DPI-based scaling factor of current display, affects uiScale only */
@@ -95,9 +96,7 @@ void showToolbars_Window (iWindow *, iBool show);
95iBool postContextClick_Window (iWindow *, const SDL_MouseButtonEvent *); 96iBool postContextClick_Window (iWindow *, const SDL_MouseButtonEvent *);
96 97
97uint32_t id_Window (const iWindow *); 98uint32_t id_Window (const iWindow *);
98iInt2 rootSize_Window (const iWindow *); 99iInt2 size_Window (const iWindow *);
99iRect safeRootRect_Window (const iWindow *);
100iInt2 visibleRootSize_Window (const iWindow *); /* may be obstructed by software keyboard */
101iInt2 maxTextureSize_Window (const iWindow *); 100iInt2 maxTextureSize_Window (const iWindow *);
102float uiScale_Window (const iWindow *); 101float uiScale_Window (const iWindow *);
103iInt2 coord_Window (const iWindow *, int x, int y); 102iInt2 coord_Window (const iWindow *, int x, int y);
@@ -106,7 +105,6 @@ uint32_t frameTime_Window (const iWindow *);
106SDL_Renderer *renderer_Window (const iWindow *); 105SDL_Renderer *renderer_Window (const iWindow *);
107int snap_Window (const iWindow *); 106int snap_Window (const iWindow *);
108iBool isFullscreen_Window (const iWindow *); 107iBool isFullscreen_Window (const iWindow *);
109iBool isNarrow_Window (const iWindow *);
110 108
111iWindow * get_Window (void); 109iWindow * get_Window (void);
112 110