From 41e92d1ba5795e9b1de9096a3c8781776a4c02e8 Mon Sep 17 00:00:00 2001 From: Saul Pwanson Date: Thu, 26 Oct 2023 22:46:55 -0700 Subject: [PATCH] [help] readd input.ddw help, move to sidebar #2085 --- visidata/_input.py | 23 ++---------- visidata/ddw/input.ddw | 80 ++++++++++++++++++++++++++++++++++++++++++ visidata/ddwplay.py | 6 ++-- visidata/help.py | 28 +++++++++------ visidata/sidebar.py | 58 ++++++++++++++++++------------ 5 files changed, 139 insertions(+), 56 deletions(-) create mode 100644 visidata/ddw/input.ddw diff --git a/visidata/_input.py b/visidata/_input.py index cb6552501..aac81c597 100644 --- a/visidata/_input.py +++ b/visidata/_input.py @@ -7,23 +7,6 @@ from visidata import vd, options, colors, dispwidth, ColorAttr from visidata import AttrDict -help_input = '''# Input Help -Start typing to overwrite the starting value. -(To edit the starting value, press a movement key first.) - -- `Ctrl+O` to open in external $EDITOR -- `Ctrl+Y` to insert clipboard text at cursor -- `Ctrl+R` to restore the starting value -- `Tab`/`Shift+Tab` to next/prev autocomplete -- `↓`/`↑` next/prev input history - -- `Ctrl+C` or `Esc` to cancel input. -- `Enter` to accept input. -- `Ctrl+G` to toggle this help sidebar - -Other standard readline keybindings apply. -[:onclick https://visidata.org/help/input]🛈 for all keybindings at visidata.org[:] -''' vd.theme_option('color_edit_unfocused', '238 on 110', 'display color for unfocused input in form') vd.theme_option('color_edit_cell', '233 on 110', 'cell color to use when editing cell') @@ -244,9 +227,9 @@ def find_nonword(s, a, b, incr): if disp_help > 0: sheet = vd.activeSheet if disp_help > 1: - help = help_input - if disp_help > 0 and help: - sheet.drawSidebarText(scr, help) + help = vd.getHelpPane('input', module='visidata') + if help: + sheet.drawSidebarText(scr, help, title="Input Help") if display: dispval = clean_printable(v) diff --git a/visidata/ddw/input.ddw b/visidata/ddw/input.ddw new file mode 100644 index 000000000..90ab3a1f6 --- /dev/null +++ b/visidata/ddw/input.ddw @@ -0,0 +1,80 @@ +{"id": null, "type": null, "x": 6, "y": 6, "text": "\u2190", "color": "keystrokes", "tags": [], "group": "", "frame": null, "rows": null, "duration_ms": null, "ref": null, "onclick": null} +{"x": 11, "y": 6, "text": "go to prev/next char", "color": "", "tags": [], "group": ""} +{"x": 3, "y": 4, "text": "Ctrl", "color": "keystrokes", "tags": [], "group": ""} +{"x": 11, "y": 4, "text": "cancel input (abort)", "color": "", "tags": [], "group": ""} +{"x": 45, "y": 4, "text": "delete one char at cursor", "color": "", "tags": [], "group": ""} +{"x": 45, "y": 5, "text": "delete one char before cursor", "color": "", "tags": [], "group": ""} +{"x": 0, "y": 13, "text": "Shift", "color": "keystrokes", "tags": [], "group": ""} +{"x": 4, "y": 3, "text": "Enter", "color": "keystrokes", "tags": [], "group": ""} +{"x": 11, "y": 3, "text": "accept input", "color": "", "tags": [], "group": ""} +{"x": 42, "y": 6, "text": "K", "color": "keystrokes", "tags": [], "group": ""} +{"x": 45, "y": 6, "text": "delete all before/after cursor", "color": "", "tags": [], "group": ""} +{"x": 42, "y": 12, "text": "O", "color": "keystrokes", "tags": [], "group": ""} +{"x": 45, "y": 12, "text": "open input in external editor", "color": "", "tags": [], "group": ""} +{"x": 42, "y": 13, "text": "R", "color": "keystrokes", "tags": [], "group": ""} +{"x": 45, "y": 13, "text": "restore starting value", "color": "", "tags": [], "group": ""} +{"x": 42, "y": 8, "text": "T", "color": "keystrokes", "tags": [], "group": ""} +{"x": 45, "y": 8, "text": "swap last two chars", "color": "", "tags": [], "group": ""} +{"x": 40, "y": 6, "text": "U", "color": "keystrokes", "tags": [], "group": ""} +{"x": 42, "y": 9, "text": "V", "color": "keystrokes", "tags": [], "group": ""} +{"x": 45, "y": 9, "text": "insert literal char", "color": "", "tags": [], "group": ""} +{"x": 42, "y": 7, "text": "W", "color": "keystrokes", "tags": [], "group": ""} +{"x": 45, "y": 7, "text": "delete one word before cursor", "color": "", "tags": [], "group": ""} +{"x": 42, "y": 11, "text": "Y", "color": "keystrokes", "tags": [], "group": ""} +{"x": 45, "y": 11, "text": "insert clipboard text at cursor", "color": "", "tags": [], "group": ""} +{"x": 6, "y": 7, "text": "\u2190", "color": "keystrokes", "tags": [], "group": ""} +{"x": 11, "y": 7, "text": "go to prev/next word", "color": "", "tags": [], "group": ""} +{"x": 0, "y": 0, "text": "Begin typing to overwrite the starting value.", "color": "", "tags": [], "group": ""} +{"x": 0, "y": 1, "text": "(To edit the starting value, press a movement key first.)", "color": "", "tags": [], "group": ""} +{"x": 6, "y": 10, "text": "\u2191", "color": "keystrokes", "tags": [], "group": ""} +{"x": 1, "y": 8, "text": "Home", "color": "keystrokes", "tags": [], "group": ""} +{"x": 11, "y": 8, "text": "go to start/end", "color": "", "tags": [], "group": ""} +{"x": 6, "y": 8, "text": "End", "color": "keystrokes", "tags": [], "group": ""} +{"x": 8, "y": 6, "text": "\u2192", "color": "keystrokes", "tags": [], "group": ""} +{"x": 37, "y": 4, "text": "Delete", "color": "keystrokes", "tags": [], "group": ""} +{"x": 34, "y": 5, "text": "Backspace", "color": "keystrokes", "tags": [], "group": ""} +{"x": 37, "y": 3, "text": "Insert", "color": "keystrokes", "tags": [], "group": ""} +{"x": 45, "y": 3, "text": "toggle insert mode", "color": "", "tags": [], "group": ""} +{"x": 6, "y": 12, "text": "Tab", "color": "keystrokes", "tags": [], "group": ""} +{"x": 1, "y": 7, "text": "Ctrl", "color": "keystrokes", "tags": [], "group": ""} +{"x": 35, "y": 6, "text": "Ctrl", "color": "keystrokes", "tags": [], "group": ""} +{"x": 37, "y": 8, "text": "Ctrl", "color": "keystrokes", "tags": [], "group": ""} +{"x": 37, "y": 7, "text": "Ctrl", "color": "keystrokes", "tags": [], "group": ""} +{"x": 37, "y": 9, "text": "Ctrl", "color": "keystrokes", "tags": [], "group": ""} +{"x": 37, "y": 11, "text": "Ctrl", "color": "keystrokes", "tags": [], "group": ""} +{"x": 37, "y": 12, "text": "Ctrl", "color": "keystrokes", "tags": [], "group": ""} +{"x": 37, "y": 13, "text": "Ctrl", "color": "keystrokes", "tags": [], "group": ""} +{"x": 5, "y": 7, "text": "+", "color": "keystrokes", "tags": [], "group": ""} +{"x": 39, "y": 6, "text": "+", "color": "keystrokes", "tags": [], "group": ""} +{"x": 41, "y": 7, "text": "+", "color": "keystrokes", "tags": [], "group": ""} +{"x": 41, "y": 8, "text": "+", "color": "keystrokes", "tags": [], "group": ""} +{"x": 41, "y": 9, "text": "+", "color": "keystrokes", "tags": [], "group": ""} +{"x": 41, "y": 11, "text": "+", "color": "keystrokes", "tags": [], "group": ""} +{"x": 41, "y": 12, "text": "+", "color": "keystrokes", "tags": [], "group": ""} +{"x": 41, "y": 13, "text": "+", "color": "keystrokes", "tags": [], "group": ""} +{"x": 11, "y": 10, "text": "prev/next in history", "color": "", "tags": [], "group": ""} +{"x": 11, "y": 12, "text": "next autocomplete", "color": "", "tags": [], "group": ""} +{"x": 11, "y": 13, "text": "prev autocomplete", "color": "", "tags": [], "group": ""} +{"x": 7, "y": 4, "text": "+", "color": "keystrokes", "tags": [], "group": ""} +{"x": 8, "y": 4, "text": "C", "color": "keystrokes", "tags": [], "group": ""} +{"x": 5, "y": 13, "text": "+", "color": "keystrokes", "tags": [], "group": ""} +{"x": 6, "y": 13, "text": "Tab", "color": "keystrokes", "tags": [], "group": ""} +{"x": 32, "y": 3, "text": "\u2502", "color": "", "tags": [], "group": ""} +{"x": 32, "y": 4, "text": "\u2502", "color": "", "tags": [], "group": ""} +{"x": 32, "y": 5, "text": "\u2502", "color": "", "tags": [], "group": ""} +{"x": 32, "y": 6, "text": "\u2502", "color": "", "tags": [], "group": ""} +{"x": 32, "y": 7, "text": "\u2502", "color": "", "tags": [], "group": ""} +{"x": 32, "y": 8, "text": "\u2502", "color": "", "tags": [], "group": ""} +{"x": 32, "y": 9, "text": "\u2502", "color": "", "tags": [], "group": ""} +{"x": 32, "y": 10, "text": "\u2502", "color": "", "tags": [], "group": ""} +{"x": 32, "y": 11, "text": "\u2502", "color": "", "tags": [], "group": ""} +{"x": 32, "y": 12, "text": "\u2502", "color": "", "tags": [], "group": ""} +{"x": 32, "y": 13, "text": "\u2502", "color": "", "tags": [], "group": ""} +{"x": 7, "y": 6, "text": "/", "color": "", "tags": [], "group": ""} +{"x": 5, "y": 8, "text": "/", "color": "", "tags": [], "group": ""} +{"x": 8, "y": 7, "text": "\u2192", "color": "keystrokes", "tags": [], "group": ""} +{"x": 8, "y": 10, "text": "\u2193", "color": "keystrokes", "tags": [], "group": ""} +{"x": 41, "y": 6, "text": "/", "color": "", "tags": [], "group": ""} +{"x": 7, "y": 10, "text": "/", "color": "", "tags": [], "group": ""} +{"x": 7, "y": 7, "text": "/", "color": "", "tags": [], "group": ""} +{"x": 75, "y": 0, "text": "?", "color": "bold 117 cyan", "tags": [], "group": "", "onclick": "https://visidata.org/input"} diff --git a/visidata/ddwplay.py b/visidata/ddwplay.py index 169f0d82a..24cc6bcec 100644 --- a/visidata/ddwplay.py +++ b/visidata/ddwplay.py @@ -1,7 +1,7 @@ from collections import defaultdict import json import time -from visidata import colors, vd, clipdraw +from visidata import colors, vd, clipdraw, ColorAttr __all__ = ['Animation', 'AnimationMgr'] @@ -66,9 +66,9 @@ def load_from(self, fp): self.width = max(self.width, x+len(r.text)) self.height = max(self.height, y) - def draw(self, scr, *, t=0, x=0, y=0, loop=False, **kwargs): + def draw(self, scr, *, t=0, x=0, y=0, loop=False, attr=ColorAttr(), **kwargs): for r, dx, dy, _ in self.iterdeep(self.frames[''].rows): - clipdraw(scr, y+dy, x+dx, r.text, colors[r.color]) + clipdraw(scr, y+dy, x+dx, r.text, attr.update(colors[r.color], 2)) if not self.total_ms: return None diff --git a/visidata/help.py b/visidata/help.py index 7e71435b0..c33ad74a3 100644 --- a/visidata/help.py +++ b/visidata/help.py @@ -4,7 +4,7 @@ from visidata import VisiData, MetaSheet, ColumnAttr, Column, BaseSheet, VisiDataMetaSheet, SuspendCurses from visidata import vd, asyncthread, ENTER -vd.option('disp_help', 1, '-1=quiet, 0=no help, 1-4=some-all help, 5-10=remove features') +vd.option('disp_help', 0, 'show help panel during input') @VisiData.api @@ -81,14 +81,22 @@ def __init__(self, name): self.parentscr = None self.amgr = visidata.AnimationMgr() - def draw(self, scr, x=None, y=None): + @property + def width(self): + return self.amgr.maxWidth + + @property + def height(self): + return self.amgr.maxHeight + + def draw(self, scr, x=None, y=None, **kwargs): if not scr: return - if vd.options.disp_help < 2: - if self.scr: - self.scr.erase() - self.scr.refresh() - self.scr = None - return +# if vd.options.disp_help <= 0: +# if self.scr: +# self.scr.erase() +# self.scr.refresh() +# self.scr = None +# return if y is None: y=0 # show at top of screen by default if x is None: x=0 hneeded = self.amgr.maxHeight+3 @@ -118,13 +126,13 @@ def draw(self, scr, x=None, y=None): self.scr.erase() self.scr.box() - self.amgr.draw(self.scr, y=1, x=2) + self.amgr.draw(self.scr, y=1, x=2, **kwargs) self.scr.refresh() @VisiData.api @functools.lru_cache(maxsize=None) -def getHelpPane(vd, name, module='vdplus'): +def getHelpPane(vd, name, module='visidata') -> HelpPane: ret = HelpPane(name) try: ret.amgr.load(name, (vd.pkg_resources_files(module)/f'ddw/{name}.ddw').open(encoding='utf-8')) diff --git a/visidata/sidebar.py b/visidata/sidebar.py index b3d4b1ead..72b52af35 100644 --- a/visidata/sidebar.py +++ b/visidata/sidebar.py @@ -1,4 +1,4 @@ -from typing import Optional +from typing import Optional, Union import textwrap from visidata import vd, VisiData, BaseSheet, colors, TextSheet, clipdraw, wraptext, dispwidth @@ -42,33 +42,42 @@ def drawSidebar(vd, scr, sheet): return sheet.drawSidebarText(scr, text=sidebar, overflowmsg=overflowmsg, bottommsg=bottommsg) @BaseSheet.api -def drawSidebarText(sheet, scr, text:Optional[str], title:str='', overflowmsg:str='', bottommsg:str=''): +def drawSidebarText(sheet, scr, text:Union[None,str,'HelpPane'], title:str='', overflowmsg:str='', bottommsg:str=''): scrh, scrw = scr.getmaxyx() maxw = sheet.options.disp_sidebar_width or scrw//2 maxh = sheet.options.disp_sidebar_height or scrh-2 + cattr = colors.get_color('color_sidebar') + text = text or '' - text = textwrap.dedent(text.strip('\n')) - if not text: - return + if hasattr(text, 'draw'): # like a HelpPane + maxlinew = text.width + winh = min(maxh, text.height+2)+1 + else: + text = textwrap.dedent(text.strip('\n')) + + if not text: + return + + lines = text.splitlines() + if not title and lines and lines[0].strip().startswith('# '): + title = lines[0][1:].strip() + text = '\n'.join(lines[1:]) - lines = text.splitlines() - if not title and lines and lines[0].strip().startswith('# '): - title = lines[0][1:].strip() - text = '\n'.join(lines[1:]) + + lines = list(wraptext(text, width=maxw-4)) + maxlinew = 0 + if lines: + maxlinew = max(maxlinew, max(dispwidth(textonly, maxwidth=maxw) for line, textonly in lines)) + winh = min(maxh, len(lines)+2) titlew = dispwidth(title) - cattr = colors.get_color('color_sidebar') - lines = list(wraptext(text, width=maxw-4)) - maxlinew = titlew - if lines: - maxlinew = max(titlew, max(dispwidth(textonly, maxwidth=maxw) for line, textonly in lines)) maxlinew = max(maxlinew, dispwidth(overflowmsg)+4) maxlinew = max(maxlinew, dispwidth(bottommsg)+4) + maxlinew = max(maxlinew, titlew) winw = min(maxw, maxlinew+4) - winh = min(maxh, len(lines)+2) x, y, w, h = scrw-winw-1, scrh-winh-1, winw, winh sidebarscr = vd.subwindow(scr, x, y, w, h) @@ -78,14 +87,17 @@ def drawSidebarText(sheet, scr, text:Optional[str], title:str='', overflowmsg:st sidebarscr.border() vd.onMouse(sidebarscr, 0, 0, w, h, BUTTON1_RELEASED='no-op', BUTTON1_PRESSED='no-op') - i = 0 - for line, _ in lines: - if i >= h-2: - bottommsg = overflowmsg - break - - x += clipdraw(sidebarscr, i+1, 2, line, cattr, w=w-3) - i += 1 + if hasattr(text, 'draw'): # like a HelpPane + text.draw(sidebarscr, attr=cattr) + else: + i = 0 + for line, _ in lines: + if i >= h-2: + bottommsg = overflowmsg + break + + x += clipdraw(sidebarscr, i+1, 2, line, cattr, w=w-3) + i += 1 x = max(0, w-titlew-6) clipdraw(sidebarscr, 0, x, f"|[:black on yellow] {title} [:]|", cattr, w=titlew+4)