From: NeilBrown Date: Mon, 10 Jul 2023 05:17:44 +0000 (+1000) Subject: autospell: provide drop-down menu on spelling errors. X-Git-Url: http://git.neil.brown.name/?a=commitdiff_plain;h=1082236c4d618eb5a0411c05ae769bceb4746417;p=edlib.git autospell: provide drop-down menu on spelling errors. Multiple enhancement to make menus work as drop-downs triggered by a mouse action. Still some problems. Signed-off-by: NeilBrown --- diff --git a/DOC/TODO.md b/DOC/TODO.md index ddbcee16..4a670a54 100644 --- a/DOC/TODO.md +++ b/DOC/TODO.md @@ -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 diff --git a/lib-input.c b/lib-input.c index f7707cf1..be774660 100644 --- a/lib-input.c +++ b/lib-input.c @@ -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); diff --git a/lib-menu.c b/lib-menu.c index 3d7f33fd..49613835 100644 --- a/lib-menu.c +++ b/lib-menu.c @@ -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); } diff --git a/lib-popup.c b/lib-popup.c index e2f91308..793651de 100644 --- a/lib-popup.c +++ b/lib-popup.c @@ -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"); diff --git a/lib-renderline.c b/lib-renderline.c index 18ecc7ff..39a069ee 100644 --- a/lib-renderline.c +++ b/lib-renderline.c @@ -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, + y, ascent, 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, y, ascent, 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, y, ascent, scale, 0, &wrap_margin, NULL, &xypos, &xyattr, &cursattr); if (want_xypos == 1) { diff --git a/mode-emacs.c b/mode-emacs.c index 29e12a7c..ea47364a 100644 --- a/mode-emacs.c +++ b/mode-emacs.c @@ -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); diff --git a/python/lib-autospell.py b/python/lib-autospell.py index 6161862e..e3564557 100644 --- a/python/lib-autospell.py +++ b/python/lib-autospell.py @@ -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() diff --git a/render-lines.c b/render-lines.c index 3a540cae..90ac61b1 100644 --- a/render-lines.c +++ b/render-lines.c @@ -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 {