]> git.neil.brown.name Git - edlib.git/commitdiff
autospell: provide drop-down menu on spelling errors.
authorNeilBrown <neil@brown.name>
Mon, 10 Jul 2023 05:17:44 +0000 (15:17 +1000)
committerNeilBrown <neil@brown.name>
Wed, 12 Jul 2023 22:17:52 +0000 (08:17 +1000)
Multiple enhancement to make menus work as drop-downs triggered by a
mouse action.  Still some problems.

Signed-off-by: NeilBrown <neil@brown.name>
DOC/TODO.md
lib-input.c
lib-menu.c
lib-popup.c
lib-renderline.c
mode-emacs.c
python/lib-autospell.py
render-lines.c

index ddbcee1681f6bd85b30797e4169f9a40c6c91cb2..4a670a541e9c1a179605d6478fc5ed5e6f842354 100644 (file)
@@ -176,6 +176,12 @@ Core features
 Module features
 ---------------
 
+### lib-askpass
+
+- [ ] New module which server can use to ask for a password.
+      When an external program is run, we pass SSH_ASKPASS=client
+      and SSH_ASKPASS_REQUIRE=force.
+
 ### lib-linecount
 
 - [X] when used in 'view' mode, stack the counting pane with all the
@@ -803,6 +809,12 @@ Module features
 - [X] popup menu to which we can add entries, pop it at a location, get
       a selection
 - [ ] menu-bar to which we can add menus from which commands are sent
+- [ ] track movement so entry under cursor can be highlighted
+- [ ] support positioning above the target is no space below.
+- [ ] support single-click to open modal menu.??
+- [ ] improve hack for communicating action position.  Maybe require
+      an 'is-button' attribute and report button height (and width?)
+      in num and num2.
 
 ### lib-url
 
index f7707cf199fe5857b091f93b6a5cd87e62dbca1b..be774660a8bec441cb9a9ca2f6471fe580735785 100644 (file)
@@ -411,6 +411,15 @@ DEF_CMD(mouse_event)
        return Efalse;
 }
 
