Lagrange [work/v1.17]

UploadWidget: Syntax highlighting and text zoom

=> b31e0ec735ec88492016ff4483dc4ec8556d3d25

diff --git a/po/en.po b/po/en.po
index 812cb2c2..8977fb17 100644
--- a/po/en.po
+++ b/po/en.po
@@ -21,7 +21,7 @@ msgid "heading.archive.userdata"
 msgstr "User Data Archive"
 
 msgid "userdata.help"
-msgstr "User data archives contain exported browsing history, identities, bookmarks, site-specific settings, and fingerprints of trusted server certificates. They can be stored as backups and used for transferring data between devices."
+msgstr "User data archives contain exported browsing history, identities, bookmarks, text snippets, site-specific settings, and fingerprints of trusted server certificates. They can be stored as backups and used for transferring data between devices."
 
 msgid "doc.archive.view"
 msgstr "View archive contents"
@@ -1139,6 +1139,9 @@ msgstr "Import Failed"
 msgid "import.userdata.bookmarks"
 msgstr "Bookmarks:"
 
+msgid "import.userdata.snippets"
+msgstr "Snippets:"
+
 msgid "import.userdata.idents"
 msgstr "Identities:"
 
@@ -2059,6 +2062,9 @@ msgstr "Font glyph warnings:"
 msgid "prefs.font.smooth"
 msgstr "Font smoothing:"
 
+msgid "prefs.editor.highlight"
+msgstr "Syntax highlighting:"
+
 msgid "prefs.linewidth"
 msgstr "Line width:"
 
