diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-03-05 13:14:02 +0200 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-03-05 13:15:20 +0200 |
commit | 71f3f34782aa6050ede4bf143fa6028653adae6d (patch) | |
tree | 1465b155b6c99ca48596263c2929da48b8795e23 | |
parent | 8417c4fb637351587e1527b94c69b752d9988323 (diff) |
Restructuring dialogs for mobile
On a phone, dialogs are restructured to be more vertical and full-width.
m--------- | lib/the_Foundation | 0 | ||||
-rw-r--r-- | src/app.c | 10 | ||||
-rw-r--r-- | src/ui/inputwidget.c | 5 | ||||
-rw-r--r-- | src/ui/util.c | 130 | ||||
-rw-r--r-- | src/ui/util.h | 2 |
5 files changed, 129 insertions, 18 deletions
diff --git a/lib/the_Foundation b/lib/the_Foundation | |||
Subproject 7784fb08a3d99b629545cc852f7869bd470646d | Subproject 0b40f677e2c738a0cede4d4a4bc5849f9fa0e5c | ||
@@ -1231,8 +1231,10 @@ static iBool handlePrefsCommands_(iWidget *d, const char *cmd) { | |||
1231 | postCommandf_App("proxy.http address:%s", | 1231 | postCommandf_App("proxy.http address:%s", |
1232 | cstr_String(text_InputWidget(findChild_Widget(d, "prefs.proxy.http")))); | 1232 | cstr_String(text_InputWidget(findChild_Widget(d, "prefs.proxy.http")))); |
1233 | const iWidget *tabs = findChild_Widget(d, "prefs.tabs"); | 1233 | const iWidget *tabs = findChild_Widget(d, "prefs.tabs"); |
1234 | postCommandf_App("prefs.dialogtab arg:%u", | 1234 | if (tabs) { |
1235 | tabPageIndex_Widget(tabs, currentTabPage_Widget(tabs))); | 1235 | postCommandf_App("prefs.dialogtab arg:%u", |
1236 | tabPageIndex_Widget(tabs, currentTabPage_Widget(tabs))); | ||
1237 | } | ||
1236 | destroy_Widget(d); | 1238 | destroy_Widget(d); |
1237 | postCommand_App("prefs.changed"); | 1239 | postCommand_App("prefs.changed"); |
1238 | return iTrue; | 1240 | return iTrue; |
@@ -1800,7 +1802,9 @@ iBool handleCommand_App(const char *cmd) { | |||
1800 | setText_InputWidget(findChild_Widget(dlg, "prefs.proxy.gopher"), &d->prefs.gopherProxy); | 1802 | setText_InputWidget(findChild_Widget(dlg, "prefs.proxy.gopher"), &d->prefs.gopherProxy); |
1801 | setText_InputWidget(findChild_Widget(dlg, "prefs.proxy.http"), &d->prefs.httpProxy); | 1803 | setText_InputWidget(findChild_Widget(dlg, "prefs.proxy.http"), &d->prefs.httpProxy); |
1802 | iWidget *tabs = findChild_Widget(dlg, "prefs.tabs"); | 1804 | iWidget *tabs = findChild_Widget(dlg, "prefs.tabs"); |
1803 | showTabPage_Widget(tabs, tabPage_Widget(tabs, d->prefs.dialogTab)); | 1805 | if (tabs) { |
1806 | showTabPage_Widget(tabs, tabPage_Widget(tabs, d->prefs.dialogTab)); | ||
1807 | } | ||
1804 | setCommandHandler_Widget(dlg, handlePrefsCommands_); | 1808 | setCommandHandler_Widget(dlg, handlePrefsCommands_); |
1805 | } | 1809 | } |
1806 | else if (equal_Command(cmd, "navigate.home")) { | 1810 | else if (equal_Command(cmd, "navigate.home")) { |
diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c index 4b5cd623..11ea0674 100644 --- a/src/ui/inputwidget.c +++ b/src/ui/inputwidget.c | |||
@@ -193,7 +193,10 @@ void setMode_InputWidget(iInputWidget *d, enum iInputMode mode) { | |||
193 | } | 193 | } |
194 | 194 | ||
195 | const iString *text_InputWidget(const iInputWidget *d) { | 195 | const iString *text_InputWidget(const iInputWidget *d) { |
196 | return collect_String(newUnicodeN_String(constData_Array(&d->text), size_Array(&d->text))); | 196 | if (d) { |
197 | return collect_String(newUnicodeN_String(constData_Array(&d->text), size_Array(&d->text))); | ||
198 | } | ||
199 | return collectNew_String(); | ||
197 | } | 200 | } |
198 | 201 | ||
199 | void setMaxLen_InputWidget(iInputWidget *d, size_t maxLen) { | 202 | void setMaxLen_InputWidget(iInputWidget *d, size_t maxLen) { |
diff --git a/src/ui/util.c b/src/ui/util.c index a36eb1a7..e8bb95d8 100644 --- a/src/ui/util.c +++ b/src/ui/util.c | |||
@@ -500,7 +500,8 @@ iWidget *makeMenu_Widget(iWidget *parent, const iMenuItem *items, size_t n) { | |||
500 | } | 500 | } |
501 | } | 501 | } |
502 | } | 502 | } |
503 | addChild_Widget(parent, iClob(menu)); | 503 | addChild_Widget(parent, menu); |
504 | iRelease(menu); /* owned by parent now */ | ||
504 | setCommandHandler_Widget(menu, menuHandler_); | 505 | setCommandHandler_Widget(menu, menuHandler_); |
505 | iWidget *cancel = addAction_Widget(menu, SDLK_ESCAPE, 0, "cancel"); | 506 | iWidget *cancel = addAction_Widget(menu, SDLK_ESCAPE, 0, "cancel"); |
506 | setId_Widget(cancel, "menu.cancel"); | 507 | setId_Widget(cancel, "menu.cancel"); |
@@ -756,6 +757,9 @@ iLabelWidget *tabButtonForPage_Widget_(iWidget *tabs, const iWidget *page) { | |||
756 | } | 757 | } |
757 | 758 | ||
758 | void showTabPage_Widget(iWidget *tabs, const iWidget *page) { | 759 | void showTabPage_Widget(iWidget *tabs, const iWidget *page) { |
760 | if (!page) { | ||
761 | return; | ||
762 | } | ||
759 | /* Select the corresponding button. */ { | 763 | /* Select the corresponding button. */ { |
760 | iWidget *buttons = findChild_Widget(tabs, "tabs.buttons"); | 764 | iWidget *buttons = findChild_Widget(tabs, "tabs.buttons"); |
761 | iForEach(ObjectList, i, buttons->children) { | 765 | iForEach(ObjectList, i, buttons->children) { |
@@ -870,8 +874,107 @@ iWidget *makeSheet_Widget(const char *id) { | |||
870 | return sheet; | 874 | return sheet; |
871 | } | 875 | } |
872 | 876 | ||
873 | void centerSheet_Widget(iWidget *sheet) { | 877 | static iBool isTwoColumnPage_(iWidget *d) { |
874 | arrange_Widget(sheet); | 878 | if (class_Widget(d) == &Class_Widget && childCount_Widget(d) == 2) { |
879 | return class_Widget(child_Widget(d, 0)) == &Class_Widget && | ||
880 | class_Widget(child_Widget(d, 1)) == &Class_Widget; | ||
881 | } | ||
882 | return iFalse; | ||
883 | } | ||
884 | |||
885 | void finalizeSheet_Widget(iWidget *sheet) { | ||
886 | if (deviceType_App() == phone_AppDeviceType) { | ||
887 | /* The sheet contents are completely rearranged on a phone. We'll set up a linear | ||
888 | fullscreen arrangement of the widgets. Sheets are already scrollable so they | ||
889 | can be taller than the display. */ | ||
890 | #if defined (iPlatformAppleMobile) | ||
891 | /* Safe area insets. */ { | ||
892 | /* TODO: Must be updated when orientation changes; use a widget flag? */ | ||
893 | float l, t, r, b; | ||
894 | safeAreaInsets_iOS(&l, &t, &r, &b); | ||
895 | setPadding_Widget(sheet, l, t, r, b); | ||
896 | } | ||
897 | #endif | ||
898 | setFlags_Widget(sheet, | ||
899 | parentCannotResize_WidgetFlag | arrangeWidth_WidgetFlag | | ||
900 | centerHorizontal_WidgetFlag, | ||
901 | iFalse); | ||
902 | setFlags_Widget(sheet, frameless_WidgetFlag | resizeWidthOfChildren_WidgetFlag, iTrue); | ||
903 | iPtrArray *contents = collect_PtrArray(new_PtrArray()); /* two-column pages */ | ||
904 | iWidget *tabs = findChild_Widget(sheet, "prefs.tabs"); | ||
905 | if (tabs) { | ||
906 | /* Pull out the pages and make them sequential. */ | ||
907 | iWidget *pages = findChild_Widget(tabs, "tabs.pages"); | ||
908 | size_t pageCount = tabCount_Widget(tabs); | ||
909 | for (size_t i = 0; i < pageCount; i++) { | ||
910 | iWidget *page = removeTabPage_Widget(tabs, 0); | ||
911 | iWidget *pageContent = child_Widget(page, 1); /* surrounded by padding widgets */ | ||
912 | pushBack_PtrArray(contents, ref_Object(pageContent)); | ||
913 | iRelease(page); | ||
914 | } | ||
915 | destroy_Widget(tabs); | ||
916 | } | ||
917 | else { | ||
918 | iForEach(ObjectList, i, children_Widget(sheet)) { | ||
919 | iWidget *child = i.object; | ||
920 | if (isTwoColumnPage_(child)) { | ||
921 | pushBack_PtrArray(contents, removeChild_Widget(sheet, child)); | ||
922 | } | ||
923 | } | ||
924 | } | ||
925 | iForEach(PtrArray, j, contents) { | ||
926 | iWidget *pageContent = j.ptr; | ||
927 | iWidget *headings = child_Widget(pageContent, 0); | ||
928 | iWidget *values = child_Widget(pageContent, 1); | ||
929 | while (!isEmpty_ObjectList(children_Widget(headings))) { | ||
930 | iWidget *heading = child_Widget(headings, 0); | ||
931 | iWidget *value = child_Widget(values, 0); | ||
932 | removeChild_Widget(headings, heading); | ||
933 | removeChild_Widget(values, value); | ||
934 | iLabelWidget *valueLabel = NULL; | ||
935 | if (isInstance_Object(value, &Class_LabelWidget)) { | ||
936 | valueLabel = (iLabelWidget *) value; | ||
937 | } | ||
938 | /* Toggles have the button on the right. */ | ||
939 | if (valueLabel && cmp_String(command_LabelWidget(valueLabel), "toggle") == 0) { | ||
940 | iWidget *div = new_Widget(); | ||
941 | addChildFlags_Widget(div, iClob(heading), 0); | ||
942 | addChildFlags_Widget(div, iClob(new_Widget()), expand_WidgetFlag); | ||
943 | addChild_Widget(div, iClob(value)); | ||
944 | addChildFlags_Widget(sheet, | ||
945 | iClob(div), | ||
946 | arrangeHeight_WidgetFlag | | ||
947 | resizeWidthOfChildren_WidgetFlag | | ||
948 | arrangeHorizontal_WidgetFlag); | ||
949 | } | ||
950 | else { | ||
951 | if (valueLabel && isEmpty_String(text_LabelWidget(valueLabel))) { | ||
952 | /* Subheading padding goes above. */ | ||
953 | addChild_Widget(sheet, iClob(value)); | ||
954 | addChild_Widget(sheet, iClob(heading)); | ||
955 | } | ||
956 | else { | ||
957 | addChild_Widget(sheet, iClob(heading)); | ||
958 | addChild_Widget(sheet, iClob(value)); | ||
959 | /* Align radio buttons to the right. */ | ||
960 | if (childCount_Widget(value)) { | ||
961 | setFlags_Widget(value, | ||
962 | resizeToParentWidth_WidgetFlag | | ||
963 | resizeWidthOfChildren_WidgetFlag, | ||
964 | iTrue); | ||
965 | } | ||
966 | } | ||
967 | } | ||
968 | } | ||
969 | destroy_Widget(pageContent); | ||
970 | } | ||
971 | destroyPending_Widget(); | ||
972 | arrange_Widget(sheet->parent); | ||
973 | printTree_Widget(sheet); | ||
974 | } | ||
975 | else { | ||
976 | arrange_Widget(sheet); | ||
977 | } | ||
875 | postRefresh_App(); | 978 | postRefresh_App(); |
876 | } | 979 | } |
877 | 980 | ||
@@ -899,7 +1002,7 @@ void makeFilePath_Widget(iWidget * parent, | |||
899 | addChild_Widget(div, iClob(newKeyMods_LabelWidget(acceptLabel, SDLK_RETURN, 0, "filepath.accept"))); | 1002 | addChild_Widget(div, iClob(newKeyMods_LabelWidget(acceptLabel, SDLK_RETURN, 0, "filepath.accept"))); |
900 | } | 1003 | } |
901 | addChild_Widget(dlg, iClob(div)); | 1004 | addChild_Widget(dlg, iClob(div)); |
902 | centerSheet_Widget(dlg); | 1005 | finalizeSheet_Widget(dlg); |
903 | setFocus_Widget(as_Widget(input)); | 1006 | setFocus_Widget(as_Widget(input)); |
904 | } | 1007 | } |
905 | 1008 | ||
@@ -920,7 +1023,7 @@ static void updateValueInputWidth_(iWidget *dlg) { | |||
920 | iWidget * prompt = findChild_Widget(dlg, "valueinput.prompt"); | 1023 | iWidget * prompt = findChild_Widget(dlg, "valueinput.prompt"); |
921 | dlg->rect.size.x = iMaxi(iMaxi(rootSize.x / 2, title->rect.size.x), prompt->rect.size.x); | 1024 | dlg->rect.size.x = iMaxi(iMaxi(rootSize.x / 2, title->rect.size.x), prompt->rect.size.x); |
922 | as_Widget(findChild_Widget(dlg, "input"))->rect.size.x = dlg->rect.size.x; | 1025 | as_Widget(findChild_Widget(dlg, "input"))->rect.size.x = dlg->rect.size.x; |
923 | centerSheet_Widget(dlg); | 1026 | finalizeSheet_Widget(dlg); |
924 | } | 1027 | } |
925 | 1028 | ||
926 | iBool valueInputHandler_(iWidget *dlg, const char *cmd) { | 1029 | iBool valueInputHandler_(iWidget *dlg, const char *cmd) { |
@@ -1039,7 +1142,7 @@ iWidget *makeValueInput_Widget(iWidget *parent, const iString *initialValue, con | |||
1039 | iClob(makeDialogButtons_Widget( | 1142 | iClob(makeDialogButtons_Widget( |
1040 | (iMenuItem[]){ { "Cancel", 0, 0, NULL }, { acceptLabel, 0, 0, "valueinput.accept" } }, | 1143 | (iMenuItem[]){ { "Cancel", 0, 0, NULL }, { acceptLabel, 0, 0, "valueinput.accept" } }, |
1041 | 2))); | 1144 | 2))); |
1042 | centerSheet_Widget(dlg); | 1145 | finalizeSheet_Widget(dlg); |
1043 | if (parent) { | 1146 | if (parent) { |
1044 | setFocus_Widget(as_Widget(input)); | 1147 | setFocus_Widget(as_Widget(input)); |
1045 | } | 1148 | } |
@@ -1086,7 +1189,7 @@ iWidget *makeQuestion_Widget(const char *title, const char *msg, | |||
1086 | addChild_Widget(get_Window()->root, iClob(dlg)); | 1189 | addChild_Widget(get_Window()->root, iClob(dlg)); |
1087 | arrange_Widget(dlg); /* BUG: This extra arrange shouldn't be needed but the dialog won't | 1190 | arrange_Widget(dlg); /* BUG: This extra arrange shouldn't be needed but the dialog won't |
1088 | be arranged correctly unless it's here. */ | 1191 | be arranged correctly unless it's here. */ |
1089 | centerSheet_Widget(dlg); | 1192 | finalizeSheet_Widget(dlg); |
1090 | return dlg; | 1193 | return dlg; |
1091 | } | 1194 | } |
1092 | 1195 | ||
@@ -1148,7 +1251,7 @@ static iWidget *appendTwoColumnPage_(iWidget *tabs, const char *title, int short | |||
1148 | *values = addChildFlags_Widget( | 1251 | *values = addChildFlags_Widget( |
1149 | columns, iClob(new_Widget()), arrangeVertical_WidgetFlag | arrangeSize_WidgetFlag); | 1252 | columns, iClob(new_Widget()), arrangeVertical_WidgetFlag | arrangeSize_WidgetFlag); |
1150 | addChildFlags_Widget(page, iClob(new_Widget()), expand_WidgetFlag); | 1253 | addChildFlags_Widget(page, iClob(new_Widget()), expand_WidgetFlag); |
1151 | appendFramelessTabPage_(tabs, page, title, shortcut, shortcut ? KMOD_PRIMARY : 0); | 1254 | appendFramelessTabPage_(tabs, iClob(page), title, shortcut, shortcut ? KMOD_PRIMARY : 0); |
1152 | return page; | 1255 | return page; |
1153 | } | 1256 | } |
1154 | 1257 | ||
@@ -1349,7 +1452,8 @@ iWidget *makePreferences_Widget(void) { | |||
1349 | addChild_Widget(headings, iClob(makeHeading_Widget("HTTP proxy:"))); | 1452 | addChild_Widget(headings, iClob(makeHeading_Widget("HTTP proxy:"))); |
1350 | setId_Widget(addChild_Widget(values, iClob(new_InputWidget(0))), "prefs.proxy.http"); | 1453 | setId_Widget(addChild_Widget(values, iClob(new_InputWidget(0))), "prefs.proxy.http"); |
1351 | } | 1454 | } |
1352 | /* Keybindings. */ { | 1455 | /* Keybindings. */ |
1456 | if (deviceType_App() == desktop_AppDeviceType) { | ||
1353 | iBindingsWidget *bind = new_BindingsWidget(); | 1457 | iBindingsWidget *bind = new_BindingsWidget(); |
1354 | setFlags_Widget(as_Widget(bind), borderTop_WidgetFlag, iTrue); | 1458 | setFlags_Widget(as_Widget(bind), borderTop_WidgetFlag, iTrue); |
1355 | appendFramelessTabPage_(tabs, iClob(bind), "Keys", '6', KMOD_PRIMARY); | 1459 | appendFramelessTabPage_(tabs, iClob(bind), "Keys", '6', KMOD_PRIMARY); |
@@ -1370,7 +1474,7 @@ iWidget *makePreferences_Widget(void) { | |||
1370 | iClob(makeDialogButtons_Widget( | 1474 | iClob(makeDialogButtons_Widget( |
1371 | (iMenuItem[]){ { "Dismiss", SDLK_ESCAPE, 0, "prefs.dismiss" } }, 1))); | 1475 | (iMenuItem[]){ { "Dismiss", SDLK_ESCAPE, 0, "prefs.dismiss" } }, 1))); |
1372 | addChild_Widget(get_Window()->root, iClob(dlg)); | 1476 | addChild_Widget(get_Window()->root, iClob(dlg)); |
1373 | centerSheet_Widget(dlg); | 1477 | finalizeSheet_Widget(dlg); |
1374 | return dlg; | 1478 | return dlg; |
1375 | } | 1479 | } |
1376 | 1480 | ||
@@ -1411,7 +1515,7 @@ iWidget *makeBookmarkEditor_Widget(void) { | |||
1411 | "bmed.accept" } }, | 1515 | "bmed.accept" } }, |
1412 | 2))); | 1516 | 2))); |
1413 | addChild_Widget(get_Window()->root, iClob(dlg)); | 1517 | addChild_Widget(get_Window()->root, iClob(dlg)); |
1414 | centerSheet_Widget(dlg); | 1518 | finalizeSheet_Widget(dlg); |
1415 | return dlg; | 1519 | return dlg; |
1416 | } | 1520 | } |
1417 | 1521 | ||
@@ -1550,7 +1654,7 @@ iWidget *makeFeedSettings_Widget(uint32_t bookmarkId) { | |||
1550 | arrange_Widget(dlg); | 1654 | arrange_Widget(dlg); |
1551 | as_Widget(input)->rect.size.x = 100 * gap_UI - headings->rect.size.x; | 1655 | as_Widget(input)->rect.size.x = 100 * gap_UI - headings->rect.size.x; |
1552 | addChild_Widget(get_Window()->root, iClob(dlg)); | 1656 | addChild_Widget(get_Window()->root, iClob(dlg)); |
1553 | centerSheet_Widget(dlg); | 1657 | finalizeSheet_Widget(dlg); |
1554 | /* Initialize. */ { | 1658 | /* Initialize. */ { |
1555 | const iBookmark *bm = bookmarkId ? get_Bookmarks(bookmarks_App(), bookmarkId) : NULL; | 1659 | const iBookmark *bm = bookmarkId ? get_Bookmarks(bookmarks_App(), bookmarkId) : NULL; |
1556 | setText_InputWidget(findChild_Widget(dlg, "feedcfg.title"), | 1660 | setText_InputWidget(findChild_Widget(dlg, "feedcfg.title"), |
@@ -1627,6 +1731,6 @@ iWidget *makeIdentityCreation_Widget(void) { | |||
1627 | "ident.accept" } }, | 1731 | "ident.accept" } }, |
1628 | 2))); | 1732 | 2))); |
1629 | addChild_Widget(get_Window()->root, iClob(dlg)); | 1733 | addChild_Widget(get_Window()->root, iClob(dlg)); |
1630 | centerSheet_Widget(dlg); | 1734 | finalizeSheet_Widget(dlg); |
1631 | return dlg; | 1735 | return dlg; |
1632 | } | 1736 | } |
diff --git a/src/ui/util.h b/src/ui/util.h index 40fd0c69..122cbb97 100644 --- a/src/ui/util.h +++ b/src/ui/util.h | |||
@@ -193,7 +193,7 @@ size_t tabCount_Widget (const iWidget *tabs); | |||
193 | /*-----------------------------------------------------------------------------------------------*/ | 193 | /*-----------------------------------------------------------------------------------------------*/ |
194 | 194 | ||
195 | iWidget * makeSheet_Widget (const char *id); | 195 | iWidget * makeSheet_Widget (const char *id); |
196 | void centerSheet_Widget (iWidget *sheet); | 196 | void finalizeSheet_Widget (iWidget *sheet); |
197 | iWidget * makeDialogButtons_Widget(const iMenuItem *actions, size_t numActions); | 197 | iWidget * makeDialogButtons_Widget(const iMenuItem *actions, size_t numActions); |
198 | 198 | ||
199 | void makeFilePath_Widget (iWidget *parent, const iString *initialPath, const char *title, | 199 | void makeFilePath_Widget (iWidget *parent, const iString *initialPath, const char *title, |