+DEF_CMD(mouse_grab)
+{
+       struct input_mode *im = ci->home->data;
+
+       im->grab = ci->focus;
+       pane_add_notify(ci->home, ci->focus, "Notify:Close");
+       return 1;
+}
+
 DEF_CMD(refocus)
 {
        struct input_mode *im = ci->home->data;
@@ -457,6 +466,7 @@ static void register_map(void)
        im_map = key_alloc();
        key_add(im_map, "Keystroke", &keystroke);
        key_add(im_map, "Mouse-event", &mouse_event);
+       key_add(im_map, "Mouse-grab", &mouse_grab);
        key_add(im_map, "Mode:set-mode", &set_mode);
        key_add(im_map, "Mode:set-num", &set_num);
        key_add(im_map, "Mode:set-num2", &set_num2);
index 3d7f33fd2ff4fb21784f4957e6e01c604d239f00..4961383578339c7ad17580b34b6e443b45051709 100644 (file)
@@ -76,13 +76,13 @@ DEF_LOOKUP_CMD(menu_handle, menu_map);
 DEF_CMD(menu_attach)
 {
        struct pane *docp, *p, *p2;
-       /* Multi-line popup with x,y location provided. */
-       const char *mode = "Mx";
+       /* Multi-line temporary popup with x,y location provided. */
+       const char *mode = "Mtx";
        const char *mmode = ci->str ?: "";
 
        if (strchr(mmode, 'D'))
                /* per-display, not per-pane */
-               mode = "DMx";
+               mode = "DMtx";
 
        docp = call_ret(pane, "attach-doc-list", ci->focus);
        if (!docp)
@@ -108,6 +108,7 @@ DEF_CMD(menu_attach)
        p2 = pane_register(p2, 0, &menu_handle.c);
        if (!p2)
                return Efail;
+       call("Mouse-grab", p2);
        return comm_call(ci->comm2, "cb:attach", p2);
 }
 
index e2f91308dde4508134b36ec3a16f56c7545731fb..793651de0ac347b287fe5a16546c46403d3ebbc2 100644 (file)
@@ -508,6 +508,7 @@ DEF_CMD(popup_attach)
        struct popup_info *ppi;
        const char *style = ci->str;
        char *in_popup;
+       struct xy xy;
        int z = 1;
 
        if (!style)
@@ -549,7 +550,8 @@ DEF_CMD(popup_attach)
                return Efail;
        ppi->style = strdup(style);
        popup_set_style(p);
-       popup_resize(p, style, ci->x, ci->y);
+       xy = pane_mapxy(ci->focus, root, ci->x, ci->y, True);
+       popup_resize(p, style, xy.x, xy.y);
        attr_set_str(&p->attrs, "render-wrap", "no");
 
        pane_add_notify(p, ppi->target, "Notify:Close");
index 18ecc7ff72ae8e7b401b98e8f88237e801b0ebee..39a069ee9136c40c10b8094355d84b7ea9a3d9bb 100644 (file)
@@ -189,7 +189,7 @@ static char *get_last_attr(const char *attrs safe, const char *attr safe)
 
 static int flush_line(struct pane *p safe, struct pane *focus safe, int dodraw,
                      struct render_list **rlp safe,
-                     int y, int scale, int wrap_pos, int *wrap_margin safe,
+                     int y, int ascent, int scale, int wrap_pos, int *wrap_margin safe,
                      int *wrap_prefix_sizep,
                      const char **xypos, const char **xyattr, const char **cursattr)
 {
@@ -248,13 +248,27 @@ static int flush_line(struct pane *p safe, struct pane *focus safe, int dodraw,
                if (dodraw)
                        home_call(focus, "Draw:text", p, cp, NULL, rl->text,
                                  scale, NULL, rl->attr,
-                                 x, y);
-               x += rl->width;
+                                 x, y + ascent);
                if (xypos && rl->xypos) {
                        *xypos = rl->xypos;
-                       if (xyattr)
-                               *xyattr = strsave(p, rl->attr);
+                       if (xyattr) {
+                               /* FIXME this is a bit of a hack.
+                                * We still the x,y co-ords of the start
+                                * of the current attr in front of the
+                                * attrs so render-lines can provide a
+                                * good location for a menu
+                                */
+                               char buf[100];
+                               struct render_list *rl2;
+                               int ax = 0;
+                               for (rl2 = *rlp; rl2 != rl; rl2 = rl2->next)
+                                       if (strcmp(rl2->attr, rl->attr) == 0)
+                                               ax = rl2->x;
+                               snprintf(buf, sizeof(buf), "%dx%d,", ax, y);
+                               *xyattr = strconcat(p, buf, rl->attr);
+                       }
                }
+               x += rl->width;
                if (cp >= 0 && cursattr)
                        *cursattr = strsave(p, rl->attr);
        }
@@ -267,7 +281,7 @@ static int flush_line(struct pane *p safe, struct pane *focus safe, int dodraw,
                if (cp >= 0 && dodraw)
                        home_call(focus, "Draw:text", p, cp, NULL, rl->text,
                                  scale, NULL, rl->attr,
-                                 rl->x, y);
+                                 rl->x, y + ascent);
                x = rl->x + rl->width;
        }
        /* Draw the wrap-tail */
@@ -275,7 +289,7 @@ static int flush_line(struct pane *p safe, struct pane *focus safe, int dodraw,
                char *e = get_last_attr(last_rl->attr, "wrap-tail");
                home_call(focus, "Draw:text", p, -1, NULL, e ?: "\\",
                          scale, NULL, "underline,fg:blue",
-                         wrap_pos, y);
+                         wrap_pos, y + ascent);
                free(e);
        }
 
@@ -803,7 +817,7 @@ DEF_CMD(renderline)
                        if (wrap && *line && *line != '\n') {
                                int wrap_prefix_size;
                                int len = flush_line(p, focus, dodraw, &rlst,
-                                                    y+ascent, scale,
+                                                    yascent, scale,
                                                     p->w - mwidth, &wrap_margin,
                                                     &wrap_prefix_size,
                                                     &xypos, &xyattr, &cursattr);
@@ -949,7 +963,7 @@ DEF_CMD(renderline)
                        break;
                case '\n':
                        xypos = line-1;
-                       flush_line(p, focus, dodraw, &rlst, y+ascent, scale, 0,
+                       flush_line(p, focus, dodraw, &rlst, yascent, scale, 0,
                                   &wrap_margin, NULL, &xypos, &xyattr, &cursattr);
                        y += line_height;
                        x = 0;
@@ -1020,7 +1034,7 @@ DEF_CMD(renderline)
                          posx, scale);
        }
 