diff --git a/res/lang/cs.bin b/res/lang/cs.bin
index d893d758..1b8c0e08 100644
Binary files a/res/lang/cs.bin and b/res/lang/cs.bin differ
diff --git a/res/lang/de.bin b/res/lang/de.bin
index 30a44438..d6d47f51 100644
Binary files a/res/lang/de.bin and b/res/lang/de.bin differ
diff --git a/res/lang/en.bin b/res/lang/en.bin
index 1ab6df1f..f8b4ffca 100644
Binary files a/res/lang/en.bin and b/res/lang/en.bin differ
diff --git a/res/lang/eo.bin b/res/lang/eo.bin
index 685ad5b5..865910c0 100644
Binary files a/res/lang/eo.bin and b/res/lang/eo.bin differ
diff --git a/res/lang/es.bin b/res/lang/es.bin
index caafcf7f..c42c7c70 100644
Binary files a/res/lang/es.bin and b/res/lang/es.bin differ
diff --git a/res/lang/es_MX.bin b/res/lang/es_MX.bin
index 030875cc..e9bafe94 100644
Binary files a/res/lang/es_MX.bin and b/res/lang/es_MX.bin differ
diff --git a/res/lang/eu.bin b/res/lang/eu.bin
index fd0a2ce0..67bfccbc 100644
Binary files a/res/lang/eu.bin and b/res/lang/eu.bin differ
diff --git a/res/lang/fi.bin b/res/lang/fi.bin
index 25f79d6f..802860a4 100644
Binary files a/res/lang/fi.bin and b/res/lang/fi.bin differ
diff --git a/res/lang/fr.bin b/res/lang/fr.bin
index caa1b1e6..99bdc5c5 100644
Binary files a/res/lang/fr.bin and b/res/lang/fr.bin differ
diff --git a/res/lang/gl.bin b/res/lang/gl.bin
index 1d9c1f9c..5acfecfc 100644
Binary files a/res/lang/gl.bin and b/res/lang/gl.bin differ
diff --git a/res/lang/hu.bin b/res/lang/hu.bin
index 389b62f6..f07665ca 100644
Binary files a/res/lang/hu.bin and b/res/lang/hu.bin differ
diff --git a/res/lang/ia.bin b/res/lang/ia.bin
index 1c6d9a6c..1e95f965 100644
Binary files a/res/lang/ia.bin and b/res/lang/ia.bin differ
diff --git a/res/lang/ie.bin b/res/lang/ie.bin
index d95a0d6a..94220e80 100644
Binary files a/res/lang/ie.bin and b/res/lang/ie.bin differ
diff --git a/res/lang/isv.bin b/res/lang/isv.bin
index 7b20db6b..e290eb93 100644
Binary files a/res/lang/isv.bin and b/res/lang/isv.bin differ
diff --git a/res/lang/it.bin b/res/lang/it.bin
index af889c0e..3b1bd5d3 100644
Binary files a/res/lang/it.bin and b/res/lang/it.bin differ
diff --git a/res/lang/ja.bin b/res/lang/ja.bin
index a4df6f94..d66a9f07 100644
Binary files a/res/lang/ja.bin and b/res/lang/ja.bin differ
diff --git a/res/lang/nl.bin b/res/lang/nl.bin
index e49f2726..944e0414 100644
Binary files a/res/lang/nl.bin and b/res/lang/nl.bin differ
diff --git a/res/lang/pl.bin b/res/lang/pl.bin
index 52578132..95d963fa 100644
Binary files a/res/lang/pl.bin and b/res/lang/pl.bin differ
diff --git a/res/lang/ru.bin b/res/lang/ru.bin
index 6f4c0ecf..c3ed24ad 100644
Binary files a/res/lang/ru.bin and b/res/lang/ru.bin differ
diff --git a/res/lang/sk.bin b/res/lang/sk.bin
index 3c1103cd..7f03e531 100644
Binary files a/res/lang/sk.bin and b/res/lang/sk.bin differ
diff --git a/res/lang/sr.bin b/res/lang/sr.bin
index 70d9ed7a..77786e29 100644
Binary files a/res/lang/sr.bin and b/res/lang/sr.bin differ
diff --git a/res/lang/tok.bin b/res/lang/tok.bin
index 03ae4b65..8a857f68 100644
Binary files a/res/lang/tok.bin and b/res/lang/tok.bin differ
diff --git a/res/lang/tr.bin b/res/lang/tr.bin
index 22d2d5b9..5a10e7c4 100644
Binary files a/res/lang/tr.bin and b/res/lang/tr.bin differ
diff --git a/res/lang/uk.bin b/res/lang/uk.bin
index 638f2591..26da3aad 100644
Binary files a/res/lang/uk.bin and b/res/lang/uk.bin differ
diff --git a/res/lang/zh_Hans.bin b/res/lang/zh_Hans.bin
index 4e306a9a..7cb820a4 100644
Binary files a/res/lang/zh_Hans.bin and b/res/lang/zh_Hans.bin differ
diff --git a/res/lang/zh_Hant.bin b/res/lang/zh_Hant.bin
index c8a9e349..24753c7e 100644
Binary files a/res/lang/zh_Hant.bin and b/res/lang/zh_Hant.bin differ
diff --git a/src/app.c b/src/app.c
index c2855c79..fd4d53ff 100644
--- a/src/app.c
+++ b/src/app.c
@@ -315,6 +315,7 @@ static iString *serializePrefs_App_(const iApp *d) {
                         cstr_String(&d->prefs.strings[monospaceDocumentFont_PrefsString]));
     appendFormat_String(str, "zoom.set arg:%d\n", d->prefs.zoomPercent);
     appendFormat_String(str, "inputzoom.set arg:%d\n", d->prefs.inputZoomLevel);
+    appendFormat_String(str, "uploadzoom.set arg:%d\n", d->prefs.editorZoomLevel);
     appendFormat_String(str, "pinsplit.set arg:%d\n", d->prefs.pinSplit);
     appendFormat_String(str, "feedinterval.set arg:%d\n", d->prefs.feedInterval);
     appendFormat_String(str, "smoothscroll arg:%d\n", d->prefs.smoothScrolling);
@@ -359,6 +360,7 @@ static iString *serializePrefs_App_(const iApp *d) {
         { "prefs.bottomtabbar", &d->prefs.bottomTabBar },
         { "prefs.centershort", &d->prefs.centerShortDocs },
         { "prefs.dataurl.openimages", &d->prefs.openDataUrlImagesOnLoad },
+        { "prefs.editor.highlight", &d->prefs.editorSyntaxHighlighting },
         { "prefs.evensplit", &d->prefs.evenSplit },
         { "prefs.font.smooth", &d->prefs.fontSmoothing },
         { "prefs.font.warnmissing", &d->prefs.warnAboutMissingGlyphs },
@@ -2518,6 +2520,14 @@ void setForceSoftwareRender_App(iBool sw) {
     app_.forceSoftwareRender = sw;
 }
 
+void setInputZoomLevel_App(int level) {
+    app_.prefs.inputZoomLevel = level;
+}
+
+void setEditorZoomLevel_App(int level) {
+    app_.prefs.editorZoomLevel = level;
+}
+
 enum iColorTheme colorTheme_App(void) {
     return app_.prefs.theme;
 }
@@ -3588,6 +3598,10 @@ static iBool handleNonWindowRelatedCommand_App_(iApp *d, const char *cmd) {
         }
         return iTrue;
     }
