Lagrange [work/v1.6]

Gempub: Linear navigation with left/right arrow keys

=> 47024a3bd5f9b9173319a0c345e546bb573daf56

diff --git a/src/defs.h b/src/defs.h
index d592abb2..d16c56f5 100644
--- a/src/defs.h
+++ b/src/defs.h
@@ -36,6 +36,8 @@ enum iFileVersion {
 
 /* Icons */
 
+#define rightArrowhead_Icon "\u27a4"
+#define leftArrowhead_Icon  "\u27a4"
 #define warning_Icon        "\u26a0"
 #define openLock_Icon       "\U0001f513"
 #define closedLock_Icon     "\U0001f512"
diff --git a/src/gempub.c b/src/gempub.c
index aa03d835..f3021add 100644
--- a/src/gempub.c
+++ b/src/gempub.c
@@ -213,6 +213,10 @@ iBool isOpen_Gempub(const iGempub *d) {
     return d->arch != NULL;
 }
 
+const iString *property_Gempub(const iGempub *d, enum iGempubProperty prop) {
+    return &d->props[prop];
+}
+
 const iString *coverPageUrl_Gempub(const iGempub *d) {
     return &d->baseUrl;
 }
@@ -232,6 +236,39 @@ const iString *navStartLinkUrl_Gempub(const iGempub *d) {
     return &((const iGempubNavLink *) constFront_Array(d->navLinks))->url;
 }
 
+size_t navSize_Gempub(const iGempub *d) {
+    parseNavigationLinks_Gempub_(d);
+    return size_Array(d->navLinks);
+}
+
+size_t navIndex_Gempub(const iGempub *d, const iString *url) {
+    parseNavigationLinks_Gempub_(d);
+    const iString *normUrl = withSpacesEncoded_String(url);
+    iConstForEach(Array, i, d->navLinks) {
+        const iGempubNavLink *nav = i.value;
+        if (equalCase_String(&nav->url, normUrl)) {
+            return index_ArrayConstIterator(&i);
+        }
+    }
+    return iInvalidPos;
+}
+
+const iString *navLinkUrl_Gempub(const iGempub *d, size_t index) {
+    parseNavigationLinks_Gempub_(d);
+    if (index < size_Array(d->navLinks)) {
+        return &constValue_Array(d->navLinks, index, iGempubNavLink).url;
+    }
+    return NULL;
+}
+
+const iString *navLinkLabel_Gempub(const iGempub *d, size_t index) {
+    parseNavigationLinks_Gempub_(d);
+    if (index < size_Array(d->navLinks)) {
+        return &constValue_Array(d->navLinks, index, iGempubNavLink).label;
+    }
+    return NULL;    
+}
+
 static iBool hasProperty_Gempub_(const iGempub *d, enum iGempubProperty prop) {
     return !isEmpty_String(&d->props[prop]);
 }
diff --git a/src/gempub.h b/src/gempub.h
index 6912bcc9..c03cabe6 100644
--- a/src/gempub.h
+++ b/src/gempub.h
@@ -60,5 +60,9 @@ const iString * property_Gempub         (const iGempub *, enum iGempubProperty);
 const iString * coverPageUrl_Gempub     (const iGempub *);
 const iString * indexPageUrl_Gempub     (const iGempub *);
 const iString * navStartLinkUrl_Gempub  (const iGempub *); /* for convenience */
+size_t          navSize_Gempub          (const iGempub *);
+size_t          navIndex_Gempub         (const iGempub *, const iString *url);
+const iString * navLinkUrl_Gempub       (const iGempub *, size_t index);
+const iString * navLinkLabel_Gempub     (const iGempub *, size_t index);
 
 extern const char *mimeType_Gempub;
diff --git a/src/gmdocument.c b/src/gmdocument.c
index ec406a63..ad8616a3 100644
--- a/src/gmdocument.c
+++ b/src/gmdocument.c
@@ -30,6 +30,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
 #include "visited.h"
 #include "bookmarks.h"
 #include "app.h"
+#include "defs.h"
 
 #include 
 #include 
@@ -400,7 +401,7 @@ static void doLayout_GmDocument_(iGmDocument *d) {
     static const float bottomMargin[max_GmLineType] = {
         0.0f, 0.333f, 1.0f, 0.5f, 0.5f, 0.5f, 0.5f, 0.25f
     };
-    static const char *arrow           = "\u27a4";
+    static const char *arrow           = rightArrowhead_Icon;
     static const char *envelope        = "\U0001f4e7";
     static const char *bullet          = "\u2022";
     static const char *folder          = "\U0001f4c1";
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index f354623e..c8aad02b 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -1222,15 +1222,89 @@ static void postProcessRequestContent_DocumentWidget_(iDocumentWidget *d, iBool
     }
     if (d->sourceGempub) {
         if (equal_String(d->mod.url, coverPageUrl_Gempub(d->sourceGempub))) {
-            makeFooterButtons_DocumentWidget_(d, (iMenuItem[]){
-                                                     { "Gempub Cover Page", 0, 0, NULL }
-                                                 }, 1);
+            if (equalCase_Rangecc(urlScheme_String(d->mod.url), "file")) {
+                iArray *items = collectNew_Array(sizeof(iMenuItem));
+                pushBack_Array(
+                    items,
+                    &(iMenuItem){ book_Icon " ${gempub.cover.view}",
+                                  0,
+                                  0,
+                                  format_CStr("!open url:%s",
+                                              cstr_String(indexPageUrl_Gempub(d->sourceGempub))) });
+                if (navSize_Gempub(d->sourceGempub) > 0) {
+                    pushBack_Array(
+                        items,
+                        &(iMenuItem){
+                            format_CStr(forwardArrow_Icon " %s",
+                                        cstr_String(navLinkLabel_Gempub(d->sourceGempub, 0))),
+                            SDLK_RIGHT,
+                            0,
+                            format_CStr("!open url:%s",
+                                        cstr_String(navLinkUrl_Gempub(d->sourceGempub, 0))) });
+                }
+                makeFooterButtons_DocumentWidget_(d, constData_Array(items), size_Array(items));
+            }
             if (preloadCoverImage_Gempub(d->sourceGempub, d->doc)) {
                 redoLayout_GmDocument(d->doc);
                 updateVisible_DocumentWidget_(d);
                 invalidate_DocumentWidget_(d);
             }
         }
+        else if (equal_String(d->mod.url, indexPageUrl_Gempub(d->sourceGempub))) {
+            makeFooterButtons_DocumentWidget_(
+                d,
+                (iMenuItem[]){ { format_CStr(book_Icon " %s",
+                                             cstr_String(property_Gempub(d->sourceGempub,
+                                                                         title_GempubProperty))),
+                                 SDLK_LEFT,
+                                 0,
+                                 format_CStr("!open url:%s",
+                                             cstr_String(coverPageUrl_Gempub(d->sourceGempub))) } },
+                1);
+        }
+        else {
+            /* Navigation buttons. */
+            iArray *items = collectNew_Array(sizeof(iMenuItem));
+            const size_t navIndex = navIndex_Gempub(d->sourceGempub, d->mod.url);
+            if (navIndex != iInvalidPos) {
+                if (navIndex < navSize_Gempub(d->sourceGempub) - 1) {
+                    pushBack_Array(
+                        items,
+                        &(iMenuItem){
+                            format_CStr(forwardArrow_Icon " %s",
+                                        cstr_String(navLinkLabel_Gempub(d->sourceGempub, navIndex + 1))),
+                            SDLK_RIGHT,
+                            0,
+                            format_CStr("!open url:%s",
+                                        cstr_String(navLinkUrl_Gempub(d->sourceGempub, navIndex + 1))) });
+                }
+                if (navIndex > 0) {
+                    pushBack_Array(
+                        items,
+                        &(iMenuItem){
+                            format_CStr(backArrow_Icon " %s",
+                                        cstr_String(navLinkLabel_Gempub(d->sourceGempub, navIndex - 1))),
+                            SDLK_LEFT,
+                            0,
+                            format_CStr("!open url:%s",
+                                        cstr_String(navLinkUrl_Gempub(d->sourceGempub, navIndex - 1))) });
+                }
+                else if (!equalCase_String(d->mod.url, indexPageUrl_Gempub(d->sourceGempub))) {
+                    pushBack_Array(
+                        items,
+                        &(iMenuItem){
+                            format_CStr(book_Icon " %s",
+                                        cstr_String(property_Gempub(d->sourceGempub, title_GempubProperty))),
+                            SDLK_LEFT,
+                            0,
+                            format_CStr("!open url:%s",
+                                        cstr_String(coverPageUrl_Gempub(d->sourceGempub))) });
+                }
+            }
+            if (!isEmpty_Array(items)) {
+                makeFooterButtons_DocumentWidget_(d, constData_Array(items), size_Array(items));                
+            }
+        }
         if (!isCached && prefs_App()->pinSplit &&
             equal_String(d->mod.url, indexPageUrl_Gempub(d->sourceGempub))) {
             const iString *navStart = navStartLinkUrl_Gempub(d->sourceGempub);
Proxy Information
Original URL
gemini://git.skyjake.fi/lagrange/work%2Fv1.6/cdiff/47024a3bd5f9b9173319a0c345e546bb573daf56
Status Code
Success (20)
Meta
text/gemini; charset=utf-8
Capsule Response Time
68.172226 milliseconds
Gemini-to-HTML Time
0.437402 milliseconds

This content has been proxied by September (3851b).