-       flush_line(p, focus, dodraw, &rlst, y+ascent, scale, 0,
+       flush_line(p, focus, dodraw, &rlst, yascent, scale, 0,
                   &wrap_margin, NULL,  &xypos, &xyattr, &cursattr);
 
        if (want_xypos == 1) {
index 29e12a7c869f6df2bc71b8b5360f54f6b0645596..ea47364a92974bdd9bd8994a11053728cfb8fa7a 100644 (file)
@@ -2536,6 +2536,24 @@ DEF_CMD(emacs_release)
        return 1;
 }
 
+DEF_CMD(emacs_menu_open)
+{
+       /* If there is a menu action here, activate it. */
+
+       return call("Move-CursorXY", ci->focus, 3, ci->mark, "menu",
+                   0, NULL, NULL, ci->x, ci->y);
+}
+
+DEF_CMD(emacs_menu_select)
+{
+       /* If a menu was opened it should have claimed the mouse focus
+        * so ci->focus is now the menu.  We want to activate the entry
+        * under the mouse
+        */
+       return call("Move-CursorXY", ci->focus, 3, ci->mark, "activate",
+                   0, NULL, NULL, ci->x, ci->y);
+}
+
 DEF_CMD(emacs_motion)
 {
        struct mark *p = call_ret(mark, "doc:point", ci->focus);
@@ -3347,6 +3365,8 @@ static void emacs_init(void)
 
        key_add(m, "M:Press-1", &emacs_press);
        key_add(m, "M:Release-1", &emacs_release);
+       key_add(m, "M:Press-3", &emacs_menu_open);
+       key_add(m, "M:Release-3", &emacs_menu_select);
        key_add(m, "M:DPress-1", &emacs_press);
        key_add(m, "M:Click-2", &emacs_paste);
        key_add(m, "M:C:Click-1", &emacs_paste);
index 6161862e458cdc51f472551dcbaf2e9492c983cd..e356455752d39d8700e07c5bae323bbaf382f297 100644 (file)
@@ -227,9 +227,44 @@ class autospell_view(edlib.Pane):
         if not str1 or not mark or not comm2:
             return edlib.Efallthrough
         if str1 == "render:spell-incorrect":
-            comm2("cb", focus, int(str2), mark, "fg:red-80,underline", 120)
+            comm2("cb", focus, int(str2), mark, "fg:red-80,underline,action-menu:autospell-menu", 120)
         return edlib.Efallthrough
 
+    def handle_click(self, key, focus, mark, xy, str1, **a):
+        "handle:autospell-menu"
+        mp = self.call("attach-menu", "", "autospell-choice", xy, ret='pane')
+        self.wordend = mark.dup()
+        st = mark.dup()
+        w = focus.call("Spell:ThisWord", focus, mark, st, ret='str')
+        self.thisword = w
+        mp.call("menu-add", "+", "[Insert in dict]")
+        mp.call("menu-add", "!", "[Accept for now]")
+        focus.call("Spell:Suggest", w, lambda key, str1, **a: mp.call("menu-add", str1))
+        mp.call("doc:file", -1)
+        self.menu = mp
+        self.add_notify(mp, "Notify:Close")
+        return 1
+
+    def handle_choice(self, key, focus, mark, str1, **a):
+        "handle:autospell-choice"
+        if not str1:
+            return None
+        m = self.wordend
+        self.wordend = None
+        st = m.dup()
+        w = focus.call("Spell:ThisWord", m, st, ret='str')
+        if str1 == "+":
+            focus.call("Spell:AddWord", 1, self.thisword)
+            focus.call("spell:dict-changed", st, m)
+            return 1
+        if str1 == '!':
+            focus.call("Spell:AddWord", 0, self.thisword)
+            focus.call("spell:dict-changed", st, m)
+            return 1
+        if w == self.thisword:
+            focus.call("doc:replace", st, m, str1)
+        return 1
+
     def handle_recheck(self, key, **a):
         "handle:spell:recheck"
         self.sched()
index 3a540cae64ff0e560c5ad269c0c7978f7a0deabe..90ac61b1430aae931ca42b027bffbe35b7a893a6 100644 (file)
@@ -1519,9 +1519,23 @@ DEF_CMD(render_lines_set_cursor)
                if (action) {
                        xyattr = pane_attr_get(m->mdata, "xyattr");
                        tag = get_action_tag(action, xyattr);
-                       if (tag)
+                       if (tag) {
+                               int x, y;
+                               /* This is a hack to get the start of these
+                                * attrs so menu can be placed correctly.
+                                * Only works for menus below the line.
+                                */
+                               if (sscanf(xyattr, "%dx%d,", &x, &y) == 2) {
+                                       cih.x = x;
+                                       cih.y = m->mdata->y + y +
+                                               attr_find_int(m->mdata->attrs,
+                                                             "line-height");
+;
+                               }
                                call(tag, focus, 0, m2, xyattr,
-                                    0, ci->mark);
+                                    0, ci->mark, NULL,
+                                    cih.x, cih.y);
+                       }
                }
                m = m2;
        } else {