+    else if (equal_Command(cmd, "prefs.editor.highlight.changed")) {
+        d->prefs.editorSyntaxHighlighting = arg_Command(cmd) != 0;
+        return iFalse;
+    }
     else if (equal_Command(cmd, "prefs.gemtext.ansi.fg.changed")) {
         iChangeFlags(d->prefs.gemtextAnsiEscapes, allowFg_AnsiFlag, arg_Command(cmd));
         return iTrue;
@@ -4430,6 +4444,11 @@ iBool handleCommand_App(const char *cmd) {
         d->prefs.inputZoomLevel = iClamp(d->prefs.inputZoomLevel, 0, 2);
         return iTrue;
     }
+    else if (equal_Command(cmd, "uploadzoom.set")) {
+        d->prefs.editorZoomLevel = arg_Command(cmd);
+        d->prefs.editorZoomLevel = iClamp(d->prefs.editorZoomLevel, 0, 3);
+        return iTrue;
+    }
     else if (equal_Command(cmd, "zoom.set")) {
         if (arg_Command(cmd) != d->prefs.zoomPercent) {
             d->prefs.zoomPercent = arg_Command(cmd);
@@ -4727,6 +4746,8 @@ iBool handleCommand_App(const char *cmd) {
         setToggle_Widget(findChild_Widget(dlg, "prefs.gemtext.ansi.fontstyle"),
                          d->prefs.gemtextAnsiEscapes & allowFontStyle_AnsiFlag);
         setToggle_Widget(findChild_Widget(dlg, "prefs.font.smooth"), d->prefs.fontSmoothing);
+        setToggle_Widget(findChild_Widget(dlg, "prefs.editor.highlight"),
+                         d->prefs.editorSyntaxHighlighting);
         setToggle_Widget(findChild_Widget(dlg, "prefs.tui.simple"), d->prefs.simpleChars);
         setFlags_Widget(
             findChild_Widget(dlg, format_CStr("prefs.linewidth.%d", d->prefs.lineWidth)),
diff --git a/src/app.h b/src/app.h
index 9242f1af..0b221b59 100644
--- a/src/app.h
+++ b/src/app.h
@@ -104,6 +104,8 @@ iBool               isTextInputActive_App       (void);
 const iPrefs *      prefs_App                   (void);
 iBool               forceSoftwareRender_App     (void);
 void                setForceSoftwareRender_App  (iBool sw);
+void                setInputZoomLevel_App       (int level);
+void                setEditorZoomLevel_App      (int level);
 enum iColorTheme    colorTheme_App              (void);
 const iString *     schemeProxy_App             (iRangecc scheme);
 iBool               schemeProxyHostAndPort_App  (iRangecc scheme, const iString **host, uint16_t *port);
diff --git a/src/prefs.c b/src/prefs.c
index 1ad789df..fe890542 100644
--- a/src/prefs.c
+++ b/src/prefs.c
@@ -26,7 +26,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
 #include 
 #include 
 
-_Static_assert(offsetof(iPrefs, plainTextWrap) == offsetof(iPrefs, bools[plainTextWrap_PrefsBool]),
+_Static_assert(offsetof(iPrefs, geminiStyledGopher) ==
+               offsetof(iPrefs, bools[geminiStyledGopher_PrefsBool]),
                "memory layout mismatch (needs struct packing?)");
 
 void init_Prefs(iPrefs *d) {
@@ -46,6 +47,8 @@ void init_Prefs(iPrefs *d) {
     d->uiAnimations      = iTrue;
     d->uiScale           = 1.0f; /* default set elsewhere */
     d->inputZoomLevel    = 0;
+    d->editorZoomLevel   = 0;
+    d->editorSyntaxHighlighting = iTrue;
     d->zoomPercent       = 100;
     d->navbarActions[0]  = back_ToolbarAction;
     d->navbarActions[1]  = forward_ToolbarAction;
diff --git a/src/prefs.h b/src/prefs.h
index fb65a132..e5114f55 100644
--- a/src/prefs.h
+++ b/src/prefs.h
@@ -73,6 +73,7 @@ enum iPrefsBool {
 
     evenSplit_PrefsBool,
     detachedPrefs_PrefsBool,
+    editorSyntaxHighlighting_PrefsBool,
 
     /* Document presentation */
     sideIcon_PrefsBool,
@@ -151,6 +152,7 @@ struct Impl_Prefs {
 
             iBool evenSplit;
             iBool detachedPrefs;
+            iBool editorSyntaxHighlighting;
 
             /* Document presentation */
             iBool sideIcon;
@@ -208,6 +210,7 @@ struct Impl_Prefs {
     enum iToolbarAction navbarActions[maxNavbarActions_Prefs];
     enum iToolbarAction toolbarActions[2];
     int              inputZoomLevel;
+    int              editorZoomLevel;
     /* Document presentation */
     int              zoomPercent;
     /* Behavior */
diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c
index bf68bfc1..e3ba6635 100644
--- a/src/ui/inputwidget.c
+++ b/src/ui/inputwidget.c
@@ -248,6 +248,8 @@ struct Impl_InputWidget {
     iTextBuf *      buffered; /* pre-rendered static text */
     iInputWidgetValidatorFunc validator;
     void *          validatorContext;
+    iInputWidgetHighlighterFunc highlighter;
+    void *          highlighterContext;
     iString *       backupPath;
     int             backupTimer;
     iString         oldText; /* for restoring if edits cancelled */
@@ -835,6 +837,8 @@ void init_InputWidget(iInputWidget *d, size_t maxLen) {
     init_Widget(w);
     d->validator = NULL;
     d->validatorContext = NULL;
+    d->highlighter = NULL;
+    d->highlighterContext = NULL;
     setFlags_Widget(w, focusable_WidgetFlag | hover_WidgetFlag, iTrue);
     setFlags_Widget(w, extraPadding_WidgetFlag, isMobile_Platform());
 #if LAGRANGE_USE_SYSTEM_TEXT_INPUT
@@ -1057,6 +1061,13 @@ void setValidator_InputWidget(iInputWidget *d, iInputWidgetValidatorFunc validat
     d->validatorContext = context;
 }
 
+void setHighlighter_InputWidget(iInputWidget *d, iInputWidgetHighlighterFunc highlighter,
+                                void *context) {
+    d->highlighter = highlighter;
+    d->highlighterContext = context;
+    invalidateBuffered_InputWidget_(d);
+}
+
 void setLineBreaksEnabled_InputWidget(iInputWidget *d, iBool lineBreaksEnabled) {
     iChangeFlags(d->inFlags, lineBreaksEnabled_InputWidgetFlag, lineBreaksEnabled);
 }
@@ -1145,6 +1156,7 @@ static void updateBuffered_InputWidget_(iInputWidget *d) {
                                  strlen(uiTextStrong_ColorEscape));
             }
         }
+        /* TODO: To apply syntax highlighting here, we'd have to draw line by line. */
         iWrapText wt = wrap_InputWidget_(d, 0);
         wt.maxLines = d->maxWrapLines;
         wt.text = range_String(visText);
@@ -2920,7 +2932,13 @@ static void draw_InputWidget_(const iInputWidget *d) {
             wrapText.text = range_String(&line->text);
             marker.line   = line;
             marker.pos    = drawPos;
-            addv_I2(&drawPos, draw_WrapText(&wrapText, d->font, drawPos, fg).advance); /* lines end with \n */
+            iInputWidgetHighlight highlight = { .font = d->font, .color = fg };
+            if (d->highlighter) {
+                highlight = d->highlighter(d, wrapText.text, d->highlighterContext);
+            }
+            addv_I2(&drawPos,
+                    draw_WrapText(&wrapText, highlight.font, drawPos, highlight.color)
+                        .advance); /* lines end with \n */
         }
         markerRects[0] = marker.firstMarkRect;
         markerRects[1] = marker.lastMarkRect;
diff --git a/src/ui/inputwidget.h b/src/ui/inputwidget.h
index 817552ad..a2c5aa1e 100644
--- a/src/ui/inputwidget.h
+++ b/src/ui/inputwidget.h
@@ -32,14 +32,21 @@ enum iInputMode {
     overwrite_InputMode,
 };
 
-iDeclareType(InputWidgetContentPadding)
+iDeclareType(InputWidgetContentPadding);
+iDeclareType(InputWidgetHighlight);
 
 struct Impl_InputWidgetContentPadding {
     int left;
     int right;
 };
 
+struct Impl_InputWidgetHighlight {
+    int font;
+    int color;
+};
+
 typedef void (*iInputWidgetValidatorFunc)(iInputWidget *, void *context);
+typedef iInputWidgetHighlight (*iInputWidgetHighlighterFunc)(const iInputWidget *, const iRangecc line, void *context);
 
 void    setHint_InputWidget             (iInputWidget *, const char *hintText);
 void    setMode_InputWidget             (iInputWidget *, enum iInputMode mode);
@@ -52,6 +59,7 @@ void    setFont_InputWidget             (iInputWidget *, int fontId);
 void    setContentPadding_InputWidget   (iInputWidget *, int left, int right); /* only affects the text entry */
 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    setLineBreaksEnabled_InputWidget(iInputWidget *, iBool lineBreaksEnabled);
 void    setEnterKeyEnabled_InputWidget  (iInputWidget *, iBool enterKeyEnabled);
 void    setOmitDefaultSchemeIfNarrow_InputWidget(iInputWidget *, iBool omitDefaultSchemeIfNarrow);
diff --git a/src/ui/uploadwidget.c b/src/ui/uploadwidget.c
index b04fa385..bef91d1f 100644
--- a/src/ui/uploadwidget.c
+++ b/src/ui/uploadwidget.c
@@ -201,6 +201,38 @@ static void updateFieldWidths_UploadWidget(iUploadWidget *d) {
     }
 }
 
+static int font_UploadWidget_(const iUploadWidget *d, enum iFontStyle style) {
+    iUnused(d);
+    static const int fontSizes_[4] = {
+        uiSmall_FontSize, uiNormal_FontSize, uiMedium_FontSize, uiBig_FontSize
+    };
+    return FONT_ID(monospace_FontId, style, fontSizes_[prefs_App()->editorZoomLevel]);
+}
+
+static iInputWidgetHighlight gemtextHighlighter_UploadWidget_(const iInputWidget *input,
+                                                              iRangecc line, void *context) {
+    const iBool isFocused = isFocused_Widget(input);
+    iUploadWidget *d = context;
+    if (startsWith_Rangecc(line, "#")) {
+        return (iInputWidgetHighlight){ font_UploadWidget_(d, bold_FontStyle),
+                                        uiTextAction_ColorId };
+    }
+    if (startsWith_Rangecc(line, ">")) {
+        return (iInputWidgetHighlight){ font_UploadWidget_(d, italic_FontStyle),
+                                        uiTextStrong_ColorId };
+    }
+    if (startsWith_Rangecc(line, "* ")) {
+        return (iInputWidgetHighlight){ font_UploadWidget_(d, regular_FontStyle),
+                                        uiTextCaution_ColorId };
+    }
+    if (startsWith_Rangecc(line, "=>")) {
+        return (iInputWidgetHighlight){ font_UploadWidget_(d, regular_FontStyle),
+                                        uiTextAction_ColorId };
+    }
+    return (iInputWidgetHighlight){ font_UploadWidget_(d, regular_FontStyle),
+                                    isFocused ? uiInputTextFocused_ColorId : uiInputText_ColorId };
+}
+
 void init_UploadWidget(iUploadWidget *d, enum iUploadProtocol protocol) {
     iWidget *w = as_Widget(d);
     init_Widget(w);
@@ -345,6 +377,9 @@ void init_UploadWidget(iUploadWidget *d, enum iUploadProtocol protocol) {
             d->input = new_InputWidget(0);
             setId_Widget(as_Widget(d->input), "upload.text");
             setFixedSize_Widget(as_Widget(d->input), init_I2(120 * gap_UI * aspectRatio, -1));
+            if (prefs_App()->editorSyntaxHighlighting) {
+                setHighlighter_InputWidget(d->input, gemtextHighlighter_UploadWidget_, d);
+            }
             addChild_Widget(page, iClob(d->input));
             appendFramelessTabPage_Widget(d->tabs, iClob(page), "${heading.upload.text}", none_ColorId, '1', 0);
         }
@@ -392,7 +427,7 @@ void init_UploadWidget(iUploadWidget *d, enum iUploadProtocol protocol) {
         updateFieldWidths_UploadWidget(d);
         setFocus_Widget(as_Widget(d->input));
     }
-    setFont_InputWidget(d->input, FONT_ID(monospace_FontId, regular_FontStyle, uiSmall_FontSize));
+    setFont_InputWidget(d->input, font_UploadWidget_(d, regular_FontStyle));
     setUseReturnKeyBehavior_InputWidget(d->input, iFalse); /* traditional text editor */
     setLineLimits_InputWidget(d->input, 7, 20);
     setHint_InputWidget(d->input, "${hint.upload.text}");
@@ -758,6 +793,31 @@ static iBool processEvent_UploadWidget_(iUploadWidget *d, const SDL_Event *ev) {
             refresh_Widget(as_Widget(d->input));
         }
     }
+    else if (isDesktop_Platform() &&
+             (equal_Command(cmd, "zoom.set") || equal_Command(cmd, "zoom.delta"))) {
+        int sizeIndex = prefs_App()->editorZoomLevel;
+        if (equal_Command(cmd, "zoom.set")) {
+            sizeIndex = 0;
+        }
+        else {
+            sizeIndex += iSign(arg_Command(cmd));
+            sizeIndex = iClamp(sizeIndex, 0, 3);
+        }
+        setEditorZoomLevel_App(sizeIndex);
+        setFont_InputWidget(d->input, font_UploadWidget_(d, regular_FontStyle));
+        refresh_Widget(d->input);
+        return iTrue;
+    }
+    else if (isCommand_UserEvent(ev, "prefs.editor.highlight.changed")) {
+        if (arg_Command(command_UserEvent(ev))) {
+            setHighlighter_InputWidget(d->input, gemtextHighlighter_UploadWidget_, d);
+        }
+        else {
+            setHighlighter_InputWidget(d->input, NULL, NULL);
+        }
+        refresh_Widget(d->input);
+        return iFalse;
+    }
     else if (isCommand_Widget(w, ev, "upload.pickfile")) {
 #if defined (iPlatformAppleMobile) || defined (iPlatformAndroidMobile)
         if (hasLabel_Command(cmd, "path")) {
diff --git a/src/ui/util.c b/src/ui/util.c
index 446e443b..296d0280 100644
--- a/src/ui/util.c
+++ b/src/ui/util.c
@@ -2401,7 +2401,7 @@ iBool valueInputHandler_(iWidget *dlg, const char *cmd) {
                 sizeIndex += iSign(arg_Command(cmd));
                 sizeIndex = iClamp(sizeIndex, 0, 2);
             }
-            ((iPrefs *) prefs_App())->inputZoomLevel = sizeIndex; /* const cast... */
+            setInputZoomLevel_App(sizeIndex);
             setFont_InputWidget(input,
                                 FONT_ID(default_FontId,
                                         regular_FontStyle,
@@ -2724,7 +2724,7 @@ static iBool toggleHandler_(iWidget *d, const char *cmd) {
     if (equal_Command(cmd, "toggle") && pointer_Command(cmd) == d) {
         setToggle_Widget(d, (flags_Widget(d) & selected_WidgetFlag) == 0);
         postCommand_Widget(d,
-                           format_CStr("%s.changed arg:%d",
+                           format_CStr("!%s.changed arg:%d",
                                        cstr_String(id_Widget(d)),
                                        isSelected_Widget(d) ? 1 : 0));
         return iTrue;
@@ -3533,6 +3533,8 @@ iWidget *makePreferences_Widget(void) {
             addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.font.ui}")));
             addFontButtons_(values, "ui");
             addDialogToggle_(headings, values, "${prefs.font.smooth}", "prefs.font.smooth");
+            addDialogPadding_(headings, values);
+            addDialogToggle_(headings, values, "${prefs.editor.highlight}", "prefs.editor.highlight");
         }
     }
     /* Page style. */ {
diff --git a/src/ui/widget.c b/src/ui/widget.c
index 384d5323..a4666c09 100644
--- a/src/ui/widget.c
+++ b/src/ui/widget.c
@@ -257,6 +257,9 @@ static void aboutToBeDestroyed_Widget_(iWidget *d) {
     if (isHover_Widget(d)) {
         win->hover = NULL;
     }
+    if (win->focus == d) {
+        win->focus = NULL;
+    }
     if (win->lastHover == d) {
         win->lastHover = NULL;
     }
Proxy Information
Original URL
gemini://git.skyjake.fi/lagrange/work%2Fv1.17/cdiff/b31e0ec735ec88492016ff4483dc4ec8556d3d25
Status Code
Success (20)
Meta
text/gemini; charset=utf-8
Capsule Response Time
91.72506 milliseconds
Gemini-to-HTML Time
0.605045 milliseconds

This content has been proxied by September (ba2dc).