diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-03-09 22:44:45 +0200 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-03-09 22:44:45 +0200 |
commit | 2002543dbedcc1666d02d11fbde55f794d692bb7 (patch) | |
tree | 853412e7593d4a1ca3f0ccdc5ca31466a3400463 | |
parent | 5ccb0e0096dff6c2bbdfa959610620a14fdc918d (diff) |
Mobile: Revising phone-style dialogs
Sliding panels and left-edge swipes.
-rw-r--r-- | src/app.c | 2 | ||||
-rw-r--r-- | src/defs.h | 2 | ||||
-rw-r--r-- | src/ui/labelwidget.c | 15 | ||||
-rw-r--r-- | src/ui/touch.c | 7 | ||||
-rw-r--r-- | src/ui/util.c | 207 | ||||
-rw-r--r-- | src/ui/widget.c | 92 | ||||
-rw-r--r-- | src/ui/widget.h | 2 | ||||
-rw-r--r-- | src/ui/window.c | 10 | ||||
-rw-r--r-- | src/ui/window.h | 1 |
9 files changed, 287 insertions, 51 deletions
@@ -1218,12 +1218,14 @@ static void updateDropdownSelection_(iLabelWidget *dropButton, const char *selec | |||
1218 | } | 1218 | } |
1219 | 1219 | ||
1220 | static void updateColorThemeButton_(iLabelWidget *button, int theme) { | 1220 | static void updateColorThemeButton_(iLabelWidget *button, int theme) { |
1221 | if (!button) return; | ||
1221 | // const char *mode = strstr(cstr_String(id_Widget(as_Widget(button))), ".dark") | 1222 | // const char *mode = strstr(cstr_String(id_Widget(as_Widget(button))), ".dark") |
1222 | // ? "dark" : "light"; | 1223 | // ? "dark" : "light"; |
1223 | updateDropdownSelection_(button, format_CStr(".set arg:%d", theme)); | 1224 | updateDropdownSelection_(button, format_CStr(".set arg:%d", theme)); |
1224 | } | 1225 | } |
1225 | 1226 | ||
1226 | static void updateFontButton_(iLabelWidget *button, int font) { | 1227 | static void updateFontButton_(iLabelWidget *button, int font) { |
1228 | if (!button) return; | ||
1227 | updateDropdownSelection_(button, format_CStr(".set arg:%d", font)); | 1229 | updateDropdownSelection_(button, format_CStr(".set arg:%d", font)); |
1228 | } | 1230 | } |
1229 | 1231 | ||
@@ -70,3 +70,5 @@ enum iFileVersion { | |||
70 | #define circleWhite_Icon "\u25cb" | 70 | #define circleWhite_Icon "\u25cb" |
71 | #define gear_Icon "\u2699" | 71 | #define gear_Icon "\u2699" |
72 | #define explosion_Icon "\U0001f4a5" | 72 | #define explosion_Icon "\U0001f4a5" |
73 | #define leftAngle_Icon "\U0001fba4" | ||
74 | #define rightAngle_Icon "\U0001fba5" | ||
diff --git a/src/ui/labelwidget.c b/src/ui/labelwidget.c index b628ac23..9af0e2e3 100644 --- a/src/ui/labelwidget.c +++ b/src/ui/labelwidget.c | |||
@@ -22,6 +22,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
22 | 22 | ||
23 | #include "labelwidget.h" | 23 | #include "labelwidget.h" |
24 | #include "text.h" | 24 | #include "text.h" |
25 | #include "defs.h" | ||
25 | #include "color.h" | 26 | #include "color.h" |
26 | #include "paint.h" | 27 | #include "paint.h" |
27 | #include "app.h" | 28 | #include "app.h" |
@@ -147,7 +148,9 @@ static void getColors_LabelWidget_(const iLabelWidget *d, int *bg, int *fg, int | |||
147 | const iBool isFrameless = (flags & frameless_WidgetFlag) != 0; | 148 | const iBool isFrameless = (flags & frameless_WidgetFlag) != 0; |
148 | const iBool isButton = d->click.button != 0; | 149 | const iBool isButton = d->click.button != 0; |
149 | /* Default color state. */ | 150 | /* Default color state. */ |
150 | *bg = isButton && ~flags & noBackground_WidgetFlag ? uiBackground_ColorId : none_ColorId; | 151 | *bg = isButton && ~flags & noBackground_WidgetFlag ? (d->widget.bgColor != none_ColorId ? |
152 | d->widget.bgColor : uiBackground_ColorId) | ||
153 | : none_ColorId; | ||
151 | *fg = uiText_ColorId; | 154 | *fg = uiText_ColorId; |
152 | *frame1 = isButton ? uiEmboss1_ColorId : d->widget.frameColor; | 155 | *frame1 = isButton ? uiEmboss1_ColorId : d->widget.frameColor; |
153 | *frame2 = isButton ? uiEmboss2_ColorId : *frame1; | 156 | *frame2 = isButton ? uiEmboss2_ColorId : *frame1; |
@@ -248,6 +251,7 @@ static void draw_LabelWidget_(const iLabelWidget *d) { | |||
248 | drawCentered_Text( | 251 | drawCentered_Text( |
249 | d->font, | 252 | d->font, |
250 | (iRect){ | 253 | (iRect){ |
254 | /* The icon position is fine-tuned; c.f. high baseline of Source Sans Pro. */ | ||
251 | add_I2(add_I2(bounds.pos, padding_(flags)), | 255 | add_I2(add_I2(bounds.pos, padding_(flags)), |
252 | init_I2((flags & extraPadding_WidgetFlag ? -2 : -1.20f) * gap_UI, -gap_UI / 8)), | 256 | init_I2((flags & extraPadding_WidgetFlag ? -2 : -1.20f) * gap_UI, -gap_UI / 8)), |
253 | init_I2(iconPad, lineHeight_Text(d->font)) }, | 257 | init_I2(iconPad, lineHeight_Text(d->font)) }, |
@@ -294,6 +298,14 @@ static void draw_LabelWidget_(const iLabelWidget *d) { | |||
294 | fg, | 298 | fg, |
295 | cstr_String(&d->label)); | 299 | cstr_String(&d->label)); |
296 | } | 300 | } |
301 | if (flags & chevron_WidgetFlag) { | ||
302 | const iRect chRect = rect; | ||
303 | const int chSize = lineHeight_Text(d->font); | ||
304 | drawCentered_Text(d->font, | ||
305 | (iRect){ addX_I2(topRight_Rect(chRect), -iconPad), | ||
306 | init_I2(chSize, height_Rect(chRect)) }, | ||
307 | iTrue, fg, rightAngle_Icon); | ||
308 | } | ||
297 | unsetClip_Paint(&p); | 309 | unsetClip_Paint(&p); |
298 | } | 310 | } |
299 | 311 | ||
@@ -436,6 +448,7 @@ iChar icon_LabelWidget(const iLabelWidget *d) { | |||
436 | } | 448 | } |
437 | 449 | ||
438 | const iString *text_LabelWidget(const iLabelWidget *d) { | 450 | const iString *text_LabelWidget(const iLabelWidget *d) { |
451 | if (!d) return collectNew_String(); | ||
439 | return &d->label; | 452 | return &d->label; |
440 | } | 453 | } |
441 | 454 | ||
diff --git a/src/ui/touch.c b/src/ui/touch.c index 4b22b8fb..14e5fe48 100644 --- a/src/ui/touch.c +++ b/src/ui/touch.c | |||
@@ -130,7 +130,7 @@ static void dispatchMotion_Touch_(iFloat3 pos, int buttonState) { | |||
130 | }); | 130 | }); |
131 | } | 131 | } |
132 | 132 | ||
133 | static void dispatchClick_Touch_(const iTouch *d, int button) { | 133 | static iBool dispatchClick_Touch_(const iTouch *d, int button) { |
134 | const iFloat3 tapPos = d->pos[0]; | 134 | const iFloat3 tapPos = d->pos[0]; |
135 | SDL_MouseButtonEvent btn = { | 135 | SDL_MouseButtonEvent btn = { |
136 | .type = SDL_MOUSEBUTTONDOWN, | 136 | .type = SDL_MOUSEBUTTONDOWN, |
@@ -142,7 +142,7 @@ static void dispatchClick_Touch_(const iTouch *d, int button) { | |||
142 | .x = x_F3(tapPos), | 142 | .x = x_F3(tapPos), |
143 | .y = y_F3(tapPos) | 143 | .y = y_F3(tapPos) |
144 | }; | 144 | }; |
145 | dispatchEvent_Widget(get_Window()->root, (SDL_Event *) &btn); | 145 | iBool wasUsed = dispatchEvent_Widget(get_Window()->root, (SDL_Event *) &btn); |
146 | /* Immediately released, too. */ | 146 | /* Immediately released, too. */ |
147 | btn.type = SDL_MOUSEBUTTONUP; | 147 | btn.type = SDL_MOUSEBUTTONUP; |
148 | btn.state = SDL_RELEASED; | 148 | btn.state = SDL_RELEASED; |
@@ -150,6 +150,7 @@ static void dispatchClick_Touch_(const iTouch *d, int button) { | |||
150 | dispatchEvent_Widget(get_Window()->root, (SDL_Event *) &btn); | 150 | dispatchEvent_Widget(get_Window()->root, (SDL_Event *) &btn); |
151 | //dispatchMotion_Touch_(zero_F3(), 0); | 151 | //dispatchMotion_Touch_(zero_F3(), 0); |
152 | setHover_Widget(NULL); /* FIXME: this doesn't seem to do anything? */ | 152 | setHover_Widget(NULL); /* FIXME: this doesn't seem to do anything? */ |
153 | return wasUsed; | ||
153 | } | 154 | } |
154 | 155 | ||
155 | static void clearWidgetMomentum_TouchState_(iTouchState *d, iWidget *widget) { | 156 | static void clearWidgetMomentum_TouchState_(iTouchState *d, iWidget *widget) { |
@@ -305,7 +306,7 @@ iBool processEvent_Touch(const SDL_Event *ev) { | |||
305 | iWidget *aff = hitChild_Widget(window->root, init_I2(iRound(x), iRound(y_F3(pos)))); | 306 | iWidget *aff = hitChild_Widget(window->root, init_I2(iRound(x), iRound(y_F3(pos)))); |
306 | /* TODO: We must retain a reference to the affinity widget, or otherwise it might | 307 | /* TODO: We must retain a reference to the affinity widget, or otherwise it might |
307 | be destroyed during the gesture. */ | 308 | be destroyed during the gesture. */ |
308 | // printf("aff:%p (%s)\n", aff, aff ? class_Widget(aff)->name : "-"); | 309 | printf("aff:%p (%s)\n", aff, aff ? class_Widget(aff)->name : "-"); |
309 | if (flags_Widget(aff) & touchDrag_WidgetFlag) { | 310 | if (flags_Widget(aff) & touchDrag_WidgetFlag) { |
310 | dispatchEvent_Widget(window->root, (SDL_Event *) &(SDL_MouseButtonEvent){ | 311 | dispatchEvent_Widget(window->root, (SDL_Event *) &(SDL_MouseButtonEvent){ |
311 | .type = SDL_MOUSEBUTTONDOWN, | 312 | .type = SDL_MOUSEBUTTONDOWN, |
diff --git a/src/ui/util.c b/src/ui/util.c index 10fa66b0..c6e2fd1a 100644 --- a/src/ui/util.c +++ b/src/ui/util.c | |||
@@ -26,6 +26,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
26 | #include "bookmarks.h" | 26 | #include "bookmarks.h" |
27 | #include "color.h" | 27 | #include "color.h" |
28 | #include "command.h" | 28 | #include "command.h" |
29 | #include "defs.h" | ||
29 | #include "documentwidget.h" | 30 | #include "documentwidget.h" |
30 | #include "gmutil.h" | 31 | #include "gmutil.h" |
31 | #include "feeds.h" | 32 | #include "feeds.h" |
@@ -517,6 +518,7 @@ iWidget *makeMenu_Widget(iWidget *parent, const iMenuItem *items, size_t n) { | |||
517 | void openMenu_Widget(iWidget *d, iInt2 coord) { | 518 | void openMenu_Widget(iWidget *d, iInt2 coord) { |
518 | const iInt2 rootSize = rootSize_Window(get_Window()); | 519 | const iInt2 rootSize = rootSize_Window(get_Window()); |
519 | const iBool isPortraitPhone = (deviceType_App() == phone_AppDeviceType && isPortrait_App()); | 520 | const iBool isPortraitPhone = (deviceType_App() == phone_AppDeviceType && isPortrait_App()); |
521 | const iBool isSlidePanel = (flags_Widget(d) & horizontalOffset_WidgetFlag) != 0; | ||
520 | /* Menu closes when commands are emitted, so handle any pending ones beforehand. */ | 522 | /* Menu closes when commands are emitted, so handle any pending ones beforehand. */ |
521 | postCommand_App("cancel"); /* dismiss any other menus */ | 523 | postCommand_App("cancel"); /* dismiss any other menus */ |
522 | processEvents_App(postedEventsOnly_AppEventMode); | 524 | processEvents_App(postedEventsOnly_AppEventMode); |
@@ -526,6 +528,9 @@ void openMenu_Widget(iWidget *d, iInt2 coord) { | |||
526 | if (isPortraitPhone) { | 528 | if (isPortraitPhone) { |
527 | setFlags_Widget(d, arrangeWidth_WidgetFlag | resizeChildrenToWidestChild_WidgetFlag, iFalse); | 529 | setFlags_Widget(d, arrangeWidth_WidgetFlag | resizeChildrenToWidestChild_WidgetFlag, iFalse); |
528 | setFlags_Widget(d, resizeWidthOfChildren_WidgetFlag, iTrue); | 530 | setFlags_Widget(d, resizeWidthOfChildren_WidgetFlag, iTrue); |
531 | if (!isSlidePanel) { | ||
532 | setFlags_Widget(d, borderTop_WidgetFlag, iTrue); | ||
533 | } | ||
529 | d->rect.size.x = rootSize_Window(get_Window()).x; | 534 | d->rect.size.x = rootSize_Window(get_Window()).x; |
530 | } | 535 | } |
531 | /* Update item fonts. */ { | 536 | /* Update item fonts. */ { |
@@ -537,7 +542,9 @@ void openMenu_Widget(iWidget *d, iInt2 coord) { | |||
537 | setFont_LabelWidget(label, isCaution ? uiLabelBold_FontId : uiLabel_FontId); | 542 | setFont_LabelWidget(label, isCaution ? uiLabelBold_FontId : uiLabel_FontId); |
538 | } | 543 | } |
539 | else if (isPortraitPhone) { | 544 | else if (isPortraitPhone) { |
540 | setFont_LabelWidget(label, isCaution ? defaultBigBold_FontId : defaultBig_FontId); | 545 | if (!isSlidePanel) { |
546 | setFont_LabelWidget(label, isCaution ? defaultBigBold_FontId : defaultBig_FontId); | ||
547 | } | ||
541 | } | 548 | } |
542 | else { | 549 | else { |
543 | setFont_LabelWidget(label, isCaution ? uiContentBold_FontId : uiContent_FontId); | 550 | setFont_LabelWidget(label, isCaution ? uiContentBold_FontId : uiContent_FontId); |
@@ -547,7 +554,12 @@ void openMenu_Widget(iWidget *d, iInt2 coord) { | |||
547 | } | 554 | } |
548 | arrange_Widget(d); | 555 | arrange_Widget(d); |
549 | if (isPortraitPhone) { | 556 | if (isPortraitPhone) { |
550 | d->rect.pos = init_I2(0, rootSize.y); | 557 | if (isSlidePanel) { |
558 | d->rect.pos = zero_I2(); //neg_I2(bounds_Widget(parent_Widget(d)).pos); | ||
559 | } | ||
560 | else { | ||
561 | d->rect.pos = init_I2(0, rootSize.y); | ||
562 | } | ||
551 | } | 563 | } |
552 | else { | 564 | else { |
553 | d->rect.pos = coord; | 565 | d->rect.pos = coord; |
@@ -568,7 +580,7 @@ void openMenu_Widget(iWidget *d, iInt2 coord) { | |||
568 | rightExcess += r; | 580 | rightExcess += r; |
569 | } | 581 | } |
570 | #endif | 582 | #endif |
571 | if (bottomExcess > 0) { | 583 | if (bottomExcess > 0 && (!isPortraitPhone || !isSlidePanel)) { |
572 | d->rect.pos.y -= bottomExcess; | 584 | d->rect.pos.y -= bottomExcess; |
573 | } | 585 | } |
574 | if (topExcess > 0) { | 586 | if (topExcess > 0) { |
@@ -583,7 +595,7 @@ void openMenu_Widget(iWidget *d, iInt2 coord) { | |||
583 | postRefresh_App(); | 595 | postRefresh_App(); |
584 | postCommand_Widget(d, "menu.opened"); | 596 | postCommand_Widget(d, "menu.opened"); |
585 | if (isPortraitPhone) { | 597 | if (isPortraitPhone) { |
586 | setVisualOffset_Widget(d, height_Widget(d), 0, 0); | 598 | setVisualOffset_Widget(d, isSlidePanel ? width_Widget(d) : height_Widget(d), 0, 0); |
587 | setVisualOffset_Widget(d, 0, 330, easeOut_AnimFlag | softer_AnimFlag); | 599 | setVisualOffset_Widget(d, 0, 330, easeOut_AnimFlag | softer_AnimFlag); |
588 | } | 600 | } |
589 | } | 601 | } |
@@ -594,7 +606,11 @@ void closeMenu_Widget(iWidget *d) { | |||
594 | postRefresh_App(); | 606 | postRefresh_App(); |
595 | postCommand_Widget(d, "menu.closed"); | 607 | postCommand_Widget(d, "menu.closed"); |
596 | if (isPortrait_App() && deviceType_App() == phone_AppDeviceType) { | 608 | if (isPortrait_App() && deviceType_App() == phone_AppDeviceType) { |
597 | setVisualOffset_Widget(d, height_Widget(d), 200, easeIn_AnimFlag | softer_AnimFlag); | 609 | setVisualOffset_Widget(d, |
610 | flags_Widget(d) & horizontalOffset_WidgetFlag ? | ||
611 | width_Widget(d) : height_Widget(d), | ||
612 | 200, | ||
613 | easeIn_AnimFlag | softer_AnimFlag); | ||
598 | } | 614 | } |
599 | } | 615 | } |
600 | 616 | ||
@@ -882,7 +898,43 @@ iWidget *makeSheet_Widget(const char *id) { | |||
882 | return sheet; | 898 | return sheet; |
883 | } | 899 | } |
884 | 900 | ||
901 | static iBool slidePanelHandler_(iWidget *d, const char *cmd) { | ||
902 | if (equal_Command(cmd, "panel.open")) { | ||
903 | iWidget *button = pointer_Command(cmd); | ||
904 | iWidget *panel = userData_Object(button); | ||
905 | openMenu_Widget(panel, zero_I2()); | ||
906 | updateTextCStr_LabelWidget(findWidget_App("panel.back"), "Settings"); | ||
907 | return iTrue; | ||
908 | } | ||
909 | if (equal_Command(cmd, "mouse.clicked") && arg_Command(cmd) && | ||
910 | argLabel_Command(cmd, "button") == SDL_BUTTON_X1) { | ||
911 | postCommand_App("panel.close"); | ||
912 | return iTrue; | ||
913 | } | ||
914 | if (equal_Command(cmd, "panel.close")) { | ||
915 | iBool wasClosed = iFalse; | ||
916 | iForEach(ObjectList, i, children_Widget(parent_Widget(d))) { | ||
917 | iWidget *child = i.object; | ||
918 | if (!cmp_String(id_Widget(child), "panel") && isVisible_Widget(child)) { | ||
919 | closeMenu_Widget(child); | ||
920 | setFocus_Widget(NULL); | ||
921 | updateTextCStr_LabelWidget(findWidget_App("panel.back"), "Back"); | ||
922 | wasClosed = iTrue; | ||
923 | } | ||
924 | } | ||
925 | if (!wasClosed) { | ||
926 | postCommand_App("prefs.dismiss"); | ||
927 | } | ||
928 | return iTrue; | ||
929 | } | ||
930 | return iFalse; | ||
931 | } | ||
932 | |||
885 | static iBool isTwoColumnPage_(iWidget *d) { | 933 | static iBool isTwoColumnPage_(iWidget *d) { |
934 | if (cmp_String(id_Widget(d), "dialogbuttons") == 0 || | ||
935 | cmp_String(id_Widget(d), "prefs.tabs") == 0) { | ||
936 | return iFalse; | ||
937 | } | ||
886 | if (class_Widget(d) == &Class_Widget && childCount_Widget(d) == 2) { | 938 | if (class_Widget(d) == &Class_Widget && childCount_Widget(d) == 2) { |
887 | return class_Widget(child_Widget(d, 0)) == &Class_Widget && | 939 | return class_Widget(child_Widget(d, 0)) == &Class_Widget && |
888 | class_Widget(child_Widget(d, 1)) == &Class_Widget; | 940 | class_Widget(child_Widget(d, 1)) == &Class_Widget; |
@@ -894,46 +946,129 @@ void finalizeSheet_Widget(iWidget *sheet) { | |||
894 | if (deviceType_App() == phone_AppDeviceType) { | 946 | if (deviceType_App() == phone_AppDeviceType) { |
895 | /* The sheet contents are completely rearranged on a phone. We'll set up a linear | 947 | /* The sheet contents are completely rearranged on a phone. We'll set up a linear |
896 | fullscreen arrangement of the widgets. Sheets are already scrollable so they | 948 | fullscreen arrangement of the widgets. Sheets are already scrollable so they |
897 | can be taller than the display. */ | 949 | can be taller than the display. In hindsight, it may have been easier to |
950 | create phone versions of each dialog, but at least this works with any future | ||
951 | changes to the UI (..."works"). */ | ||
952 | int topSafe = 0; | ||
953 | int navBarHeight = lineHeight_Text(defaultBig_FontId) + 4 * gap_UI; | ||
898 | #if defined (iPlatformAppleMobile) | 954 | #if defined (iPlatformAppleMobile) |
899 | /* Safe area insets. */ { | 955 | /* Safe area insets. */ { |
900 | /* TODO: Must be updated when orientation changes; use a widget flag? */ | 956 | /* TODO: Must be updated when orientation changes; use a widget flag? */ |
901 | float l, t, r, b; | 957 | float l, t, r, b; |
902 | safeAreaInsets_iOS(&l, &t, &r, &b); | 958 | safeAreaInsets_iOS(&l, &t, &r, &b); |
903 | setPadding_Widget(sheet, l, t, r, b); | 959 | // setPadding_Widget(sheet, l, t, r, b); |
960 | setPadding1_Widget(sheet, 0); | ||
961 | topSafe = t; | ||
962 | navBarHeight += t; | ||
904 | } | 963 | } |
905 | #endif | 964 | #endif |
906 | setFlags_Widget(sheet, | 965 | setFlags_Widget(sheet, |
907 | parentCannotResize_WidgetFlag | arrangeWidth_WidgetFlag | | 966 | keepOnTop_WidgetFlag | |
908 | centerHorizontal_WidgetFlag, | 967 | parentCannotResize_WidgetFlag | |
968 | arrangeSize_WidgetFlag | | ||
969 | centerHorizontal_WidgetFlag | | ||
970 | arrangeVertical_WidgetFlag | | ||
971 | arrangeHorizontal_WidgetFlag | | ||
972 | overflowScrollable_WidgetFlag, | ||
909 | iFalse); | 973 | iFalse); |
910 | setFlags_Widget(sheet, frameless_WidgetFlag | resizeWidthOfChildren_WidgetFlag, iTrue); | 974 | setFlags_Widget(sheet, |
975 | commandOnClick_WidgetFlag | | ||
976 | frameless_WidgetFlag | | ||
977 | resizeWidthOfChildren_WidgetFlag, | ||
978 | iTrue); | ||
979 | setBackgroundColor_Widget(sheet, green_ColorId); | ||
911 | iPtrArray *contents = collect_PtrArray(new_PtrArray()); /* two-column pages */ | 980 | iPtrArray *contents = collect_PtrArray(new_PtrArray()); /* two-column pages */ |
981 | iPtrArray *panelButtons = collect_PtrArray(new_PtrArray()); | ||
912 | iWidget *tabs = findChild_Widget(sheet, "prefs.tabs"); | 982 | iWidget *tabs = findChild_Widget(sheet, "prefs.tabs"); |
983 | iWidget *topPanel = new_Widget(); | ||
984 | setId_Widget(topPanel, "panel.top"); | ||
985 | //setBackgroundColor_Widget(topPanel, red_ColorId); | ||
913 | if (tabs) { | 986 | if (tabs) { |
914 | /* Pull out the pages and make them sequential. */ | 987 | iRelease(removeChild_Widget(sheet, child_Widget(sheet, 0))); /* heading */ |
988 | iRelease(removeChild_Widget(sheet, findChild_Widget(sheet, "dialogbuttons"))); | ||
989 | /* Pull out the pages and make them panels. */ | ||
915 | iWidget *pages = findChild_Widget(tabs, "tabs.pages"); | 990 | iWidget *pages = findChild_Widget(tabs, "tabs.pages"); |
916 | size_t pageCount = tabCount_Widget(tabs); | 991 | size_t pageCount = tabCount_Widget(tabs); |
917 | for (size_t i = 0; i < pageCount; i++) { | 992 | for (size_t i = 0; i < pageCount; i++) { |
993 | iString *text = copy_String(text_LabelWidget(tabPageButton_Widget(tabs, tabPage_Widget(tabs, 0)))); | ||
918 | iWidget *page = removeTabPage_Widget(tabs, 0); | 994 | iWidget *page = removeTabPage_Widget(tabs, 0); |
919 | iWidget *pageContent = child_Widget(page, 1); /* surrounded by padding widgets */ | 995 | iWidget *pageContent = child_Widget(page, 1); /* surrounded by padding widgets */ |
920 | pushBack_PtrArray(contents, ref_Object(pageContent)); | 996 | pushBack_PtrArray(contents, ref_Object(pageContent)); |
997 | iLabelWidget *panelButton; | ||
998 | pushBack_PtrArray(panelButtons, | ||
999 | addChildFlags_Widget(topPanel, | ||
1000 | iClob(panelButton = new_LabelWidget | ||
1001 | (i == 1 ? "User Interface" : cstr_String(text), | ||
1002 | "panel.open")), | ||
1003 | alignLeft_WidgetFlag | frameless_WidgetFlag | | ||
1004 | borderBottom_WidgetFlag | extraPadding_WidgetFlag | | ||
1005 | chevron_WidgetFlag)); | ||
1006 | const iChar icons[] = { | ||
1007 | 0x2699, /* gear */ | ||
1008 | 0x1f4f1, /* mobile phone */ | ||
1009 | 0x1f3a8, /* palette */ | ||
1010 | 0x1f523, | ||
1011 | 0x1f5a7, /* computer network */ | ||
1012 | }; | ||
1013 | setIcon_LabelWidget(panelButton, icons[i]); | ||
1014 | setFont_LabelWidget(panelButton, defaultBig_FontId); | ||
1015 | setBackgroundColor_Widget(as_Widget(panelButton), uiBackgroundSidebar_ColorId); | ||
921 | iRelease(page); | 1016 | iRelease(page); |
1017 | delete_String(text); | ||
922 | } | 1018 | } |
923 | destroy_Widget(tabs); | 1019 | destroy_Widget(tabs); |
924 | } | 1020 | } |
925 | else { | 1021 | iForEach(ObjectList, i, children_Widget(sheet)) { |
926 | iForEach(ObjectList, i, children_Widget(sheet)) { | 1022 | iWidget *child = i.object; |
927 | iWidget *child = i.object; | 1023 | if (isTwoColumnPage_(child)) { |
928 | if (isTwoColumnPage_(child)) { | 1024 | printf("Non-tabbed two-column page:\n"); |
929 | pushBack_PtrArray(contents, removeChild_Widget(sheet, child)); | 1025 | printTree_Widget(child); |
930 | } | 1026 | pushBack_PtrArray(contents, removeChild_Widget(sheet, child)); |
1027 | } | ||
1028 | else { | ||
1029 | removeChild_Widget(sheet, child); | ||
1030 | addChild_Widget(topPanel, child); | ||
1031 | iRelease(child); | ||
931 | } | 1032 | } |
932 | } | 1033 | } |
1034 | iAssert(size_PtrArray(contents) == size_PtrArray(panelButtons)); | ||
1035 | topPanel->rect.pos = init_I2(0, navBarHeight); | ||
1036 | addChildFlags_Widget(sheet, iClob(topPanel), | ||
1037 | arrangeVertical_WidgetFlag | | ||
1038 | resizeWidthOfChildren_WidgetFlag | arrangeHeight_WidgetFlag | | ||
1039 | overflowScrollable_WidgetFlag | | ||
1040 | commandOnClick_WidgetFlag); | ||
1041 | setCommandHandler_Widget(topPanel, slidePanelHandler_); | ||
933 | iForEach(PtrArray, j, contents) { | 1042 | iForEach(PtrArray, j, contents) { |
1043 | iWidget *owner = topPanel; | ||
1044 | if (!isEmpty_PtrArray(panelButtons)) { | ||
1045 | iLabelWidget *button = at_PtrArray(panelButtons, index_PtrArrayIterator(&j)); | ||
1046 | owner = new_Widget(); | ||
1047 | setId_Widget(owner, "panel"); | ||
1048 | setUserData_Object(button, owner); | ||
1049 | setBackgroundColor_Widget(owner, uiBackground_ColorId); | ||
1050 | addChild_Widget(owner, iClob(makePadding_Widget(navBarHeight - topSafe))); | ||
1051 | iLabelWidget *title = addChildFlags_Widget(owner, | ||
1052 | iClob(new_LabelWidget(cstr_String(text_LabelWidget(button)), NULL)), alignLeft_WidgetFlag | frameless_WidgetFlag); | ||
1053 | setFont_LabelWidget(title, uiLabelLargeBold_FontId); | ||
1054 | setTextColor_LabelWidget(title, uiHeading_ColorId); | ||
1055 | addChildFlags_Widget(sheet, | ||
1056 | iClob(owner), | ||
1057 | focusRoot_WidgetFlag | | ||
1058 | //mouseModal_WidgetFlag | | ||
1059 | hidden_WidgetFlag | | ||
1060 | disabled_WidgetFlag | | ||
1061 | arrangeVertical_WidgetFlag | | ||
1062 | resizeWidthOfChildren_WidgetFlag | | ||
1063 | arrangeHeight_WidgetFlag | | ||
1064 | overflowScrollable_WidgetFlag | | ||
1065 | horizontalOffset_WidgetFlag | | ||
1066 | commandOnClick_WidgetFlag); | ||
1067 | } | ||
934 | iWidget *pageContent = j.ptr; | 1068 | iWidget *pageContent = j.ptr; |
935 | iWidget *headings = child_Widget(pageContent, 0); | 1069 | iWidget *headings = child_Widget(pageContent, 0); |
936 | iWidget *values = child_Widget(pageContent, 1); | 1070 | iWidget *values = child_Widget(pageContent, 1); |
1071 | iBool isFirst = iTrue; | ||
937 | while (!isEmpty_ObjectList(children_Widget(headings))) { | 1072 | while (!isEmpty_ObjectList(children_Widget(headings))) { |
938 | iWidget *heading = child_Widget(headings, 0); | 1073 | iWidget *heading = child_Widget(headings, 0); |
939 | iWidget *value = child_Widget(values, 0); | 1074 | iWidget *value = child_Widget(values, 0); |
@@ -968,7 +1103,7 @@ void finalizeSheet_Widget(iWidget *sheet) { | |||
968 | setFont_LabelWidget((iLabelWidget *) heading, defaultBig_FontId); | 1103 | setFont_LabelWidget((iLabelWidget *) heading, defaultBig_FontId); |
969 | addChildFlags_Widget(div, iClob(new_Widget()), expand_WidgetFlag); | 1104 | addChildFlags_Widget(div, iClob(new_Widget()), expand_WidgetFlag); |
970 | addChild_Widget(div, iClob(value)); | 1105 | addChild_Widget(div, iClob(value)); |
971 | addChildFlags_Widget(sheet, | 1106 | addChildFlags_Widget(owner, |
972 | iClob(div), | 1107 | iClob(div), |
973 | borderBottom_WidgetFlag | arrangeHeight_WidgetFlag | | 1108 | borderBottom_WidgetFlag | arrangeHeight_WidgetFlag | |
974 | resizeWidthOfChildren_WidgetFlag | | 1109 | resizeWidthOfChildren_WidgetFlag | |
@@ -977,12 +1112,17 @@ void finalizeSheet_Widget(iWidget *sheet) { | |||
977 | else { | 1112 | else { |
978 | if (valueLabel && isEmpty_String(text_LabelWidget(valueLabel))) { | 1113 | if (valueLabel && isEmpty_String(text_LabelWidget(valueLabel))) { |
979 | /* Subheading padding goes above. */ | 1114 | /* Subheading padding goes above. */ |
980 | addChild_Widget(sheet, iClob(value)); | 1115 | // if (!isFirst) { |
981 | addChildFlags_Widget(sheet, iClob(heading), 0); | 1116 | // addChild_Widget(owner, iClob(value)); |
1117 | // } | ||
1118 | // else { | ||
1119 | iRelease(value); | ||
1120 | // } | ||
1121 | addChildFlags_Widget(owner, iClob(heading), 0); | ||
982 | setFont_LabelWidget(headingLabel, uiLabelBold_FontId); | 1122 | setFont_LabelWidget(headingLabel, uiLabelBold_FontId); |
983 | } | 1123 | } |
984 | else { | 1124 | else { |
985 | addChildFlags_Widget(sheet, iClob(heading), borderBottom_WidgetFlag); | 1125 | addChildFlags_Widget(owner, iClob(heading), borderBottom_WidgetFlag); |
986 | if (headingLabel) { | 1126 | if (headingLabel) { |
987 | setTextColor_LabelWidget(headingLabel, uiSubheading_ColorId); | 1127 | setTextColor_LabelWidget(headingLabel, uiSubheading_ColorId); |
988 | setText_LabelWidget(headingLabel, | 1128 | setText_LabelWidget(headingLabel, |
@@ -999,14 +1139,14 @@ void finalizeSheet_Widget(iWidget *sheet) { | |||
999 | setBackgroundColor_Widget(pad, uiBackgroundSidebar_ColorId); | 1139 | setBackgroundColor_Widget(pad, uiBackgroundSidebar_ColorId); |
1000 | setPadding_Widget(pad, 0, 1 * gap_UI, 0, 1 * gap_UI); | 1140 | setPadding_Widget(pad, 0, 1 * gap_UI, 0, 1 * gap_UI); |
1001 | addChild_Widget(pad, iClob(value)); | 1141 | addChild_Widget(pad, iClob(value)); |
1002 | addChildFlags_Widget(sheet, iClob(pad), borderBottom_WidgetFlag | | 1142 | addChildFlags_Widget(owner, iClob(pad), borderBottom_WidgetFlag | |
1003 | arrangeVertical_WidgetFlag | | 1143 | arrangeVertical_WidgetFlag | |
1004 | resizeToParentWidth_WidgetFlag | | 1144 | resizeToParentWidth_WidgetFlag | |
1005 | resizeWidthOfChildren_WidgetFlag | | 1145 | resizeWidthOfChildren_WidgetFlag | |
1006 | arrangeHeight_WidgetFlag); | 1146 | arrangeHeight_WidgetFlag); |
1007 | } | 1147 | } |
1008 | else { | 1148 | else { |
1009 | addChild_Widget(sheet, iClob(value)); | 1149 | addChild_Widget(owner, iClob(value)); |
1010 | } | 1150 | } |
1011 | /* Align radio buttons to the right. */ | 1151 | /* Align radio buttons to the right. */ |
1012 | if (childCount_Widget(value) >= 2) { | 1152 | if (childCount_Widget(value) >= 2) { |
@@ -1021,15 +1161,35 @@ void finalizeSheet_Widget(iWidget *sheet) { | |||
1021 | if (isInstance_Object(sub.object, &Class_LabelWidget)) { | 1161 | if (isInstance_Object(sub.object, &Class_LabelWidget)) { |
1022 | iLabelWidget *opt = sub.object; | 1162 | iLabelWidget *opt = sub.object; |
1023 | setFont_LabelWidget(opt, defaultMedium_FontId); | 1163 | setFont_LabelWidget(opt, defaultMedium_FontId); |
1164 | setFlags_Widget(as_Widget(opt), noBackground_WidgetFlag, iTrue); | ||
1024 | } | 1165 | } |
1025 | } | 1166 | } |
1026 | } | 1167 | } |
1027 | } | 1168 | } |
1028 | } | 1169 | } |
1170 | isFirst = iFalse; | ||
1029 | } | 1171 | } |
1030 | destroy_Widget(pageContent); | 1172 | destroy_Widget(pageContent); |
1173 | addChildFlags_Widget(owner, iClob(new_Widget()), expand_WidgetFlag); | ||
1031 | } | 1174 | } |
1032 | destroyPending_Widget(); | 1175 | destroyPending_Widget(); |
1176 | /* Navbar. */ { | ||
1177 | iWidget *navi = new_Widget(); | ||
1178 | setSize_Widget(navi, init_I2(-1, navBarHeight)); | ||
1179 | setBackgroundColor_Widget(navi, uiBackground_ColorId); | ||
1180 | addChild_Widget(navi, iClob(makePadding_Widget(topSafe))); | ||
1181 | iLabelWidget *back = addChildFlags_Widget(navi, | ||
1182 | iClob(new_LabelWidget(leftAngle_Icon " Back", "panel.close")), | ||
1183 | noBackground_WidgetFlag | frameless_WidgetFlag | | ||
1184 | alignLeft_WidgetFlag | extraPadding_WidgetFlag); | ||
1185 | setId_Widget(as_Widget(back), "panel.back"); | ||
1186 | checkIcon_LabelWidget(back); | ||
1187 | setFont_LabelWidget(back, defaultBig_FontId); | ||
1188 | addChildFlags_Widget(sheet, iClob(navi), | ||
1189 | arrangeHeight_WidgetFlag | resizeWidthOfChildren_WidgetFlag | | ||
1190 | resizeToParentWidth_WidgetFlag | arrangeVertical_WidgetFlag | | ||
1191 | borderBottom_WidgetFlag); | ||
1192 | } | ||
1033 | arrange_Widget(sheet->parent); | 1193 | arrange_Widget(sheet->parent); |
1034 | printTree_Widget(sheet); | 1194 | printTree_Widget(sheet); |
1035 | } | 1195 | } |
@@ -1125,6 +1285,7 @@ iBool valueInputHandler_(iWidget *dlg, const char *cmd) { | |||
1125 | 1285 | ||
1126 | iWidget *makeDialogButtons_Widget(const iMenuItem *actions, size_t numActions) { | 1286 | iWidget *makeDialogButtons_Widget(const iMenuItem *actions, size_t numActions) { |
1127 | iWidget *div = new_Widget(); | 1287 | iWidget *div = new_Widget(); |
1288 | setId_Widget(div, "dialogbuttons"); | ||
1128 | setFlags_Widget(div, | 1289 | setFlags_Widget(div, |
1129 | arrangeHorizontal_WidgetFlag | arrangeHeight_WidgetFlag | | 1290 | arrangeHorizontal_WidgetFlag | arrangeHeight_WidgetFlag | |
1130 | resizeToParentWidth_WidgetFlag | | 1291 | resizeToParentWidth_WidgetFlag | |
diff --git a/src/ui/widget.c b/src/ui/widget.c index d7543615..ec229b86 100644 --- a/src/ui/widget.c +++ b/src/ui/widget.c | |||
@@ -153,11 +153,13 @@ void setFlags_Widget(iWidget *d, int64_t flags, iBool set) { | |||
153 | } | 153 | } |
154 | iChangeFlags(d->flags, flags, set); | 154 | iChangeFlags(d->flags, flags, set); |
155 | if (flags & keepOnTop_WidgetFlag) { | 155 | if (flags & keepOnTop_WidgetFlag) { |
156 | iPtrArray *onTop = onTop_RootData_(); | ||
156 | if (set) { | 157 | if (set) { |
157 | pushBack_PtrArray(onTop_RootData_(), d); | 158 | iAssert(indexOf_PtrArray(onTop, d) == iInvalidPos); |
159 | pushBack_PtrArray(onTop, d); | ||
158 | } | 160 | } |
159 | else { | 161 | else { |
160 | removeOne_PtrArray(onTop_RootData_(), d); | 162 | removeOne_PtrArray(onTop, d); |
161 | } | 163 | } |
162 | } | 164 | } |
163 | } | 165 | } |
@@ -169,10 +171,17 @@ void setPos_Widget(iWidget *d, iInt2 pos) { | |||
169 | } | 171 | } |
170 | 172 | ||
171 | void setSize_Widget(iWidget *d, iInt2 size) { | 173 | void setSize_Widget(iWidget *d, iInt2 size) { |
172 | if (size.x < 0) size.x = d->rect.size.x; | 174 | int flags = fixedSize_WidgetFlag; |
173 | if (size.y < 0) size.y = d->rect.size.y; | 175 | if (size.x < 0) { |
176 | size.x = d->rect.size.x; | ||
177 | flags &= ~fixedWidth_WidgetFlag; | ||
178 | } | ||
179 | if (size.y < 0) { | ||
180 | size.y = d->rect.size.y; | ||
181 | flags &= ~fixedHeight_WidgetFlag; | ||
182 | } | ||
174 | d->rect.size = size; | 183 | d->rect.size = size; |
175 | setFlags_Widget(d, fixedSize_WidgetFlag, iTrue); | 184 | setFlags_Widget(d, flags, iTrue); |
176 | } | 185 | } |
177 | 186 | ||
178 | void setPadding_Widget(iWidget *d, int left, int top, int right, int bottom) { | 187 | void setPadding_Widget(iWidget *d, int left, int top, int right, int bottom) { |
@@ -498,16 +507,24 @@ void arrange_Widget(iWidget *d) { | |||
498 | } | 507 | } |
499 | } | 508 | } |
500 | 509 | ||
501 | iRect bounds_Widget(const iWidget *d) { | 510 | static void applyVisualOffset_Widget_(const iWidget *d, iInt2 *pos) { |
502 | iRect bounds = d->rect; | ||
503 | if (d->flags & visualOffset_WidgetFlag) { | 511 | if (d->flags & visualOffset_WidgetFlag) { |
504 | bounds.pos.y += iRound(value_Anim(&d->visualOffset)); | 512 | const int off = iRound(value_Anim(&d->visualOffset)); |
513 | if (d->flags & horizontalOffset_WidgetFlag) { | ||
514 | pos->x += off; | ||
515 | } | ||
516 | else { | ||
517 | pos->y += off; | ||
518 | } | ||
505 | } | 519 | } |
520 | } | ||
521 | |||
522 | iRect bounds_Widget(const iWidget *d) { | ||
523 | iRect bounds = d->rect; | ||
524 | applyVisualOffset_Widget_(d, &bounds.pos); | ||
506 | for (const iWidget *w = d->parent; w; w = w->parent) { | 525 | for (const iWidget *w = d->parent; w; w = w->parent) { |
507 | iInt2 pos = w->rect.pos; | 526 | iInt2 pos = w->rect.pos; |
508 | if (w->flags & visualOffset_WidgetFlag) { | 527 | applyVisualOffset_Widget_(w, &pos); |
509 | pos.y += iRound(value_Anim(&w->visualOffset)); | ||
510 | } | ||
511 | addv_I2(&bounds.pos, pos); | 528 | addv_I2(&bounds.pos, pos); |
512 | } | 529 | } |
513 | #if defined (iPlatformMobile) | 530 | #if defined (iPlatformMobile) |
@@ -595,6 +612,14 @@ iBool dispatchEvent_Widget(iWidget *d, const SDL_Event *ev) { | |||
595 | if (dispatchEvent_Widget(child, ev)) { | 612 | if (dispatchEvent_Widget(child, ev)) { |
596 | #if 0 | 613 | #if 0 |
597 | if (ev->type == SDL_MOUSEBUTTONDOWN) { | 614 | if (ev->type == SDL_MOUSEBUTTONDOWN) { |
615 | printf("[%p] %s:'%s' ate the button %d\n", | ||
616 | child, class_Widget(child)->name, | ||
617 | cstr_String(id_Widget(child)), ev->button.button); | ||
618 | fflush(stdout); | ||
619 | } | ||
620 | #endif | ||
621 | #if 0 | ||
622 | if (ev->type == SDL_MOUSEBUTTONDOWN) { | ||
598 | printf("widget %p ('%s' class:%s) ate the mouse down\n", | 623 | printf("widget %p ('%s' class:%s) ate the mouse down\n", |
599 | child, cstr_String(id_Widget(child)), | 624 | child, cstr_String(id_Widget(child)), |
600 | class_Widget(child)->name); | 625 | class_Widget(child)->name); |
@@ -637,20 +662,25 @@ iBool processEvent_Widget(iWidget *d, const SDL_Event *ev) { | |||
637 | postCommand_Widget(d, "mouse.moved coord:%d %d", ev->motion.x, ev->motion.y); | 662 | postCommand_Widget(d, "mouse.moved coord:%d %d", ev->motion.x, ev->motion.y); |
638 | return iTrue; | 663 | return iTrue; |
639 | } | 664 | } |
640 | else if (d->flags & overflowScrollable_WidgetFlag && ev->type == SDL_MOUSEWHEEL) { | 665 | else if (d->flags & overflowScrollable_WidgetFlag && ev->type == SDL_MOUSEWHEEL && |
666 | ~d->flags & visualOffset_WidgetFlag) { | ||
641 | iRect bounds = bounds_Widget(d); | 667 | iRect bounds = bounds_Widget(d); |
642 | const iInt2 winSize = rootSize_Window(get_Window()); | 668 | const iInt2 rootSize = rootSize_Window(get_Window()); |
643 | if (height_Rect(bounds) > winSize.y) { | 669 | const iRect winRect = safeRootRect_Window(get_Window()); |
670 | const int yTop = top_Rect(winRect); | ||
671 | const int yBottom = bottom_Rect(winRect); | ||
672 | const int safeBottom = rootSize.y - yBottom; | ||
673 | if (height_Rect(bounds) > height_Rect(winRect)) { | ||
644 | int step = ev->wheel.y; | 674 | int step = ev->wheel.y; |
645 | #if !defined (iPlatformApple) | 675 | if (!isPerPixel_MouseWheelEvent(&ev->wheel)) { |
646 | step *= lineHeight_Text(uiLabel_FontId); | 676 | step *= lineHeight_Text(uiLabel_FontId); |
647 | #endif | 677 | } |
648 | bounds.pos.y += step; | 678 | bounds.pos.y += step; |
649 | if (step > 0) { | 679 | if (step > 0) { |
650 | bounds.pos.y = iMin(bounds.pos.y, 0); | 680 | bounds.pos.y = iMin(bounds.pos.y, yTop); |
651 | } | 681 | } |
652 | else { | 682 | else { |
653 | bounds.pos.y = iMax(bounds.pos.y, winSize.y - height_Rect(bounds)); | 683 | bounds.pos.y = iMax(bounds.pos.y, rootSize.y + safeBottom - height_Rect(bounds)); |
654 | } | 684 | } |
655 | d->rect.pos = localCoord_Widget(d->parent, bounds.pos); | 685 | d->rect.pos = localCoord_Widget(d->parent, bounds.pos); |
656 | refresh_Widget(d); | 686 | refresh_Widget(d); |
@@ -676,6 +706,15 @@ iBool processEvent_Widget(iWidget *d, const SDL_Event *ev) { | |||
676 | ev->button.y); | 706 | ev->button.y); |
677 | } | 707 | } |
678 | if (d->flags & mouseModal_WidgetFlag && isMouseEvent_(ev)) { | 708 | if (d->flags & mouseModal_WidgetFlag && isMouseEvent_(ev)) { |
709 | if ((ev->type == SDL_MOUSEBUTTONDOWN || ev->type == SDL_MOUSEBUTTONUP) && | ||
710 | d->flags & commandOnClick_WidgetFlag) { | ||
711 | postCommand_Widget(d, | ||
712 | "mouse.clicked arg:%d button:%d coord:%d %d", | ||
713 | ev->type == SDL_MOUSEBUTTONDOWN ? 1 : 0, | ||
714 | ev->button.button, | ||
715 | ev->button.x, | ||
716 | ev->button.y); | ||
717 | } | ||
679 | setCursor_Window(get_Window(), SDL_SYSTEM_CURSOR_ARROW); | 718 | setCursor_Window(get_Window(), SDL_SYSTEM_CURSOR_ARROW); |
680 | return iTrue; | 719 | return iTrue; |
681 | } | 720 | } |
@@ -801,14 +840,15 @@ void drawChildren_Widget(const iWidget *d) { | |||
801 | /* Root draws the on-top widgets on top of everything else. */ | 840 | /* Root draws the on-top widgets on top of everything else. */ |
802 | if (!d->parent) { | 841 | if (!d->parent) { |
803 | iConstForEach(PtrArray, i, onTop_RootData_()) { | 842 | iConstForEach(PtrArray, i, onTop_RootData_()) { |
804 | draw_Widget(*i.value); | 843 | const iWidget *top = *i.value; |
844 | draw_Widget(top); | ||
805 | } | 845 | } |
806 | } | 846 | } |
807 | } | 847 | } |
808 | 848 | ||
809 | void draw_Widget(const iWidget *d) { | 849 | void draw_Widget(const iWidget *d) { |
810 | drawBackground_Widget(d); | 850 | drawBackground_Widget(d); |
811 | drawChildren_Widget(d); | 851 | drawChildren_Widget(d); |
812 | } | 852 | } |
813 | 853 | ||
814 | iAny *addChild_Widget(iWidget *d, iAnyObject *child) { | 854 | iAny *addChild_Widget(iWidget *d, iAnyObject *child) { |
@@ -903,7 +943,7 @@ iAny *hitChild_Widget(const iWidget *d, iInt2 coord) { | |||
903 | } | 943 | } |
904 | /* Check for on-top widgets first. */ | 944 | /* Check for on-top widgets first. */ |
905 | if (!d->parent) { | 945 | if (!d->parent) { |
906 | iForEach(PtrArray, i, onTop_RootData_()) { | 946 | iReverseForEach(PtrArray, i, onTop_RootData_()) { |
907 | iWidget *child = i.ptr; | 947 | iWidget *child = i.ptr; |
908 | // printf("ontop: %s (%s) hidden:%d hittable:%d\n", cstr_String(id_Widget(child)), | 948 | // printf("ontop: %s (%s) hidden:%d hittable:%d\n", cstr_String(id_Widget(child)), |
909 | // class_Widget(child)->name, | 949 | // class_Widget(child)->name, |
@@ -920,8 +960,8 @@ iAny *hitChild_Widget(const iWidget *d, iInt2 coord) { | |||
920 | if (found) return found; | 960 | if (found) return found; |
921 | } | 961 | } |
922 | } | 962 | } |
923 | if ((d->flags & overflowScrollable_WidgetFlag || class_Widget(d) != &Class_Widget) && | 963 | if ((d->flags & overflowScrollable_WidgetFlag || class_Widget(d) != &Class_Widget || |
924 | ~d->flags & unhittable_WidgetFlag && | 964 | d->flags & mouseModal_WidgetFlag) && ~d->flags & unhittable_WidgetFlag && |
925 | contains_Widget(d, coord)) { | 965 | contains_Widget(d, coord)) { |
926 | return iConstCast(iWidget *, d); | 966 | return iConstCast(iWidget *, d); |
927 | } | 967 | } |
@@ -1168,6 +1208,10 @@ iBool hasVisibleChildOnTop_Widget(const iWidget *parent) { | |||
1168 | } | 1208 | } |
1169 | 1209 | ||
1170 | void printTree_Widget(const iWidget *d) { | 1210 | void printTree_Widget(const iWidget *d) { |
1211 | if (!d) { | ||
1212 | printf("[NULL]\n"); | ||
1213 | return; | ||
1214 | } | ||
1171 | printTree_Widget_(d, 0); | 1215 | printTree_Widget_(d, 0); |
1172 | } | 1216 | } |
1173 | 1217 | ||
diff --git a/src/ui/widget.h b/src/ui/widget.h index 1229ba7f..4b1f42d3 100644 --- a/src/ui/widget.h +++ b/src/ui/widget.h | |||
@@ -103,6 +103,8 @@ enum iWidgetFlag { | |||
103 | #define unpadded_WidgetFlag iBit64(48) /* ignore parent's padding */ | 103 | #define unpadded_WidgetFlag iBit64(48) /* ignore parent's padding */ |
104 | #define extraPadding_WidgetFlag iBit64(49) | 104 | #define extraPadding_WidgetFlag iBit64(49) |
105 | #define borderBottom_WidgetFlag iBit64(50) | 105 | #define borderBottom_WidgetFlag iBit64(50) |
106 | #define horizontalOffset_WidgetFlag iBit64(51) /* default is vertical offset */ | ||
107 | #define chevron_WidgetFlag iBit64(52) | ||
106 | 108 | ||
107 | enum iWidgetAddPos { | 109 | enum iWidgetAddPos { |
108 | back_WidgetAddPos, | 110 | back_WidgetAddPos, |
diff --git a/src/ui/window.c b/src/ui/window.c index 3c362986..af9b20ba 100644 --- a/src/ui/window.c +++ b/src/ui/window.c | |||
@@ -1808,6 +1808,16 @@ iInt2 rootSize_Window(const iWindow *d) { | |||
1808 | return d ? d->root->rect.size : zero_I2(); | 1808 | return d ? d->root->rect.size : zero_I2(); |
1809 | } | 1809 | } |
1810 | 1810 | ||
1811 | iRect safeRootRect_Window(const iWindow *d) { | ||
1812 | iRect rect = { zero_I2(), d->root->rect.size }; | ||
1813 | #if defined (iPlatformAppleMobile) | ||
1814 | float left, top, right, bottom; | ||
1815 | safeAreaInsets_iOS(&left, &top, &right, &bottom); | ||
1816 | adjustEdges_Rect(&rect, top, right, bottom, left); | ||
1817 | #endif | ||
1818 | return rect; | ||
1819 | } | ||
1820 | |||
1811 | iInt2 visibleRootSize_Window(const iWindow *d) { | 1821 | iInt2 visibleRootSize_Window(const iWindow *d) { |
1812 | return addY_I2(rootSize_Window(d), -d->keyboardHeight); | 1822 | return addY_I2(rootSize_Window(d), -d->keyboardHeight); |
1813 | } | 1823 | } |
diff --git a/src/ui/window.h b/src/ui/window.h index 9a70fdec..dc865277 100644 --- a/src/ui/window.h +++ b/src/ui/window.h | |||
@@ -92,6 +92,7 @@ void setKeyboardHeight_Window(iWindow *, int height); | |||
92 | 92 | ||
93 | uint32_t id_Window (const iWindow *); | 93 | uint32_t id_Window (const iWindow *); |
94 | iInt2 rootSize_Window (const iWindow *); | 94 | iInt2 rootSize_Window (const iWindow *); |
95 | iRect safeRootRect_Window (const iWindow *); | ||
95 | iInt2 visibleRootSize_Window (const iWindow *); /* may be obstructed by software keyboard */ | 96 | iInt2 visibleRootSize_Window (const iWindow *); /* may be obstructed by software keyboard */ |
96 | iInt2 maxTextureSize_Window (const iWindow *); | 97 | iInt2 maxTextureSize_Window (const iWindow *); |
97 | float uiScale_Window (const iWindow *); | 98 | float uiScale_Window (const iWindow *); |