Lagrange [dev]

Improved arrow key focus navigation (relating to InputWidget)

=> bb9ce59f9885baf3acaf569e965bdf8f0525f3e8

diff --git a/src/app.c b/src/app.c
index e22743f5..b7faef55 100644
--- a/src/app.c
+++ b/src/app.c
@@ -2809,7 +2809,7 @@ iBool moveFocusWithArrows_App(const void *sdlEvent) {
                                                                           : none_Direction);
     if (nextFocus) {
         setCurrent_Window(window_Widget(focus_Widget()));
-        setFocus_Widget(nextFocus);
+        setFocusWithMethod_Widget(nextFocus, arrowKeys_FocusMethod);
         return iTrue;
     }
     return focusRoot_Widget(focus_Widget()) != get_Root()->widget;
diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c
index a65dc6b3..4975c845 100644
--- a/src/ui/inputwidget.c
+++ b/src/ui/inputwidget.c
@@ -231,6 +231,7 @@ enum iInputWidgetFlag {
     dragMarkerStart_InputWidgetFlag      = iBit(15),
     dragMarkerEnd_InputWidgetFlag        = iBit(16),
     omitDefaultSchemeIfNarrow_InputWidgetFlag = iBit(17),
+    arrowFocusNavigable_InputWidgetFlag  = iBit(18),
 };
 
 /*----------------------------------------------------------------------------------------------*/
@@ -864,12 +865,13 @@ void init_InputWidget(iInputWidget *d, size_t maxLen) {
     init_String(&d->oldText);
     init_String(&d->srcHint);
     init_String(&d->hint);
-    d->font         = uiInput_FontId | alwaysVariableFlag_FontId;
-    d->leftPadding  = 0;
-    d->rightPadding = 0;
+    d->font            = uiInput_FontId | alwaysVariableFlag_FontId;
+    d->leftPadding     = 0;
+    d->rightPadding    = 0;
     d->lastUpdateWidth = 0;
     d->inFlags         = eatEscape_InputWidgetFlag | enterKeyEnabled_InputWidgetFlag |
-                         lineBreaksEnabled_InputWidgetFlag | useReturnKeyBehavior_InputWidgetFlag;
+                         lineBreaksEnabled_InputWidgetFlag | useReturnKeyBehavior_InputWidgetFlag |
+                         arrowFocusNavigable_InputWidgetFlag;
     setMaxLen_InputWidget(d, maxLen);
     d->visWrapLines.start = 0;
     d->visWrapLines.end = 1;
@@ -1080,6 +1082,10 @@ void setLineBreaksEnabled_InputWidget(iInputWidget *d, iBool lineBreaksEnabled)
     iChangeFlags(d->inFlags, lineBreaksEnabled_InputWidgetFlag, lineBreaksEnabled);
 }
 
+void setArrowFocusNavigable_InputWidget(iInputWidget *d, iBool arrowFocusNavigable) {
+    iChangeFlags(d->inFlags, arrowFocusNavigable_InputWidgetFlag, arrowFocusNavigable);
+}
+
 void setEnterKeyEnabled_InputWidget(iInputWidget *d, iBool enterKeyEnabled) {
     iChangeFlags(d->inFlags, enterKeyEnabled_InputWidgetFlag, enterKeyEnabled);
 }
@@ -1312,7 +1318,7 @@ void systemInputChanged_InputWidget_(iSystemTextInput *sysCtrl, void *widget) {
 }
 #endif
 
