Lagrange [work/v1.7]

Added a key binding mechanism

=> b1e9b313f6810afbadde0f8079326ecf04c89817

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 20def6a7..7d73956d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -135,6 +135,7 @@ set (SOURCES
     src/ui/listwidget.h
     src/ui/lookupwidget.c
     src/ui/lookupwidget.h
+    src/ui/keys.c
     src/ui/keys.h
     src/ui/metrics.c
     src/ui/metrics.h
diff --git a/src/app.c b/src/app.c
index e4c31eaf..10f5f0d2 100644
--- a/src/app.c
+++ b/src/app.c
@@ -32,6 +32,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
 #include "ui/command.h"
 #include "ui/documentwidget.h"
 #include "ui/inputwidget.h"
+#include "ui/keys.h"
 #include "ui/labelwidget.h"
 #include "ui/sidebarwidget.h"
 #include "ui/text.h"
@@ -357,9 +358,11 @@ static void init_App_(iApp *d, int argc, char **argv) {
 #if defined (iPlatformApple)
     setupApplication_MacOS();
 #endif
+    init_Keys();
     loadPrefs_App_(d);
+    load_Keys(dataDir_App_);
     load_Visited(d->visited, dataDir_App_);
-    load_Bookmarks(d->bookmarks, dataDir_App_);
+    load_Bookmarks(d->bookmarks, dataDir_App_);    
     if (isFirstRun) {
         /* Create the default bookmarks for a quick start. */
         add_Bookmarks(d->bookmarks,
@@ -426,6 +429,7 @@ static void init_App_(iApp *d, int argc, char **argv) {
 
 static void deinit_App(iApp *d) {
     saveState_App_(d);
+    save_Keys(dataDir_App_);
     savePrefs_App_(d);
     deinit_Prefs(&d->prefs);
     save_Bookmarks(d->bookmarks, dataDir_App_);
@@ -501,6 +505,10 @@ void processEvents_App(enum iAppEventMode eventMode) {
             }
             default: {
                 iBool wasUsed = processEvent_Window(d->window, &ev);
+                if (!wasUsed) {
+                    /* There may be a key bindings for this. */
+                    wasUsed = processEvent_Keys(&ev);
+                }
                 if (ev.type == SDL_USEREVENT && ev.user.code == command_UserEventCode) {
 #if defined (iPlatformApple) && !defined (iPlatformIOS)
                     handleCommand_MacOS(command_UserEvent(&ev));
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index 5c6780d1..a2bea17b 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -1536,7 +1536,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
         updateVisible_DocumentWidget_(d);
         return iTrue;
     }
-    else if (equalWidget_Command(cmd, w, "scroll.page")) {
+    else if (equal_Command(cmd, "scroll.page") && document_App() == d) {
         if (argLabel_Command(cmd, "repeat")) {
             /* TODO: Adjust scroll animation to be linear during repeated scroll? */
         }
@@ -1547,6 +1547,40 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
                                      smoothDuration_DocumentWidget_);
         return iTrue;
     }
+    else if (equal_Command(cmd, "scroll.top") && document_App() == d) {
+        init_Anim(&d->scrollY, 0);
+        invalidate_VisBuf(d->visBuf);
+        scroll_DocumentWidget_(d, 0);
+        updateVisible_DocumentWidget_(d);
+        refresh_Widget(w);
+        return iTrue;
+    }
+    else if (equal_Command(cmd, "scroll.bottom") && document_App() == d) {
+        init_Anim(&d->scrollY, scrollMax_DocumentWidget_(d));
+        invalidate_VisBuf(d->visBuf);
+        scroll_DocumentWidget_(d, 0);
+        updateVisible_DocumentWidget_(d);
+        refresh_Widget(w);
+        return iTrue;
+    }
+    else if (equal_Command(cmd, "scroll.step") && document_App() == d) {
+        smoothScroll_DocumentWidget_(d,
+                                     3 * lineHeight_Text(paragraph_FontId) * arg_Command(cmd),
+                                     smoothDuration_DocumentWidget_);
+        return iTrue;
+    }
+#if 0
+        case SDLK_PAGEUP:
+        case SDLK_PAGEDOWN:
+        case SDLK_SPACE:
+            postCommand_Widget(
+                w,
+                "scroll.page arg:%d repeat:%d",
+                (key == SDLK_SPACE && mods & KMOD_SHIFT) || key == SDLK_PAGEUP ? -1 : +1,
+                ev->key.repeat != 0);
+            return iTrue;
+    }
+#endif
     else if (equal_Command(cmd, "document.goto") && document_App() == d) {
         const iRangecc heading = range_Command(cmd, "heading");
         if (heading.start) {
@@ -1790,45 +1824,6 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
                     refresh_Widget(w);
                 }
                 break;
-            case SDLK_HOME:
-                init_Anim(&d->scrollY, 0);
-                invalidate_VisBuf(d->visBuf);
-                scroll_DocumentWidget_(d, 0);
-                updateVisible_DocumentWidget_(d);
-                refresh_Widget(w);
-                return iTrue;
-            case SDLK_END:
-                init_Anim(&d->scrollY, scrollMax_DocumentWidget_(d));
-                invalidate_VisBuf(d->visBuf);
-                scroll_DocumentWidget_(d, 0);
-                updateVisible_DocumentWidget_(d);
-                refresh_Widget(w);
-                return iTrue;
-            case SDLK_UP:
-            case SDLK_DOWN:
-                if (mods == 0) {
-                    if (ev->key.repeat) {
-//                        if (!d->smoothContinue) {
-//                            d->smoothContinue = iTrue;
-//                        }
-//                        else return iTrue;
-                    }
-                    smoothScroll_DocumentWidget_(d,
-                                                 3 * lineHeight_Text(paragraph_FontId) *
-                                                     (key == SDLK_UP ? -1 : 1),
-                                                 /*gap_Text * */smoothDuration_DocumentWidget_);
-                    return iTrue;
-                }
-                break;
-            case SDLK_PAGEUP:
-            case SDLK_PAGEDOWN:
-            case SDLK_SPACE:
-                postCommand_Widget(
-                    w,
-                    "scroll.page arg:%d repeat:%d",
-                    (key == SDLK_SPACE && mods & KMOD_SHIFT) || key == SDLK_PAGEUP ? -1 : +1,
-                    ev->key.repeat != 0);
-                return iTrue;
 #if 1
             case SDLK_KP_1:
             case '`': {
diff --git a/src/ui/keys.c b/src/ui/keys.c
new file mode 100644
index 00000000..d46d20bf
--- /dev/null
+++ b/src/ui/keys.c
@@ -0,0 +1,154 @@
+/* Copyright 2020 Jaakko Keränen 
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#include "keys.h"
+#include "util.h"
+#include "app.h"
+
+#include 
+
+iDeclareType(Keys)
+iDeclareType(Binding)
+
+struct Impl_Binding {
+    int key;
+    int mods;
+    iString command;
+    iString label;
+};
+
+static int cmp_Binding_(const void *a, const void *b) {
+    const iBinding *d = a, *other = b;
+    const int cmp = iCmp(d->key, other->key);
+    if (cmp == 0) {
+        return iCmp(d->mods, other->mods);
+    }
+    return cmp;
+}
+
+struct Impl_Keys {
+    iSortedArray bindings;
+};
+
+static iKeys keys_;
+
+static void clear_Keys_(iKeys *d) {
+    iForEach(Array, i, &d->bindings.values) {
+        iBinding *bind = i.value;
+        deinit_String(&bind->command);
+        deinit_String(&bind->label);
+    }
+}
+
+static void bindDefaults_(void) {
+    bind_Keys("scroll.top", SDLK_HOME, 0);
+    bind_Keys("scroll.bottom", SDLK_END, 0);
+    bind_Keys("scroll.step arg:-1", SDLK_UP, 0);
+    bind_Keys("scroll.step arg:1", SDLK_DOWN, 0);
+    bind_Keys("scroll.page arg:-1", SDLK_PAGEUP, 0);
+    bind_Keys("scroll.page arg:-1", SDLK_SPACE, KMOD_SHIFT);
+    bind_Keys("scroll.page arg:1", SDLK_PAGEDOWN, 0);
+    bind_Keys("scroll.page arg:1", SDLK_SPACE, 0);
+}
+
+static iBinding *find_Keys_(iKeys *d, int key, int mods) {
+    const iBinding bind = { .key = key, .mods = mods };
+    size_t pos;
+    if (locate_SortedArray(&d->bindings, &bind, &pos)) {
+        return at_SortedArray(&d->bindings, pos);
+    }
+    return NULL;
+}
+
+static iBinding *findCommand_Keys_(iKeys *d, const char *command) {
+    /* Note: O(n) */
+    iForEach(Array, i, &d->bindings.values) {
+        iBinding *bind = i.value;
+        if (!cmp_String(&bind->command, command)) {
+            return bind;
+        }
+    }
+    return NULL;
+}
+
+/*----------------------------------------------------------------------------------------------*/
+
+void init_Keys(void) {
+    iKeys *d = &keys_;
+    init_SortedArray(&d->bindings, sizeof(iBinding), cmp_Binding_);
+    bindDefaults_();
+}
+
+void deinit_Keys(void) {
+    iKeys *d = &keys_;
+    clear_Keys_(d);
+    deinit_SortedArray(&d->bindings);
+}
+
+void load_Keys(const char *saveDir) {
+
+}
+
+void save_Keys(const char *saveDir) {
+
+}
+
+void bind_Keys(const char *command, int key, int mods) {
+    iKeys *d = &keys_;
+    iBinding *bind = find_Keys_(d, key, mods);
+    if (bind) {
+        setCStr_String(&bind->command, command);
+    }
+    else {
+        iBinding bind;
+        bind.key = key;
+        bind.mods = mods;
+        initCStr_String(&bind.command, command);
+        init_String(&bind.label);
+        insert_SortedArray(&d->bindings, &bind);
+    }
+}
+
+void setLabel_Keys(const char *command, const char *label) {
+    iBinding *bind = findCommand_Keys_(&keys_, command);
+    if (bind) {
+        setCStr_String(&bind->label, label);
+    }
+}
+
+//const iString *label_Keys(const char *command) {
+
+//}
+
+//const char *shortcutLabel_Keys(const char *command) {}
+
+iBool processEvent_Keys(const SDL_Event *ev) {
+    iKeys *d = &keys_;
+    if (ev->type == SDL_KEYDOWN) {
+        const iBinding *bind = find_Keys_(d, ev->key.keysym.sym, keyMods_Sym(ev->key.keysym.mod));
+        if (bind) {
+            postCommandString_App(&bind->command);
+            return iTrue;
+        }
+    }
+    return iFalse;
+}
diff --git a/src/ui/keys.h b/src/ui/keys.h
index a9b13df3..157ddea5 100644
--- a/src/ui/keys.h
+++ b/src/ui/keys.h
@@ -22,7 +22,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
 
 #pragma once
 
-#include 
+#include 
+#include 
 
 #if defined (iPlatformApple)
 #   define reload_KeyShortcut           SDLK_r,             KMOD_PRIMARY
@@ -41,3 +42,17 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
 #   define byWord_KeyModifier           KMOD_CTRL
 #   define byLine_KeyModifier           0
 #endif
+
+void            init_Keys           (void);
+void            deinit_Keys         (void);
+
+void            load_Keys           (const char *saveDir);
+void            save_Keys           (const char *saveDir);
+
+void            bind_Keys           (const char *command, int key, int mods);
+void            setLabel_Keys       (const char *command, const char *label);
+
+//const iString * label_Keys          (const char *command);
+//const char *    shortcutLabel_Keys  (const char *command);
+
+iBool           processEvent_Keys   (const SDL_Event *);
Proxy Information
Original URL
gemini://git.skyjake.fi/lagrange/work%2Fv1.7/cdiff/b1e9b313f6810afbadde0f8079326ecf04c89817
Status Code
Success (20)
Meta
text/gemini; charset=utf-8
Capsule Response Time
71.68199 milliseconds
Gemini-to-HTML Time
0.523686 milliseconds

This content has been proxied by September (ba2dc).