summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-05-22 22:24:13 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-05-22 22:24:13 +0300
commitfd94d906676b0dcef2b783c992f75254a61ae025 (patch)
treea117549152c70d3105b7f94d910ba1333368b2af
parent4108d5b0154775e2bf2c3af5003cf840675d789a (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.
-rw-r--r--src/ui/documentwidget.c72
-rw-r--r--src/ui/keys.c1
-rw-r--r--src/ui/keys.h2
-rw-r--r--src/ui/root.c4
-rw-r--r--src/ui/sidebarwidget.c7
-rw-r--r--src/ui/widget.c6
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_[] = {
128static const iMenuItem identityButtonMenuItems_[] = { 128static 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}