-void begin_InputWidget(iInputWidget *d) {
+static void begin_InputWidget_(iInputWidget *d, iBool allowSelectAll) {
     iWidget *w = as_Widget(d);
     if (isEditing_InputWidget_(d)) {
         /* Already active. */
@@ -1369,7 +1375,7 @@ void begin_InputWidget(iInputWidget *d) {
     showCursor_InputWidget_(d);
     refresh_Widget(w);
     startOrStopCursorTimer_InputWidget_(d, iTrue);
-    if (d->inFlags & selectAllOnFocus_InputWidgetFlag) {
+    if (allowSelectAll && d->inFlags & selectAllOnFocus_InputWidgetFlag) {
         d->mark = (iRanges){ 0, lastLine_InputWidget_(d)->range.end };
         d->cursor = cursorMax_InputWidget_(d);
     }
@@ -1382,6 +1388,10 @@ void begin_InputWidget(iInputWidget *d) {
 #endif
 }
 
+void begin_InputWidget(iInputWidget *d) {
+    begin_InputWidget_(d, iTrue);
+}
+
 void end_InputWidget(iInputWidget *d, iBool accept) {
     iWidget *w = as_Widget(d);
     if (!isEditing_InputWidget_(d)) {
@@ -2357,7 +2367,8 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) {
             setFocus_Widget(NULL);
         }
         else {
-            begin_InputWidget(d);
+            enum iFocusMethod method = arg_Command(command_UserEvent(ev));
+            begin_InputWidget_(d, method != arrowKeys_FocusMethod);
         }
         return iFalse;
     }
@@ -2826,7 +2837,10 @@ static iBool processEvent_InputWidget_(iInputWidget *d, const SDL_Event *ev) {
                 if (processEvent_Widget(as_Widget(d), ev)) {
                     return iTrue;
                 }
-                return moveFocusWithArrows_App(ev);
+                if (d->inFlags & arrowFocusNavigable_InputWidgetFlag) {
+                    return moveFocusWithArrows_App(ev);
+                }
+                return iTrue;
             case SDLK_PAGEUP:
             case SDLK_PAGEDOWN:
                 for (int count = 0; count < 5; count++) {
diff --git a/src/ui/inputwidget.h b/src/ui/inputwidget.h
index a2c5aa1e..f236901d 100644
--- a/src/ui/inputwidget.h
+++ b/src/ui/inputwidget.h
@@ -60,6 +60,7 @@ void    setContentPadding_InputWidget   (iInputWidget *, int left, int right); /
 void    setLineLimits_InputWidget       (iInputWidget *, int minLines, int maxLines);
 void    setValidator_InputWidget        (iInputWidget *, iInputWidgetValidatorFunc validator, void *context);
 void    setHighlighter_InputWidget      (iInputWidget *, iInputWidgetHighlighterFunc highlighter, void *context);
+void    setArrowFocusNavigable_InputWidget(iInputWidget *, iBool arrowFocusNavigable);
 void    setLineBreaksEnabled_InputWidget(iInputWidget *, iBool lineBreaksEnabled);
 void    setEnterKeyEnabled_InputWidget  (iInputWidget *, iBool enterKeyEnabled);
 void    setOmitDefaultSchemeIfNarrow_InputWidget(iInputWidget *, iBool omitDefaultSchemeIfNarrow);
diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c
index 8df64a68..d9627119 100644
--- a/src/ui/sidebarwidget.c
+++ b/src/ui/sidebarwidget.c
@@ -2232,7 +2232,7 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev)
                 if (isFolder_Bookmark(bm)) {
                     contextMenu = d->folderMenu;
                 }
-                else if (!isVisible_Widget(d->menu)) {                    
+                else if (!isVisible_Widget(d->menu)) {
                     const iBool        isRemote        = (bm->flags & remote_BookmarkFlag) != 0;
                     static const char *localOnlyCmds[] = { "bookmark.edit",
                                                            "bookmark.delete",
@@ -2429,8 +2429,8 @@ static void draw_SidebarItem_(const iSidebarItem *d, iPaint *p, iRect itemRect,
         iString str;
         init_String(&str);
         appendChar_String(&str, d->icon ? d->icon : 0x1f588);
-        const int leftIndent = d->indent * gap_UI * 4;
-        const iRect iconArea = { addX_I2(pos, gap_UI + leftIndent),
+        const int leftIndent = d->indent * gap_UI * 4 * aspect_UI;
+        const iRect iconArea = { addX_I2(pos, aspect_UI * gap_UI + leftIndent),
                                  init_I2(!isTerminal_Platform() ? 1.75f * lineHeight_Text(font) : 5, itemHeight) };
         drawCentered_Text(font,
                           iconArea,
@@ -2444,7 +2444,7 @@ static void draw_SidebarItem_(const iSidebarItem *d, iPaint *p, iRect itemRect,
         const iInt2 textPos = addY_I2(topRight_Rect(iconArea), (itemHeight - lineHeight_Text(font)) / 2);
         drawRange_Text(font, textPos, fg, range_String(&d->label));
         const int metaFont = uiLabel_FontId;
-        const int metaIconWidth = 4.5f * gap_UI;
+        const int metaIconWidth = 4.5f * gap_UI * aspect_UI;
         if (isEditing) {
             iRect dragRect = {
                 addX_I2(topRight_Rect(itemRect), -itemHeight * 3 / 2),
diff --git a/src/ui/uploadwidget.c b/src/ui/uploadwidget.c
index 226d3aae..3ee14b7b 100644
--- a/src/ui/uploadwidget.c
+++ b/src/ui/uploadwidget.c
@@ -393,6 +393,9 @@ void init_UploadWidget(iUploadWidget *d, enum iUploadProtocol protocol) {
             setFlags_Widget(page, arrangeSize_WidgetFlag, iTrue);
             d->input = new_InputWidget(0);
             setId_Widget(as_Widget(d->input), "upload.text");
+            /* It would be annoying for focus to exit the widget accidentally when typing text.
+               One needs to use TAB to move focus. */
+            setArrowFocusNavigable_InputWidget(d->input, iFalse);
             setFixedSize_Widget(as_Widget(d->input), init_I2(120 * gap_UI * aspectRatio, -1));
             if (prefs_App()->editorSyntaxHighlighting) {
                 setHighlighter_InputWidget(d->input, gemtextHighlighter_UploadWidget_, d);
diff --git a/src/ui/widget.c b/src/ui/widget.c
index 9618b5dc..d1f23a8d 100644
--- a/src/ui/widget.c
+++ b/src/ui/widget.c
@@ -2431,6 +2431,10 @@ iBool isAffectedByVisualOffset_Widget(const iWidget *d) {
 }
 
 void setFocus_Widget(iWidget *d) {
+    setFocusWithMethod_Widget(d, none_FocusMethod);
+}
+
+void setFocusWithMethod_Widget(iWidget *d, enum iFocusMethod method) {
     iWindow *win = d ? window_Widget(d) : get_Window();
     iAssert(win);
     if (win->focus != d) {
@@ -2445,7 +2449,12 @@ void setFocus_Widget(iWidget *d) {
         win->focus = d;
         if (d) {
             setKeyRoot_Window(get_Window(), d->root);
-            postCommand_Widget(d, "focus.gained");
+            if (method) {
+                postCommand_Widget(d, "focus.gained arg:%d", method);
+            }
+            else {
+                postCommand_Widget(d, "focus.gained");
+            }
         }
     }
 }
diff --git a/src/ui/widget.h b/src/ui/widget.h
index 28ebe856..100ea7c6 100644
--- a/src/ui/widget.h
+++ b/src/ui/widget.h
@@ -358,8 +358,14 @@ void        drawScrollIndicator_Widget  (const iWidget *, const iWidgetScrollInf
 
 int         backgroundFadeColor_Widget  (void);
 
+enum iFocusMethod {
+    none_FocusMethod,
+    arrowKeys_FocusMethod,
+};
+
 const iWidget *focusRoot_Widget     (const iWidget *);
 void        setFocus_Widget         (iWidget *); /* widget must be flagged `focusable` */
+void        setFocusWithMethod_Widget(iWidget *, enum iFocusMethod method);
 iWidget *   focus_Widget            (void);
 iBool       setHover_Widget         (iWidget *);
 iWidget *   hover_Widget            (void);
Proxy Information
Original URL
gemini://git.skyjake.fi/lagrange/dev/cdiff/bb9ce59f9885baf3acaf569e965bdf8f0525f3e8
Status Code
Success (20)
Meta
text/gemini; charset=utf-8
Capsule Response Time
88.663842 milliseconds
Gemini-to-HTML Time
0.669196 milliseconds

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