diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-05-22 22:24:13 +0300 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-05-22 22:24:13 +0300 |
commit | fd94d906676b0dcef2b783c992f75254a61ae025 (patch) | |
tree | a117549152c70d3105b7f94d910ba1333368b2af /src/ui | |
parent | 4108d5b0154775e2bf2c3af5003cf840675d789a (diff) |
Document footer buttons
In some situations, e.g., when a client certificate is required but not active, make available action buttons that offer related shortcuts.
Selecting or deselecting an identity for use will automatically reload the current page for convenience.
Animate closing sidebars with Escape key.
Diffstat (limited to 'src/ui')
-rw-r--r-- | src/ui/documentwidget.c | 72 | ||||
-rw-r--r-- | src/ui/keys.c | 1 | ||||
-rw-r--r-- | src/ui/keys.h | 2 | ||||
-rw-r--r-- | src/ui/root.c | 4 | ||||
-rw-r--r-- | src/ui/sidebarwidget.c | 7 | ||||
-rw-r--r-- | src/ui/widget.c | 6 |
6 files changed, 63 insertions, 29 deletions
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index b6f9298c..74a101ad 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -503,7 +503,7 @@ static iRect documentBounds_DocumentWidget_(const iDocumentWidget *d) { | |||
503 | rect.size.y -= margin; | 503 | rect.size.y -= margin; |
504 | } | 504 | } |
505 | if (d->flags & centerVertically_DocumentWidgetFlag) { | 505 | if (d->flags & centerVertically_DocumentWidgetFlag) { |
506 | const iInt2 docSize = addY_I2(size_GmDocument(d->doc), height_Widget(d->footerButtons)); | 506 | const iInt2 docSize = size_GmDocument(d->doc); |
507 | if (docSize.y < rect.size.y) { | 507 | if (docSize.y < rect.size.y) { |
508 | /* Center vertically if short. There is one empty paragraph line's worth of margin | 508 | /* Center vertically if short. There is one empty paragraph line's worth of margin |
509 | between the banner and the page contents. */ | 509 | between the banner and the page contents. */ |
@@ -828,6 +828,26 @@ static void updateVisible_DocumentWidget_(iDocumentWidget *d) { | |||
828 | const iRangei visRange = visibleRange_DocumentWidget_(d); | 828 | const iRangei visRange = visibleRange_DocumentWidget_(d); |
829 | const iRect bounds = bounds_Widget(as_Widget(d)); | 829 | const iRect bounds = bounds_Widget(as_Widget(d)); |
830 | const int scrollMax = scrollMax_DocumentWidget_(d); | 830 | const int scrollMax = scrollMax_DocumentWidget_(d); |
831 | /* Reposition the footer buttons as appropriate. */ | ||
832 | /* TODO: You can just position `footerButtons` here completely without having to get | ||
833 | `Widget` involved with the offset in any way. */ | ||
834 | if (d->footerButtons) { | ||
835 | const iRect bounds = bounds_Widget(as_Widget(d)); | ||
836 | const iRect docBounds = documentBounds_DocumentWidget_(d); | ||
837 | const int hPad = (width_Rect(bounds) - iMin(120 * gap_UI, width_Rect(docBounds))) / 2; | ||
838 | const int vPad = 3 * gap_UI; | ||
839 | setPadding_Widget(d->footerButtons, hPad, vPad, hPad, vPad); | ||
840 | arrange_Widget(d->footerButtons); | ||
841 | d->footerButtons->animOffsetRef = (scrollMax > 0 ? &d->scrollY.pos : NULL); | ||
842 | if (scrollMax <= 0) { | ||
843 | d->footerButtons->animOffsetRef = NULL; | ||
844 | d->footerButtons->rect.pos.y = height_Rect(bounds) - height_Widget(d->footerButtons); | ||
845 | } | ||
846 | else { | ||
847 | d->footerButtons->animOffsetRef = &d->scrollY.pos; | ||
848 | d->footerButtons->rect.pos.y = size_GmDocument(d->doc).y + 2 * gap_UI * d->pageMargin; | ||
849 | } | ||
850 | } | ||
831 | setMax_SmoothScroll(&d->scrollY, scrollMax); | 851 | setMax_SmoothScroll(&d->scrollY, scrollMax); |
832 | setRange_ScrollWidget(d->scroll, (iRangei){ 0, scrollMax }); | 852 | setRange_ScrollWidget(d->scroll, (iRangei){ 0, scrollMax }); |
833 | const int docSize = size_GmDocument(d->doc).y; | 853 | const int docSize = size_GmDocument(d->doc).y; |
@@ -1041,24 +1061,18 @@ static void makeFooterButtons_DocumentWidget_(iDocumentWidget *d, const iMenuIte | |||
1041 | return; | 1061 | return; |
1042 | } | 1062 | } |
1043 | d->footerButtons = new_Widget(); | 1063 | d->footerButtons = new_Widget(); |
1044 | d->footerButtons->animOffsetRef = &d->scrollY.pos; | ||
1045 | setFlags_Widget(d->footerButtons, | 1064 | setFlags_Widget(d->footerButtons, |
1046 | unhittable_WidgetFlag | arrangeVertical_WidgetFlag | | 1065 | unhittable_WidgetFlag | arrangeVertical_WidgetFlag | |
1047 | resizeWidthOfChildren_WidgetFlag | arrangeHeight_WidgetFlag | | 1066 | resizeWidthOfChildren_WidgetFlag | arrangeHeight_WidgetFlag | |
1048 | fixedPosition_WidgetFlag | resizeToParentWidth_WidgetFlag | | 1067 | fixedPosition_WidgetFlag | resizeToParentWidth_WidgetFlag, |
1049 | moveToParentBottomEdge_WidgetFlag, | ||
1050 | iTrue); | 1068 | iTrue); |
1051 | setBackgroundColor_Widget(d->footerButtons, tmBannerBackground_ColorId); | 1069 | setBackgroundColor_Widget(d->footerButtons, tmBackground_ColorId); |
1052 | const iRect bounds = bounds_Widget(w); | ||
1053 | const iRect docBounds = documentBounds_DocumentWidget_(d); | ||
1054 | const int hPad = (width_Rect(bounds) - width_Rect(docBounds)) / 2; | ||
1055 | const int vPad = 3 * gap_UI; | ||
1056 | setPadding_Widget(d->footerButtons, hPad, vPad, hPad, vPad); | ||
1057 | for (size_t i = 0; i < count; ++i) { | 1070 | for (size_t i = 0; i < count; ++i) { |
1058 | iLabelWidget *button = | 1071 | iLabelWidget *button = addChildFlags_Widget( |
1059 | addChild_Widget(d->footerButtons, | 1072 | d->footerButtons, |
1060 | iClob(newKeyMods_LabelWidget( | 1073 | iClob(newKeyMods_LabelWidget( |
1061 | items[i].label, items[i].key, items[i].kmods, items[i].command))); | 1074 | items[i].label, items[i].key, items[i].kmods, items[i].command)), |
1075 | alignLeft_WidgetFlag | drawKey_WidgetFlag); | ||
1062 | checkIcon_LabelWidget(button); | 1076 | checkIcon_LabelWidget(button); |
1063 | setFont_LabelWidget(button, uiContent_FontId); | 1077 | setFont_LabelWidget(button, uiContent_FontId); |
1064 | } | 1078 | } |
@@ -1092,10 +1106,10 @@ static void showErrorPage_DocumentWidget_(iDocumentWidget *d, enum iGmStatusCode | |||
1092 | iString *key = collectNew_String(); | 1106 | iString *key = collectNew_String(); |
1093 | toString_Sym(SDLK_s, KMOD_PRIMARY, key); | 1107 | toString_Sym(SDLK_s, KMOD_PRIMARY, key); |
1094 | appendFormat_String(src, "\n```\n%s\n```\n", cstr_String(meta)); | 1108 | appendFormat_String(src, "\n```\n%s\n```\n", cstr_String(meta)); |
1095 | appendFormat_String(src, | 1109 | // appendFormat_String(src, |
1096 | cstr_Lang("error.unsupported.suggestsave"), | 1110 | // cstr_Lang("error.unsupported.suggestsave"), |
1097 | cstr_String(key), | 1111 | // cstr_String(key), |
1098 | saveToDownloads_Label); | 1112 | // saveToDownloads_Label); |
1099 | makeFooterButtons_DocumentWidget_( | 1113 | makeFooterButtons_DocumentWidget_( |
1100 | d, | 1114 | d, |
1101 | (iMenuItem[]){ { translateCStr_Lang(download_Icon " " saveToDownloads_Label), | 1115 | (iMenuItem[]){ { translateCStr_Lang(download_Icon " " saveToDownloads_Label), |
@@ -1116,6 +1130,13 @@ static void showErrorPage_DocumentWidget_(iDocumentWidget *d, enum iGmStatusCode | |||
1116 | break; | 1130 | break; |
1117 | } | 1131 | } |
1118 | } | 1132 | } |
1133 | if (category_GmStatusCode(code) == categoryClientCertificate_GmStatus) { | ||
1134 | makeFooterButtons_DocumentWidget_( | ||
1135 | d, | ||
1136 | (iMenuItem[]){ { leftHalf_Icon " ${menu.show.identities}", '4', KMOD_PRIMARY, "sidebar.mode arg:3 show:1" }, | ||
1137 | { person_Icon " ${menu.identity.new}", newIdentity_KeyShortcut, "ident.new" } }, | ||
1138 | 2); | ||
1139 | } | ||
1119 | setBanner_GmDocument(d->doc, useBanner ? bannerType_DocumentWidget_(d) : none_GmDocumentBanner); | 1140 | setBanner_GmDocument(d->doc, useBanner ? bannerType_DocumentWidget_(d) : none_GmDocumentBanner); |
1120 | setFormat_GmDocument(d->doc, gemini_GmDocumentFormat); | 1141 | setFormat_GmDocument(d->doc, gemini_GmDocumentFormat); |
1121 | translate_Lang(src); | 1142 | translate_Lang(src); |
@@ -1200,11 +1221,15 @@ static void postProcessRequestContent_DocumentWidget_(iDocumentWidget *d, iBool | |||
1200 | } | 1221 | } |
1201 | } | 1222 | } |
1202 | if (d->sourceGempub) { | 1223 | if (d->sourceGempub) { |
1203 | if (equal_String(d->mod.url, coverPageUrl_Gempub(d->sourceGempub)) && | 1224 | if (equal_String(d->mod.url, coverPageUrl_Gempub(d->sourceGempub))) { |
1204 | preloadCoverImage_Gempub(d->sourceGempub, d->doc)) { | 1225 | makeFooterButtons_DocumentWidget_(d, (iMenuItem[]){ |
1205 | redoLayout_GmDocument(d->doc); | 1226 | { "Gempub Cover Page", 0, 0, NULL } |
1206 | updateVisible_DocumentWidget_(d); | 1227 | }, 1); |
1207 | invalidate_DocumentWidget_(d); | 1228 | if (preloadCoverImage_Gempub(d->sourceGempub, d->doc)) { |
1229 | redoLayout_GmDocument(d->doc); | ||
1230 | updateVisible_DocumentWidget_(d); | ||
1231 | invalidate_DocumentWidget_(d); | ||
1232 | } | ||
1208 | } | 1233 | } |
1209 | if (!isCached && prefs_App()->pinSplit && | 1234 | if (!isCached && prefs_App()->pinSplit && |
1210 | equal_String(d->mod.url, indexPageUrl_Gempub(d->sourceGempub))) { | 1235 | equal_String(d->mod.url, indexPageUrl_Gempub(d->sourceGempub))) { |
@@ -2108,6 +2133,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
2108 | const iBool keepCenter = equal_Command(cmd, "font.changed"); | 2133 | const iBool keepCenter = equal_Command(cmd, "font.changed"); |
2109 | updateDocumentWidthRetainingScrollPosition_DocumentWidget_(d, keepCenter); | 2134 | updateDocumentWidthRetainingScrollPosition_DocumentWidget_(d, keepCenter); |
2110 | d->drawBufs->flags |= updateSideBuf_DrawBufsFlag; | 2135 | d->drawBufs->flags |= updateSideBuf_DrawBufsFlag; |
2136 | updateVisible_DocumentWidget_(d); | ||
2111 | invalidate_DocumentWidget_(d); | 2137 | invalidate_DocumentWidget_(d); |
2112 | dealloc_VisBuf(d->visBuf); | 2138 | dealloc_VisBuf(d->visBuf); |
2113 | updateWindowTitle_DocumentWidget_(d); | 2139 | updateWindowTitle_DocumentWidget_(d); |
diff --git a/src/ui/keys.c b/src/ui/keys.c index 8af7867c..87a5fb88 100644 --- a/src/ui/keys.c +++ b/src/ui/keys.c | |||
@@ -237,6 +237,7 @@ static const struct { int id; iMenuItem bind; int flags; } defaultBindings_[] = | |||
237 | { 98, { "${keys.split.menu.vert12}", SDLK_f, 0, "ui.split arg:1 axis:1", }, noDirectTrigger_BindFlag }, | 237 | { 98, { "${keys.split.menu.vert12}", SDLK_f, 0, "ui.split arg:1 axis:1", }, noDirectTrigger_BindFlag }, |
238 | { 99, { "${keys.split.menu.vert21}", SDLK_r, 0, "ui.split arg:2 axis:1", }, noDirectTrigger_BindFlag }, | 238 | { 99, { "${keys.split.menu.vert21}", SDLK_r, 0, "ui.split arg:2 axis:1", }, noDirectTrigger_BindFlag }, |
239 | { 100,{ "${keys.hoverurl}", '/', KMOD_PRIMARY, "prefs.hoverlink.toggle" }, 0 }, | 239 | { 100,{ "${keys.hoverurl}", '/', KMOD_PRIMARY, "prefs.hoverlink.toggle" }, 0 }, |
240 | { 110,{ "${menu.save.downloads}", SDLK_s, KMOD_PRIMARY, "document.save" }, 0 }, | ||
240 | /* The following cannot currently be changed (built-in duplicates). */ | 241 | /* The following cannot currently be changed (built-in duplicates). */ |
241 | #if defined (iPlatformApple) | 242 | #if defined (iPlatformApple) |
242 | { 1002, { NULL, SDLK_LEFTBRACKET, KMOD_PRIMARY, "navigate.back" }, 0 }, | 243 | { 1002, { NULL, SDLK_LEFTBRACKET, KMOD_PRIMARY, "navigate.back" }, 0 }, |
diff --git a/src/ui/keys.h b/src/ui/keys.h index 4cbca3b7..6273027a 100644 --- a/src/ui/keys.h +++ b/src/ui/keys.h | |||
@@ -26,6 +26,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ | |||
26 | #include <the_Foundation/ptrarray.h> | 26 | #include <the_Foundation/ptrarray.h> |
27 | #include <SDL_events.h> | 27 | #include <SDL_events.h> |
28 | 28 | ||
29 | #define newIdentity_KeyShortcut SDLK_n, KMOD_PRIMARY | KMOD_SHIFT | ||
30 | |||
29 | #if defined (iPlatformApple) | 31 | #if defined (iPlatformApple) |
30 | # define reload_KeyShortcut SDLK_r, KMOD_PRIMARY | 32 | # define reload_KeyShortcut SDLK_r, KMOD_PRIMARY |
31 | # define newTab_KeyShortcut SDLK_t, KMOD_PRIMARY | 33 | # define newTab_KeyShortcut SDLK_t, KMOD_PRIMARY |
diff --git a/src/ui/root.c b/src/ui/root.c index 2a2130d8..7a409a75 100644 --- a/src/ui/root.c +++ b/src/ui/root.c | |||
@@ -128,7 +128,7 @@ static const iMenuItem phoneNavMenuItems_[] = { | |||
128 | static const iMenuItem identityButtonMenuItems_[] = { | 128 | static const iMenuItem identityButtonMenuItems_[] = { |
129 | { "${menu.identity.notactive}", 0, 0, "ident.showactive" }, | 129 | { "${menu.identity.notactive}", 0, 0, "ident.showactive" }, |
130 | { "---", 0, 0, NULL }, | 130 | { "---", 0, 0, NULL }, |
131 | { add_Icon " ${menu.identity.new}", SDLK_n, KMOD_PRIMARY | KMOD_SHIFT, "ident.new" }, | 131 | { add_Icon " ${menu.identity.new}", newIdentity_KeyShortcut, "ident.new" }, |
132 | { "${menu.identity.import}", SDLK_i, KMOD_PRIMARY | KMOD_SHIFT, "ident.import" }, | 132 | { "${menu.identity.import}", SDLK_i, KMOD_PRIMARY | KMOD_SHIFT, "ident.import" }, |
133 | { "---", 0, 0, NULL }, | 133 | { "---", 0, 0, NULL }, |
134 | { person_Icon " ${menu.show.identities}", 0, 0, "toolbar.showident" }, | 134 | { person_Icon " ${menu.show.identities}", 0, 0, "toolbar.showident" }, |
@@ -138,7 +138,7 @@ static const iMenuItem identityButtonMenuItems_[] = { | |||
138 | { "${menu.identity.notactive}", 0, 0, "ident.showactive" }, | 138 | { "${menu.identity.notactive}", 0, 0, "ident.showactive" }, |
139 | { "---", 0, 0, NULL }, | 139 | { "---", 0, 0, NULL }, |
140 | # if !defined (iPlatformAppleDesktop) | 140 | # if !defined (iPlatformAppleDesktop) |
141 | { add_Icon " ${menu.identity.new}", SDLK_n, KMOD_PRIMARY | KMOD_SHIFT, "ident.new" }, | 141 | { add_Icon " ${menu.identity.new}", newIdentity_KeyShortcut, "ident.new" }, |
142 | { "${menu.identity.import}", SDLK_i, KMOD_PRIMARY | KMOD_SHIFT, "ident.import" }, | 142 | { "${menu.identity.import}", SDLK_i, KMOD_PRIMARY | KMOD_SHIFT, "ident.import" }, |
143 | { "---", 0, 0, NULL }, | 143 | { "---", 0, 0, NULL }, |
144 | { person_Icon " ${menu.show.identities}", '4', KMOD_PRIMARY, "sidebar.mode arg:3 show:1" }, | 144 | { person_Icon " ${menu.show.identities}", '4', KMOD_PRIMARY, "sidebar.mode arg:3 show:1" }, |
diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c index f9bdbf67..950db596 100644 --- a/src/ui/sidebarwidget.c +++ b/src/ui/sidebarwidget.c | |||
@@ -1249,9 +1249,11 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
1249 | } | 1249 | } |
1250 | else if (arg_Command(cmd)) { | 1250 | else if (arg_Command(cmd)) { |
1251 | signIn_GmCerts(certs_App(), ident, tabUrl); | 1251 | signIn_GmCerts(certs_App(), ident, tabUrl); |
1252 | postCommand_App("navigate.reload"); | ||
1252 | } | 1253 | } |
1253 | else { | 1254 | else { |
1254 | signOut_GmCerts(certs_App(), tabUrl); | 1255 | signOut_GmCerts(certs_App(), tabUrl); |
1256 | postCommand_App("navigate.reload"); | ||
1255 | } | 1257 | } |
1256 | saveIdentities_GmCerts(certs_App()); | 1258 | saveIdentities_GmCerts(certs_App()); |
1257 | updateItems_SidebarWidget_(d); | 1259 | updateItems_SidebarWidget_(d); |
@@ -1491,10 +1493,7 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev) | |||
1491 | const int kmods = keyMods_Sym(ev->key.keysym.mod); | 1493 | const int kmods = keyMods_Sym(ev->key.keysym.mod); |
1492 | /* Hide the sidebar when Escape is pressed. */ | 1494 | /* Hide the sidebar when Escape is pressed. */ |
1493 | if (kmods == 0 && key == SDLK_ESCAPE && isVisible_Widget(d)) { | 1495 | if (kmods == 0 && key == SDLK_ESCAPE && isVisible_Widget(d)) { |
1494 | setFlags_Widget(w, hidden_WidgetFlag, iTrue); | 1496 | postCommand_Widget(d, "%s.toggle", cstr_String(id_Widget(w))); |
1495 | arrange_Widget(w->parent); | ||
1496 | updateSize_DocumentWidget(document_App()); | ||
1497 | refresh_Widget(w->parent); | ||
1498 | return iTrue; | 1497 | return iTrue; |
1499 | } | 1498 | } |
1500 | } | 1499 | } |
diff --git a/src/ui/widget.c b/src/ui/widget.c index 8d9c6f3b..543b8bc9 100644 --- a/src/ui/widget.c +++ b/src/ui/widget.c | |||
@@ -845,6 +845,12 @@ iBool containsExpanded_Widget(const iWidget *d, iInt2 windowCoord, int expand) { | |||
845 | addY_I2(d->rect.size, | 845 | addY_I2(d->rect.size, |
846 | d->flags & drawBackgroundToBottom_WidgetFlag ? size_Root(d->root).y : 0) | 846 | d->flags & drawBackgroundToBottom_WidgetFlag ? size_Root(d->root).y : 0) |
847 | }; | 847 | }; |
848 | /* Apply the animated offset. (Visual offsets don't affect interaction.) */ | ||
849 | for (const iWidget *w = d; w; w = w->parent) { | ||
850 | if (w->animOffsetRef) { | ||
851 | windowCoord.y += value_Anim(w->animOffsetRef); | ||
852 | } | ||
853 | } | ||
848 | return contains_Rect(expand ? expanded_Rect(bounds, init1_I2(expand)) : bounds, | 854 | return contains_Rect(expand ? expanded_Rect(bounds, init1_I2(expand)) : bounds, |
849 | windowToInner_Widget(d, windowCoord)); | 855 | windowToInner_Widget(d, windowCoord)); |
850 | } | 856 | } |