v2->v3: - Rebase on master - Fix applying underline styles in templates (mpldr) - Update vaxis (term perf improvements, minor bugfixes) v1->v2: - Resend and hope the cover letter makes it this time - Add relevant commit trailers Hey everyone - This series supersedes the previous tcell replacement series I sent. I restarted the versioning because this series _fully_ replaces tcell by the last commit. This series replaces tcell with Vaxis, a library I wrote which has some benefits over tcell: - More performant - Doesn't rely on built in terminfo - Doesn't rely on system terminfo - Uses "standard" VT parser - More features out of the box - Developed with email based workflow :) - Images with fallback rendering where appropriate (support for kitty and sixel, with cell based renderers as fallbacks) - Kitty keyboard support (currently disabled in aerc) Overall with this series you should expect to see very few changes, with the exception of the image feature added. At some point in the future I expect that we'll turn on the kitty keyboard feature, however I chose not to introduce too many changes in this series. Tim Culverhouse (17): parse/ansi: remove tcell/terminfo dependency ui: so long tcell ui: create and expose vaxis Window with Context msgviewer: implement inline image viewing aerc: change event interfaces to vaxis events ui: remove screen and viewports ui: initialize vaxis directly, drop tcell.Screen initialization fill: replace tcell.Style with vaxis.Style aerc: replace tcell keys with vaxis keys style: use vaxis style everywhere terminal: replace tcell-term with vaxis terminal paste: use vaxis paste events mouse: use vaxis mouse events aerc: remove tcell import aerc: set title using vaxis docs: update docs to remove tcell reference vaxis: update to v0.5.1 app/account-wizard.go | 27 +-- app/account.go | 5 +- app/aerc.go | 78 ++++--- app/authinfo.go | 4 +- app/compose.go | 20 +- app/dirlist.go | 25 +-- app/dirtree.go | 14 +- app/exline.go | 17 +- app/getpasswd.go | 13 +- app/listbox.go | 36 +-- app/msglist.go | 18 +- app/msgviewer.go | 108 ++++++++- app/partswitcher.go | 18 +- app/pgpinfo.go | 4 +- app/selector.go | 28 +-- app/spinner.go | 5 +- app/status.go | 8 +- app/terminal.go | 73 +++---- commands/patch/list.go | 4 +- commands/send-keys.go | 7 +- commands/util.go | 4 +- config/binds.go | 458 +++++++++++++++++++-------------------- config/binds_test.go | 36 +-- config/style.go | 233 +++++++++++++++++--- config/ui.go | 16 +- doc/aerc-stylesets.7.scd | 3 +- go.mod | 20 +- go.sum | 39 ++-- lib/parse/ansi.go | 252 ++++++++++----------- lib/parse/ansi_test.go | 60 ++--- lib/ui/borders.go | 5 +- lib/ui/box.go | 6 +- lib/ui/context.go | 94 ++++---- lib/ui/fill.go | 8 +- lib/ui/grid.go | 6 +- lib/ui/interfaces.go | 8 +- lib/ui/popover.go | 4 +- lib/ui/stack.go | 5 +- lib/ui/tab.go | 18 +- lib/ui/table.go | 18 +- lib/ui/text.go | 6 +- lib/ui/textinput.go | 62 +++--- lib/ui/ui.go | 81 +++---- main.go | 32 --- 44 files changed, 1103 insertions(+), 883 deletions(-) -- 2.43.0
aerc/patches: SUCCESS in 5m0s [Replace tcell][0] v3 from [Tim Culverhouse][1] [0]: https://lists.sr.ht/~rjarry/aerc-devel/patches/48376 [1]: mailto:tim@timculverhouse.com ✓ #1129000 SUCCESS aerc/patches/alpine-edge.yml https://builds.sr.ht/~rjarry/job/1129000 ✓ #1129001 SUCCESS aerc/patches/openbsd.yml https://builds.sr.ht/~rjarry/job/1129001
Copy & paste the following snippet into your terminal to import this patchset into git:
curl -s https://lists.sr.ht/~rjarry/aerc-devel/patches/48376/mbox | git am -3Learn more about email & git
The parse library builds an ansi-escaped string based on a buffer of styled cells. Use constants which aerc will still parse properly (and are the same as the terminfo package was pulling in) to remove dependency on tcell/terminfo. Additionally, we can use the internal go "fmt" package to write strings instead of the terminfo.TParm method (which is much slower at formatting strings). Signed-off-by: Tim Culverhouse <tim@timculverhouse.com> --- lib/parse/ansi.go | 78 +++++++++++++++++++++--------------------- lib/parse/ansi_test.go | 60 ++++++++++++++++---------------- 2 files changed, 69 insertions(+), 69 deletions(-) diff --git a/lib/parse/ansi.go b/lib/parse/ansi.go index ff44f8ba5c51..3a802a51f78c 100644 --- a/lib/parse/ansi.go +++ b/lib/parse/ansi.go @@ -13,12 +13,28 @@ import ( "git.sr.ht/~rjarry/aerc/log" "github.com/gdamore/tcell/v2" - "github.com/gdamore/tcell/v2/terminfo" "github.com/mattn/go-runewidth" ) var AnsiReg = regexp.MustCompile("\x1B\\[[0-?]*[ -/]*[@-~]") +const ( + setfgbgrgb = "\x1b[38;2;%d;%d;%d;48;2;%d;%d;%dm" + setfgrgb = "\x1b[38;2;%d;%d;%dm" + setbgrgb = "\x1b[48;2;%d;%d;%dm" + setfgbg = "\x1b[38;5;%d;48;5;%dm" + setfg = "\x1b[38;5;%dm" + setbg = "\x1b[48;5;%dm" + attrOff = "\x1B[m" + bold = "\x1B[1m" + dim = "\x1B[2m" + italic = "\x1B[3m" + underline = "\x1B[4m" + blink = "\x1B[5m" + reverse = "\x1B[7m" + strikethrough = "\x1B[9m" +) + // StripAnsi strips ansi escape codes from the reader func StripAnsi(r io.Reader) io.Reader { buf := bytes.NewBuffer(nil) @@ -101,16 +117,8 @@ func (rb *RuneBuffer) String() string { // string returns a string no longer than n runes. If 'left' is true, the left // side of the text is truncated. Pass 0 to return the full string func (rb *RuneBuffer) string(n int, left bool, char rune) string { - // Use xterm-256color to generate the string. Ultimately all output will - // be re-parsed as 'xterm-256color' and tcell will handle the final - // output sequences based on the user's TERM - ti, err := terminfo.LookupTerminfo("xterm-256color") - if err != nil { - // Who knows what happened - return "" - } var ( - s = strings.Builder{} + s = bytes.NewBuffer(nil) style = tcell.StyleDefault hasStyle = false // w will track the length we have written, or would have @@ -127,60 +135,52 @@ func (rb *RuneBuffer) string(n int, left bool, char rune) string { if style != r.Style { hasStyle = true style = r.Style - s.WriteString(ti.AttrOff) + s.WriteString(attrOff) fg, bg, attrs := style.Decompose() switch { - case fg.IsRGB() && bg.IsRGB() && ti.SetFgBgRGB != "": + case fg.IsRGB() && bg.IsRGB(): fr, fg, fb := fg.RGB() br, bg, bb := bg.RGB() - s.WriteString(ti.TParm( - ti.SetFgBgRGB, - int(fr), - int(fg), - int(fb), - int(br), - int(bg), - int(bb), - )) - case fg.IsRGB() && ti.SetFgRGB != "": + fmt.Fprintf(s, setfgbgrgb, fr, fg, fb, br, bg, bb) + case fg.IsRGB(): // RGB r, g, b := fg.RGB() - s.WriteString(ti.TParm(ti.SetFgRGB, int(r), int(g), int(b))) - case bg.IsRGB() && ti.SetBgRGB != "": + fmt.Fprintf(s, setfgrgb, r, g, b) + case bg.IsRGB(): // RGB r, g, b := bg.RGB() - s.WriteString(ti.TParm(ti.SetBgRGB, int(r), int(g), int(b))) + fmt.Fprintf(s, setbgrgb, r, g, b) // Indexed - case fg.Valid() && bg.Valid() && ti.SetFgBg != "": - s.WriteString(ti.TParm(ti.SetFgBg, int(fg&0xff), int(bg&0xff))) - case fg.Valid() && ti.SetFg != "": - s.WriteString(ti.TParm(ti.SetFg, int(fg&0xff))) - case bg.Valid() && ti.SetBg != "": - s.WriteString(ti.TParm(ti.SetBg, int(bg&0xff))) + case fg.Valid() && bg.Valid(): + fmt.Fprintf(s, setfgbg, fg&0xFF, bg&0xFF) + case fg.Valid(): + fmt.Fprintf(s, setfg, fg&0xFF) + case bg.Valid(): + fmt.Fprintf(s, setbg, bg&0xFF) } if attrs&tcell.AttrBold != 0 { - s.WriteString(ti.Bold) + s.WriteString(bold) } if attrs&tcell.AttrUnderline != 0 { - s.WriteString(ti.Underline) + s.WriteString(underline) } if attrs&tcell.AttrReverse != 0 { - s.WriteString(ti.Reverse) + s.WriteString(reverse) } if attrs&tcell.AttrBlink != 0 { - s.WriteString(ti.Blink) + s.WriteString(blink) } if attrs&tcell.AttrDim != 0 { - s.WriteString(ti.Dim) + s.WriteString(dim) } if attrs&tcell.AttrItalic != 0 { - s.WriteString(ti.Italic) + s.WriteString(italic) } if attrs&tcell.AttrStrikeThrough != 0 { - s.WriteString(ti.StrikeThrough) + s.WriteString(strikethrough) } } @@ -200,7 +200,7 @@ func (rb *RuneBuffer) string(n int, left bool, char rune) string { } } if hasStyle { - s.WriteString(ti.AttrOff) + s.WriteString(attrOff) } return s.String() } diff --git a/lib/parse/ansi_test.go b/lib/parse/ansi_test.go index f916412bbee6..6aa95b185ded 100644 --- a/lib/parse/ansi_test.go +++ b/lib/parse/ansi_test.go @@ -26,49 +26,49 @@ func TestParser(t *testing.T) { { name: "bold", input: "\x1b[1mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[1mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[1mhello, world\x1b[m", expectedLen: 12, }, { name: "dim", input: "\x1b[2mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[2mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[2mhello, world\x1b[m", expectedLen: 12, }, { name: "bold and dim", input: "\x1b[1;2mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[1m\x1b[2mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[1m\x1b[2mhello, world\x1b[m", expectedLen: 12, }, { name: "italic", input: "\x1b[3mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[3mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[3mhello, world\x1b[m", expectedLen: 12, }, { name: "underline", input: "\x1b[4mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[4mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[4mhello, world\x1b[m", expectedLen: 12, }, { name: "blink", input: "\x1b[5mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[5mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[5mhello, world\x1b[m", expectedLen: 12, }, { name: "fast blink", input: "\x1b[6mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[5mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[5mhello, world\x1b[m", expectedLen: 12, }, { name: "reverse", input: "\x1b[7mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[7mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[7mhello, world\x1b[m", expectedLen: 12, }, { @@ -80,121 +80,121 @@ func TestParser(t *testing.T) { { name: "strikethrough", input: "\x1b[9mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[9mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[9mhello, world\x1b[m", expectedLen: 12, }, { name: "bold hello, normal world", input: "\x1b[1mhello, \x1b[21mworld", - expectedString: "\x1b(B\x1b[m\x1b[1mhello, \x1b(B\x1b[mworld\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[1mhello, \x1b[mworld\x1b[m", expectedLen: 12, }, { name: "bold hello, normal world v2", input: "\x1b[1mhello, \x1b[mworld", - expectedString: "\x1b(B\x1b[m\x1b[1mhello, \x1b(B\x1b[mworld\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[1mhello, \x1b[mworld\x1b[m", expectedLen: 12, }, { name: "8 bit color: foreground", input: "\x1b[30mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[30mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[38;5;0mhello, world\x1b[m", expectedLen: 12, }, { name: "8 bit color: background", input: "\x1b[41mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[41mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[48;5;1mhello, world\x1b[m", expectedLen: 12, }, { name: "8 bit color: foreground and background", input: "\x1b[31;41mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[31;41mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[38;5;1;48;5;1mhello, world\x1b[m", expectedLen: 12, }, { name: "16 bit color: foreground", input: "\x1b[90mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[90mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[38;5;8mhello, world\x1b[m", expectedLen: 12, }, { name: "16 bit color: background", input: "\x1b[101mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[101mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[48;5;9mhello, world\x1b[m", expectedLen: 12, }, { name: "16 bit color: foreground and background", input: "\x1b[91;101mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[91;101mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[38;5;9;48;5;9mhello, world\x1b[m", expectedLen: 12, }, { name: "256 color: foreground", input: "\x1b[38;5;2mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[32mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[38;5;2mhello, world\x1b[m", expectedLen: 12, }, { name: "256 color: foreground", input: "\x1b[38;5;132mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[38;5;132mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[38;5;132mhello, world\x1b[m", expectedLen: 12, }, { name: "256 color: background", input: "\x1b[48;5;132mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[48;5;132mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[48;5;132mhello, world\x1b[m", expectedLen: 12, }, { name: "256 color: foreground and background", input: "\x1b[38;5;20;48;5;20mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[38;5;20;48;5;20mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[38;5;20;48;5;20mhello, world\x1b[m", expectedLen: 12, }, { name: "256 color: background", input: "\x1b[48;5;2mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[42mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[48;5;2mhello, world\x1b[m", expectedLen: 12, }, { name: "true color: foreground", input: "\x1b[38;2;0;0;0mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[38;2;0;0;0mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[38;2;0;0;0mhello, world\x1b[m", expectedLen: 12, }, { name: "true color: foreground with color space", input: "\x1b[38;2;;0;0;0mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[38;2;0;0;0mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[38;2;0;0;0mhello, world\x1b[m", expectedLen: 12, }, { name: "true color: foreground with color space and colons", input: "\x1b[38:2::0:0:0mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[38;2;0;0;0mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[38;2;0;0;0mhello, world\x1b[m", expectedLen: 12, }, { name: "true color: background", input: "\x1b[48;2;0;0;0mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[48;2;0;0;0mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[48;2;0;0;0mhello, world\x1b[m", expectedLen: 12, }, { name: "true color: background with color space", input: "\x1b[48;2;;0;0;0mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[48;2;0;0;0mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[48;2;0;0;0mhello, world\x1b[m", expectedLen: 12, }, { name: "true color: foreground and background", input: "\x1b[38;2;200;200;200;48;2;0;0;0mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[38;2;200;200;200;48;2;0;0;0mhello, world\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[38;2;200;200;200;48;2;0;0;0mhello, world\x1b[m", expectedLen: 12, }, } @@ -222,7 +222,7 @@ func TestTruncate(t *testing.T) { { name: "bold, truncate at 5", input: "\x1b[1mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[1mhello\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[1mhello\x1b[m", }, } @@ -249,7 +249,7 @@ func TestTruncateHead(t *testing.T) { { name: "bold, truncate head at 5", input: "\x1b[1mhello, world", - expectedString: "\x1b(B\x1b[m\x1b[1mworld\x1b(B\x1b[m", + expectedString: "\x1b[m\x1b[1mworld\x1b[m", }, } -- 2.43.0
Replace tcell with vaxis. Vaxis provides several new features (none of which are included in this commit). All behavior should be exactly the same as previous, with one exception: Vaxis does not have an internal terminfo library. Some terminals will now have RGB that didn't before, as well as any other feature that was falling back to some unknown state. Changelog-changed: replaced tcell with vaxis Signed-off-by: Tim Culverhouse <tim@timculverhouse.com> --- go.mod | 18 +++++++++++------- go.sum | 46 +++++++++++++++++++++++++++++++++------------- lib/ui/ui.go | 12 +++++++++++- 3 files changed, 55 insertions(+), 21 deletions(-) diff --git a/go.mod b/go.mod index 6e88e5ec14dd..87b7ad3adce6 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( git.sr.ht/~rjarry/go-opt v1.3.0 git.sr.ht/~rockorager/go-jmap v0.3.0 git.sr.ht/~rockorager/tcell-term v0.10.0 + git.sr.ht/~rockorager/vaxis v0.4.7 github.com/ProtonMail/go-crypto v0.0.0-20230417170513-8ee5748c52b5 github.com/arran4/golang-ical v0.0.0-20230318005454-19abf92700cc github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 @@ -25,7 +26,7 @@ require ( github.com/go-ini/ini v1.67.0 github.com/lithammer/fuzzysearch v1.1.5 github.com/mattn/go-isatty v0.0.18 - github.com/mattn/go-runewidth v0.0.14 + github.com/mattn/go-runewidth v0.0.15 github.com/pkg/errors v0.9.1 github.com/rivo/uniseg v0.4.4 github.com/riywo/loginshell v0.0.0-20200815045211-7d26008be1ab @@ -33,31 +34,34 @@ require ( github.com/syndtr/goleveldb v1.0.0 github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e golang.org/x/oauth2 v0.7.0 - golang.org/x/sys v0.7.0 + golang.org/x/sys v0.13.0 golang.org/x/tools v0.6.0 ) require ( github.com/cloudflare/circl v1.3.2 // indirect + github.com/containerd/console v1.0.3 // indirect github.com/creack/pty v1.1.18 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594 // indirect - github.com/gdamore/encoding v1.0.0 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/kr/pretty v0.3.0 // indirect - github.com/lucasb-eyer/go-colorful v1.2.0 // indirect + github.com/mattn/go-sixel v0.0.5 // indirect github.com/onsi/gomega v1.20.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rogpeppe/go-internal v1.8.1 // indirect - golang.org/x/crypto v0.8.0 // indirect + github.com/soniakeys/quant v1.0.0 // indirect + golang.org/x/crypto v0.7.0 // indirect + golang.org/x/image v0.13.0 // indirect golang.org/x/mod v0.8.0 // indirect golang.org/x/net v0.9.0 // indirect - golang.org/x/term v0.7.0 // indirect - golang.org/x/text v0.12.0 // indirect + golang.org/x/text v0.13.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.30.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) replace golang.org/x/crypto => github.com/ProtonMail/crypto v0.0.0-20200420072808-71bec3603bf3 + +replace github.com/gdamore/tcell/v2 => git.sr.ht/~rockorager/vaxis-tcell v0.4.7 diff --git a/go.sum b/go.sum index 2d657644a56c..48122d9f3f22 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,10 @@ git.sr.ht/~rockorager/go-jmap v0.3.0 h1:h2WuPcNyXRYFg9+W2HGf/mzIqC6ISy9EaS/BGa7Z git.sr.ht/~rockorager/go-jmap v0.3.0/go.mod h1:aOTCtwpZSINpDDSOkLGpHU0Kbbm5lcSDMcobX3ZtOjY= git.sr.ht/~rockorager/tcell-term v0.10.0 h1:BqxJjtCMmLIfS6fdIal8TSiH3qPiYTjATuIRIWNVPbo= git.sr.ht/~rockorager/tcell-term v0.10.0/go.mod h1:Snxh5CrziiA2CjyLOZ6tGAg5vMPlE+REMWT3rtKuyyQ= +git.sr.ht/~rockorager/vaxis v0.4.7 h1:9VlkBBF9dxy62AMHnKAU8GqEs2/mUTlke/ZJ9o/6luk= +git.sr.ht/~rockorager/vaxis v0.4.7/go.mod h1:h94aKek3frIV1hJbdXjqnBqaLkbWXvV+UxAsQHg9bns= +git.sr.ht/~rockorager/vaxis-tcell v0.4.7 h1:ISMSnvbz1jnG9Ppi9y3NJKaLl7Nu67qMkpEXbXwHgmg= +git.sr.ht/~rockorager/vaxis-tcell v0.4.7/go.mod h1:mpNiMGQDJ3fiwVO8pvz0GENWCdCXEE50beqCbgGoXLc= github.com/ProtonMail/crypto v0.0.0-20200420072808-71bec3603bf3 h1:JW27/kGLQzeM1Fxg5YQhdkTEAU7HIAHMgSag35zVTnY= github.com/ProtonMail/crypto v0.0.0-20200420072808-71bec3603bf3/go.mod h1:Pxr7w4gA2ikI4sWyYwEffm+oew1WAJHzG1SiDpQMkrI= github.com/ProtonMail/go-crypto v0.0.0-20211112122917-428f8eabeeb3/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= @@ -17,6 +21,8 @@ github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a/go.mod h1:2GxOXO github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= github.com/cloudflare/circl v1.3.2 h1:VWp8dY3yH69fdM7lM6A1+NhhVoDu9vqK0jOgmkQHFWk= github.com/cloudflare/circl v1.3.2/go.mod h1:+CauBF6R70Jqcyl8N2hC8pAXYbWkGIezuSbuGLtRhnw= +github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= +github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= @@ -61,11 +67,7 @@ github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4 github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/gatherstars-com/jwz v1.4.0 h1:HrCJmTss6/PTzBxxQUGbJ5f0aFYjnfrfqpGlyWH+R5A= github.com/gatherstars-com/jwz v1.4.0/go.mod h1:twtXjMamfC5/NRCTJ9vDiHGeDivORkTAvOMUX/qo0Ik= -github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= -github.com/gdamore/tcell/v2 v2.5.4/go.mod h1:dZgRy5v4iMobMEcWNYBtREnDZAT9DYmfqIkrgEMxLyw= -github.com/gdamore/tcell/v2 v2.6.0 h1:OKbluoP9VYmJwZwq/iLb4BxwKcwGthaa1YNBJIyCySg= -github.com/gdamore/tcell/v2 v2.6.0/go.mod h1:be9omFATkdr0D9qewWW3d+MEvl5dha+Etb5y65J2H8Y= github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-test/deep v1.0.7/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8= @@ -82,6 +84,7 @@ github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk= @@ -99,15 +102,17 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lithammer/fuzzysearch v1.1.5 h1:Ag7aKU08wp0R9QCfF4GoGST9HbmAIeLP7xwMrOBEp1c= github.com/lithammer/fuzzysearch v1.1.5/go.mod h1:1R1LRNk7yKid1BaQkmuLQaHruxcC4HmAH30Dh61Ih1Q= -github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/martinlindhe/base36 v1.0.0/go.mod h1:+AtEs8xrBpCeYgSLoY/aJ6Wf37jtBuR0s35750M27+8= github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= -github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-sixel v0.0.5 h1:55w2FR5ncuhKhXrM5ly1eiqMQfZsnAHIpYNGZX03Cv8= +github.com/mattn/go-sixel v0.0.5/go.mod h1:h2Sss+DiUEHy0pUqcIB6PFXo5Cy8sTQEFr3a9/5ZLNw= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= @@ -133,6 +138,8 @@ github.com/riywo/loginshell v0.0.0-20200815045211-7d26008be1ab/go.mod h1:/PfPXh0 github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= +github.com/soniakeys/quant v1.0.0 h1:N1um9ktjbkZVcywBVAAYpZYSHxEfJGzshHCxx/DaI0Y= +github.com/soniakeys/quant v1.0.0/go.mod h1:HI1k023QuVbD4H8i9YdfZP2munIHU4QpjsImz6Y6zds= github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo= github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -143,6 +150,7 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= @@ -150,8 +158,13 @@ github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpP github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E= +golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc= +golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= +golang.org/x/image v0.9.0/go.mod h1:jtrku+n79PfroUbvDdeUWMAI+heR786BofxrbiSF+J0= +golang.org/x/image v0.13.0 h1:3cge/F/QTkNLauhf2QoE9zp+7sr+ZcL4HnoZmdwg9sg= +golang.org/x/image v0.13.0/go.mod h1:6mmbMOeV28HuMTgA6OSRkdXKYw/t5W9Uwn2Yv1r3Yxk= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -159,6 +172,7 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= @@ -173,33 +187,39 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ= -golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= +golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/lib/ui/ui.go b/lib/ui/ui.go index 6a28eb9ce509..b5c66530caa6 100644 --- a/lib/ui/ui.go +++ b/lib/ui/ui.go @@ -6,6 +6,8 @@ import ( "sync/atomic" "syscall" + "git.sr.ht/~rjarry/aerc/config" + "git.sr.ht/~rockorager/vaxis" "github.com/gdamore/tcell/v2" ) @@ -51,7 +53,10 @@ func Initialize(content DrawableInteractive) error { return err } - if err = screen.Init(); err != nil { + opts := vaxis.Options{ + DisableMouse: !config.Ui.MouseEnabled, + } + if err = screen.Init(opts); err != nil { return err } @@ -140,6 +145,11 @@ func HandleEvent(event tcell.Event) { state.ctx = NewContext(width, height, state.screen, onPopover) Invalidate() } + if event, ok := event.(tcell.VaxisEvent); ok { + if _, ok := event.Vaxis().(vaxis.Redraw); ok { + Invalidate() + } + } // if we have a popover, and it can handle the event, it does so if state.popover == nil || !state.popover.Event(event) { // otherwise, we send the event to the main content -- 2.43.0
Create and expose a vaxis.Window object with each Context. vaxis.Windows are used for creating local coordinates (similar to the views.View API that tcell provides). Signed-off-by: Tim Culverhouse <tim@timculverhouse.com> --- lib/ui/context.go | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/ui/context.go b/lib/ui/context.go index 9ca7cc9d56c0..39933fa30789 100644 --- a/lib/ui/context.go +++ b/lib/ui/context.go @@ -4,6 +4,7 @@ import ( "fmt" "git.sr.ht/~rjarry/aerc/lib/parse" + "git.sr.ht/~rockorager/vaxis" "github.com/gdamore/tcell/v2" "github.com/gdamore/tcell/v2/views" ) @@ -12,6 +13,7 @@ import ( type Context struct { screen tcell.Screen viewport *views.ViewPort + window vaxis.Window x, y int onPopover func(*Popover) } @@ -36,9 +38,15 @@ func (ctx *Context) Height() int { return height } +// returns the vaxis Window for this context +func (ctx *Context) Window() vaxis.Window { + return ctx.window +} + func NewContext(width, height int, screen tcell.Screen, p func(*Popover)) *Context { vp := views.NewViewPort(screen, 0, 0, width, height) - return &Context{screen, vp, 0, 0, p} + win := screen.Vaxis().Window() + return &Context{screen, vp, win, 0, 0, p} } func (ctx *Context) Subcontext(x, y, width, height int) *Context { @@ -50,7 +58,8 @@ func (ctx *Context) Subcontext(x, y, width, height int) *Context { panic(fmt.Errorf("Attempted to create context larger than parent")) } vp := views.NewViewPort(ctx.viewport, x, y, width, height) - return &Context{ctx.screen, vp, ctx.x + x, ctx.y + y, ctx.onPopover} + win := ctx.window.New(x, y, width, height) + return &Context{ctx.screen, vp, win, ctx.x + x, ctx.y + y, ctx.onPopover} } func (ctx *Context) SetCell(x, y int, ch rune, style tcell.Style) { -- 2.43.0
Implement inline image viewing for jpeg, png, bmp, tiff, and webp formats. When a user has no configured image filter and the image is supported and the terminal has either sixel or kitty image protocol support, the image will be displayed in the message viewer. Always clear the screen before each draw. This call is necessary in vaxis to allow for images to be cleared properly between renders. There is no performance impact: the call only resets each cell to a blank cell, and aerc will redraw each one already. Changelog-added: inline image previews using kitty, sixel, or cells Signed-off-by: Tim Culverhouse <tim@timculverhouse.com> --- app/msgviewer.go | 101 ++++++++++++++++++++++++++++++++++++++++++++++- go.mod | 2 +- lib/ui/ui.go | 1 + 3 files changed, 101 insertions(+), 3 deletions(-) diff --git a/app/msgviewer.go b/app/msgviewer.go index e45a6d3ec75c..8d2e380a9987 100644 --- a/app/msgviewer.go +++ b/app/msgviewer.go @@ -4,6 +4,7 @@ import ( "bytes" "errors" "fmt" + "image" "io" "os" "os/exec" @@ -24,8 +25,28 @@ import ( "git.sr.ht/~rjarry/aerc/log" "git.sr.ht/~rjarry/aerc/models" "git.sr.ht/~rjarry/go-opt" + "git.sr.ht/~rockorager/vaxis" + "git.sr.ht/~rockorager/vaxis/widgets/align" + + // Image support + _ "image/jpeg" + _ "image/png" + + _ "golang.org/x/image/bmp" + _ "golang.org/x/image/tiff" + _ "golang.org/x/image/webp" ) +// All imported image types need to be explicitly stated here. We want to check +// if we _can_ display something before we download it +var supportedImageTypes = []string{ + "image/jpeg", + "image/png", + "image/bmp", + "image/tiff", + "image/webp", +} + var _ ProvidesMessages = (*MessageViewer)(nil) type MessageViewer struct { @@ -405,6 +426,11 @@ type PartViewer struct { noFilter *ui.Grid uiConfig *config.UIConfig copying int32 + inlineImg bool + image image.Image + graphic vaxis.Image + width int + height int links []string } @@ -535,7 +561,27 @@ func NewPartViewer( func (pv *PartViewer) SetSource(reader io.Reader) { pv.source = reader - pv.attemptCopy() + switch pv.inlineImg { + case true: + pv.decodeImage() + default: + pv.attemptCopy() + } +} + +func (pv *PartViewer) decodeImage() { + atomic.StoreInt32(&pv.copying, copying) + go func() { + defer log.PanicHandler() + defer pv.Invalidate() + defer atomic.StoreInt32(&pv.copying, 0) + img, _, err := image.Decode(pv.source) + if err != nil { + log.Errorf("error decoding image: %v", err) + return + } + pv.image = img + }() } func (pv *PartViewer) attemptCopy() { @@ -711,7 +757,13 @@ func (pv *PartViewer) Invalidate() { func (pv *PartViewer) Draw(ctx *ui.Context) { style := pv.uiConfig.GetStyle(config.STYLE_DEFAULT) - if pv.filter == nil { + switch { + case pv.filter == nil && canInline(pv.part.FullMIMEType()) && pv.err == nil: + pv.inlineImg = true + case pv.filter == nil: + // No filter, can't inline, and/or we attempted to inline an image + // and resulted in an error (maybe because of a bad encoding or + // the terminal doesn't support any graphics protocol). ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', style) pv.noFilter.Draw(ctx) return @@ -728,12 +780,48 @@ func (pv *PartViewer) Draw(ctx *ui.Context) { if pv.term != nil { pv.term.Draw(ctx) } + if pv.image != nil && (pv.resized(ctx) || pv.graphic == nil) { + // This path should only occur on resizes or the first pass + // after the image is downloaded and could be slow due to + // encoding the image to either sixel or uploading via the kitty + // protocol. Generally it's pretty fast since we will only ever + // be downsizing images + vx := ctx.Window().Vx + if pv.graphic == nil { + var err error + pv.graphic, err = vx.NewImage(pv.image) + if err != nil { + log.Errorf("Couldn't create image: %v", err) + return + } + } + pv.graphic.Resize(pv.width, pv.height) + } + if pv.graphic != nil { + w, h := pv.graphic.CellSize() + win := align.Center(ctx.Window(), w, h) + pv.graphic.Draw(win) + } } func (pv *PartViewer) Cleanup() { if pv.term != nil { pv.term.Close() } + if pv.graphic != nil { + pv.graphic.Destroy() + } +} + +func (pv *PartViewer) resized(ctx *ui.Context) bool { + w := ctx.Width() + h := ctx.Height() + if pv.width != w || pv.height != h { + pv.width = w + pv.height = h + return true + } + return false } func (pv *PartViewer) Event(event tcell.Event) bool { @@ -779,3 +867,12 @@ func (hv *HeaderView) Draw(ctx *ui.Context) { func (hv *HeaderView) Invalidate() { ui.Invalidate() } + +func canInline(mime string) bool { + for _, ext := range supportedImageTypes { + if mime == ext { + return true + } + } + return false +} diff --git a/go.mod b/go.mod index 87b7ad3adce6..63f3e7ba3646 100644 --- a/go.mod +++ b/go.mod @@ -33,6 +33,7 @@ require ( github.com/stretchr/testify v1.8.4 github.com/syndtr/goleveldb v1.0.0 github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e + golang.org/x/image v0.13.0 golang.org/x/oauth2 v0.7.0 golang.org/x/sys v0.13.0 golang.org/x/tools v0.6.0 @@ -53,7 +54,6 @@ require ( github.com/rogpeppe/go-internal v1.8.1 // indirect github.com/soniakeys/quant v1.0.0 // indirect golang.org/x/crypto v0.7.0 // indirect - golang.org/x/image v0.13.0 // indirect golang.org/x/mod v0.8.0 // indirect golang.org/x/net v0.9.0 // indirect golang.org/x/text v0.13.0 // indirect diff --git a/lib/ui/ui.go b/lib/ui/ui.go index b5c66530caa6..b80309499248 100644 --- a/lib/ui/ui.go +++ b/lib/ui/ui.go @@ -123,6 +123,7 @@ func Close() { func Render() { if atomic.SwapUint32(&state.dirty, 0) != 0 { + state.screen.Clear() // reset popover for the next Draw state.popover = nil state.content.Draw(state.ctx) -- 2.43.0
Modify the function signature of Event and MouseEvent interfaces to accept vaxis events. Note that because a vaxis event is an empty interface, the implementations are not affected and the events are delivered as they were before Signed-off-by: Tim Culverhouse <tim@timculverhouse.com> --- app/account-wizard.go | 3 ++- app/account.go | 5 ++--- app/aerc.go | 3 ++- app/compose.go | 11 ++++++----- app/dirlist.go | 3 ++- app/dirtree.go | 3 ++- app/exline.go | 3 ++- app/getpasswd.go | 3 ++- app/listbox.go | 3 ++- app/msglist.go | 3 ++- app/msgviewer.go | 7 +++---- app/partswitcher.go | 5 +++-- app/selector.go | 5 +++-- app/terminal.go | 7 ++++--- commands/patch/list.go | 4 ++-- commands/util.go | 4 ++-- lib/ui/borders.go | 5 ++--- lib/ui/box.go | 6 +++--- lib/ui/grid.go | 3 ++- lib/ui/interfaces.go | 6 +++--- lib/ui/popover.go | 4 ++-- lib/ui/tab.go | 3 ++- lib/ui/textinput.go | 5 +++-- lib/ui/ui.go | 2 +- 24 files changed, 59 insertions(+), 47 deletions(-) diff --git a/app/account-wizard.go b/app/account-wizard.go index 9ee69627745c..e47acc53a856 100644 --- a/app/account-wizard.go +++ b/app/account-wizard.go @@ -22,6 +22,7 @@ import ( "git.sr.ht/~rjarry/aerc/lib/ui" "git.sr.ht/~rjarry/aerc/lib/xdg" "git.sr.ht/~rjarry/aerc/log" + "git.sr.ht/~rockorager/vaxis" ) const ( @@ -747,7 +748,7 @@ func (wizard *AccountWizard) Focus(focus bool) { } } -func (wizard *AccountWizard) Event(event tcell.Event) bool { +func (wizard *AccountWizard) Event(event vaxis.Event) bool { interactive := wizard.getInteractive() if event, ok := event.(*tcell.EventKey); ok { switch event.Key() { diff --git a/app/account.go b/app/account.go index 5b5405238725..4b5fa30b92db 100644 --- a/app/account.go +++ b/app/account.go @@ -7,8 +7,6 @@ import ( "sync" "time" - "github.com/gdamore/tcell/v2" - "git.sr.ht/~rjarry/aerc/config" "git.sr.ht/~rjarry/aerc/lib" "git.sr.ht/~rjarry/aerc/lib/hooks" @@ -21,6 +19,7 @@ import ( "git.sr.ht/~rjarry/aerc/models" "git.sr.ht/~rjarry/aerc/worker" "git.sr.ht/~rjarry/aerc/worker/types" + "git.sr.ht/~rockorager/vaxis" ) var _ ProvidesMessages = (*AccountView)(nil) @@ -168,7 +167,7 @@ func (acct *AccountView) Draw(ctx *ui.Context) { acct.grid.Draw(ctx) } -func (acct *AccountView) MouseEvent(localX int, localY int, event tcell.Event) { +func (acct *AccountView) MouseEvent(localX int, localY int, event vaxis.Event) { acct.grid.MouseEvent(localX, localY, event) } diff --git a/app/aerc.go b/app/aerc.go index c73075401bbe..291142836c24 100644 --- a/app/aerc.go +++ b/app/aerc.go @@ -11,6 +11,7 @@ import ( "time" "git.sr.ht/~rjarry/go-opt" + "git.sr.ht/~rockorager/vaxis" "github.com/ProtonMail/go-crypto/openpgp" "github.com/emersion/go-message/mail" "github.com/gdamore/tcell/v2" @@ -291,7 +292,7 @@ func (aerc *Aerc) simulate(strokes []config.KeyStroke) { } } -func (aerc *Aerc) Event(event tcell.Event) bool { +func (aerc *Aerc) Event(event vaxis.Event) bool { if aerc.dialog != nil { return aerc.dialog.Event(event) } diff --git a/app/compose.go b/app/compose.go index 35fdd9bfae79..a73127c9c3fd 100644 --- a/app/compose.go +++ b/app/compose.go @@ -32,6 +32,7 @@ import ( "git.sr.ht/~rjarry/aerc/log" "git.sr.ht/~rjarry/aerc/models" "git.sr.ht/~rjarry/aerc/worker/types" + "git.sr.ht/~rockorager/vaxis" ) type Composer struct { @@ -834,7 +835,7 @@ func (c *Composer) focusActiveWidget(focus bool) { } } -func (c *Composer) Event(event tcell.Event) bool { +func (c *Composer) Event(event vaxis.Event) bool { c.Lock() defer c.Unlock() if w := c.focusedWidget(); c.editor != nil && w != nil { @@ -843,7 +844,7 @@ func (c *Composer) Event(event tcell.Event) bool { return false } -func (c *Composer) MouseEvent(localX int, localY int, event tcell.Event) { +func (c *Composer) MouseEvent(localX int, localY int, event vaxis.Event) { c.Lock() for _, e := range c.focusable { he, ok := e.(*headerEditor) @@ -1222,7 +1223,7 @@ func (c *Composer) resetReview() { } } -func (c *Composer) termEvent(event tcell.Event) bool { +func (c *Composer) termEvent(event vaxis.Event) bool { if event, ok := event.(*tcell.EventMouse); ok { if event.Buttons() == tcell.Button1 { c.FocusTerminal() @@ -1643,7 +1644,7 @@ func (he *headerEditor) Draw(ctx *ui.Context) { he.input.Draw(ctx.Subcontext(size, 0, ctx.Width()-size, 1)) } -func (he *headerEditor) MouseEvent(localX int, localY int, event tcell.Event) { +func (he *headerEditor) MouseEvent(localX int, localY int, event vaxis.Event) { if event, ok := event.(*tcell.EventMouse); ok { if event.Buttons() == tcell.Button1 { he.focused = true @@ -1665,7 +1666,7 @@ func (he *headerEditor) Focus(focused bool) { he.input.Focus(focused) } -func (he *headerEditor) Event(event tcell.Event) bool { +func (he *headerEditor) Event(event vaxis.Event) bool { return he.input.Event(event) } diff --git a/app/dirlist.go b/app/dirlist.go index d260d7c195f3..53aeed9d15de 100644 --- a/app/dirlist.go +++ b/app/dirlist.go @@ -19,6 +19,7 @@ import ( "git.sr.ht/~rjarry/aerc/log" "git.sr.ht/~rjarry/aerc/models" "git.sr.ht/~rjarry/aerc/worker/types" + "git.sr.ht/~rockorager/vaxis" ) type DirectoryLister interface { @@ -374,7 +375,7 @@ func (dirlist *DirectoryList) drawScrollbar(ctx *ui.Context) { ctx.Fill(0, pillOffset, 1, pillSize, ' ', pillStyle) } -func (dirlist *DirectoryList) MouseEvent(localX int, localY int, event tcell.Event) { +func (dirlist *DirectoryList) MouseEvent(localX int, localY int, event vaxis.Event) { if event, ok := event.(*tcell.EventMouse); ok { switch event.Buttons() { case tcell.Button1: diff --git a/app/dirtree.go b/app/dirtree.go index 559a717e1008..c3ad2ef6246b 100644 --- a/app/dirtree.go +++ b/app/dirtree.go @@ -14,6 +14,7 @@ import ( "git.sr.ht/~rjarry/aerc/log" "git.sr.ht/~rjarry/aerc/models" "git.sr.ht/~rjarry/aerc/worker/types" + "git.sr.ht/~rockorager/vaxis" "github.com/gdamore/tcell/v2" ) @@ -166,7 +167,7 @@ func (dt *DirectoryTree) Draw(ctx *ui.Context) { } } -func (dt *DirectoryTree) MouseEvent(localX int, localY int, event tcell.Event) { +func (dt *DirectoryTree) MouseEvent(localX int, localY int, event vaxis.Event) { if event, ok := event.(*tcell.EventMouse); ok { switch event.Buttons() { case tcell.Button1: diff --git a/app/exline.go b/app/exline.go index 61fc340a7a2f..7eb6fde3ea26 100644 --- a/app/exline.go +++ b/app/exline.go @@ -6,6 +6,7 @@ import ( "git.sr.ht/~rjarry/aerc/config" "git.sr.ht/~rjarry/aerc/lib" "git.sr.ht/~rjarry/aerc/lib/ui" + "git.sr.ht/~rockorager/vaxis" ) type ExLine struct { @@ -81,7 +82,7 @@ func (ex *ExLine) Focus(focus bool) { ex.input.Focus(focus) } -func (ex *ExLine) Event(event tcell.Event) bool { +func (ex *ExLine) Event(event vaxis.Event) bool { if event, ok := event.(*tcell.EventKey); ok { switch event.Key() { case tcell.KeyEnter, tcell.KeyCtrlJ: diff --git a/app/getpasswd.go b/app/getpasswd.go index 9a3dbf3b1c02..8781bce79a87 100644 --- a/app/getpasswd.go +++ b/app/getpasswd.go @@ -7,6 +7,7 @@ import ( "git.sr.ht/~rjarry/aerc/config" "git.sr.ht/~rjarry/aerc/lib/ui" + "git.sr.ht/~rockorager/vaxis" ) type GetPasswd struct { @@ -44,7 +45,7 @@ func (gp *GetPasswd) Invalidate() { ui.Invalidate() } -func (gp *GetPasswd) Event(event tcell.Event) bool { +func (gp *GetPasswd) Event(event vaxis.Event) bool { switch event := event.(type) { case *tcell.EventKey: switch event.Key() { diff --git a/app/listbox.go b/app/listbox.go index 5a80261e8925..f6b97e23dd29 100644 --- a/app/listbox.go +++ b/app/listbox.go @@ -8,6 +8,7 @@ import ( "git.sr.ht/~rjarry/aerc/config" "git.sr.ht/~rjarry/aerc/lib/ui" "git.sr.ht/~rjarry/aerc/log" + "git.sr.ht/~rockorager/vaxis" "github.com/gdamore/tcell/v2" "github.com/mattn/go-runewidth" ) @@ -213,7 +214,7 @@ func (lb *ListBox) Invalidate() { ui.Invalidate() } -func (lb *ListBox) Event(event tcell.Event) bool { +func (lb *ListBox) Event(event vaxis.Event) bool { if event, ok := event.(*tcell.EventKey); ok { switch event.Key() { case tcell.KeyLeft: diff --git a/app/msglist.go b/app/msglist.go index 7948e6a169d4..9217423941ce 100644 --- a/app/msglist.go +++ b/app/msglist.go @@ -17,6 +17,7 @@ import ( "git.sr.ht/~rjarry/aerc/log" "git.sr.ht/~rjarry/aerc/models" "git.sr.ht/~rjarry/aerc/worker/types" + "git.sr.ht/~rockorager/vaxis" ) type MessageList struct { @@ -253,7 +254,7 @@ func (ml *MessageList) drawScrollbar(ctx *ui.Context) { ctx.Fill(0, pillOffset, 1, pillSize, ' ', pillStyle) } -func (ml *MessageList) MouseEvent(localX int, localY int, event tcell.Event) { +func (ml *MessageList) MouseEvent(localX int, localY int, event vaxis.Event) { if event, ok := event.(*tcell.EventMouse); ok { switch event.Buttons() { case tcell.Button1: diff --git a/app/msgviewer.go b/app/msgviewer.go index 8d2e380a9987..a22d2c15df70 100644 --- a/app/msgviewer.go +++ b/app/msgviewer.go @@ -13,7 +13,6 @@ import ( "github.com/danwakefield/fnmatch" "github.com/emersion/go-message/textproto" - "github.com/gdamore/tcell/v2" "github.com/mattn/go-runewidth" "git.sr.ht/~rjarry/aerc/config" @@ -286,7 +285,7 @@ func (mv *MessageViewer) Draw(ctx *ui.Context) { mv.grid.Draw(ctx) } -func (mv *MessageViewer) MouseEvent(localX int, localY int, event tcell.Event) { +func (mv *MessageViewer) MouseEvent(localX int, localY int, event vaxis.Event) { if mv.err != nil { return } @@ -398,7 +397,7 @@ func (mv *MessageViewer) Close() { } } -func (mv *MessageViewer) Event(event tcell.Event) bool { +func (mv *MessageViewer) Event(event vaxis.Event) bool { return mv.switcher.Event(event) } @@ -824,7 +823,7 @@ func (pv *PartViewer) resized(ctx *ui.Context) bool { return false } -func (pv *PartViewer) Event(event tcell.Event) bool { +func (pv *PartViewer) Event(event vaxis.Event) bool { if pv.term != nil { return pv.term.Event(event) } diff --git a/app/partswitcher.go b/app/partswitcher.go index 3f070c44883c..8552d32b4682 100644 --- a/app/partswitcher.go +++ b/app/partswitcher.go @@ -5,6 +5,7 @@ import ( "git.sr.ht/~rjarry/aerc/config" "git.sr.ht/~rjarry/aerc/lib/ui" + "git.sr.ht/~rockorager/vaxis" "github.com/gdamore/tcell/v2" "github.com/mattn/go-runewidth" ) @@ -79,7 +80,7 @@ func (ps *PartSwitcher) Show(visible bool) { } } -func (ps *PartSwitcher) Event(event tcell.Event) bool { +func (ps *PartSwitcher) Event(event vaxis.Event) bool { return ps.parts[ps.selected].Event(event) } @@ -161,7 +162,7 @@ func (ps *PartSwitcher) drawScrollbar(ctx *ui.Context) { ctx.Fill(0, pillOffset, 1, pillSize, ' ', pillStyle) } -func (ps *PartSwitcher) MouseEvent(localX int, localY int, event tcell.Event) { +func (ps *PartSwitcher) MouseEvent(localX int, localY int, event vaxis.Event) { if localY < ps.offset && ps.parts[ps.selected].term != nil { ps.parts[ps.selected].term.MouseEvent(localX, localY, event) return diff --git a/app/selector.go b/app/selector.go index 7f4730d07f7e..fe8c4d963d5c 100644 --- a/app/selector.go +++ b/app/selector.go @@ -9,6 +9,7 @@ import ( "git.sr.ht/~rjarry/aerc/config" "git.sr.ht/~rjarry/aerc/lib/ui" + "git.sr.ht/~rockorager/vaxis" ) type Selector struct { @@ -143,7 +144,7 @@ func (sel *Selector) Focus(focus bool) { sel.Invalidate() } -func (sel *Selector) Event(event tcell.Event) bool { +func (sel *Selector) Event(event vaxis.Event) bool { if event, ok := event.(*tcell.EventKey); ok { switch event.Key() { case tcell.KeyCtrlH: @@ -239,7 +240,7 @@ func (gp *SelectorDialog) Invalidate() { ui.Invalidate() } -func (gp *SelectorDialog) Event(event tcell.Event) bool { +func (gp *SelectorDialog) Event(event vaxis.Event) bool { switch event := event.(type) { case *tcell.EventKey: switch event.Key() { diff --git a/app/terminal.go b/app/terminal.go index f0335de32815..c99af26ab9c1 100644 --- a/app/terminal.go +++ b/app/terminal.go @@ -8,6 +8,7 @@ import ( "git.sr.ht/~rjarry/aerc/lib/ui" "git.sr.ht/~rjarry/aerc/log" tcellterm "git.sr.ht/~rockorager/tcell-term" + "git.sr.ht/~rockorager/vaxis" "github.com/gdamore/tcell/v2" ) @@ -26,7 +27,7 @@ type Terminal struct { running bool OnClose func(err error) - OnEvent func(event tcell.Event) bool + OnEvent func(event vaxis.Event) bool OnStart func() OnTitle func(title string) } @@ -124,7 +125,7 @@ func (term *Terminal) Terminal() *Terminal { return term } -func (term *Terminal) MouseEvent(localX int, localY int, event tcell.Event) { +func (term *Terminal) MouseEvent(localX int, localY int, event vaxis.Event) { ev, ok := event.(*tcell.EventMouse) if !ok { return @@ -176,7 +177,7 @@ func (term *Terminal) HandleEvent(ev tcell.Event) { } } -func (term *Terminal) Event(event tcell.Event) bool { +func (term *Terminal) Event(event vaxis.Event) bool { if term.OnEvent != nil { if term.OnEvent(event) { return true diff --git a/commands/patch/list.go b/commands/patch/list.go index 6dcc06e806e6..61f577067abd 100644 --- a/commands/patch/list.go +++ b/commands/patch/list.go @@ -13,7 +13,7 @@ import ( "git.sr.ht/~rjarry/aerc/lib/pama/models" "git.sr.ht/~rjarry/aerc/lib/ui" "git.sr.ht/~rjarry/go-opt" - "github.com/gdamore/tcell/v2" + "git.sr.ht/~rockorager/vaxis" ) type List struct { @@ -65,7 +65,7 @@ func (l List) Execute(args []string) error { app.CloseDialog() return } - term.OnEvent = func(_ tcell.Event) bool { + term.OnEvent = func(_ vaxis.Event) bool { app.CloseDialog() return true } diff --git a/commands/util.go b/commands/util.go index 6247f579776c..5b5e093e0754 100644 --- a/commands/util.go +++ b/commands/util.go @@ -19,7 +19,7 @@ import ( "git.sr.ht/~rjarry/aerc/models" "git.sr.ht/~rjarry/aerc/worker/types" "git.sr.ht/~rjarry/go-opt" - "github.com/gdamore/tcell/v2" + "git.sr.ht/~rockorager/vaxis" ) // QuickTerm is an ephemeral terminal for running a single command and quitting. @@ -43,7 +43,7 @@ func QuickTerm(args []string, stdin io.Reader) (*app.Terminal, error) { } else { app.PushStatus("Process complete, press any key to close.", 10*time.Second) - term.OnEvent = func(event tcell.Event) bool { + term.OnEvent = func(event vaxis.Event) bool { app.RemoveTab(term, true) return true } diff --git a/lib/ui/borders.go b/lib/ui/borders.go index 81d5db61c029..fb3db6b128f5 100644 --- a/lib/ui/borders.go +++ b/lib/ui/borders.go @@ -1,9 +1,8 @@ package ui import ( - "github.com/gdamore/tcell/v2" - "git.sr.ht/~rjarry/aerc/config" + "git.sr.ht/~rockorager/vaxis" ) const ( @@ -69,7 +68,7 @@ func (bordered *Bordered) Draw(ctx *Context) { bordered.content.Draw(subctx) } -func (bordered *Bordered) MouseEvent(localX int, localY int, event tcell.Event) { +func (bordered *Bordered) MouseEvent(localX int, localY int, event vaxis.Event) { if content, ok := bordered.content.(Mouseable); ok { content.MouseEvent(localX, localY, event) } diff --git a/lib/ui/box.go b/lib/ui/box.go index 96b95d590556..9f6f9442f9dd 100644 --- a/lib/ui/box.go +++ b/lib/ui/box.go @@ -4,7 +4,7 @@ import ( "strings" "git.sr.ht/~rjarry/aerc/config" - "github.com/gdamore/tcell/v2" + "git.sr.ht/~rockorager/vaxis" "github.com/mattn/go-runewidth" ) @@ -57,13 +57,13 @@ func (b *Box) Invalidate() { b.content.Invalidate() } -func (b *Box) MouseEvent(localX int, localY int, event tcell.Event) { +func (b *Box) MouseEvent(localX int, localY int, event vaxis.Event) { if content, ok := b.content.(Mouseable); ok { content.MouseEvent(localX, localY, event) } } -func (b *Box) Event(e tcell.Event) bool { +func (b *Box) Event(e vaxis.Event) bool { if content, ok := b.content.(Interactive); ok { return content.Event(e) } diff --git a/lib/ui/grid.go b/lib/ui/grid.go index 28640d033a63..00f759bf9cc3 100644 --- a/lib/ui/grid.go +++ b/lib/ui/grid.go @@ -4,6 +4,7 @@ import ( "math" "sync" + "git.sr.ht/~rockorager/vaxis" "github.com/gdamore/tcell/v2" ) @@ -128,7 +129,7 @@ func (grid *Grid) Draw(ctx *Context) { } } -func (grid *Grid) MouseEvent(localX int, localY int, event tcell.Event) { +func (grid *Grid) MouseEvent(localX int, localY int, event vaxis.Event) { if event, ok := event.(*tcell.EventMouse); ok { grid.mutex.RLock() diff --git a/lib/ui/interfaces.go b/lib/ui/interfaces.go index a8520ff5db22..8ed727ccedb9 100644 --- a/lib/ui/interfaces.go +++ b/lib/ui/interfaces.go @@ -1,7 +1,7 @@ package ui import ( - "github.com/gdamore/tcell/v2" + "git.sr.ht/~rockorager/vaxis" ) // Drawable is a UI component that can draw. Unless specified, all methods must @@ -24,7 +24,7 @@ type Visible interface { type Interactive interface { // Returns true if the event was handled by this component - Event(event tcell.Event) bool + Event(event vaxis.Event) bool // Indicates whether or not this control will receive input events Focus(focus bool) } @@ -53,7 +53,7 @@ type Container interface { type MouseHandler interface { // Handle a mouse event which occurred at the local x and y positions - MouseEvent(localX int, localY int, event tcell.Event) + MouseEvent(localX int, localY int, event vaxis.Event) } // A drawable that can be interacted with by the mouse diff --git a/lib/ui/popover.go b/lib/ui/popover.go index 9cc491dab7a3..5a6d05426951 100644 --- a/lib/ui/popover.go +++ b/lib/ui/popover.go @@ -1,6 +1,6 @@ package ui -import "github.com/gdamore/tcell/v2" +import "git.sr.ht/~rockorager/vaxis" type Popover struct { x, y, width, height int @@ -39,7 +39,7 @@ func (p *Popover) Draw(ctx *Context) { p.content.Draw(subcontext) } -func (p *Popover) Event(e tcell.Event) bool { +func (p *Popover) Event(e vaxis.Event) bool { if di, ok := p.content.(DrawableInteractive); ok { return di.Event(e) } diff --git a/lib/ui/tab.go b/lib/ui/tab.go index 43b6e14f6cdb..07b304ee500e 100644 --- a/lib/ui/tab.go +++ b/lib/ui/tab.go @@ -7,6 +7,7 @@ import ( "github.com/mattn/go-runewidth" "git.sr.ht/~rjarry/aerc/config" + "git.sr.ht/~rockorager/vaxis" ) const tabRuneWidth int = 32 // TODO: make configurable @@ -482,7 +483,7 @@ func (content *TabContent) Draw(ctx *Context) { tab.Content.Draw(ctx) } -func (content *TabContent) MouseEvent(localX int, localY int, event tcell.Event) { +func (content *TabContent) MouseEvent(localX int, localY int, event vaxis.Event) { content.parent.m.Lock() tab := content.tabs[content.curIndex] content.parent.m.Unlock() diff --git a/lib/ui/textinput.go b/lib/ui/textinput.go index 32a177e150be..0bdcd4359a95 100644 --- a/lib/ui/textinput.go +++ b/lib/ui/textinput.go @@ -11,6 +11,7 @@ import ( "git.sr.ht/~rjarry/aerc/config" "git.sr.ht/~rjarry/aerc/log" + "git.sr.ht/~rockorager/vaxis" ) // TODO: Attach history providers @@ -332,7 +333,7 @@ func (ti *TextInput) OnFocusLost(onFocusLost func(ti *TextInput)) { ti.focusLost = append(ti.focusLost, onFocusLost) } -func (ti *TextInput) Event(event tcell.Event) bool { +func (ti *TextInput) Event(event vaxis.Event) bool { ti.Lock() defer ti.Unlock() if event, ok := event.(*tcell.EventKey); ok { @@ -480,7 +481,7 @@ func (c *completions) exec() { Invalidate() } -func (c *completions) Event(e tcell.Event) bool { +func (c *completions) Event(e vaxis.Event) bool { if e, ok := e.(*tcell.EventKey); ok { k := c.ti.completeKey if k != nil && k.Key == e.Key() && k.Modifiers == e.Modifiers() { diff --git a/lib/ui/ui.go b/lib/ui/ui.go index b80309499248..e13f4f5cf6e7 100644 --- a/lib/ui/ui.go +++ b/lib/ui/ui.go @@ -139,7 +139,7 @@ func EnableMouse() { state.screen.EnableMouse() } -func HandleEvent(event tcell.Event) { +func HandleEvent(event vaxis.Event) { if event, ok := event.(*tcell.EventResize); ok { state.screen.Clear() width, height := event.Size() -- 2.43.0
Remove references to tcell.Screen or views.Viewports. Convert Contexts and the core UI struct to use Vaxis objects only. Signed-off-by: Tim Culverhouse <tim@timculverhouse.com> --- app/terminal.go | 12 +++--- lib/ui/context.go | 89 +++++++++++++++++++++++---------------------- lib/ui/textinput.go | 4 +- lib/ui/ui.go | 49 +++++++++++++------------ main.go | 4 -- 5 files changed, 78 insertions(+), 80 deletions(-) diff --git a/app/terminal.go b/app/terminal.go index c99af26ab9c1..33478206f166 100644 --- a/app/terminal.go +++ b/app/terminal.go @@ -83,11 +83,11 @@ func (term *Terminal) Invalidate() { } func (term *Terminal) Draw(ctx *ui.Context) { - term.vterm.SetSurface(ctx.View()) + term.vterm.SetSurface(ctx) - w, h := ctx.View().Size() + w, h := ctx.Size() if !term.isClosed() && term.ctx != nil { - ow, oh := term.ctx.View().Size() + ow, oh := term.ctx.Size() if w != ow || h != oh { term.vterm.Resize(w, h) } @@ -109,8 +109,7 @@ func (term *Terminal) Draw(ctx *ui.Context) { if term.focus { y, x, style, vis := term.vterm.Cursor() if vis && !term.isClosed() { - ctx.SetCursor(x, y) - ctx.SetCursorStyle(style) + ctx.SetCursor(x, y, vaxis.CursorStyle(style)) } else { ctx.HideCursor() } @@ -150,8 +149,7 @@ func (term *Terminal) Focus(focus bool) { term.ctx.HideCursor() } else { y, x, style, _ := term.vterm.Cursor() - term.ctx.SetCursor(x, y) - term.ctx.SetCursorStyle(style) + term.ctx.SetCursor(x, y, vaxis.CursorStyle(style)) term.Invalidate() } } diff --git a/lib/ui/context.go b/lib/ui/context.go index 39933fa30789..10089179c6c3 100644 --- a/lib/ui/context.go +++ b/lib/ui/context.go @@ -6,35 +6,22 @@ import ( "git.sr.ht/~rjarry/aerc/lib/parse" "git.sr.ht/~rockorager/vaxis" "github.com/gdamore/tcell/v2" - "github.com/gdamore/tcell/v2/views" ) // A context allows you to draw in a sub-region of the terminal type Context struct { - screen tcell.Screen - viewport *views.ViewPort window vaxis.Window x, y int onPopover func(*Popover) } -func (ctx *Context) X() int { - x, _, _, _ := ctx.viewport.GetPhysical() - return x -} - -func (ctx *Context) Y() int { - _, y, _, _ := ctx.viewport.GetPhysical() - return y -} - func (ctx *Context) Width() int { - width, _ := ctx.viewport.Size() + width, _ := ctx.window.Size() return width } func (ctx *Context) Height() int { - _, height := ctx.viewport.Size() + _, height := ctx.window.Size() return height } @@ -43,39 +30,37 @@ func (ctx *Context) Window() vaxis.Window { return ctx.window } -func NewContext(width, height int, screen tcell.Screen, p func(*Popover)) *Context { - vp := views.NewViewPort(screen, 0, 0, width, height) - win := screen.Vaxis().Window() - return &Context{screen, vp, win, 0, 0, p} +func NewContext(width, height int, vx *vaxis.Vaxis, p func(*Popover)) *Context { + win := vx.Window() + return &Context{win, 0, 0, p} } func (ctx *Context) Subcontext(x, y, width, height int) *Context { - vp_width, vp_height := ctx.viewport.Size() if x < 0 || y < 0 { panic(fmt.Errorf("Attempted to create context with negative offset")) } - if x+width > vp_width || y+height > vp_height { - panic(fmt.Errorf("Attempted to create context larger than parent")) - } - vp := views.NewViewPort(ctx.viewport, x, y, width, height) win := ctx.window.New(x, y, width, height) - return &Context{ctx.screen, vp, win, ctx.x + x, ctx.y + y, ctx.onPopover} + return &Context{win, x, y, ctx.onPopover} } func (ctx *Context) SetCell(x, y int, ch rune, style tcell.Style) { - width, height := ctx.viewport.Size() + width, height := ctx.window.Size() if x >= width || y >= height { // no-op when dims are inadequate return } - crunes := []rune{} - ctx.viewport.SetContent(x, y, ch, crunes, style) + ctx.window.SetCell(x, y, vaxis.Cell{ + Character: vaxis.Character{ + Grapheme: string(ch), + }, + Style: tcell.VaxisStyle(style), + }) } func (ctx *Context) Printf(x, y int, style tcell.Style, format string, a ...interface{}, ) int { - width, height := ctx.viewport.Size() + width, height := ctx.window.Size() if x >= width || y >= height { // no-op when dims are inadequate @@ -103,8 +88,13 @@ func (ctx *Context) Printf(x, y int, style tcell.Style, case '\r': x = old_x default: - crunes := []rune{} - ctx.viewport.SetContent(x, y, sr.Value, crunes, sr.Style) + ctx.window.SetCell(x, y, vaxis.Cell{ + Character: vaxis.Character{ + Grapheme: string(sr.Value), + Width: sr.Width, + }, + Style: tcell.VaxisStyle(sr.Style), + }) x += sr.Width if x == old_x+width { if !newline() { @@ -118,20 +108,22 @@ func (ctx *Context) Printf(x, y int, style tcell.Style, } func (ctx *Context) Fill(x, y, width, height int, rune rune, style tcell.Style) { - vp := views.NewViewPort(ctx.viewport, x, y, width, height) - vp.Fill(rune, style) + win := ctx.window.New(x, y, width, height) + win.Fill(vaxis.Cell{ + Character: vaxis.Character{ + Grapheme: string(rune), + Width: 1, + }, + Style: tcell.VaxisStyle(style), + }) } -func (ctx *Context) SetCursor(x, y int) { - ctx.screen.ShowCursor(ctx.x+x, ctx.y+y) -} - -func (ctx *Context) SetCursorStyle(cs tcell.CursorStyle) { - ctx.screen.SetCursorStyle(cs) +func (ctx *Context) SetCursor(x, y int, style vaxis.CursorStyle) { + ctx.window.ShowCursor(x, y, style) } func (ctx *Context) HideCursor() { - ctx.screen.HideCursor() + ctx.window.Vx.HideCursor() } func (ctx *Context) Popover(x, y, width, height int, d Drawable) { @@ -144,10 +136,19 @@ func (ctx *Context) Popover(x, y, width, height int, d Drawable) { }) } -func (ctx *Context) View() *views.ViewPort { - return ctx.viewport +// SetContent is used to update the content of the Surface at the given +// location. +func (ctx *Context) SetContent(x int, y int, ch rune, comb []rune, style tcell.Style) { + g := []rune{ch} + g = append(g, comb...) + ctx.window.SetCell(x, y, vaxis.Cell{ + Character: vaxis.Character{ + Grapheme: string(g), + }, + Style: tcell.VaxisStyle(style), + }) } -func (ctx *Context) Show() { - ctx.screen.Show() +func (ctx *Context) Size() (int, int) { + return ctx.window.Size() } diff --git a/lib/ui/textinput.go b/lib/ui/textinput.go index 0bdcd4359a95..107d354c1980 100644 --- a/lib/ui/textinput.go +++ b/lib/ui/textinput.go @@ -120,7 +120,7 @@ func (ti *TextInput) Draw(ctx *Context) { } cells := runewidth.StringWidth(string(text[:sindex]) + ti.prompt) if ti.focus { - ctx.SetCursor(cells, 0) + ctx.SetCursor(cells, 0, vaxis.CursorDefault) ti.drawPopover(ctx) } } @@ -163,7 +163,7 @@ func (ti *TextInput) Focus(focus bool) { ti.focus = focus if focus && ti.ctx != nil { cells := runewidth.StringWidth(string(ti.text[:ti.index])) - ti.ctx.SetCursor(cells+1, 0) + ti.ctx.SetCursor(cells+1, 0, vaxis.CursorDefault) } else if !focus && ti.ctx != nil { ti.ctx.HideCursor() } diff --git a/lib/ui/ui.go b/lib/ui/ui.go index e13f4f5cf6e7..02e945c040ef 100644 --- a/lib/ui/ui.go +++ b/lib/ui/ui.go @@ -7,14 +7,15 @@ import ( "syscall" "git.sr.ht/~rjarry/aerc/config" + "git.sr.ht/~rjarry/aerc/log" "git.sr.ht/~rockorager/vaxis" "github.com/gdamore/tcell/v2" ) // Use unbuffered channels (always blocking unless somebody can read -// immediately) We are merely using this as a proxy to tcell screen internal -// event channel. -var Events = make(chan tcell.Event) +// immediately) We are merely using this as a proxy to the internal vaxis event +// channel. +var Events = make(chan vaxis.Event) var Quit = make(chan struct{}) @@ -40,7 +41,7 @@ func Invalidate() { var state struct { content DrawableInteractive ctx *Context - screen tcell.Screen + vx *vaxis.Vaxis popover *Popover dirty uint32 // == 1 if render has been queued in Redraw channel // == 1 if suspend is pending @@ -60,15 +61,16 @@ func Initialize(content DrawableInteractive) error { return err } - screen.Clear() - screen.HideCursor() - screen.EnablePaste() + vx := screen.Vaxis() - width, height := screen.Size() + vx.Window().Clear() + vx.HideCursor() + + width, height := vx.Window().Size() state.content = content - state.screen = screen - state.ctx = NewContext(width, height, state.screen, onPopover) + state.vx = vx + state.ctx = NewContext(width, height, state.vx, onPopover) Invalidate() if beeper, ok := content.(DrawableInteractiveBeeper); ok { @@ -76,7 +78,12 @@ func Initialize(content DrawableInteractive) error { } content.Focus(true) - go state.screen.ChannelEvents(Events, Quit) + go func() { + defer log.PanicHandler() + for event := range vx.Events() { + Events <- tcell.TcellEvent(event) + } + }() return nil } @@ -100,7 +107,7 @@ func QueueSuspend() { func Suspend() error { var err error if atomic.SwapUint32(&state.suspending, 0) != 0 { - err = state.screen.Suspend() + err = state.vx.Suspend() if err == nil { sigcont := make(chan os.Signal, 1) signal.Notify(sigcont, syscall.SIGCONT) @@ -109,21 +116,21 @@ func Suspend() error { <-sigcont } signal.Reset(syscall.SIGCONT) - err = state.screen.Resume() + err = state.vx.Resume() state.content.Draw(state.ctx) - state.screen.Show() + state.vx.Render() } } return err } func Close() { - state.screen.Fini() + state.vx.Close() } func Render() { if atomic.SwapUint32(&state.dirty, 0) != 0 { - state.screen.Clear() + state.vx.Window().Clear() // reset popover for the next Draw state.popover = nil state.content.Draw(state.ctx) @@ -131,19 +138,15 @@ func Render() { // if the Draw resulted in a popover, draw it state.popover.Draw(state.ctx) } - state.screen.Show() + state.vx.Render() } } -func EnableMouse() { - state.screen.EnableMouse() -} - func HandleEvent(event vaxis.Event) { if event, ok := event.(*tcell.EventResize); ok { - state.screen.Clear() + state.vx.Window().Clear() width, height := event.Size() - state.ctx = NewContext(width, height, state.screen, onPopover) + state.ctx = NewContext(width, height, state.vx, onPopover) Invalidate() } if event, ok := event.(tcell.VaxisEvent); ok { diff --git a/main.go b/main.go index a7e2980deede..f592189adce2 100644 --- a/main.go +++ b/main.go @@ -301,10 +301,6 @@ func main() { } close(deferLoop) - if config.Ui.MouseEnabled { - ui.EnableMouse() - } - as, err := ipc.StartServer(app.IPCHandler()) if err != nil { log.Warnf("Failed to start Unix server: %v", err) -- 2.43.0
Use Vaxis library directly to initialize the UI, dropping the need for a tcell Screen implementation Signed-off-by: Tim Culverhouse <tim@timculverhouse.com> --- lib/ui/context.go | 2 +- lib/ui/interfaces.go | 2 +- lib/ui/ui.go | 47 ++++++++++++++++++-------------------------- 3 files changed, 21 insertions(+), 30 deletions(-) diff --git a/lib/ui/context.go b/lib/ui/context.go index 10089179c6c3..f1cc2a951349 100644 --- a/lib/ui/context.go +++ b/lib/ui/context.go @@ -30,7 +30,7 @@ func (ctx *Context) Window() vaxis.Window { return ctx.window } -func NewContext(width, height int, vx *vaxis.Vaxis, p func(*Popover)) *Context { +func NewContext(vx *vaxis.Vaxis, p func(*Popover)) *Context { win := vx.Window() return &Context{win, 0, 0, p} } diff --git a/lib/ui/interfaces.go b/lib/ui/interfaces.go index 8ed727ccedb9..3f2f95091036 100644 --- a/lib/ui/interfaces.go +++ b/lib/ui/interfaces.go @@ -30,7 +30,7 @@ type Interactive interface { } type Beeper interface { - OnBeep(func() error) + OnBeep(func()) } type DrawableInteractive interface { diff --git a/lib/ui/ui.go b/lib/ui/ui.go index 02e945c040ef..f17ca4b383d5 100644 --- a/lib/ui/ui.go +++ b/lib/ui/ui.go @@ -49,39 +49,32 @@ var state struct { } func Initialize(content DrawableInteractive) error { - screen, err := tcell.NewScreen() + opts := vaxis.Options{ + DisableMouse: !config.Ui.MouseEnabled, + DisableKittyKeyboard: true, + } + vx, err := vaxis.New(opts) if err != nil { return err } - opts := vaxis.Options{ - DisableMouse: !config.Ui.MouseEnabled, - } - if err = screen.Init(opts); err != nil { - return err - } - - vx := screen.Vaxis() - vx.Window().Clear() vx.HideCursor() - width, height := vx.Window().Size() - state.content = content state.vx = vx - state.ctx = NewContext(width, height, state.vx, onPopover) + state.ctx = NewContext(state.vx, onPopover) Invalidate() if beeper, ok := content.(DrawableInteractiveBeeper); ok { - beeper.OnBeep(screen.Beep) + beeper.OnBeep(vx.Bell) } content.Focus(true) go func() { defer log.PanicHandler() for event := range vx.Events() { - Events <- tcell.TcellEvent(event) + Events <- event } }() @@ -143,20 +136,18 @@ func Render() { } func HandleEvent(event vaxis.Event) { - if event, ok := event.(*tcell.EventResize); ok { - state.vx.Window().Clear() - width, height := event.Size() - state.ctx = NewContext(width, height, state.vx, onPopover) + switch event := event.(type) { + case vaxis.Resize: + state.ctx = NewContext(state.vx, onPopover) Invalidate() - } - if event, ok := event.(tcell.VaxisEvent); ok { - if _, ok := event.Vaxis().(vaxis.Redraw); ok { - Invalidate() + case vaxis.Redraw: + Invalidate() + default: + event = tcell.TcellEvent(event) + // if we have a popover, and it can handle the event, it does so + if state.popover == nil || !state.popover.Event(event) { + // otherwise, we send the event to the main content + state.content.Event(event) } } - // if we have a popover, and it can handle the event, it does so - if state.popover == nil || !state.popover.Event(event) { - // otherwise, we send the event to the main content - state.content.Event(event) - } } -- 2.43.0
Replace the Fill implementation with vaxis style objects Signed-off-by: Tim Culverhouse <tim@timculverhouse.com> --- app/account-wizard.go | 2 +- app/aerc.go | 4 ++-- app/compose.go | 2 +- app/msgviewer.go | 5 +++-- lib/ui/context.go | 4 ++-- lib/ui/fill.go | 8 +++----- 6 files changed, 12 insertions(+), 13 deletions(-) diff --git a/app/account-wizard.go b/app/account-wizard.go index e47acc53a856..0fd426ca8b70 100644 --- a/app/account-wizard.go +++ b/app/account-wizard.go @@ -128,7 +128,7 @@ func (s *configStep) Grid() *ui.Grid { grid := ui.NewGrid().Rows(spec).Columns([]ui.GridSpec{justify}) intro := ui.NewText(introduction, config.Ui.GetStyle(config.STYLE_DEFAULT)) - fill := ui.NewFill(' ', tcell.StyleDefault) + fill := ui.NewFill(' ', vaxis.Style{}) grid.AddChild(fill).At(0, 0) grid.AddChild(intro).At(1, 0) diff --git a/app/aerc.go b/app/aerc.go index 291142836c24..495545015913 100644 --- a/app/aerc.go +++ b/app/aerc.go @@ -860,9 +860,9 @@ func errorScreen(s string) ui.Drawable { }).Columns([]ui.GridSpec{ {Strategy: ui.SIZE_WEIGHT, Size: ui.Const(1)}, }) - grid.AddChild(ui.NewFill(' ', tcell.StyleDefault)).At(0, 0) + grid.AddChild(ui.NewFill(' ', vaxis.Style{})).At(0, 0) grid.AddChild(text).At(1, 0) - grid.AddChild(ui.NewFill(' ', tcell.StyleDefault)).At(2, 0) + grid.AddChild(ui.NewFill(' ', vaxis.Style{})).At(2, 0) return grid } diff --git a/app/compose.go b/app/compose.go index a73127c9c3fd..14341ef6f166 100644 --- a/app/compose.go +++ b/app/compose.go @@ -1547,7 +1547,7 @@ func (c *Composer) updateGrid() { borderChar := c.acct.UiConfig().BorderCharHorizontal grid.AddChild(heditors).At(0, 0) grid.AddChild(c.crypto).At(1, 0) - grid.AddChild(ui.NewFill(borderChar, borderStyle)).At(2, 0) + grid.AddChild(ui.NewFill(borderChar, tcell.VaxisStyle(borderStyle))).At(2, 0) if c.review != nil { grid.AddChild(c.review).At(3, 0) } else if c.editor != nil { diff --git a/app/msgviewer.go b/app/msgviewer.go index a22d2c15df70..179deca410d2 100644 --- a/app/msgviewer.go +++ b/app/msgviewer.go @@ -13,6 +13,7 @@ import ( "github.com/danwakefield/fnmatch" "github.com/emersion/go-message/textproto" + "github.com/gdamore/tcell/v2" "github.com/mattn/go-runewidth" "git.sr.ht/~rjarry/aerc/config" @@ -145,10 +146,10 @@ func NewMessageViewer( grid.AddChild(header).At(0, 0) if msg.MessageDetails() != nil || acct.UiConfig().IconUnencrypted != "" { grid.AddChild(NewPGPInfo(msg.MessageDetails(), acct.UiConfig())).At(1, 0) - grid.AddChild(ui.NewFill(borderChar, borderStyle)).At(2, 0) + grid.AddChild(ui.NewFill(borderChar, tcell.VaxisStyle(borderStyle))).At(2, 0) grid.AddChild(switcher).At(3, 0) } else { - grid.AddChild(ui.NewFill(borderChar, borderStyle)).At(1, 0) + grid.AddChild(ui.NewFill(borderChar, tcell.VaxisStyle(borderStyle))).At(1, 0) grid.AddChild(switcher).At(2, 0) } diff --git a/lib/ui/context.go b/lib/ui/context.go index f1cc2a951349..12621c8af97a 100644 --- a/lib/ui/context.go +++ b/lib/ui/context.go @@ -43,7 +43,7 @@ func (ctx *Context) Subcontext(x, y, width, height int) *Context { return &Context{win, x, y, ctx.onPopover} } -func (ctx *Context) SetCell(x, y int, ch rune, style tcell.Style) { +func (ctx *Context) SetCell(x, y int, ch rune, style vaxis.Style) { width, height := ctx.window.Size() if x >= width || y >= height { // no-op when dims are inadequate @@ -53,7 +53,7 @@ func (ctx *Context) SetCell(x, y int, ch rune, style tcell.Style) { Character: vaxis.Character{ Grapheme: string(ch), }, - Style: tcell.VaxisStyle(style), + Style: style, }) } diff --git a/lib/ui/fill.go b/lib/ui/fill.go index ff248905223c..eedb481e177d 100644 --- a/lib/ui/fill.go +++ b/lib/ui/fill.go @@ -1,15 +1,13 @@ package ui -import ( - "github.com/gdamore/tcell/v2" -) +import "git.sr.ht/~rockorager/vaxis" type Fill struct { Rune rune - Style tcell.Style + Style vaxis.Style } -func NewFill(f rune, s tcell.Style) Fill { +func NewFill(f rune, s vaxis.Style) Fill { return Fill{f, s} } -- 2.43.0
Replace all instances of tcell key usage with vaxis keys Signed-off-by: Tim Culverhouse <tim@timculverhouse.com> --- app/account-wizard.go | 22 +- app/aerc.go | 51 +++-- app/exline.go | 14 +- app/getpasswd.go | 10 +- app/listbox.go | 28 +-- app/selector.go | 23 +-- app/terminal.go | 1 + commands/send-keys.go | 7 +- config/binds.go | 458 +++++++++++++++++++++--------------------- config/binds_test.go | 36 ++-- lib/ui/textinput.go | 46 +++-- lib/ui/ui.go | 2 - 12 files changed, 352 insertions(+), 346 deletions(-) diff --git a/app/account-wizard.go b/app/account-wizard.go index 0fd426ca8b70..36bc0392c1bd 100644 --- a/app/account-wizard.go +++ b/app/account-wizard.go @@ -13,7 +13,6 @@ import ( "sync" "github.com/emersion/go-message/mail" - "github.com/gdamore/tcell/v2" "github.com/go-ini/ini" "golang.org/x/sys/unix" @@ -750,13 +749,11 @@ func (wizard *AccountWizard) Focus(focus bool) { func (wizard *AccountWizard) Event(event vaxis.Event) bool { interactive := wizard.getInteractive() - if event, ok := event.(*tcell.EventKey); ok { - switch event.Key() { - case tcell.KeyUp: - fallthrough - case tcell.KeyBacktab: - fallthrough - case tcell.KeyCtrlK: + if key, ok := event.(vaxis.Key); ok { + switch { + case key.Matches('k', vaxis.ModCtrl), + key.Matches(vaxis.KeyTab, vaxis.ModShift), + key.Matches(vaxis.KeyUp): if interactive != nil { interactive[wizard.focus].Focus(false) wizard.focus-- @@ -767,11 +764,10 @@ func (wizard *AccountWizard) Event(event vaxis.Event) bool { } wizard.Invalidate() return true - case tcell.KeyDown: - fallthrough - case tcell.KeyTab: - fallthrough - case tcell.KeyCtrlJ: + case key.Matches('j', vaxis.ModCtrl), + key.Matches(vaxis.KeyTab), + key.Matches(vaxis.KeyDown): + if interactive != nil { interactive[wizard.focus].Focus(false) wizard.focus++ diff --git a/app/aerc.go b/app/aerc.go index 495545015913..00f1cb5fc385 100644 --- a/app/aerc.go +++ b/app/aerc.go @@ -9,6 +9,7 @@ import ( "sort" "strings" "time" + "unicode" "git.sr.ht/~rjarry/go-opt" "git.sr.ht/~rockorager/vaxis" @@ -222,7 +223,7 @@ func (aerc *Aerc) HumanReadableBindings() []string { } result = append(result, fmt.Sprintf(fmtStr, "$ex", - fmt.Sprintf("'%c'", binds.ExKey.Rune), + fmt.Sprintf("'%c'", binds.ExKey.Key), )) result = append(result, fmt.Sprintf(fmtStr, "Globals", @@ -277,8 +278,23 @@ func (aerc *Aerc) simulate(strokes []config.KeyStroke) { aerc.pendingKeys = []config.KeyStroke{} aerc.simulating += 1 for _, stroke := range strokes { - simulated := tcell.NewEventKey( - stroke.Key, stroke.Rune, tcell.ModNone) + simulated := vaxis.Key{ + Keycode: stroke.Key, + Modifiers: stroke.Modifiers, + } + if unicode.IsUpper(stroke.Key) { + simulated.Keycode = unicode.ToLower(stroke.Key) + simulated.Modifiers |= vaxis.ModShift + } + // If none of these mods are present, set the text field to + // enable matching keys like ":" + if stroke.Modifiers&vaxis.ModCtrl == 0 && + stroke.Modifiers&vaxis.ModAlt == 0 && + stroke.Modifiers&vaxis.ModSuper == 0 && + stroke.Modifiers&vaxis.ModHyper == 0 { + + simulated.Text = string(stroke.Key) + } aerc.Event(simulated) } aerc.simulating -= 1 @@ -288,7 +304,7 @@ func (aerc *Aerc) simulate(strokes []config.KeyStroke) { return aerc.complete(cmd) }) // send tab to text input to trigger completion - exline.Event(tcell.NewEventKey(tcell.KeyTab, 0, tcell.ModNone)) + exline.Event(vaxis.Key{Keycode: vaxis.KeyTab}) } } @@ -302,7 +318,8 @@ func (aerc *Aerc) Event(event vaxis.Event) bool { } switch event := event.(type) { - case *tcell.EventKey: + // TODO: more vaxis events handling + case vaxis.Key: // If we are in a bracketed paste, don't process the keys for // bindings if aerc.pasting { @@ -313,11 +330,17 @@ func (aerc *Aerc) Event(event vaxis.Event) bool { return false } aerc.statusline.Expire() - aerc.pendingKeys = append(aerc.pendingKeys, config.KeyStroke{ - Modifiers: event.Modifiers(), - Key: event.Key(), - Rune: event.Rune(), - }) + stroke := config.KeyStroke{ + Modifiers: event.Modifiers, + } + switch { + case event.ShiftedCode != 0: + stroke.Key = event.ShiftedCode + stroke.Modifiers &^= vaxis.ModShift + default: + stroke.Key = event.Keycode + } + aerc.pendingKeys = append(aerc.pendingKeys, stroke) ui.Invalidate() bindings := aerc.getBindings() incomplete := false @@ -866,12 +889,8 @@ func errorScreen(s string) ui.Drawable { return grid } -func (aerc *Aerc) isExKey(event *tcell.EventKey, exKey config.KeyStroke) bool { - if event.Key() == tcell.KeyRune { - // Compare runes if it's a KeyRune - return event.Modifiers() == exKey.Modifiers && event.Rune() == exKey.Rune - } - return event.Modifiers() == exKey.Modifiers && event.Key() == exKey.Key +func (aerc *Aerc) isExKey(key vaxis.Key, exKey config.KeyStroke) bool { + return key.Matches(exKey.Key, exKey.Modifiers) } // CmdFallbackSearch checks cmds for the first executable availabe in PATH. An error is diff --git a/app/exline.go b/app/exline.go index 7eb6fde3ea26..e8b0069ea7f0 100644 --- a/app/exline.go +++ b/app/exline.go @@ -1,8 +1,6 @@ package app import ( - "github.com/gdamore/tcell/v2" - "git.sr.ht/~rjarry/aerc/config" "git.sr.ht/~rjarry/aerc/lib" "git.sr.ht/~rjarry/aerc/lib/ui" @@ -83,20 +81,20 @@ func (ex *ExLine) Focus(focus bool) { } func (ex *ExLine) Event(event vaxis.Event) bool { - if event, ok := event.(*tcell.EventKey); ok { - switch event.Key() { - case tcell.KeyEnter, tcell.KeyCtrlJ: + if key, ok := event.(vaxis.Key); ok { + switch { + case key.Matches(vaxis.KeyEnter), key.Matches('j', vaxis.ModCtrl): cmd := ex.input.String() ex.input.Focus(false) ex.commit(cmd) ex.finish() - case tcell.KeyUp: + case key.Matches(vaxis.KeyUp): ex.input.Set(ex.cmdHistory.Prev()) ex.Invalidate() - case tcell.KeyDown: + case key.Matches(vaxis.KeyDown): ex.input.Set(ex.cmdHistory.Next()) ex.Invalidate() - case tcell.KeyEsc, tcell.KeyCtrlC: + case key.Matches(vaxis.KeyEsc), key.Matches('c', vaxis.ModCtrl): ex.input.Focus(false) ex.cmdHistory.Reset() ex.finish() diff --git a/app/getpasswd.go b/app/getpasswd.go index 8781bce79a87..e5726d91a083 100644 --- a/app/getpasswd.go +++ b/app/getpasswd.go @@ -3,8 +3,6 @@ package app import ( "fmt" - "github.com/gdamore/tcell/v2" - "git.sr.ht/~rjarry/aerc/config" "git.sr.ht/~rjarry/aerc/lib/ui" "git.sr.ht/~rockorager/vaxis" @@ -47,12 +45,12 @@ func (gp *GetPasswd) Invalidate() { func (gp *GetPasswd) Event(event vaxis.Event) bool { switch event := event.(type) { - case *tcell.EventKey: - switch event.Key() { - case tcell.KeyEnter: + case vaxis.Key: + switch { + case event.Matches(vaxis.KeyEnter): gp.input.Focus(false) gp.callback(gp.input.String(), nil) - case tcell.KeyEsc: + case event.Matches(vaxis.KeyEsc): gp.input.Focus(false) gp.callback("", fmt.Errorf("no password provided")) default: diff --git a/app/listbox.go b/app/listbox.go index f6b97e23dd29..d891b24dff08 100644 --- a/app/listbox.go +++ b/app/listbox.go @@ -215,17 +215,17 @@ func (lb *ListBox) Invalidate() { } func (lb *ListBox) Event(event vaxis.Event) bool { - if event, ok := event.(*tcell.EventKey); ok { - switch event.Key() { - case tcell.KeyLeft: + if key, ok := event.(vaxis.Key); ok { + switch { + case key.Matches(vaxis.KeyLeft): lb.moveHorizontal(-1) lb.Invalidate() return true - case tcell.KeyRight: + case key.Matches(vaxis.KeyRight): lb.moveHorizontal(+1) lb.Invalidate() return true - case tcell.KeyCtrlB: + case key.Matches('b', vaxis.ModCtrl): line := lb.selected[:lb.horizPos] fds := strings.Fields(line) if len(fds) > 1 { @@ -237,7 +237,7 @@ func (lb *ListBox) Event(event vaxis.Event) bool { } lb.Invalidate() return true - case tcell.KeyCtrlW: + case key.Matches('w', vaxis.ModCtrl): line := lb.selected[lb.horizPos+1:] fds := strings.Fields(line) if len(fds) > 1 { @@ -245,37 +245,37 @@ func (lb *ListBox) Event(event vaxis.Event) bool { } lb.Invalidate() return true - case tcell.KeyCtrlA, tcell.KeyHome: + case key.Matches('a', vaxis.ModCtrl), key.Matches(vaxis.KeyHome): lb.horizPos = 0 lb.Invalidate() return true - case tcell.KeyCtrlE, tcell.KeyEnd: + case key.Matches('e', vaxis.ModCtrl), key.Matches(vaxis.KeyEnd): lb.horizPos = len(lb.selected) lb.Invalidate() return true - case tcell.KeyCtrlP, tcell.KeyUp: + case key.Matches('p', vaxis.ModCtrl), key.Matches(vaxis.KeyUp): lb.moveCursor(-1) lb.Invalidate() return true - case tcell.KeyCtrlN, tcell.KeyDown: + case key.Matches('n', vaxis.ModCtrl), key.Matches(vaxis.KeyDown): lb.moveCursor(+1) lb.Invalidate() return true - case tcell.KeyPgUp: + case key.Matches(vaxis.KeyPgUp): if lb.jump >= 0 { lb.moveCursor(-lb.jump) lb.Invalidate() } return true - case tcell.KeyPgDn: + case key.Matches(vaxis.KeyPgDown): if lb.jump >= 0 { lb.moveCursor(+lb.jump) lb.Invalidate() } return true - case tcell.KeyEnter: + case key.Matches(vaxis.KeyEnter): return lb.quit(lb.selected) - case tcell.KeyEsc: + case key.Matches(vaxis.KeyEsc): return lb.quit("") } } diff --git a/app/selector.go b/app/selector.go index fe8c4d963d5c..252616f05486 100644 --- a/app/selector.go +++ b/app/selector.go @@ -4,7 +4,6 @@ import ( "fmt" "strings" - "github.com/gdamore/tcell/v2" "github.com/mattn/go-runewidth" "git.sr.ht/~rjarry/aerc/config" @@ -145,11 +144,11 @@ func (sel *Selector) Focus(focus bool) { } func (sel *Selector) Event(event vaxis.Event) bool { - if event, ok := event.(*tcell.EventKey); ok { - switch event.Key() { - case tcell.KeyCtrlH: + if key, ok := event.(vaxis.Key); ok { + switch { + case key.Matches('h', vaxis.ModCtrl): fallthrough - case tcell.KeyLeft: + case key.Matches(vaxis.KeyLeft): if sel.focus > 0 { sel.focus-- sel.Invalidate() @@ -157,9 +156,9 @@ func (sel *Selector) Event(event vaxis.Event) bool { if sel.onSelect != nil { sel.onSelect(sel.Selected()) } - case tcell.KeyCtrlL: + case key.Matches('l', vaxis.ModCtrl): fallthrough - case tcell.KeyRight: + case key.Matches(vaxis.KeyRight): if sel.focus < len(sel.options)-1 { sel.focus++ sel.Invalidate() @@ -167,7 +166,7 @@ func (sel *Selector) Event(event vaxis.Event) bool { if sel.onSelect != nil { sel.onSelect(sel.Selected()) } - case tcell.KeyEnter: + case key.Matches(vaxis.KeyEnter): if sel.onChoose != nil { sel.onChoose(sel.Selected()) } @@ -242,12 +241,12 @@ func (gp *SelectorDialog) Invalidate() { func (gp *SelectorDialog) Event(event vaxis.Event) bool { switch event := event.(type) { - case *tcell.EventKey: - switch event.Key() { - case tcell.KeyEnter: + case vaxis.Key: + switch { + case event.Matches(vaxis.KeyEnter): gp.selector.Focus(false) gp.callback(gp.selector.Selected(), nil) - case tcell.KeyEsc: + case event.Matches(vaxis.KeyEsc): gp.selector.Focus(false) gp.callback("", ErrNoOptionSelected) default: diff --git a/app/terminal.go b/app/terminal.go index 33478206f166..97cd4410591b 100644 --- a/app/terminal.go +++ b/app/terminal.go @@ -176,6 +176,7 @@ func (term *Terminal) HandleEvent(ev tcell.Event) { } func (term *Terminal) Event(event vaxis.Event) bool { + event = tcell.TcellEvent(event) if term.OnEvent != nil { if term.OnEvent(event) { return true diff --git a/commands/send-keys.go b/commands/send-keys.go index bb872a3342ad..ff79a8652ada 100644 --- a/commands/send-keys.go +++ b/commands/send-keys.go @@ -3,7 +3,7 @@ package commands import ( "git.sr.ht/~rjarry/aerc/app" "git.sr.ht/~rjarry/aerc/config" - "github.com/gdamore/tcell/v2" + "git.sr.ht/~rockorager/vaxis" "github.com/pkg/errors" ) @@ -36,7 +36,10 @@ func (s SendKeys) Execute(args []string) error { } for _, key := range keys2send { - ev := tcell.NewEventKey(key.Key, key.Rune, key.Modifiers) + ev := vaxis.Key{ + Keycode: key.Key, + Modifiers: key.Modifiers, + } term.Event(ev) } diff --git a/config/binds.go b/config/binds.go index 8eda890d6e36..7ea2f00efcea 100644 --- a/config/binds.go +++ b/config/binds.go @@ -9,9 +9,10 @@ import ( "path" "regexp" "strings" + "unicode" "git.sr.ht/~rjarry/aerc/log" - "github.com/gdamore/tcell/v2" + "git.sr.ht/~rockorager/vaxis" "github.com/go-ini/ini" ) @@ -41,9 +42,8 @@ type BindingConfigContext struct { } type KeyStroke struct { - Modifiers tcell.ModMask - Key tcell.Key - Rune rune + Modifiers vaxis.ModifierMask + Key rune } type Binding struct { @@ -82,7 +82,7 @@ type BindingSearchResult int func defaultBindsConfig() *BindingConfig { // These bindings are not configurable wizard := NewKeyBindings() - wizard.ExKey = KeyStroke{Key: tcell.KeyCtrlE} + wizard.ExKey = KeyStroke{Key: 'e', Modifiers: vaxis.ModCtrl} wizard.Globals = false quit, _ := ParseBinding("<C-q>", ":quit<Enter>") wizard.Add(quit) @@ -275,8 +275,8 @@ func LoadBinds(binds *ini.File, baseName string, baseGroup **KeyBindings) error func NewKeyBindings() *KeyBindings { return &KeyBindings{ - ExKey: KeyStroke{tcell.ModNone, tcell.KeyRune, ':'}, - CompleteKey: KeyStroke{tcell.ModNone, tcell.KeyTab, 0}, + ExKey: KeyStroke{0, ':'}, + CompleteKey: KeyStroke{0, vaxis.KeyTab}, Globals: true, contextualCache: make(map[bindsContextKey]*KeyBindings), contextualCounts: make(map[bindsContextType]int), @@ -404,11 +404,6 @@ func (bindings *KeyBindings) GetBinding( if stroke.Key != binding.Input[i].Key { goto next } - if stroke.Key == tcell.KeyRune && - stroke.Rune != binding.Input[i].Rune { - - goto next - } } if len(binding.Input) != len(input) { incomplete = true @@ -437,9 +432,6 @@ func (bindings *KeyBindings) GetReverseBindings(output []KeyStroke) [][]KeyStrok if stroke.Key != binding.Output[i].Key { goto next } - if stroke.Key == tcell.KeyRune && stroke.Rune != binding.Output[i].Rune { - goto next - } } inputs = append(inputs, binding.Input) next: @@ -453,7 +445,7 @@ func FormatKeyStrokes(keystrokes []KeyStroke) string { for _, stroke := range keystrokes { s := "" for name, ks := range keyNames { - if ks.Modifiers == stroke.Modifiers && ks.Key == stroke.Key && ks.Rune == stroke.Rune { + if ks.Modifiers == stroke.Modifiers && ks.Key == stroke.Key { switch name { case "cr", "c-m": s = "<enter>" @@ -469,8 +461,8 @@ func FormatKeyStrokes(keystrokes []KeyStroke) string { break } } - if s == "" && stroke.Key == tcell.KeyRune { - s = string(stroke.Rune) + if s == "" && stroke.Key < unicode.MaxRune { + s = string(stroke.Key) } sb.WriteString(s) } @@ -490,219 +482,218 @@ func FormatKeyStrokes(keystrokes []KeyStroke) string { var spaceTrimRe = regexp.MustCompile(`^(\s*)(.*?)(\s*)$`) var keyNames = map[string]KeyStroke{ - "space": {tcell.ModNone, tcell.KeyRune, ' '}, - "semicolon": {tcell.ModNone, tcell.KeyRune, ';'}, - "enter": {tcell.ModNone, tcell.KeyEnter, 0}, - "c-enter": {tcell.ModCtrl, tcell.KeyEnter, 0}, - "a-enter": {tcell.ModAlt, tcell.KeyEnter, 0}, - "up": {tcell.ModNone, tcell.KeyUp, 0}, - "c-up": {tcell.ModCtrl, tcell.KeyUp, 0}, - "a-up": {tcell.ModAlt, tcell.KeyUp, 0}, - "down": {tcell.ModNone, tcell.KeyDown, 0}, - "c-down": {tcell.ModCtrl, tcell.KeyDown, 0}, - "a-down": {tcell.ModAlt, tcell.KeyDown, 0}, - "right": {tcell.ModNone, tcell.KeyRight, 0}, - "c-right": {tcell.ModCtrl, tcell.KeyRight, 0}, - "a-right": {tcell.ModAlt, tcell.KeyRight, 0}, - "left": {tcell.ModNone, tcell.KeyLeft, 0}, - "c-left": {tcell.ModCtrl, tcell.KeyLeft, 0}, - "a-left": {tcell.ModAlt, tcell.KeyLeft, 0}, - "upleft": {tcell.ModNone, tcell.KeyUpLeft, 0}, - "upright": {tcell.ModNone, tcell.KeyUpRight, 0}, - "downleft": {tcell.ModNone, tcell.KeyDownLeft, 0}, - "downright": {tcell.ModNone, tcell.KeyDownRight, 0}, - "center": {tcell.ModNone, tcell.KeyCenter, 0}, - "pgup": {tcell.ModNone, tcell.KeyPgUp, 0}, - "c-pgup": {tcell.ModCtrl, tcell.KeyPgUp, 0}, - "a-pgup": {tcell.ModAlt, tcell.KeyPgUp, 0}, - "pgdn": {tcell.ModNone, tcell.KeyPgDn, 0}, - "c-pgdn": {tcell.ModCtrl, tcell.KeyPgDn, 0}, - "a-pgdn": {tcell.ModAlt, tcell.KeyPgDn, 0}, - "home": {tcell.ModNone, tcell.KeyHome, 0}, - "end": {tcell.ModNone, tcell.KeyEnd, 0}, - "insert": {tcell.ModNone, tcell.KeyInsert, 0}, - "delete": {tcell.ModNone, tcell.KeyDelete, 0}, - "c-delete": {tcell.ModCtrl, tcell.KeyDelete, 0}, - "a-delete": {tcell.ModAlt, tcell.KeyDelete, 0}, - "backspace": {tcell.ModNone, tcell.KeyBackspace2, 0}, - "help": {tcell.ModNone, tcell.KeyHelp, 0}, - "exit": {tcell.ModNone, tcell.KeyExit, 0}, - "clear": {tcell.ModNone, tcell.KeyClear, 0}, - "cancel": {tcell.ModNone, tcell.KeyCancel, 0}, - "print": {tcell.ModNone, tcell.KeyPrint, 0}, - "pause": {tcell.ModNone, tcell.KeyPause, 0}, - "backtab": {tcell.ModNone, tcell.KeyBacktab, 0}, - "f1": {tcell.ModNone, tcell.KeyF1, 0}, - "f2": {tcell.ModNone, tcell.KeyF2, 0}, - "f3": {tcell.ModNone, tcell.KeyF3, 0}, - "f4": {tcell.ModNone, tcell.KeyF4, 0}, - "f5": {tcell.ModNone, tcell.KeyF5, 0}, - "f6": {tcell.ModNone, tcell.KeyF6, 0}, - "f7": {tcell.ModNone, tcell.KeyF7, 0}, - "f8": {tcell.ModNone, tcell.KeyF8, 0}, - "f9": {tcell.ModNone, tcell.KeyF9, 0}, - "f10": {tcell.ModNone, tcell.KeyF10, 0}, - "f11": {tcell.ModNone, tcell.KeyF11, 0}, - "f12": {tcell.ModNone, tcell.KeyF12, 0}, - "f13": {tcell.ModNone, tcell.KeyF13, 0}, - "f14": {tcell.ModNone, tcell.KeyF14, 0}, - "f15": {tcell.ModNone, tcell.KeyF15, 0}, - "f16": {tcell.ModNone, tcell.KeyF16, 0}, - "f17": {tcell.ModNone, tcell.KeyF17, 0}, - "f18": {tcell.ModNone, tcell.KeyF18, 0}, - "f19": {tcell.ModNone, tcell.KeyF19, 0}, - "f20": {tcell.ModNone, tcell.KeyF20, 0}, - "f21": {tcell.ModNone, tcell.KeyF21, 0}, - "f22": {tcell.ModNone, tcell.KeyF22, 0}, - "f23": {tcell.ModNone, tcell.KeyF23, 0}, - "f24": {tcell.ModNone, tcell.KeyF24, 0}, - "f25": {tcell.ModNone, tcell.KeyF25, 0}, - "f26": {tcell.ModNone, tcell.KeyF26, 0}, - "f27": {tcell.ModNone, tcell.KeyF27, 0}, - "f28": {tcell.ModNone, tcell.KeyF28, 0}, - "f29": {tcell.ModNone, tcell.KeyF29, 0}, - "f30": {tcell.ModNone, tcell.KeyF30, 0}, - "f31": {tcell.ModNone, tcell.KeyF31, 0}, - "f32": {tcell.ModNone, tcell.KeyF32, 0}, - "f33": {tcell.ModNone, tcell.KeyF33, 0}, - "f34": {tcell.ModNone, tcell.KeyF34, 0}, - "f35": {tcell.ModNone, tcell.KeyF35, 0}, - "f36": {tcell.ModNone, tcell.KeyF36, 0}, - "f37": {tcell.ModNone, tcell.KeyF37, 0}, - "f38": {tcell.ModNone, tcell.KeyF38, 0}, - "f39": {tcell.ModNone, tcell.KeyF39, 0}, - "f40": {tcell.ModNone, tcell.KeyF40, 0}, - "f41": {tcell.ModNone, tcell.KeyF41, 0}, - "f42": {tcell.ModNone, tcell.KeyF42, 0}, - "f43": {tcell.ModNone, tcell.KeyF43, 0}, - "f44": {tcell.ModNone, tcell.KeyF44, 0}, - "f45": {tcell.ModNone, tcell.KeyF45, 0}, - "f46": {tcell.ModNone, tcell.KeyF46, 0}, - "f47": {tcell.ModNone, tcell.KeyF47, 0}, - "f48": {tcell.ModNone, tcell.KeyF48, 0}, - "f49": {tcell.ModNone, tcell.KeyF49, 0}, - "f50": {tcell.ModNone, tcell.KeyF50, 0}, - "f51": {tcell.ModNone, tcell.KeyF51, 0}, - "f52": {tcell.ModNone, tcell.KeyF52, 0}, - "f53": {tcell.ModNone, tcell.KeyF53, 0}, - "f54": {tcell.ModNone, tcell.KeyF54, 0}, - "f55": {tcell.ModNone, tcell.KeyF55, 0}, - "f56": {tcell.ModNone, tcell.KeyF56, 0}, - "f57": {tcell.ModNone, tcell.KeyF57, 0}, - "f58": {tcell.ModNone, tcell.KeyF58, 0}, - "f59": {tcell.ModNone, tcell.KeyF59, 0}, - "f60": {tcell.ModNone, tcell.KeyF60, 0}, - "f61": {tcell.ModNone, tcell.KeyF61, 0}, - "f62": {tcell.ModNone, tcell.KeyF62, 0}, - "f63": {tcell.ModNone, tcell.KeyF63, 0}, - "f64": {tcell.ModNone, tcell.KeyF64, 0}, - "c-space": {tcell.ModCtrl, tcell.KeyCtrlSpace, 0}, - "c-a": {tcell.ModCtrl, tcell.KeyCtrlA, 0}, - "c-b": {tcell.ModCtrl, tcell.KeyCtrlB, 0}, - "c-c": {tcell.ModCtrl, tcell.KeyCtrlC, 0}, - "c-d": {tcell.ModCtrl, tcell.KeyCtrlD, 0}, - "c-e": {tcell.ModCtrl, tcell.KeyCtrlE, 0}, - "c-f": {tcell.ModCtrl, tcell.KeyCtrlF, 0}, - "c-g": {tcell.ModCtrl, tcell.KeyCtrlG, 0}, - "c-h": {tcell.ModNone, tcell.KeyCtrlH, 0}, - "c-i": {tcell.ModNone, tcell.KeyCtrlI, 0}, - "c-j": {tcell.ModCtrl, tcell.KeyCtrlJ, 0}, - "c-k": {tcell.ModCtrl, tcell.KeyCtrlK, 0}, - "c-l": {tcell.ModCtrl, tcell.KeyCtrlL, 0}, - "c-m": {tcell.ModNone, tcell.KeyCtrlM, 0}, - "c-n": {tcell.ModCtrl, tcell.KeyCtrlN, 0}, - "c-o": {tcell.ModCtrl, tcell.KeyCtrlO, 0}, - "c-p": {tcell.ModCtrl, tcell.KeyCtrlP, 0}, - "c-q": {tcell.ModCtrl, tcell.KeyCtrlQ, 0}, - "c-r": {tcell.ModCtrl, tcell.KeyCtrlR, 0}, - "c-s": {tcell.ModCtrl, tcell.KeyCtrlS, 0}, - "c-t": {tcell.ModCtrl, tcell.KeyCtrlT, 0}, - "c-u": {tcell.ModCtrl, tcell.KeyCtrlU, 0}, - "c-v": {tcell.ModCtrl, tcell.KeyCtrlV, 0}, - "c-w": {tcell.ModCtrl, tcell.KeyCtrlW, 0}, - "c-x": {tcell.ModCtrl, tcell.KeyCtrlX, rune(tcell.KeyCAN)}, - "c-y": {tcell.ModCtrl, tcell.KeyCtrlY, 0}, // TODO: runes for the rest - "c-z": {tcell.ModCtrl, tcell.KeyCtrlZ, 0}, - "c-]": {tcell.ModCtrl, tcell.KeyCtrlRightSq, 0}, - "c-\\": {tcell.ModCtrl, tcell.KeyCtrlBackslash, 0}, - "c-[": {tcell.ModCtrl, tcell.KeyCtrlLeftSq, 0}, - "c-^": {tcell.ModCtrl, tcell.KeyCtrlCarat, 0}, - "c-_": {tcell.ModCtrl, tcell.KeyCtrlUnderscore, 0}, - "a-space": {tcell.ModAlt, tcell.KeyRune, ' '}, - "a-0": {tcell.ModAlt, tcell.KeyRune, '0'}, - "a-1": {tcell.ModAlt, tcell.KeyRune, '1'}, - "a-2": {tcell.ModAlt, tcell.KeyRune, '2'}, - "a-3": {tcell.ModAlt, tcell.KeyRune, '3'}, - "a-4": {tcell.ModAlt, tcell.KeyRune, '4'}, - "a-5": {tcell.ModAlt, tcell.KeyRune, '5'}, - "a-6": {tcell.ModAlt, tcell.KeyRune, '6'}, - "a-7": {tcell.ModAlt, tcell.KeyRune, '7'}, - "a-8": {tcell.ModAlt, tcell.KeyRune, '8'}, - "a-9": {tcell.ModAlt, tcell.KeyRune, '9'}, - "a-a": {tcell.ModAlt, tcell.KeyRune, 'a'}, - "a-b": {tcell.ModAlt, tcell.KeyRune, 'b'}, - "a-c": {tcell.ModAlt, tcell.KeyRune, 'c'}, - "a-d": {tcell.ModAlt, tcell.KeyRune, 'd'}, - "a-e": {tcell.ModAlt, tcell.KeyRune, 'e'}, - "a-f": {tcell.ModAlt, tcell.KeyRune, 'f'}, - "a-g": {tcell.ModAlt, tcell.KeyRune, 'g'}, - "a-h": {tcell.ModAlt, tcell.KeyRune, 'h'}, - "a-i": {tcell.ModAlt, tcell.KeyRune, 'i'}, - "a-j": {tcell.ModAlt, tcell.KeyRune, 'j'}, - "a-k": {tcell.ModAlt, tcell.KeyRune, 'k'}, - "a-l": {tcell.ModAlt, tcell.KeyRune, 'l'}, - "a-m": {tcell.ModAlt, tcell.KeyRune, 'm'}, - "a-n": {tcell.ModAlt, tcell.KeyRune, 'n'}, - "a-o": {tcell.ModAlt, tcell.KeyRune, 'o'}, - "a-p": {tcell.ModAlt, tcell.KeyRune, 'p'}, - "a-q": {tcell.ModAlt, tcell.KeyRune, 'q'}, - "a-r": {tcell.ModAlt, tcell.KeyRune, 'r'}, - "a-s": {tcell.ModAlt, tcell.KeyRune, 's'}, - "a-t": {tcell.ModAlt, tcell.KeyRune, 't'}, - "a-u": {tcell.ModAlt, tcell.KeyRune, 'u'}, - "a-v": {tcell.ModAlt, tcell.KeyRune, 'v'}, - "a-w": {tcell.ModAlt, tcell.KeyRune, 'w'}, - "a-x": {tcell.ModAlt, tcell.KeyRune, 'x'}, - "a-y": {tcell.ModAlt, tcell.KeyRune, 'y'}, - "a-z": {tcell.ModAlt, tcell.KeyRune, 'z'}, - "a-]": {tcell.ModAlt, tcell.KeyRune, ']'}, - "a-\\": {tcell.ModAlt, tcell.KeyRune, '\\'}, - "a-[": {tcell.ModAlt, tcell.KeyRune, '['}, - "a-^": {tcell.ModAlt, tcell.KeyRune, '^'}, - "a-_": {tcell.ModAlt, tcell.KeyRune, '_'}, - "nul": {tcell.ModNone, tcell.KeyNUL, 0}, - "soh": {tcell.ModNone, tcell.KeySOH, 0}, - "stx": {tcell.ModNone, tcell.KeySTX, 0}, - "etx": {tcell.ModNone, tcell.KeyETX, 0}, - "eot": {tcell.ModNone, tcell.KeyEOT, 0}, - "enq": {tcell.ModNone, tcell.KeyENQ, 0}, - "ack": {tcell.ModNone, tcell.KeyACK, 0}, - "bel": {tcell.ModNone, tcell.KeyBEL, 0}, - "bs": {tcell.ModNone, tcell.KeyBS, 0}, - "tab": {tcell.ModNone, tcell.KeyTAB, 0}, - "lf": {tcell.ModNone, tcell.KeyLF, 0}, - "vt": {tcell.ModNone, tcell.KeyVT, 0}, - "ff": {tcell.ModNone, tcell.KeyFF, 0}, - "cr": {tcell.ModNone, tcell.KeyCR, 0}, - "so": {tcell.ModNone, tcell.KeySO, 0}, - "si": {tcell.ModNone, tcell.KeySI, 0}, - "dle": {tcell.ModNone, tcell.KeyDLE, 0}, - "dc1": {tcell.ModNone, tcell.KeyDC1, 0}, - "dc2": {tcell.ModNone, tcell.KeyDC2, 0}, - "dc3": {tcell.ModNone, tcell.KeyDC3, 0}, - "dc4": {tcell.ModNone, tcell.KeyDC4, 0}, - "nak": {tcell.ModNone, tcell.KeyNAK, 0}, - "syn": {tcell.ModNone, tcell.KeySYN, 0}, - "etb": {tcell.ModNone, tcell.KeyETB, 0}, - "can": {tcell.ModNone, tcell.KeyCAN, 0}, - "em": {tcell.ModNone, tcell.KeyEM, 0}, - "sub": {tcell.ModNone, tcell.KeySUB, 0}, - "esc": {tcell.ModNone, tcell.KeyESC, 0}, - "fs": {tcell.ModNone, tcell.KeyFS, 0}, - "gs": {tcell.ModNone, tcell.KeyGS, 0}, - "rs": {tcell.ModNone, tcell.KeyRS, 0}, - "us": {tcell.ModNone, tcell.KeyUS, 0}, - "del": {tcell.ModNone, tcell.KeyDEL, 0}, + "space": {vaxis.ModifierMask(0), ' '}, + "semicolon": {vaxis.ModifierMask(0), ';'}, + "enter": {vaxis.ModifierMask(0), vaxis.KeyEnter}, + "c-enter": {vaxis.ModCtrl, vaxis.KeyEnter}, + "a-enter": {vaxis.ModAlt, vaxis.KeyEnter}, + "up": {vaxis.ModifierMask(0), vaxis.KeyUp}, + "c-up": {vaxis.ModCtrl, vaxis.KeyUp}, + "a-up": {vaxis.ModAlt, vaxis.KeyUp}, + "down": {vaxis.ModifierMask(0), vaxis.KeyDown}, + "c-down": {vaxis.ModCtrl, vaxis.KeyDown}, + "a-down": {vaxis.ModAlt, vaxis.KeyDown}, + "right": {vaxis.ModifierMask(0), vaxis.KeyRight}, + "c-right": {vaxis.ModCtrl, vaxis.KeyRight}, + "a-right": {vaxis.ModAlt, vaxis.KeyRight}, + "left": {vaxis.ModifierMask(0), vaxis.KeyLeft}, + "c-left": {vaxis.ModCtrl, vaxis.KeyLeft}, + "a-left": {vaxis.ModAlt, vaxis.KeyLeft}, + "upleft": {vaxis.ModifierMask(0), vaxis.KeyUpLeft}, + "upright": {vaxis.ModifierMask(0), vaxis.KeyUpRight}, + "downleft": {vaxis.ModifierMask(0), vaxis.KeyDownLeft}, + "downright": {vaxis.ModifierMask(0), vaxis.KeyDownRight}, + "center": {vaxis.ModifierMask(0), vaxis.KeyCenter}, + "pgup": {vaxis.ModifierMask(0), vaxis.KeyPgUp}, + "c-pgup": {vaxis.ModCtrl, vaxis.KeyPgUp}, + "a-pgup": {vaxis.ModAlt, vaxis.KeyPgUp}, + "pgdn": {vaxis.ModifierMask(0), vaxis.KeyPgDown}, + "c-pgdn": {vaxis.ModCtrl, vaxis.KeyPgDown}, + "a-pgdn": {vaxis.ModAlt, vaxis.KeyPgDown}, + "home": {vaxis.ModifierMask(0), vaxis.KeyHome}, + "end": {vaxis.ModifierMask(0), vaxis.KeyEnd}, + "insert": {vaxis.ModifierMask(0), vaxis.KeyInsert}, + "delete": {vaxis.ModifierMask(0), vaxis.KeyDelete}, + "c-delete": {vaxis.ModCtrl, vaxis.KeyDelete}, + "a-delete": {vaxis.ModAlt, vaxis.KeyDelete}, + "backspace": {vaxis.ModifierMask(0), vaxis.KeyBackspace}, + // "help": {vaxis.ModifierMask(0), vaxis.KeyHelp}, + "exit": {vaxis.ModifierMask(0), vaxis.KeyExit}, + "clear": {vaxis.ModifierMask(0), vaxis.KeyClear}, + "cancel": {vaxis.ModifierMask(0), vaxis.KeyCancel}, + "print": {vaxis.ModifierMask(0), vaxis.KeyPrint}, + "pause": {vaxis.ModifierMask(0), vaxis.KeyPause}, + "backtab": {vaxis.ModShift, vaxis.KeyTab}, + "f1": {vaxis.ModifierMask(0), vaxis.KeyF01}, + "f2": {vaxis.ModifierMask(0), vaxis.KeyF02}, + "f3": {vaxis.ModifierMask(0), vaxis.KeyF03}, + "f4": {vaxis.ModifierMask(0), vaxis.KeyF04}, + "f5": {vaxis.ModifierMask(0), vaxis.KeyF05}, + "f6": {vaxis.ModifierMask(0), vaxis.KeyF06}, + "f7": {vaxis.ModifierMask(0), vaxis.KeyF07}, + "f8": {vaxis.ModifierMask(0), vaxis.KeyF08}, + "f9": {vaxis.ModifierMask(0), vaxis.KeyF09}, + "f10": {vaxis.ModifierMask(0), vaxis.KeyF10}, + "f11": {vaxis.ModifierMask(0), vaxis.KeyF11}, + "f12": {vaxis.ModifierMask(0), vaxis.KeyF12}, + "f13": {vaxis.ModifierMask(0), vaxis.KeyF13}, + "f14": {vaxis.ModifierMask(0), vaxis.KeyF14}, + "f15": {vaxis.ModifierMask(0), vaxis.KeyF15}, + "f16": {vaxis.ModifierMask(0), vaxis.KeyF16}, + "f17": {vaxis.ModifierMask(0), vaxis.KeyF17}, + "f18": {vaxis.ModifierMask(0), vaxis.KeyF18}, + "f19": {vaxis.ModifierMask(0), vaxis.KeyF19}, + "f20": {vaxis.ModifierMask(0), vaxis.KeyF20}, + "f21": {vaxis.ModifierMask(0), vaxis.KeyF21}, + "f22": {vaxis.ModifierMask(0), vaxis.KeyF22}, + "f23": {vaxis.ModifierMask(0), vaxis.KeyF23}, + "f24": {vaxis.ModifierMask(0), vaxis.KeyF24}, + "f25": {vaxis.ModifierMask(0), vaxis.KeyF25}, + "f26": {vaxis.ModifierMask(0), vaxis.KeyF26}, + "f27": {vaxis.ModifierMask(0), vaxis.KeyF27}, + "f28": {vaxis.ModifierMask(0), vaxis.KeyF28}, + "f29": {vaxis.ModifierMask(0), vaxis.KeyF29}, + "f30": {vaxis.ModifierMask(0), vaxis.KeyF30}, + "f31": {vaxis.ModifierMask(0), vaxis.KeyF31}, + "f32": {vaxis.ModifierMask(0), vaxis.KeyF32}, + "f33": {vaxis.ModifierMask(0), vaxis.KeyF33}, + "f34": {vaxis.ModifierMask(0), vaxis.KeyF34}, + "f35": {vaxis.ModifierMask(0), vaxis.KeyF35}, + "f36": {vaxis.ModifierMask(0), vaxis.KeyF36}, + "f37": {vaxis.ModifierMask(0), vaxis.KeyF37}, + "f38": {vaxis.ModifierMask(0), vaxis.KeyF38}, + "f39": {vaxis.ModifierMask(0), vaxis.KeyF39}, + "f40": {vaxis.ModifierMask(0), vaxis.KeyF40}, + "f41": {vaxis.ModifierMask(0), vaxis.KeyF41}, + "f42": {vaxis.ModifierMask(0), vaxis.KeyF42}, + "f43": {vaxis.ModifierMask(0), vaxis.KeyF43}, + "f44": {vaxis.ModifierMask(0), vaxis.KeyF44}, + "f45": {vaxis.ModifierMask(0), vaxis.KeyF45}, + "f46": {vaxis.ModifierMask(0), vaxis.KeyF46}, + "f47": {vaxis.ModifierMask(0), vaxis.KeyF47}, + "f48": {vaxis.ModifierMask(0), vaxis.KeyF48}, + "f49": {vaxis.ModifierMask(0), vaxis.KeyF49}, + "f50": {vaxis.ModifierMask(0), vaxis.KeyF50}, + "f51": {vaxis.ModifierMask(0), vaxis.KeyF51}, + "f52": {vaxis.ModifierMask(0), vaxis.KeyF52}, + "f53": {vaxis.ModifierMask(0), vaxis.KeyF53}, + "f54": {vaxis.ModifierMask(0), vaxis.KeyF54}, + "f55": {vaxis.ModifierMask(0), vaxis.KeyF55}, + "f56": {vaxis.ModifierMask(0), vaxis.KeyF56}, + "f57": {vaxis.ModifierMask(0), vaxis.KeyF57}, + "f58": {vaxis.ModifierMask(0), vaxis.KeyF58}, + "f59": {vaxis.ModifierMask(0), vaxis.KeyF59}, + "f60": {vaxis.ModifierMask(0), vaxis.KeyF60}, + "f61": {vaxis.ModifierMask(0), vaxis.KeyF61}, + "f62": {vaxis.ModifierMask(0), vaxis.KeyF62}, + "f63": {vaxis.ModifierMask(0), vaxis.KeyF63}, + "c-space": {vaxis.ModCtrl, ' '}, + "c-a": {vaxis.ModCtrl, 'a'}, + "c-b": {vaxis.ModCtrl, 'b'}, + "c-c": {vaxis.ModCtrl, 'c'}, + "c-d": {vaxis.ModCtrl, 'd'}, + "c-e": {vaxis.ModCtrl, 'e'}, + "c-f": {vaxis.ModCtrl, 'f'}, + "c-g": {vaxis.ModCtrl, 'g'}, + "c-h": {vaxis.ModCtrl, 'h'}, + "c-i": {vaxis.ModCtrl, 'i'}, + "c-j": {vaxis.ModCtrl, 'j'}, + "c-k": {vaxis.ModCtrl, 'k'}, + "c-l": {vaxis.ModCtrl, 'l'}, + "c-m": {vaxis.ModCtrl, 'm'}, + "c-n": {vaxis.ModCtrl, 'n'}, + "c-o": {vaxis.ModCtrl, 'o'}, + "c-p": {vaxis.ModCtrl, 'p'}, + "c-q": {vaxis.ModCtrl, 'q'}, + "c-r": {vaxis.ModCtrl, 'r'}, + "c-s": {vaxis.ModCtrl, 's'}, + "c-t": {vaxis.ModCtrl, 't'}, + "c-u": {vaxis.ModCtrl, 'u'}, + "c-v": {vaxis.ModCtrl, 'v'}, + "c-w": {vaxis.ModCtrl, 'w'}, + "c-x": {vaxis.ModCtrl, 'x'}, + "c-y": {vaxis.ModCtrl, 'y'}, + "c-z": {vaxis.ModCtrl, 'z'}, + "c-]": {vaxis.ModCtrl, ']'}, + "c-\\": {vaxis.ModCtrl, '\\'}, + "c-[": {vaxis.ModCtrl, '['}, + "c-^": {vaxis.ModCtrl, '^'}, + "c-_": {vaxis.ModCtrl, '_'}, + "a-space": {vaxis.ModAlt, ' '}, + "a-0": {vaxis.ModAlt, '0'}, + "a-1": {vaxis.ModAlt, '1'}, + "a-2": {vaxis.ModAlt, '2'}, + "a-3": {vaxis.ModAlt, '3'}, + "a-4": {vaxis.ModAlt, '4'}, + "a-5": {vaxis.ModAlt, '5'}, + "a-6": {vaxis.ModAlt, '6'}, + "a-7": {vaxis.ModAlt, '7'}, + "a-8": {vaxis.ModAlt, '8'}, + "a-9": {vaxis.ModAlt, '9'}, + "a-a": {vaxis.ModAlt, 'a'}, + "a-b": {vaxis.ModAlt, 'b'}, + "a-c": {vaxis.ModAlt, 'c'}, + "a-d": {vaxis.ModAlt, 'd'}, + "a-e": {vaxis.ModAlt, 'e'}, + "a-f": {vaxis.ModAlt, 'f'}, + "a-g": {vaxis.ModAlt, 'g'}, + "a-h": {vaxis.ModAlt, 'h'}, + "a-i": {vaxis.ModAlt, 'i'}, + "a-j": {vaxis.ModAlt, 'j'}, + "a-k": {vaxis.ModAlt, 'k'}, + "a-l": {vaxis.ModAlt, 'l'}, + "a-m": {vaxis.ModAlt, 'm'}, + "a-n": {vaxis.ModAlt, 'n'}, + "a-o": {vaxis.ModAlt, 'o'}, + "a-p": {vaxis.ModAlt, 'p'}, + "a-q": {vaxis.ModAlt, 'q'}, + "a-r": {vaxis.ModAlt, 'r'}, + "a-s": {vaxis.ModAlt, 's'}, + "a-t": {vaxis.ModAlt, 't'}, + "a-u": {vaxis.ModAlt, 'u'}, + "a-v": {vaxis.ModAlt, 'v'}, + "a-w": {vaxis.ModAlt, 'w'}, + "a-x": {vaxis.ModAlt, 'x'}, + "a-y": {vaxis.ModAlt, 'y'}, + "a-z": {vaxis.ModAlt, 'z'}, + "a-]": {vaxis.ModAlt, ']'}, + "a-\\": {vaxis.ModAlt, '\\'}, + "a-[": {vaxis.ModAlt, '['}, + "a-^": {vaxis.ModAlt, '^'}, + "a-_": {vaxis.ModAlt, '_'}, + "nul": {vaxis.ModCtrl, ' '}, + "soh": {vaxis.ModCtrl, 'a'}, + "stx": {vaxis.ModCtrl, 'b'}, + "etx": {vaxis.ModCtrl, 'c'}, + "eot": {vaxis.ModCtrl, 'd'}, + "enq": {vaxis.ModCtrl, 'e'}, + "ack": {vaxis.ModCtrl, 'f'}, + "bel": {vaxis.ModCtrl, 'g'}, + "bs": {vaxis.ModCtrl, 'h'}, + "tab": {vaxis.ModifierMask(0), vaxis.KeyTab}, + "lf": {vaxis.ModCtrl, 'j'}, + "vt": {vaxis.ModCtrl, 'k'}, + "ff": {vaxis.ModCtrl, 'l'}, + "cr": {vaxis.ModifierMask(0), vaxis.KeyEnter}, + "so": {vaxis.ModCtrl, 'n'}, + "si": {vaxis.ModCtrl, 'o'}, + "dle": {vaxis.ModCtrl, 'p'}, + "dc1": {vaxis.ModCtrl, 'q'}, + "dc2": {vaxis.ModCtrl, 'r'}, + "dc3": {vaxis.ModCtrl, 's'}, + "dc4": {vaxis.ModCtrl, 't'}, + "nak": {vaxis.ModCtrl, 'u'}, + "syn": {vaxis.ModCtrl, 'v'}, + "etb": {vaxis.ModCtrl, 'w'}, + "can": {vaxis.ModCtrl, 'x'}, + "em": {vaxis.ModCtrl, 'y'}, + "sub": {vaxis.ModCtrl, 'z'}, + "esc": {vaxis.ModifierMask(0), vaxis.KeyEsc}, + "fs": {vaxis.ModCtrl, '\\'}, + "gs": {vaxis.ModCtrl, ']'}, + "rs": {vaxis.ModCtrl, '^'}, + "us": {vaxis.ModCtrl, '_'}, + "del": {vaxis.ModifierMask(0), vaxis.KeyDelete}, } func ParseKeyStrokes(keystrokes string) ([]KeyStroke, error) { @@ -746,9 +737,8 @@ func ParseKeyStrokes(keystrokes string) ([]KeyStroke, error) { fallthrough default: strokes = append(strokes, KeyStroke{ - Modifiers: tcell.ModNone, - Key: tcell.KeyRune, - Rune: tok, + Modifiers: vaxis.ModifierMask(0), + Key: tok, }) } } diff --git a/config/binds_test.go b/config/binds_test.go index dab3b9f14290..c5f573ee44ea 100644 --- a/config/binds_test.go +++ b/config/binds_test.go @@ -4,7 +4,7 @@ import ( "fmt" "testing" - "github.com/gdamore/tcell/v2" + "git.sr.ht/~rockorager/vaxis" "github.com/stretchr/testify/assert" ) @@ -32,26 +32,26 @@ func TestGetBinding(t *testing.T) { } test([]KeyStroke{ - {tcell.ModNone, tcell.KeyRune, 'a'}, + {vaxis.ModifierMask(0), 'a'}, }, BINDING_INCOMPLETE, "") test([]KeyStroke{ - {tcell.ModNone, tcell.KeyRune, 'a'}, - {tcell.ModNone, tcell.KeyRune, 'b'}, - {tcell.ModNone, tcell.KeyRune, 'c'}, + {vaxis.ModifierMask(0), 'a'}, + {vaxis.ModifierMask(0), 'b'}, + {vaxis.ModifierMask(0), 'c'}, }, BINDING_FOUND, ":abc") test([]KeyStroke{ - {tcell.ModNone, tcell.KeyRune, 'c'}, - {tcell.ModNone, tcell.KeyRune, 'b'}, - {tcell.ModNone, tcell.KeyRune, 'a'}, + {vaxis.ModifierMask(0), 'c'}, + {vaxis.ModifierMask(0), 'b'}, + {vaxis.ModifierMask(0), 'a'}, }, BINDING_FOUND, ":cba") test([]KeyStroke{ - {tcell.ModNone, tcell.KeyRune, 'f'}, - {tcell.ModNone, tcell.KeyRune, 'o'}, + {vaxis.ModifierMask(0), 'f'}, + {vaxis.ModifierMask(0), 'o'}, }, BINDING_INCOMPLETE, "") test([]KeyStroke{ - {tcell.ModNone, tcell.KeyRune, '4'}, - {tcell.ModNone, tcell.KeyRune, '0'}, - {tcell.ModNone, tcell.KeyRune, '4'}, + {vaxis.ModifierMask(0), '4'}, + {vaxis.ModifierMask(0), '0'}, + {vaxis.ModifierMask(0), '4'}, }, BINDING_NOT_FOUND, "") add("<C-a>", "c-a") @@ -59,18 +59,18 @@ func TestGetBinding(t *testing.T) { add("<C-PgUp>", ":prev") add("<C-Enter>", ":open") test([]KeyStroke{ - {tcell.ModCtrl, tcell.KeyCtrlA, 0}, + {vaxis.ModCtrl, 'a'}, }, BINDING_FOUND, "c-a") test([]KeyStroke{ - {tcell.ModCtrl, tcell.KeyDown, 0}, + {vaxis.ModCtrl, vaxis.KeyDown}, }, BINDING_FOUND, ":next") test([]KeyStroke{ - {tcell.ModCtrl, tcell.KeyPgUp, 0}, + {vaxis.ModCtrl, vaxis.KeyPgUp}, }, BINDING_FOUND, ":prev") test([]KeyStroke{ - {tcell.ModCtrl, tcell.KeyPgDn, 0}, + {vaxis.ModCtrl, vaxis.KeyPgDown}, }, BINDING_NOT_FOUND, "") test([]KeyStroke{ - {tcell.ModCtrl, tcell.KeyEnter, 0}, + {vaxis.ModCtrl, vaxis.KeyEnter}, }, BINDING_FOUND, ":open") } diff --git a/lib/ui/textinput.go b/lib/ui/textinput.go index 107d354c1980..d52ee07a687c 100644 --- a/lib/ui/textinput.go +++ b/lib/ui/textinput.go @@ -336,50 +336,52 @@ func (ti *TextInput) OnFocusLost(onFocusLost func(ti *TextInput)) { func (ti *TextInput) Event(event vaxis.Event) bool { ti.Lock() defer ti.Unlock() - if event, ok := event.(*tcell.EventKey); ok { + if key, ok := event.(vaxis.Key); ok { c := ti.completeKey - if c != nil && c.Key == event.Key() && c.Modifiers == event.Modifiers() { + if c != nil && key.Matches(c.Key, c.Modifiers) { ti.showCompletions(true) return true } ti.invalidateCompletions() - switch event.Key() { - case tcell.KeyBackspace, tcell.KeyBackspace2: + switch { + case key.Matches(vaxis.KeyBackspace): ti.backspace() - case tcell.KeyCtrlD, tcell.KeyDelete: + case key.Matches('d', vaxis.ModCtrl), key.Matches(vaxis.KeyDelete): ti.deleteChar() - case tcell.KeyCtrlB, tcell.KeyLeft: + case key.Matches('b', vaxis.ModCtrl), key.Matches(vaxis.KeyLeft): if ti.index > 0 { ti.index-- ti.ensureScroll() ti.Invalidate() } - case tcell.KeyCtrlF, tcell.KeyRight: + case key.Matches('f', vaxis.ModCtrl), key.Matches(vaxis.KeyRight): if ti.index < len(ti.text) { ti.index++ ti.ensureScroll() ti.Invalidate() } - case tcell.KeyCtrlA, tcell.KeyHome: + case key.Matches('a', vaxis.ModCtrl), key.Matches(vaxis.KeyHome): ti.index = 0 ti.ensureScroll() ti.Invalidate() - case tcell.KeyCtrlE, tcell.KeyEnd: + case key.Matches('e', vaxis.ModCtrl), key.Matches(vaxis.KeyEnd): ti.index = len(ti.text) ti.ensureScroll() ti.Invalidate() - case tcell.KeyCtrlK: + case key.Matches('k', vaxis.ModCtrl): ti.deleteLineForward() - case tcell.KeyCtrlW: + case key.Matches('w', vaxis.ModCtrl): ti.deleteWord() - case tcell.KeyCtrlU: + case key.Matches('u', vaxis.ModCtrl): ti.deleteLineBackward() - case tcell.KeyESC: + case key.Matches(vaxis.KeyEsc): ti.Invalidate() - case tcell.KeyRune: - ti.insert(event.Rune()) + case key.Text != "": + for _, ch := range key.Text { + ti.insert(ch) + } } } return true @@ -482,9 +484,9 @@ func (c *completions) exec() { } func (c *completions) Event(e vaxis.Event) bool { - if e, ok := e.(*tcell.EventKey); ok { + if e, ok := e.(vaxis.Key); ok { k := c.ti.completeKey - if k != nil && k.Key == e.Key() && k.Modifiers == e.Modifiers() { + if k != nil && e.Matches(k.Key, k.Modifiers) { if len(c.ti.completions) == 1 { c.ti.completeIndex = 0 c.exec() @@ -498,14 +500,16 @@ func (c *completions) Event(e vaxis.Event) bool { return true } - switch e.Key() { - case tcell.KeyCtrlN, tcell.KeyDown: + switch { + case e.Matches('n', vaxis.ModCtrl), e.Matches(vaxis.KeyDown): c.next() return true - case tcell.KeyBacktab, tcell.KeyCtrlP, tcell.KeyUp: + case e.Matches(vaxis.KeyTab, vaxis.ModShift), + e.Matches('p', vaxis.ModCtrl), + e.Matches(vaxis.KeyUp): c.prev() return true - case tcell.KeyEnter: + case e.Matches(vaxis.KeyEnter): if c.index() >= 0 { c.exec() return true diff --git a/lib/ui/ui.go b/lib/ui/ui.go index f17ca4b383d5..84c34459f038 100644 --- a/lib/ui/ui.go +++ b/lib/ui/ui.go @@ -9,7 +9,6 @@ import ( "git.sr.ht/~rjarry/aerc/config" "git.sr.ht/~rjarry/aerc/log" "git.sr.ht/~rockorager/vaxis" - "github.com/gdamore/tcell/v2" ) // Use unbuffered channels (always blocking unless somebody can read @@ -143,7 +142,6 @@ func HandleEvent(event vaxis.Event) { case vaxis.Redraw: Invalidate() default: - event = tcell.TcellEvent(event) // if we have a popover, and it can handle the event, it does so if state.popover == nil || !state.popover.Event(event) { // otherwise, we send the event to the main content -- 2.43.0
Replace all tcell.Style objects with vaxis.Style objects Signed-off-by: Tim Culverhouse <tim@timculverhouse.com> --- app/authinfo.go | 4 +- app/compose.go | 2 +- app/dirlist.go | 10 +- app/listbox.go | 5 +- app/msglist.go | 4 +- app/msgviewer.go | 5 +- app/partswitcher.go | 2 +- app/pgpinfo.go | 4 +- app/spinner.go | 5 +- app/status.go | 8 +- config/style.go | 233 ++++++++++++++++++++++++++++++++++------- config/ui.go | 16 +-- lib/parse/ansi.go | 202 ++++++++++++++++++----------------- lib/parse/ansi_test.go | 8 +- lib/ui/context.go | 8 +- lib/ui/table.go | 18 ++-- lib/ui/text.go | 6 +- 17 files changed, 354 insertions(+), 186 deletions(-) diff --git a/app/authinfo.go b/app/authinfo.go index 982edcb2de11..d201aa8d8edd 100644 --- a/app/authinfo.go +++ b/app/authinfo.go @@ -6,7 +6,7 @@ import ( "git.sr.ht/~rjarry/aerc/config" "git.sr.ht/~rjarry/aerc/lib/auth" "git.sr.ht/~rjarry/aerc/lib/ui" - "github.com/gdamore/tcell/v2" + "git.sr.ht/~rockorager/vaxis" "github.com/mattn/go-runewidth" ) @@ -36,7 +36,7 @@ func (a *AuthInfo) Draw(ctx *ui.Context) { checkBounds := func(x int) bool { return x < ctx.Width() } - setResult := func(result auth.Result) (string, tcell.Style) { + setResult := func(result auth.Result) (string, vaxis.Style) { switch result { case auth.ResultNone: return "none", defaultStyle diff --git a/app/compose.go b/app/compose.go index 14341ef6f166..a73127c9c3fd 100644 --- a/app/compose.go +++ b/app/compose.go @@ -1547,7 +1547,7 @@ func (c *Composer) updateGrid() { borderChar := c.acct.UiConfig().BorderCharHorizontal grid.AddChild(heditors).At(0, 0) grid.AddChild(c.crypto).At(1, 0) - grid.AddChild(ui.NewFill(borderChar, tcell.VaxisStyle(borderStyle))).At(2, 0) + grid.AddChild(ui.NewFill(borderChar, borderStyle)).At(2, 0) if c.review != nil { grid.AddChild(c.review).At(3, 0) } else if c.editor != nil { diff --git a/app/dirlist.go b/app/dirlist.go index 53aeed9d15de..e92283740ed9 100644 --- a/app/dirlist.go +++ b/app/dirlist.go @@ -295,12 +295,12 @@ func (dirlist *DirectoryList) Draw(ctx *ui.Context) { func (dirlist *DirectoryList) renderDir( path string, conf *config.UIConfig, data models.TemplateData, selected bool, width int, -) (string, string, tcell.Style) { +) (string, string, vaxis.Style) { var left, right string var buf bytes.Buffer var styles []config.StyleObject - var style tcell.Style + var style vaxis.Style r, u, _ := dirlist.GetRUECount(path) if u > 0 { @@ -353,7 +353,7 @@ func (dirlist *DirectoryList) renderDir( left = lbuf.Truncate(lwidth-1, '…') } else { for i := 0; i < (width - lwidth - rwidth - 1); i += 1 { - lbuf.Write(' ', tcell.StyleDefault) + lbuf.Write(' ', vaxis.Style{}) } left = lbuf.String() right = rbuf.String() @@ -363,8 +363,8 @@ func (dirlist *DirectoryList) renderDir( } func (dirlist *DirectoryList) drawScrollbar(ctx *ui.Context) { - gutterStyle := tcell.StyleDefault - pillStyle := tcell.StyleDefault.Reverse(true) + gutterStyle := vaxis.Style{} + pillStyle := vaxis.Style{Attribute: vaxis.AttrReverse} // gutter ctx.Fill(0, 0, 1, ctx.Height(), ' ', gutterStyle) diff --git a/app/listbox.go b/app/listbox.go index d891b24dff08..93d78828e6d7 100644 --- a/app/listbox.go +++ b/app/listbox.go @@ -9,7 +9,6 @@ import ( "git.sr.ht/~rjarry/aerc/lib/ui" "git.sr.ht/~rjarry/aerc/log" "git.sr.ht/~rockorager/vaxis" - "github.com/gdamore/tcell/v2" "github.com/mattn/go-runewidth" ) @@ -197,8 +196,8 @@ func (lb *ListBox) drawBox(ctx *ui.Context) { } func (lb *ListBox) drawScrollbar(ctx *ui.Context) { - gutterStyle := tcell.StyleDefault - pillStyle := tcell.StyleDefault.Reverse(true) + gutterStyle := vaxis.Style{} + pillStyle := vaxis.Style{Attribute: vaxis.AttrReverse} // gutter h := ctx.Height() diff --git a/app/msglist.go b/app/msglist.go index 9217423941ce..8106c3cfc694 100644 --- a/app/msglist.go +++ b/app/msglist.go @@ -107,8 +107,8 @@ func (ml *MessageList) Draw(ctx *ui.Context) { return false } - getRowStyle := func(t *ui.Table, r int) tcell.Style { - var style tcell.Style + getRowStyle := func(t *ui.Table, r int) vaxis.Style { + var style vaxis.Style row := &t.Rows[r] params, _ := row.Priv.(messageRowParams) if params.uid == store.SelectedUid() { diff --git a/app/msgviewer.go b/app/msgviewer.go index 179deca410d2..a22d2c15df70 100644 --- a/app/msgviewer.go +++ b/app/msgviewer.go @@ -13,7 +13,6 @@ import ( "github.com/danwakefield/fnmatch" "github.com/emersion/go-message/textproto" - "github.com/gdamore/tcell/v2" "github.com/mattn/go-runewidth" "git.sr.ht/~rjarry/aerc/config" @@ -146,10 +145,10 @@ func NewMessageViewer( grid.AddChild(header).At(0, 0) if msg.MessageDetails() != nil || acct.UiConfig().IconUnencrypted != "" { grid.AddChild(NewPGPInfo(msg.MessageDetails(), acct.UiConfig())).At(1, 0) - grid.AddChild(ui.NewFill(borderChar, tcell.VaxisStyle(borderStyle))).At(2, 0) + grid.AddChild(ui.NewFill(borderChar, borderStyle)).At(2, 0) grid.AddChild(switcher).At(3, 0) } else { - grid.AddChild(ui.NewFill(borderChar, tcell.VaxisStyle(borderStyle))).At(1, 0) + grid.AddChild(ui.NewFill(borderChar, borderStyle)).At(1, 0) grid.AddChild(switcher).At(2, 0) } diff --git a/app/partswitcher.go b/app/partswitcher.go index 8552d32b4682..5dc996f783d5 100644 --- a/app/partswitcher.go +++ b/app/partswitcher.go @@ -103,7 +103,7 @@ func (ps *PartSwitcher) Draw(ctx *ui.Context) { ps.UpdateScroller(ps.height, n) ps.EnsureScroll(ps.selected) - var styleSwitcher, styleFile, styleMime tcell.Style + var styleSwitcher, styleFile, styleMime vaxis.Style scrollbarWidth := 0 if ps.NeedScrollbar() { diff --git a/app/pgpinfo.go b/app/pgpinfo.go index d5bbc696a3c2..d2361a01afef 100644 --- a/app/pgpinfo.go +++ b/app/pgpinfo.go @@ -8,7 +8,7 @@ import ( "git.sr.ht/~rjarry/aerc/config" "git.sr.ht/~rjarry/aerc/lib/ui" "git.sr.ht/~rjarry/aerc/models" - "github.com/gdamore/tcell/v2" + "git.sr.ht/~rockorager/vaxis" ) type PGPInfo struct { @@ -27,7 +27,7 @@ func (p *PGPInfo) DrawSignature(ctx *ui.Context) { defaultStyle := p.uiConfig.GetStyle(config.STYLE_DEFAULT) var icon string - var indicatorStyle, textstyle tcell.Style + var indicatorStyle, textstyle vaxis.Style textstyle = defaultStyle var indicatorText, messageText string // TODO: Nicer prompt for TOFU, fetch from keyserver, etc diff --git a/app/spinner.go b/app/spinner.go index bcf8168ad350..2c675e3c5495 100644 --- a/app/spinner.go +++ b/app/spinner.go @@ -5,11 +5,10 @@ import ( "sync/atomic" "time" - "github.com/gdamore/tcell/v2" - "git.sr.ht/~rjarry/aerc/config" "git.sr.ht/~rjarry/aerc/lib/ui" "git.sr.ht/~rjarry/aerc/log" + "git.sr.ht/~rockorager/vaxis" ) type Spinner struct { @@ -17,7 +16,7 @@ type Spinner struct { frames []string interval time.Duration stop chan struct{} - style tcell.Style + style vaxis.Style } func NewSpinner(uiConf *config.UIConfig) *Spinner { diff --git a/app/status.go b/app/status.go index dbedea7e8f06..952a3b98a1fd 100644 --- a/app/status.go +++ b/app/status.go @@ -5,7 +5,6 @@ import ( "sync" "time" - "github.com/gdamore/tcell/v2" "github.com/mattn/go-runewidth" "git.sr.ht/~rjarry/aerc/config" @@ -13,6 +12,7 @@ import ( "git.sr.ht/~rjarry/aerc/lib/templates" "git.sr.ht/~rjarry/aerc/lib/ui" "git.sr.ht/~rjarry/aerc/log" + "git.sr.ht/~rockorager/vaxis" ) type StatusLine struct { @@ -23,7 +23,7 @@ type StatusLine struct { } type StatusMessage struct { - style tcell.Style + style vaxis.Style message string } @@ -60,7 +60,7 @@ func (status *StatusLine) Draw(ctx *ui.Context) { config.Statusline.StatusColumns, config.Statusline.ColumnSeparator, nil, - func(*ui.Table, int) tcell.Style { return style }, + func(*ui.Table, int) vaxis.Style { return style }, ) var buf bytes.Buffer cells := make([]string, len(table.Columns)) @@ -156,6 +156,6 @@ func (status *StatusLine) uiConfig() *config.UIConfig { return SelectedAccountUiConfig() } -func (msg *StatusMessage) Color(style tcell.Style) { +func (msg *StatusMessage) Color(style vaxis.Style) { msg.style = style } diff --git a/config/style.go b/config/style.go index 8a88dcfc9e1b..efe847220767 100644 --- a/config/style.go +++ b/config/style.go @@ -9,8 +9,8 @@ import ( "strings" "git.sr.ht/~rjarry/aerc/lib/xdg" + "git.sr.ht/~rockorager/vaxis" "github.com/emersion/go-message/mail" - "github.com/gdamore/tcell/v2" "github.com/go-ini/ini" ) @@ -116,8 +116,8 @@ var StyleNames = map[string]StyleObject{ } type Style struct { - Fg tcell.Color - Bg tcell.Color + Fg vaxis.Color + Bg vaxis.Color Bold bool Blink bool Underline bool @@ -129,16 +129,30 @@ type Style struct { re *regexp.Regexp // only for msglist } -func (s Style) Get() tcell.Style { - return tcell.StyleDefault. - Foreground(s.Fg). - Background(s.Bg). - Bold(s.Bold). - Blink(s.Blink). - Underline(s.Underline). - Reverse(s.Reverse). - Italic(s.Italic). - Dim(s.Dim) +func (s Style) Get() vaxis.Style { + vx := vaxis.Style{ + Foreground: s.Fg, + Background: s.Bg, + } + if s.Bold { + vx.Attribute |= vaxis.AttrBold + } + if s.Blink { + vx.Attribute |= vaxis.AttrBlink + } + if s.Underline { + vx.UnderlineStyle |= vaxis.UnderlineSingle + } + if s.Reverse { + vx.Attribute |= vaxis.AttrReverse + } + if s.Italic { + vx.Attribute |= vaxis.AttrItalic + } + if s.Dim { + vx.Attribute |= vaxis.AttrDim + } + return vx } func (s *Style) Normal() { @@ -151,8 +165,8 @@ func (s *Style) Normal() { } func (s *Style) Default() *Style { - s.Fg = tcell.ColorDefault - s.Bg = tcell.ColorDefault + s.Fg = 0 + s.Bg = 0 return s } @@ -176,15 +190,22 @@ func boolSwitch(val string, cur_val bool) (bool, error) { } } -func extractColor(val string) tcell.Color { +func extractColor(val string) vaxis.Color { // Check if the string can be interpreted as a number, indicating a // reference to the color number. Otherwise retrieve the number based // on the name. if i, err := strconv.ParseUint(val, 10, 8); err == nil { - return tcell.PaletteColor(int(i)) - } else { - return tcell.GetColor(val) + return vaxis.IndexColor(uint8(i)) } + if strings.HasPrefix(val, "#") { + val = strings.TrimPrefix(val, "#") + hex, err := strconv.ParseUint(val, 16, 32) + if err != nil { + return 0 + } + return vaxis.HexColor(uint32(hex)) + } + return colorNames[val] } func (s *Style) Set(attr, val string) error { @@ -243,10 +264,10 @@ func (s *Style) Set(attr, val string) error { func (s Style) composeWith(styles []*Style) Style { newStyle := s for _, st := range styles { - if st.Fg != s.Fg && st.Fg != tcell.ColorDefault { + if st.Fg != s.Fg && st.Fg != 0 { newStyle.Fg = st.Fg } - if st.Bg != s.Bg && st.Bg != tcell.ColorDefault { + if st.Bg != s.Bg && st.Bg != 0 { newStyle.Bg = st.Bg } if st.Bold != s.Bold { @@ -297,13 +318,13 @@ func NewStyleSet() StyleSet { // *error.bold=true conf.base.Bold = true // error.fg=red - conf.base.Fg = tcell.ColorRed + conf.base.Fg = vaxis.IndexColor(1) case STYLE_WARNING: // warning.fg=yellow - conf.base.Fg = tcell.ColorYellow + conf.base.Fg = vaxis.IndexColor(3) case STYLE_SUCCESS: // success.fg=green - conf.base.Fg = tcell.ColorGreen + conf.base.Fg = vaxis.IndexColor(2) case STYLE_TITLE: // title.reverse=true conf.base.Reverse = true @@ -315,28 +336,28 @@ func NewStyleSet() StyleSet { conf.base.Reverse = true case STYLE_STATUSLINE_ERROR: // *error.bold=true - conf.base.Fg = tcell.ColorRed + conf.base.Fg = vaxis.IndexColor(1) // statusline_error.fg=red conf.base.Bold = true // statusline_error.reverse=true conf.base.Reverse = true case STYLE_STATUSLINE_WARNING: // statusline_warning.fg=yellow - conf.base.Fg = tcell.ColorYellow + conf.base.Fg = vaxis.IndexColor(3) // statusline_warning.reverse=true conf.base.Reverse = true case STYLE_STATUSLINE_SUCCESS: - conf.base.Fg = tcell.ColorGreen + conf.base.Fg = vaxis.IndexColor(2) conf.base.Reverse = true case STYLE_MSGLIST_UNREAD: // msglist_unread.bold=true conf.base.Bold = true case STYLE_MSGLIST_DELETED: // msglist_deleted.fg=gray - conf.base.Fg = tcell.ColorGray + conf.base.Fg = vaxis.IndexColor(8) case STYLE_MSGLIST_RESULT: // msglist_result.fg=green - conf.base.Fg = tcell.ColorGreen + conf.base.Fg = vaxis.IndexColor(2) case STYLE_MSGLIST_PILL: // msglist_pill.reverse=true conf.base.Reverse = true @@ -391,24 +412,24 @@ func (c *StyleConf) getStyle(h *mail.Header) *Style { return &c.base } -func (ss StyleSet) Get(so StyleObject, h *mail.Header) tcell.Style { +func (ss StyleSet) Get(so StyleObject, h *mail.Header) vaxis.Style { return ss.objects[so].getStyle(h).Get() } -func (ss StyleSet) Selected(so StyleObject, h *mail.Header) tcell.Style { +func (ss StyleSet) Selected(so StyleObject, h *mail.Header) vaxis.Style { return ss.selected[so].getStyle(h).Get() } -func (ss StyleSet) UserStyle(name string) tcell.Style { +func (ss StyleSet) UserStyle(name string) vaxis.Style { if style, found := ss.user[name]; found { return style.Get() } - return tcell.StyleDefault + return vaxis.Style{} } func (ss StyleSet) Compose( so StyleObject, sos []StyleObject, h *mail.Header, -) tcell.Style { +) vaxis.Style { base := *ss.objects[so].getStyle(h) styles := make([]*Style, len(sos)) for i, so := range sos { @@ -420,7 +441,7 @@ func (ss StyleSet) Compose( func (ss StyleSet) ComposeSelected( so StyleObject, sos []StyleObject, h *mail.Header, -) tcell.Style { +) vaxis.Style { base := *ss.selected[so].getStyle(h) styles := make([]*Style, len(sos)) for i, so := range sos { @@ -590,3 +611,145 @@ func fnmatchToRegex(pattern string) (*regexp.Regexp, error) { p = strings.ReplaceAll(p, `\*`, `.*`) return regexp.Compile(strings.ReplaceAll(p, `\?`, `.`)) } + +var colorNames = map[string]vaxis.Color{ + "black": vaxis.IndexColor(0), + "maroon": vaxis.IndexColor(1), + "green": vaxis.IndexColor(2), + "olive": vaxis.IndexColor(3), + "navy": vaxis.IndexColor(4), + "purple": vaxis.IndexColor(5), + "teal": vaxis.IndexColor(6), + "silver": vaxis.IndexColor(7), + "gray": vaxis.IndexColor(8), + "red": vaxis.IndexColor(9), + "lime": vaxis.IndexColor(10), + "yellow": vaxis.IndexColor(11), + "blue": vaxis.IndexColor(12), + "fuchsia": vaxis.IndexColor(13), + "aqua": vaxis.IndexColor(14), + "white": vaxis.IndexColor(15), + "aliceblue": vaxis.HexColor(0xF0F8FF), + "antiquewhite": vaxis.HexColor(0xFAEBD7), + "aquamarine": vaxis.HexColor(0x7FFFD4), + "azure": vaxis.HexColor(0xF0FFFF), + "beige": vaxis.HexColor(0xF5F5DC), + "bisque": vaxis.HexColor(0xFFE4C4), + "blanchedalmond": vaxis.HexColor(0xFFEBCD), + "blueviolet": vaxis.HexColor(0x8A2BE2), + "brown": vaxis.HexColor(0xA52A2A), + "burlywood": vaxis.HexColor(0xDEB887), + "cadetblue": vaxis.HexColor(0x5F9EA0), + "chartreuse": vaxis.HexColor(0x7FFF00), + "chocolate": vaxis.HexColor(0xD2691E), + "coral": vaxis.HexColor(0xFF7F50), + "cornflowerblue": vaxis.HexColor(0x6495ED), + "cornsilk": vaxis.HexColor(0xFFF8DC), + "crimson": vaxis.HexColor(0xDC143C), + "darkblue": vaxis.HexColor(0x00008B), + "darkcyan": vaxis.HexColor(0x008B8B), + "darkgoldenrod": vaxis.HexColor(0xB8860B), + "darkgray": vaxis.HexColor(0xA9A9A9), + "darkgreen": vaxis.HexColor(0x006400), + "darkkhaki": vaxis.HexColor(0xBDB76B), + "darkmagenta": vaxis.HexColor(0x8B008B), + "darkolivegreen": vaxis.HexColor(0x556B2F), + "darkorange": vaxis.HexColor(0xFF8C00), + "darkorchid": vaxis.HexColor(0x9932CC), + "darkred": vaxis.HexColor(0x8B0000), + "darksalmon": vaxis.HexColor(0xE9967A), + "darkseagreen": vaxis.HexColor(0x8FBC8F), + "darkslateblue": vaxis.HexColor(0x483D8B), + "darkslategray": vaxis.HexColor(0x2F4F4F), + "darkturquoise": vaxis.HexColor(0x00CED1), + "darkviolet": vaxis.HexColor(0x9400D3), + "deeppink": vaxis.HexColor(0xFF1493), + "deepskyblue": vaxis.HexColor(0x00BFFF), + "dimgray": vaxis.HexColor(0x696969), + "dodgerblue": vaxis.HexColor(0x1E90FF), + "firebrick": vaxis.HexColor(0xB22222), + "floralwhite": vaxis.HexColor(0xFFFAF0), + "forestgreen": vaxis.HexColor(0x228B22), + "gainsboro": vaxis.HexColor(0xDCDCDC), + "ghostwhite": vaxis.HexColor(0xF8F8FF), + "gold": vaxis.HexColor(0xFFD700), + "goldenrod": vaxis.HexColor(0xDAA520), + "greenyellow": vaxis.HexColor(0xADFF2F), + "honeydew": vaxis.HexColor(0xF0FFF0), + "hotpink": vaxis.HexColor(0xFF69B4), + "indianred": vaxis.HexColor(0xCD5C5C), + "indigo": vaxis.HexColor(0x4B0082), + "ivory": vaxis.HexColor(0xFFFFF0), + "khaki": vaxis.HexColor(0xF0E68C), + "lavender": vaxis.HexColor(0xE6E6FA), + "lavenderblush": vaxis.HexColor(0xFFF0F5), + "lawngreen": vaxis.HexColor(0x7CFC00), + "lemonchiffon": vaxis.HexColor(0xFFFACD), + "lightblue": vaxis.HexColor(0xADD8E6), + "lightcoral": vaxis.HexColor(0xF08080), + "lightcyan": vaxis.HexColor(0xE0FFFF), + "lightgoldenrodyellow": vaxis.HexColor(0xFAFAD2), + "lightgray": vaxis.HexColor(0xD3D3D3), + "lightgreen": vaxis.HexColor(0x90EE90), + "lightpink": vaxis.HexColor(0xFFB6C1), + "lightsalmon": vaxis.HexColor(0xFFA07A), + "lightseagreen": vaxis.HexColor(0x20B2AA), + "lightskyblue": vaxis.HexColor(0x87CEFA), + "lightslategray": vaxis.HexColor(0x778899), + "lightsteelblue": vaxis.HexColor(0xB0C4DE), + "lightyellow": vaxis.HexColor(0xFFFFE0), + "limegreen": vaxis.HexColor(0x32CD32), + "linen": vaxis.HexColor(0xFAF0E6), + "mediumaquamarine": vaxis.HexColor(0x66CDAA), + "mediumblue": vaxis.HexColor(0x0000CD), + "mediumorchid": vaxis.HexColor(0xBA55D3), + "mediumpurple": vaxis.HexColor(0x9370DB), + "mediumseagreen": vaxis.HexColor(0x3CB371), + "mediumslateblue": vaxis.HexColor(0x7B68EE), + "mediumspringgreen": vaxis.HexColor(0x00FA9A), + "mediumturquoise": vaxis.HexColor(0x48D1CC), + "mediumvioletred": vaxis.HexColor(0xC71585), + "midnightblue": vaxis.HexColor(0x191970), + "mintcream": vaxis.HexColor(0xF5FFFA), + "mistyrose": vaxis.HexColor(0xFFE4E1), + "moccasin": vaxis.HexColor(0xFFE4B5), + "navajowhite": vaxis.HexColor(0xFFDEAD), + "oldlace": vaxis.HexColor(0xFDF5E6), + "olivedrab": vaxis.HexColor(0x6B8E23), + "orange": vaxis.HexColor(0xFFA500), + "orangered": vaxis.HexColor(0xFF4500), + "orchid": vaxis.HexColor(0xDA70D6), + "palegoldenrod": vaxis.HexColor(0xEEE8AA), + "palegreen": vaxis.HexColor(0x98FB98), + "paleturquoise": vaxis.HexColor(0xAFEEEE), + "palevioletred": vaxis.HexColor(0xDB7093), + "papayawhip": vaxis.HexColor(0xFFEFD5), + "peachpuff": vaxis.HexColor(0xFFDAB9), + "peru": vaxis.HexColor(0xCD853F), + "pink": vaxis.HexColor(0xFFC0CB), + "plum": vaxis.HexColor(0xDDA0DD), + "powderblue": vaxis.HexColor(0xB0E0E6), + "rebeccapurple": vaxis.HexColor(0x663399), + "rosybrown": vaxis.HexColor(0xBC8F8F), + "royalblue": vaxis.HexColor(0x4169E1), + "saddlebrown": vaxis.HexColor(0x8B4513), + "salmon": vaxis.HexColor(0xFA8072), + "sandybrown": vaxis.HexColor(0xF4A460), + "seagreen": vaxis.HexColor(0x2E8B57), + "seashell": vaxis.HexColor(0xFFF5EE), + "sienna": vaxis.HexColor(0xA0522D), + "skyblue": vaxis.HexColor(0x87CEEB), + "slateblue": vaxis.HexColor(0x6A5ACD), + "slategray": vaxis.HexColor(0x708090), + "snow": vaxis.HexColor(0xFFFAFA), + "springgreen": vaxis.HexColor(0x00FF7F), + "steelblue": vaxis.HexColor(0x4682B4), + "tan": vaxis.HexColor(0xD2B48C), + "thistle": vaxis.HexColor(0xD8BFD8), + "tomato": vaxis.HexColor(0xFF6347), + "turquoise": vaxis.HexColor(0x40E0D0), + "violet": vaxis.HexColor(0xEE82EE), + "wheat": vaxis.HexColor(0xF5DEB3), + "whitesmoke": vaxis.HexColor(0xF5F5F5), + "yellowgreen": vaxis.HexColor(0x9ACD32), +} diff --git a/config/ui.go b/config/ui.go index f339066e1160..f19e563609d3 100644 --- a/config/ui.go +++ b/config/ui.go @@ -13,8 +13,8 @@ import ( "git.sr.ht/~rjarry/aerc/lib/templates" "git.sr.ht/~rjarry/aerc/log" + "git.sr.ht/~rockorager/vaxis" "github.com/emersion/go-message/mail" - "github.com/gdamore/tcell/v2" "github.com/go-ini/ini" ) @@ -520,39 +520,39 @@ func (base *UIConfig) mergeContextual( return base } -func (uiConfig *UIConfig) GetUserStyle(name string) tcell.Style { +func (uiConfig *UIConfig) GetUserStyle(name string) vaxis.Style { return uiConfig.style.UserStyle(name) } -func (uiConfig *UIConfig) GetStyle(so StyleObject) tcell.Style { +func (uiConfig *UIConfig) GetStyle(so StyleObject) vaxis.Style { return uiConfig.style.Get(so, nil) } -func (uiConfig *UIConfig) GetStyleSelected(so StyleObject) tcell.Style { +func (uiConfig *UIConfig) GetStyleSelected(so StyleObject) vaxis.Style { return uiConfig.style.Selected(so, nil) } func (uiConfig *UIConfig) GetComposedStyle(base StyleObject, styles []StyleObject, -) tcell.Style { +) vaxis.Style { return uiConfig.style.Compose(base, styles, nil) } func (uiConfig *UIConfig) GetComposedStyleSelected( base StyleObject, styles []StyleObject, -) tcell.Style { +) vaxis.Style { return uiConfig.style.ComposeSelected(base, styles, nil) } func (uiConfig *UIConfig) MsgComposedStyle( base StyleObject, styles []StyleObject, h *mail.Header, -) tcell.Style { +) vaxis.Style { return uiConfig.style.Compose(base, styles, h) } func (uiConfig *UIConfig) MsgComposedStyleSelected( base StyleObject, styles []StyleObject, h *mail.Header, -) tcell.Style { +) vaxis.Style { return uiConfig.style.ComposeSelected(base, styles, h) } diff --git a/lib/parse/ansi.go b/lib/parse/ansi.go index 3a802a51f78c..7fc378344f76 100644 --- a/lib/parse/ansi.go +++ b/lib/parse/ansi.go @@ -12,7 +12,7 @@ import ( "strings" "git.sr.ht/~rjarry/aerc/log" - "github.com/gdamore/tcell/v2" + "git.sr.ht/~rockorager/vaxis" "github.com/mattn/go-runewidth" ) @@ -63,10 +63,10 @@ func StripAnsi(r io.Reader) io.Reader { type StyledRune struct { Value rune Width int - Style tcell.Style + Style vaxis.Style } -// RuneBuffer is a buffer of runes styled with tcell.Style objects +// RuneBuffer is a buffer of runes styled with vaxis.Style objects type RuneBuffer struct { buf []*StyledRune } @@ -77,13 +77,13 @@ func (rb *RuneBuffer) Runes() []*StyledRune { } // Write writes a rune and it's associated style to the RuneBuffer -func (rb *RuneBuffer) Write(r rune, style tcell.Style) { +func (rb *RuneBuffer) Write(r rune, style vaxis.Style) { w := runewidth.RuneWidth(r) rb.buf = append(rb.buf, &StyledRune{r, w, style}) } // Prepend inserts the rune at the beginning of the rune buffer -func (rb *RuneBuffer) PadLeft(width int, r rune, style tcell.Style) { +func (rb *RuneBuffer) PadLeft(width int, r rune, style vaxis.Style) { w := rb.Len() if w >= width { return @@ -96,7 +96,7 @@ func (rb *RuneBuffer) PadLeft(width int, r rune, style tcell.Style) { } } -func (rb *RuneBuffer) PadRight(width int, r rune, style tcell.Style) { +func (rb *RuneBuffer) PadRight(width int, r rune, style vaxis.Style) { w := rb.Len() if w >= width { return @@ -119,7 +119,7 @@ func (rb *RuneBuffer) String() string { func (rb *RuneBuffer) string(n int, left bool, char rune) string { var ( s = bytes.NewBuffer(nil) - style = tcell.StyleDefault + style = vaxis.Style{} hasStyle = false // w will track the length we have written, or would have // written in the case of left truncate @@ -136,52 +136,55 @@ func (rb *RuneBuffer) string(n int, left bool, char rune) string { hasStyle = true style = r.Style s.WriteString(attrOff) - fg, bg, attrs := style.Decompose() - - switch { - case fg.IsRGB() && bg.IsRGB(): - fr, fg, fb := fg.RGB() - br, bg, bb := bg.RGB() - fmt.Fprintf(s, setfgbgrgb, fr, fg, fb, br, bg, bb) - case fg.IsRGB(): - // RGB - r, g, b := fg.RGB() - fmt.Fprintf(s, setfgrgb, r, g, b) - case bg.IsRGB(): - // RGB - r, g, b := bg.RGB() - fmt.Fprintf(s, setbgrgb, r, g, b) - - // Indexed - case fg.Valid() && bg.Valid(): - fmt.Fprintf(s, setfgbg, fg&0xFF, bg&0xFF) - case fg.Valid(): - fmt.Fprintf(s, setfg, fg&0xFF) - case bg.Valid(): - fmt.Fprintf(s, setbg, bg&0xFF) + // fg, bg, attrs := style.Decompose() + fg := style.Foreground.Params() + switch len(fg) { + case 0: + // default + case 1: + // indexed + fmt.Fprintf(s, setfg, fg[0]) + case 3: + // rgb + fmt.Fprintf(s, setfgrgb, fg[0], fg[1], fg[2]) } - if attrs&tcell.AttrBold != 0 { + bg := style.Background.Params() + switch len(bg) { + case 0: + // default + case 1: + // indexed + fmt.Fprintf(s, setbg, bg[0]) + case 3: + // rgb + fmt.Fprintf(s, setbgrgb, bg[0], bg[1], bg[2]) + } + + attrs := style.Attribute + + if attrs&vaxis.AttrBold != 0 { s.WriteString(bold) } - if attrs&tcell.AttrUnderline != 0 { - s.WriteString(underline) - } - if attrs&tcell.AttrReverse != 0 { + if attrs&vaxis.AttrReverse != 0 { s.WriteString(reverse) } - if attrs&tcell.AttrBlink != 0 { + if attrs&vaxis.AttrBlink != 0 { s.WriteString(blink) } - if attrs&tcell.AttrDim != 0 { + if attrs&vaxis.AttrDim != 0 { s.WriteString(dim) } - if attrs&tcell.AttrItalic != 0 { + if attrs&vaxis.AttrItalic != 0 { s.WriteString(italic) } - if attrs&tcell.AttrStrikeThrough != 0 { + if attrs&vaxis.AttrStrikethrough != 0 { s.WriteString(strikethrough) } + + if style.UnderlineStyle != vaxis.UnderlineOff { + s.WriteString(underline) + } } w += r.Width @@ -230,9 +233,10 @@ func (rb *RuneBuffer) TruncateHead(n int, char rune) string { // Applies a style to the buffer. Any currently applied styles will not be // overwritten -func (rb *RuneBuffer) ApplyStyle(style tcell.Style) { +func (rb *RuneBuffer) ApplyStyle(style vaxis.Style) { + d := vaxis.Style{} for _, sr := range rb.buf { - if sr.Style == tcell.StyleDefault { + if sr.Style == d { sr.Style = style } } @@ -240,26 +244,30 @@ func (rb *RuneBuffer) ApplyStyle(style tcell.Style) { // ApplyAttrs applies the style, and if another style is present ORs the // attributes -func (rb *RuneBuffer) ApplyAttrs(style tcell.Style) { - fg, bg, attrs := style.Decompose() +func (rb *RuneBuffer) ApplyAttrs(style vaxis.Style) { for _, sr := range rb.buf { - srFg, srBg, srAttrs := sr.Style.Decompose() - if fg != tcell.ColorDefault { - srFg = fg + if style.Foreground != 0 { + sr.Style.Foreground = style.Foreground } - if bg != tcell.ColorDefault { - srBg = bg + if style.Background != 0 { + sr.Style.Background = style.Background + } + sr.Style.Attribute |= style.Attribute + if style.UnderlineColor != 0 { + sr.Style.UnderlineColor = style.UnderlineColor + } + if style.UnderlineStyle != vaxis.UnderlineOff { + sr.Style.UnderlineStyle = style.UnderlineStyle } - sr.Style = sr.Style.Attributes(attrs | srAttrs). - Foreground(srFg).Background(srBg) } } // Applies a style to a string. Any currently applied styles will not be overwritten -func ApplyStyle(style tcell.Style, str string) string { +func ApplyStyle(style vaxis.Style, str string) string { rb := ParseANSI(str) + d := vaxis.Style{} for _, sr := range rb.buf { - if sr.Style == tcell.StyleDefault { + if sr.Style == d { sr.Style = style } } @@ -270,7 +278,7 @@ func ApplyStyle(style tcell.Style, str string) string { func ParseANSI(s string) *RuneBuffer { p := &parser{ buf: &RuneBuffer{}, - curStyle: tcell.StyleDefault, + curStyle: vaxis.Style{}, } rdr := strings.NewReader(s) @@ -292,7 +300,7 @@ func ParseANSI(s string) *RuneBuffer { // A parser parses a string into a RuneBuffer type parser struct { buf *RuneBuffer - curStyle tcell.Style + curStyle vaxis.Style } func (p *parser) handleSeq(rdr io.RuneReader) { @@ -357,97 +365,97 @@ outer: param := params[i] switch param { case 0: - p.curStyle = tcell.StyleDefault + p.curStyle = vaxis.Style{} case 1: - p.curStyle = p.curStyle.Bold(true) + p.curStyle.Attribute |= vaxis.AttrBold case 2: - p.curStyle = p.curStyle.Dim(true) + p.curStyle.Attribute |= vaxis.AttrDim case 3: - p.curStyle = p.curStyle.Italic(true) + p.curStyle.Attribute |= vaxis.AttrItalic case 4: - p.curStyle = p.curStyle.Underline(true) + p.curStyle.UnderlineStyle = vaxis.UnderlineSingle case 5: - p.curStyle = p.curStyle.Blink(true) + p.curStyle.Attribute |= vaxis.AttrBlink case 6: - // rapid blink, not supported by tcell. fallback to slow + // rapid blink, not supported by vaxis. fallback to slow // blink - p.curStyle = p.curStyle.Blink(true) + p.curStyle.Attribute |= vaxis.AttrBlink case 7: - p.curStyle = p.curStyle.Reverse(true) + p.curStyle.Attribute |= vaxis.AttrReverse case 8: - // Hidden. not supported by tcell + // Hidden. not supported by vaxis case 9: - p.curStyle = p.curStyle.StrikeThrough(true) + p.curStyle.Attribute |= vaxis.AttrStrikethrough case 21: - p.curStyle = p.curStyle.Bold(false) + p.curStyle.Attribute &^= vaxis.AttrBold case 22: - p.curStyle = p.curStyle.Dim(false) + p.curStyle.Attribute &^= vaxis.AttrDim case 23: - p.curStyle = p.curStyle.Italic(false) + p.curStyle.Attribute &^= vaxis.AttrItalic case 24: - p.curStyle = p.curStyle.Underline(false) + p.curStyle.UnderlineStyle = vaxis.UnderlineOff case 25: - p.curStyle = p.curStyle.Blink(false) + p.curStyle.Attribute &^= vaxis.AttrBlink case 26: - // rapid blink, not supported by tcell. fallback to slow + // rapid blink, not supported by vaxis. fallback to slow // blink - p.curStyle = p.curStyle.Blink(false) + p.curStyle.Attribute &^= vaxis.AttrBlink case 27: - p.curStyle = p.curStyle.Reverse(false) + p.curStyle.Attribute &^= vaxis.AttrReverse case 28: - // Hidden. unsupported by tcell + // Hidden. unsupported by vaxis case 29: - p.curStyle = p.curStyle.StrikeThrough(false) + p.curStyle.Attribute &^= vaxis.AttrStrikethrough case 30, 31, 32, 33, 34, 35, 36, 37: - p.curStyle = p.curStyle.Foreground(tcell.PaletteColor(param - 30)) + p.curStyle.Foreground = vaxis.IndexColor(uint8(param - 30)) case 38: if i+2 < len(params) && params[i+1] == 5 { - p.curStyle = p.curStyle.Foreground(tcell.PaletteColor(params[i+2])) + p.curStyle.Foreground = vaxis.IndexColor(uint8(params[i+2])) i += 2 } if i+4 < len(params) && params[i+1] == 2 { switch len(params) { case 6: - r := int32(params[i+3]) - g := int32(params[i+4]) - b := int32(params[i+5]) - p.curStyle = p.curStyle.Foreground(tcell.NewRGBColor(r, g, b)) + r := uint8(params[i+3]) + g := uint8(params[i+4]) + b := uint8(params[i+5]) + p.curStyle.Foreground = vaxis.RGBColor(r, g, b) i += 5 default: - r := int32(params[i+2]) - g := int32(params[i+3]) - b := int32(params[i+4]) - p.curStyle = p.curStyle.Foreground(tcell.NewRGBColor(r, g, b)) + r := uint8(params[i+2]) + g := uint8(params[i+3]) + b := uint8(params[i+4]) + p.curStyle.Foreground = vaxis.RGBColor(r, g, b) i += 4 } } case 40, 41, 42, 43, 44, 45, 46, 47: - p.curStyle = p.curStyle.Background(tcell.PaletteColor(param - 40)) + p.curStyle.Background = vaxis.IndexColor(uint8(param - 40)) case 48: if i+2 < len(params) && params[i+1] == 5 { - p.curStyle = p.curStyle.Background(tcell.PaletteColor(params[i+2])) + p.curStyle.Background = vaxis.IndexColor(uint8(params[i+2])) i += 2 } if i+4 < len(params) && params[i+1] == 2 { switch len(params) { case 6: - r := int32(params[i+3]) - g := int32(params[i+4]) - b := int32(params[i+5]) - p.curStyle = p.curStyle.Background(tcell.NewRGBColor(r, g, b)) + r := uint8(params[i+3]) + g := uint8(params[i+4]) + b := uint8(params[i+5]) + p.curStyle.Background = vaxis.RGBColor(r, g, b) i += 5 default: - r := int32(params[i+2]) - g := int32(params[i+3]) - b := int32(params[i+4]) - p.curStyle = p.curStyle.Background(tcell.NewRGBColor(r, g, b)) + r := uint8(params[i+2]) + g := uint8(params[i+3]) + b := uint8(params[i+4]) + p.curStyle.Background = vaxis.RGBColor(r, g, b) i += 4 } } case 90, 91, 92, 93, 94, 95, 96, 97: - p.curStyle = p.curStyle.Foreground(tcell.PaletteColor(param - 90 + 8)) + p.curStyle.Foreground = vaxis.IndexColor(uint8(param - 90 + 8)) case 100, 101, 102, 103, 104, 105, 106, 107: - p.curStyle = p.curStyle.Background(tcell.PaletteColor(param - 100 + 8)) + p.curStyle.Background = vaxis.IndexColor(uint8(param - 100 + 8)) } } } diff --git a/lib/parse/ansi_test.go b/lib/parse/ansi_test.go index 6aa95b185ded..5d8e8572fc2b 100644 --- a/lib/parse/ansi_test.go +++ b/lib/parse/ansi_test.go @@ -110,7 +110,7 @@ func TestParser(t *testing.T) { { name: "8 bit color: foreground and background", input: "\x1b[31;41mhello, world", - expectedString: "\x1b[m\x1b[38;5;1;48;5;1mhello, world\x1b[m", + expectedString: "\x1b[m\x1b[38;5;1m\x1b[48;5;1mhello, world\x1b[m", expectedLen: 12, }, { @@ -128,7 +128,7 @@ func TestParser(t *testing.T) { { name: "16 bit color: foreground and background", input: "\x1b[91;101mhello, world", - expectedString: "\x1b[m\x1b[38;5;9;48;5;9mhello, world\x1b[m", + expectedString: "\x1b[m\x1b[38;5;9m\x1b[48;5;9mhello, world\x1b[m", expectedLen: 12, }, { @@ -152,7 +152,7 @@ func TestParser(t *testing.T) { { name: "256 color: foreground and background", input: "\x1b[38;5;20;48;5;20mhello, world", - expectedString: "\x1b[m\x1b[38;5;20;48;5;20mhello, world\x1b[m", + expectedString: "\x1b[m\x1b[38;5;20m\x1b[48;5;20mhello, world\x1b[m", expectedLen: 12, }, { @@ -194,7 +194,7 @@ func TestParser(t *testing.T) { { name: "true color: foreground and background", input: "\x1b[38;2;200;200;200;48;2;0;0;0mhello, world", - expectedString: "\x1b[m\x1b[38;2;200;200;200;48;2;0;0;0mhello, world\x1b[m", + expectedString: "\x1b[m\x1b[38;2;200;200;200m\x1b[48;2;0;0;0mhello, world\x1b[m", expectedLen: 12, }, } diff --git a/lib/ui/context.go b/lib/ui/context.go index 12621c8af97a..693611208a77 100644 --- a/lib/ui/context.go +++ b/lib/ui/context.go @@ -57,7 +57,7 @@ func (ctx *Context) SetCell(x, y int, ch rune, style vaxis.Style) { }) } -func (ctx *Context) Printf(x, y int, style tcell.Style, +func (ctx *Context) Printf(x, y int, style vaxis.Style, format string, a ...interface{}, ) int { width, height := ctx.window.Size() @@ -93,7 +93,7 @@ func (ctx *Context) Printf(x, y int, style tcell.Style, Grapheme: string(sr.Value), Width: sr.Width, }, - Style: tcell.VaxisStyle(sr.Style), + Style: sr.Style, }) x += sr.Width if x == old_x+width { @@ -107,14 +107,14 @@ func (ctx *Context) Printf(x, y int, style tcell.Style, return buf.Len() } -func (ctx *Context) Fill(x, y, width, height int, rune rune, style tcell.Style) { +func (ctx *Context) Fill(x, y, width, height int, rune rune, style vaxis.Style) { win := ctx.window.New(x, y, width, height) win.Fill(vaxis.Cell{ Character: vaxis.Character{ Grapheme: string(rune), Width: 1, }, - Style: tcell.VaxisStyle(style), + Style: style, }) } diff --git a/lib/ui/table.go b/lib/ui/table.go index 9cb96bd9ccea..ceb01c78c5cc 100644 --- a/lib/ui/table.go +++ b/lib/ui/table.go @@ -6,7 +6,7 @@ import ( "git.sr.ht/~rjarry/aerc/config" "git.sr.ht/~rjarry/aerc/lib/parse" - "github.com/gdamore/tcell/v2" + "git.sr.ht/~rockorager/vaxis" "github.com/mattn/go-runewidth" ) @@ -18,7 +18,7 @@ type Table struct { // of table rows. If true is returned, the default routine is skipped. CustomDraw func(t *Table, row int, c *Context) bool // Optional callback that allows returning a custom style for the row. - GetRowStyle func(t *Table, row int) tcell.Style + GetRowStyle func(t *Table, row int) vaxis.Style // true if at least one column has WIDTH_FIT autoFitWidths bool @@ -42,14 +42,14 @@ func NewTable( height int, columnDefs []*config.ColumnDef, separator string, customDraw func(*Table, int, *Context) bool, - getRowStyle func(*Table, int) tcell.Style, + getRowStyle func(*Table, int) vaxis.Style, ) Table { if customDraw == nil { customDraw = func(*Table, int, *Context) bool { return false } } if getRowStyle == nil { - getRowStyle = func(*Table, int) tcell.Style { - return tcell.StyleDefault + getRowStyle = func(*Table, int) vaxis.Style { + return vaxis.Style{} } } columns := make([]Column, len(columnDefs)) @@ -166,7 +166,7 @@ func (col *Column) alignCell(cell string) string { switch { case col.Def.Flags.Has(config.ALIGN_LEFT): if width < col.Width { - buf.PadRight(col.Width, ' ', tcell.StyleDefault) + buf.PadRight(col.Width, ' ', vaxis.Style{}) cell = buf.String() } else if width > col.Width { cell = buf.Truncate(col.Width, '…') @@ -174,15 +174,15 @@ func (col *Column) alignCell(cell string) string { case col.Def.Flags.Has(config.ALIGN_CENTER): if width < col.Width { pad := col.Width - width - buf.PadLeft(col.Width-(pad/2), ' ', tcell.StyleDefault) - buf.PadRight(col.Width, ' ', tcell.StyleDefault) + buf.PadLeft(col.Width-(pad/2), ' ', vaxis.Style{}) + buf.PadRight(col.Width, ' ', vaxis.Style{}) cell = buf.String() } else if width > col.Width { cell = buf.Truncate(col.Width, '…') } case col.Def.Flags.Has(config.ALIGN_RIGHT): if width < col.Width { - buf.PadLeft(col.Width, ' ', tcell.StyleDefault) + buf.PadLeft(col.Width, ' ', vaxis.Style{}) cell = buf.String() } else if width > col.Width { cell = buf.TruncateHead(col.Width, '…') diff --git a/lib/ui/text.go b/lib/ui/text.go index def61a0a70e1..19a0b90028df 100644 --- a/lib/ui/text.go +++ b/lib/ui/text.go @@ -1,7 +1,7 @@ package ui import ( - "github.com/gdamore/tcell/v2" + "git.sr.ht/~rockorager/vaxis" "github.com/mattn/go-runewidth" ) @@ -14,10 +14,10 @@ const ( type Text struct { text string strategy uint - style tcell.Style + style vaxis.Style } -func NewText(text string, style tcell.Style) *Text { +func NewText(text string, style vaxis.Style) *Text { return &Text{ text: text, style: style, -- 2.43.0
Replace tcell terminal with the vaxis terminal. The vaxis terminal is a port of tcell term. Changelog-changed: replaced tcell-term with vaxis terminal Signed-off-by: Tim Culverhouse <tim@timculverhouse.com> --- app/terminal.go | 54 ++++++++++++++++++----------------------------- go.mod | 2 +- go.sum | 5 ----- lib/ui/context.go | 14 ------------ lib/ui/ui.go | 1 + 5 files changed, 22 insertions(+), 54 deletions(-) diff --git a/app/terminal.go b/app/terminal.go index 97cd4410591b..d894368f2b92 100644 --- a/app/terminal.go +++ b/app/terminal.go @@ -7,8 +7,8 @@ import ( "git.sr.ht/~rjarry/aerc/config" "git.sr.ht/~rjarry/aerc/lib/ui" "git.sr.ht/~rjarry/aerc/log" - tcellterm "git.sr.ht/~rockorager/tcell-term" "git.sr.ht/~rockorager/vaxis" + "git.sr.ht/~rockorager/vaxis/widgets/term" "github.com/gdamore/tcell/v2" ) @@ -23,7 +23,7 @@ type Terminal struct { ctx *ui.Context focus bool visible bool - vterm *tcellterm.VT + vterm *term.Model running bool OnClose func(err error) @@ -35,7 +35,7 @@ type Terminal struct { func NewTerminal(cmd *exec.Cmd) (*Terminal, error) { term := &Terminal{ cmd: cmd, - vterm: tcellterm.New(), + vterm: term.New(), visible: true, } term.vterm.OSC8 = config.General.EnableOSC8 @@ -83,8 +83,6 @@ func (term *Terminal) Invalidate() { } func (term *Terminal) Draw(ctx *ui.Context) { - term.vterm.SetSurface(ctx) - w, h := ctx.Size() if !term.isClosed() && term.ctx != nil { ow, oh := term.ctx.Size() @@ -105,15 +103,7 @@ func (term *Terminal) Draw(ctx *ui.Context) { term.OnStart() } } - term.vterm.Draw() - if term.focus { - y, x, style, vis := term.vterm.Cursor() - if vis && !term.isClosed() { - ctx.SetCursor(x, y, vaxis.CursorStyle(style)) - } else { - ctx.HideCursor() - } - } + term.vterm.Draw(ctx.Window()) } func (term *Terminal) Show(visible bool) { @@ -136,7 +126,7 @@ func (term *Terminal) MouseEvent(localX int, localY int, event vaxis.Event) { return } e := tcell.NewEventMouse(localX, localY, ev.Buttons(), ev.Modifiers()) - term.vterm.HandleEvent(e) + term.vterm.Update(e) } func (term *Terminal) Focus(focus bool) { @@ -144,39 +134,34 @@ func (term *Terminal) Focus(focus bool) { return } term.focus = focus - if term.ctx != nil { - if !term.focus { - term.ctx.HideCursor() - } else { - y, x, style, _ := term.vterm.Cursor() - term.ctx.SetCursor(x, y, vaxis.CursorStyle(style)) - term.Invalidate() - } + if term.focus { + term.vterm.Focus() + } else { + term.vterm.Blur() } } // HandleEvent is used to watch the underlying terminal events -func (term *Terminal) HandleEvent(ev tcell.Event) { - if term.isClosed() { +func (t *Terminal) HandleEvent(ev vaxis.Event) { + if t.isClosed() { return } switch ev := ev.(type) { - case *tcellterm.EventRedraw: - if term.visible { + case vaxis.Redraw: + if t.visible { ui.Invalidate() } - case *tcellterm.EventTitle: - if term.OnTitle != nil { - term.OnTitle(ev.Title()) + case term.EventTitle: + if t.OnTitle != nil { + t.OnTitle(string(ev)) } - case *tcellterm.EventClosed: - term.Close() + case term.EventClosed: + t.Close() ui.Invalidate() } } func (term *Terminal) Event(event vaxis.Event) bool { - event = tcell.TcellEvent(event) if term.OnEvent != nil { if term.OnEvent(event) { return true @@ -185,5 +170,6 @@ func (term *Terminal) Event(event vaxis.Event) bool { if term.isClosed() { return false } - return term.vterm.HandleEvent(event) + term.vterm.Update(event) + return true } diff --git a/go.mod b/go.mod index 63f3e7ba3646..1d8491e79610 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,6 @@ go 1.18 require ( git.sr.ht/~rjarry/go-opt v1.3.0 git.sr.ht/~rockorager/go-jmap v0.3.0 - git.sr.ht/~rockorager/tcell-term v0.10.0 git.sr.ht/~rockorager/vaxis v0.4.7 github.com/ProtonMail/go-crypto v0.0.0-20230417170513-8ee5748c52b5 github.com/arran4/golang-ical v0.0.0-20230318005454-19abf92700cc @@ -59,6 +58,7 @@ require ( golang.org/x/text v0.13.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.30.0 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 48122d9f3f22..25327ff4a36b 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,6 @@ git.sr.ht/~rjarry/go-opt v1.3.0 h1:9BLOcXi5OhDYVzH3Td48i2uM/byMGNqXY7YhBzvEZg8= git.sr.ht/~rjarry/go-opt v1.3.0/go.mod h1:oEPZUTJKGn1FVye0znaLoeskE/QTuyoJw5q+fjusdM4= git.sr.ht/~rockorager/go-jmap v0.3.0 h1:h2WuPcNyXRYFg9+W2HGf/mzIqC6ISy9EaS/BGa7Z5RY= git.sr.ht/~rockorager/go-jmap v0.3.0/go.mod h1:aOTCtwpZSINpDDSOkLGpHU0Kbbm5lcSDMcobX3ZtOjY= -git.sr.ht/~rockorager/tcell-term v0.10.0 h1:BqxJjtCMmLIfS6fdIal8TSiH3qPiYTjATuIRIWNVPbo= -git.sr.ht/~rockorager/tcell-term v0.10.0/go.mod h1:Snxh5CrziiA2CjyLOZ6tGAg5vMPlE+REMWT3rtKuyyQ= git.sr.ht/~rockorager/vaxis v0.4.7 h1:9VlkBBF9dxy62AMHnKAU8GqEs2/mUTlke/ZJ9o/6luk= git.sr.ht/~rockorager/vaxis v0.4.7/go.mod h1:h94aKek3frIV1hJbdXjqnBqaLkbWXvV+UxAsQHg9bns= git.sr.ht/~rockorager/vaxis-tcell v0.4.7 h1:ISMSnvbz1jnG9Ppi9y3NJKaLl7Nu67qMkpEXbXwHgmg= @@ -24,7 +22,6 @@ github.com/cloudflare/circl v1.3.2/go.mod h1:+CauBF6R70Jqcyl8N2hC8pAXYbWkGIezuSb github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ= @@ -149,7 +146,6 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= @@ -200,7 +196,6 @@ golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= diff --git a/lib/ui/context.go b/lib/ui/context.go index 693611208a77..2ca0d310b32b 100644 --- a/lib/ui/context.go +++ b/lib/ui/context.go @@ -5,7 +5,6 @@ import ( "git.sr.ht/~rjarry/aerc/lib/parse" "git.sr.ht/~rockorager/vaxis" - "github.com/gdamore/tcell/v2" ) // A context allows you to draw in a sub-region of the terminal @@ -136,19 +135,6 @@ func (ctx *Context) Popover(x, y, width, height int, d Drawable) { }) } -// SetContent is used to update the content of the Surface at the given -// location. -func (ctx *Context) SetContent(x int, y int, ch rune, comb []rune, style tcell.Style) { - g := []rune{ch} - g = append(g, comb...) - ctx.window.SetCell(x, y, vaxis.Cell{ - Character: vaxis.Character{ - Grapheme: string(g), - }, - Style: tcell.VaxisStyle(style), - }) -} - func (ctx *Context) Size() (int, int) { return ctx.window.Size() } diff --git a/lib/ui/ui.go b/lib/ui/ui.go index 84c34459f038..a7233f49da4e 100644 --- a/lib/ui/ui.go +++ b/lib/ui/ui.go @@ -125,6 +125,7 @@ func Render() { state.vx.Window().Clear() // reset popover for the next Draw state.popover = nil + state.vx.HideCursor() state.content.Draw(state.ctx) if state.popover != nil { // if the Draw resulted in a popover, draw it -- 2.43.0
Replace tcell paste events with vaxis paste events Signed-off-by: Tim Culverhouse <tim@timculverhouse.com> --- app/aerc.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/app/aerc.go b/app/aerc.go index 00f1cb5fc385..4c96ab70ea17 100644 --- a/app/aerc.go +++ b/app/aerc.go @@ -385,13 +385,15 @@ func (aerc *Aerc) Event(event vaxis.Event) bool { x, y := event.Position() aerc.grid.MouseEvent(x, y, event) return true - case *tcell.EventPaste: - if event.Start() { - aerc.pasting = true - } - if event.End() { - aerc.pasting = false + case vaxis.PasteStartEvent: + aerc.pasting = true + interactive, ok := aerc.SelectedTabContent().(ui.Interactive) + if ok { + return interactive.Event(event) } + return false + case vaxis.PasteEndEvent: + aerc.pasting = false interactive, ok := aerc.SelectedTabContent().(ui.Interactive) if ok { return interactive.Event(event) -- 2.43.0
Replace all tcell.EventMouse events with vaxis mouse events Signed-off-by: Tim Culverhouse <tim@timculverhouse.com> --- app/aerc.go | 6 ++---- app/compose.go | 9 ++++----- app/dirlist.go | 12 +++++------- app/dirtree.go | 11 +++++------ app/msglist.go | 11 +++++------ app/partswitcher.go | 11 +++++------ app/terminal.go | 9 ++++----- lib/ui/grid.go | 3 +-- lib/ui/stack.go | 5 ++--- lib/ui/tab.go | 15 +++++++-------- lib/ui/textinput.go | 7 +++---- 11 files changed, 43 insertions(+), 56 deletions(-) diff --git a/app/aerc.go b/app/aerc.go index 4c96ab70ea17..1df89552adad 100644 --- a/app/aerc.go +++ b/app/aerc.go @@ -15,7 +15,6 @@ import ( "git.sr.ht/~rockorager/vaxis" "github.com/ProtonMail/go-crypto/openpgp" "github.com/emersion/go-message/mail" - "github.com/gdamore/tcell/v2" "git.sr.ht/~rjarry/aerc/config" "git.sr.ht/~rjarry/aerc/lib" @@ -381,9 +380,8 @@ func (aerc *Aerc) Event(event vaxis.Event) bool { } return false } - case *tcell.EventMouse: - x, y := event.Position() - aerc.grid.MouseEvent(x, y, event) + case vaxis.Mouse: + aerc.grid.MouseEvent(event.Col, event.Row, event) return true case vaxis.PasteStartEvent: aerc.pasting = true diff --git a/app/compose.go b/app/compose.go index a73127c9c3fd..2c5247d33323 100644 --- a/app/compose.go +++ b/app/compose.go @@ -17,7 +17,6 @@ import ( "time" "github.com/emersion/go-message/mail" - "github.com/gdamore/tcell/v2" "github.com/mattn/go-runewidth" "github.com/pkg/errors" @@ -1224,8 +1223,8 @@ func (c *Composer) resetReview() { } func (c *Composer) termEvent(event vaxis.Event) bool { - if event, ok := event.(*tcell.EventMouse); ok { - if event.Buttons() == tcell.Button1 { + if event, ok := event.(vaxis.Mouse); ok { + if event.Button == vaxis.MouseLeftButton { c.FocusTerminal() return true } @@ -1645,8 +1644,8 @@ func (he *headerEditor) Draw(ctx *ui.Context) { } func (he *headerEditor) MouseEvent(localX int, localY int, event vaxis.Event) { - if event, ok := event.(*tcell.EventMouse); ok { - if event.Buttons() == tcell.Button1 { + if event, ok := event.(vaxis.Mouse); ok { + if event.Button == vaxis.MouseLeftButton { he.focused = true } diff --git a/app/dirlist.go b/app/dirlist.go index e92283740ed9..71db2b6a0047 100644 --- a/app/dirlist.go +++ b/app/dirlist.go @@ -8,8 +8,6 @@ import ( "sort" "time" - "github.com/gdamore/tcell/v2" - "git.sr.ht/~rjarry/aerc/config" "git.sr.ht/~rjarry/aerc/lib" "git.sr.ht/~rjarry/aerc/lib/parse" @@ -376,16 +374,16 @@ func (dirlist *DirectoryList) drawScrollbar(ctx *ui.Context) { } func (dirlist *DirectoryList) MouseEvent(localX int, localY int, event vaxis.Event) { - if event, ok := event.(*tcell.EventMouse); ok { - switch event.Buttons() { - case tcell.Button1: + if event, ok := event.(vaxis.Mouse); ok { + switch event.Button { + case vaxis.MouseLeftButton: clickedDir, ok := dirlist.Clicked(localX, localY) if ok { dirlist.Select(clickedDir) } - case tcell.WheelDown: + case vaxis.MouseWheelDown: dirlist.Next() - case tcell.WheelUp: + case vaxis.MouseWheelUp: dirlist.Prev() } } diff --git a/app/dirtree.go b/app/dirtree.go index c3ad2ef6246b..5c1fcbcc4edd 100644 --- a/app/dirtree.go +++ b/app/dirtree.go @@ -15,7 +15,6 @@ import ( "git.sr.ht/~rjarry/aerc/models" "git.sr.ht/~rjarry/aerc/worker/types" "git.sr.ht/~rockorager/vaxis" - "github.com/gdamore/tcell/v2" ) type DirectoryTree struct { @@ -168,16 +167,16 @@ func (dt *DirectoryTree) Draw(ctx *ui.Context) { } func (dt *DirectoryTree) MouseEvent(localX int, localY int, event vaxis.Event) { - if event, ok := event.(*tcell.EventMouse); ok { - switch event.Buttons() { - case tcell.Button1: + if event, ok := event.(vaxis.Mouse); ok { + switch event.Button { + case vaxis.MouseLeftButton: clickedDir, ok := dt.Clicked(localX, localY) if ok { dt.Select(clickedDir) } - case tcell.WheelDown: + case vaxis.MouseWheelDown: dt.NextPrev(1) - case tcell.WheelUp: + case vaxis.MouseWheelUp: dt.NextPrev(-1) } } diff --git a/app/msglist.go b/app/msglist.go index 8106c3cfc694..4b4e41294e49 100644 --- a/app/msglist.go +++ b/app/msglist.go @@ -7,7 +7,6 @@ import ( sortthread "github.com/emersion/go-imap-sortthread" "github.com/emersion/go-message/mail" - "github.com/gdamore/tcell/v2" "github.com/mattn/go-runewidth" "git.sr.ht/~rjarry/aerc/config" @@ -255,9 +254,9 @@ func (ml *MessageList) drawScrollbar(ctx *ui.Context) { } func (ml *MessageList) MouseEvent(localX int, localY int, event vaxis.Event) { - if event, ok := event.(*tcell.EventMouse); ok { - switch event.Buttons() { - case tcell.Button1: + if event, ok := event.(vaxis.Mouse); ok { + switch event.Button { + case vaxis.MouseLeftButton: selectedMsg, ok := ml.Clicked(localX, localY) if ok { ml.Select(selectedMsg) @@ -281,12 +280,12 @@ func (ml *MessageList) MouseEvent(localX int, localY int, event vaxis.Event) { NewTab(viewer, msg.Envelope.Subject) }) } - case tcell.WheelDown: + case vaxis.MouseWheelDown: if ml.store != nil { ml.store.Next() } ml.Invalidate() - case tcell.WheelUp: + case vaxis.MouseWheelUp: if ml.store != nil { ml.store.Prev() } diff --git a/app/partswitcher.go b/app/partswitcher.go index 5dc996f783d5..5a935a8c571f 100644 --- a/app/partswitcher.go +++ b/app/partswitcher.go @@ -6,7 +6,6 @@ import ( "git.sr.ht/~rjarry/aerc/config" "git.sr.ht/~rjarry/aerc/lib/ui" "git.sr.ht/~rockorager/vaxis" - "github.com/gdamore/tcell/v2" "github.com/mattn/go-runewidth" ) @@ -168,7 +167,7 @@ func (ps *PartSwitcher) MouseEvent(localX int, localY int, event vaxis.Event) { return } - e, ok := event.(*tcell.EventMouse) + e, ok := event.(vaxis.Mouse) if !ok { return } @@ -177,8 +176,8 @@ func (ps *PartSwitcher) MouseEvent(localX int, localY int, event vaxis.Event) { ps.parts[ps.selected].term.Focus(false) } - switch e.Buttons() { - case tcell.Button1: + switch e.Button { + case vaxis.MouseLeftButton: i := localY - ps.offset + ps.Scroll() if i < 0 || i >= len(ps.parts) { break @@ -188,10 +187,10 @@ func (ps *PartSwitcher) MouseEvent(localX int, localY int, event vaxis.Event) { } ps.selected = i ps.Invalidate() - case tcell.WheelDown: + case vaxis.MouseWheelDown: ps.NextPart() ps.Invalidate() - case tcell.WheelUp: + case vaxis.MouseWheelUp: ps.PreviousPart() ps.Invalidate() } diff --git a/app/terminal.go b/app/terminal.go index d894368f2b92..77da644ec6e6 100644 --- a/app/terminal.go +++ b/app/terminal.go @@ -9,8 +9,6 @@ import ( "git.sr.ht/~rjarry/aerc/log" "git.sr.ht/~rockorager/vaxis" "git.sr.ht/~rockorager/vaxis/widgets/term" - - "github.com/gdamore/tcell/v2" ) type HasTerminal interface { @@ -115,7 +113,7 @@ func (term *Terminal) Terminal() *Terminal { } func (term *Terminal) MouseEvent(localX int, localY int, event vaxis.Event) { - ev, ok := event.(*tcell.EventMouse) + ev, ok := event.(vaxis.Mouse) if !ok { return } @@ -125,8 +123,9 @@ func (term *Terminal) MouseEvent(localX int, localY int, event vaxis.Event) { if term.isClosed() { return } - e := tcell.NewEventMouse(localX, localY, ev.Buttons(), ev.Modifiers()) - term.vterm.Update(e) + ev.Row = localY + ev.Col = localX + term.vterm.Update(ev) } func (term *Terminal) Focus(focus bool) { diff --git a/lib/ui/grid.go b/lib/ui/grid.go index 00f759bf9cc3..ce3d37d6179e 100644 --- a/lib/ui/grid.go +++ b/lib/ui/grid.go @@ -5,7 +5,6 @@ import ( "sync" "git.sr.ht/~rockorager/vaxis" - "github.com/gdamore/tcell/v2" ) type Grid struct { @@ -130,7 +129,7 @@ func (grid *Grid) Draw(ctx *Context) { } func (grid *Grid) MouseEvent(localX int, localY int, event vaxis.Event) { - if event, ok := event.(*tcell.EventMouse); ok { + if event, ok := event.(vaxis.Mouse); ok { grid.mutex.RLock() defer grid.mutex.RUnlock() diff --git a/lib/ui/stack.go b/lib/ui/stack.go index a4017007bd6e..890ab272f856 100644 --- a/lib/ui/stack.go +++ b/lib/ui/stack.go @@ -4,8 +4,7 @@ import ( "fmt" "git.sr.ht/~rjarry/aerc/config" - - "github.com/gdamore/tcell/v2" + "git.sr.ht/~rockorager/vaxis" ) type Stack struct { @@ -34,7 +33,7 @@ func (stack *Stack) Draw(ctx *Context) { } } -func (stack *Stack) MouseEvent(localX int, localY int, event tcell.Event) { +func (stack *Stack) MouseEvent(localX int, localY int, event vaxis.Event) { if len(stack.children) > 0 { if element, ok := stack.Peek().(Mouseable); ok { element.MouseEvent(localX, localY, event) diff --git a/lib/ui/tab.go b/lib/ui/tab.go index 07b304ee500e..6149c4fda886 100644 --- a/lib/ui/tab.go +++ b/lib/ui/tab.go @@ -3,7 +3,6 @@ package ui import ( "sync" - "github.com/gdamore/tcell/v2" "github.com/mattn/go-runewidth" "git.sr.ht/~rjarry/aerc/config" @@ -395,7 +394,7 @@ func (strip *TabStrip) Invalidate() { Invalidate() } -func (strip *TabStrip) MouseEvent(localX int, localY int, event tcell.Event) { +func (strip *TabStrip) MouseEvent(localX int, localY int, event vaxis.Event) { strip.parent.m.Lock() defer strip.parent.m.Unlock() changeFocus := func(focus bool) { @@ -406,9 +405,9 @@ func (strip *TabStrip) MouseEvent(localX int, localY int, event tcell.Event) { } unfocus := func() { changeFocus(false) } refocus := func() { changeFocus(true) } - if event, ok := event.(*tcell.EventMouse); ok { - switch event.Buttons() { - case tcell.Button1: + if event, ok := event.(vaxis.Mouse); ok { + switch event.Button { + case vaxis.MouseLeftButton: selectedTab, ok := strip.clicked(localX, localY) if !ok || selectedTab == strip.parent.curIndex { return @@ -416,7 +415,7 @@ func (strip *TabStrip) MouseEvent(localX int, localY int, event tcell.Event) { unfocus() strip.parent.selectPriv(selectedTab) refocus() - case tcell.WheelDown: + case vaxis.MouseWheelDown: unfocus() index := strip.parent.curIndex + 1 if index >= len(strip.parent.tabs) { @@ -424,7 +423,7 @@ func (strip *TabStrip) MouseEvent(localX int, localY int, event tcell.Event) { } strip.parent.selectPriv(index) refocus() - case tcell.WheelUp: + case vaxis.MouseWheelUp: unfocus() index := strip.parent.curIndex - 1 if index < 0 { @@ -432,7 +431,7 @@ func (strip *TabStrip) MouseEvent(localX int, localY int, event tcell.Event) { } strip.parent.selectPriv(index) refocus() - case tcell.Button3: + case vaxis.MouseMiddleButton: selectedTab, ok := strip.clicked(localX, localY) if !ok { return diff --git a/lib/ui/textinput.go b/lib/ui/textinput.go index d52ee07a687c..a01184f2ac36 100644 --- a/lib/ui/textinput.go +++ b/lib/ui/textinput.go @@ -6,7 +6,6 @@ import ( "sync" "time" - "github.com/gdamore/tcell/v2" "github.com/mattn/go-runewidth" "git.sr.ht/~rjarry/aerc/config" @@ -144,9 +143,9 @@ func (ti *TextInput) drawPopover(ctx *Context) { ctx.Popover(pos, 0, width, height, cmp) } -func (ti *TextInput) MouseEvent(localX int, localY int, event tcell.Event) { - if event, ok := event.(*tcell.EventMouse); ok { - if event.Buttons() == tcell.Button1 { +func (ti *TextInput) MouseEvent(localX int, localY int, event vaxis.Event) { + if event, ok := event.(vaxis.Mouse); ok { + if event.Button == vaxis.MouseLeftButton { if localX >= len(ti.prompt)+1 && localX <= len(ti.text[ti.scroll:])+len(ti.prompt)+1 { ti.index = localX - len(ti.prompt) - 1 ti.ensureScroll() -- 2.43.0
All references to tcell have been replaced with vaxis Signed-off-by: Tim Culverhouse <tim@timculverhouse.com> --- go.mod | 3 --- go.sum | 19 +------------------ 2 files changed, 1 insertion(+), 21 deletions(-) diff --git a/go.mod b/go.mod index 1d8491e79610..26eaf474cb22 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,6 @@ require ( github.com/fsnotify/fsevents v0.1.1 github.com/fsnotify/fsnotify v1.6.0 github.com/gatherstars-com/jwz v1.4.0 - github.com/gdamore/tcell/v2 v2.6.0 github.com/go-ini/ini v1.67.0 github.com/lithammer/fuzzysearch v1.1.5 github.com/mattn/go-isatty v0.0.18 @@ -63,5 +62,3 @@ require ( ) replace golang.org/x/crypto => github.com/ProtonMail/crypto v0.0.0-20200420072808-71bec3603bf3 - -replace github.com/gdamore/tcell/v2 => git.sr.ht/~rockorager/vaxis-tcell v0.4.7 diff --git a/go.sum b/go.sum index 25327ff4a36b..0ec530333815 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,6 @@ git.sr.ht/~rockorager/go-jmap v0.3.0 h1:h2WuPcNyXRYFg9+W2HGf/mzIqC6ISy9EaS/BGa7Z git.sr.ht/~rockorager/go-jmap v0.3.0/go.mod h1:aOTCtwpZSINpDDSOkLGpHU0Kbbm5lcSDMcobX3ZtOjY= git.sr.ht/~rockorager/vaxis v0.4.7 h1:9VlkBBF9dxy62AMHnKAU8GqEs2/mUTlke/ZJ9o/6luk= git.sr.ht/~rockorager/vaxis v0.4.7/go.mod h1:h94aKek3frIV1hJbdXjqnBqaLkbWXvV+UxAsQHg9bns= -git.sr.ht/~rockorager/vaxis-tcell v0.4.7 h1:ISMSnvbz1jnG9Ppi9y3NJKaLl7Nu67qMkpEXbXwHgmg= -git.sr.ht/~rockorager/vaxis-tcell v0.4.7/go.mod h1:mpNiMGQDJ3fiwVO8pvz0GENWCdCXEE50beqCbgGoXLc= github.com/ProtonMail/crypto v0.0.0-20200420072808-71bec3603bf3 h1:JW27/kGLQzeM1Fxg5YQhdkTEAU7HIAHMgSag35zVTnY= github.com/ProtonMail/crypto v0.0.0-20200420072808-71bec3603bf3/go.mod h1:Pxr7w4gA2ikI4sWyYwEffm+oew1WAJHzG1SiDpQMkrI= github.com/ProtonMail/go-crypto v0.0.0-20211112122917-428f8eabeeb3/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= @@ -65,6 +63,7 @@ github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbS github.com/gatherstars-com/jwz v1.4.0 h1:HrCJmTss6/PTzBxxQUGbJ5f0aFYjnfrfqpGlyWH+R5A= github.com/gatherstars-com/jwz v1.4.0/go.mod h1:twtXjMamfC5/NRCTJ9vDiHGeDivORkTAvOMUX/qo0Ik= github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= +github.com/gdamore/tcell/v2 v2.6.0/go.mod h1:be9omFATkdr0D9qewWW3d+MEvl5dha+Etb5y65J2H8Y= github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-test/deep v1.0.7/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8= @@ -81,7 +80,6 @@ github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk= @@ -140,13 +138,9 @@ github.com/soniakeys/quant v1.0.0/go.mod h1:HI1k023QuVbD4H8i9YdfZP2munIHU4QpjsIm github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo= github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= @@ -155,12 +149,9 @@ github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavM github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc= -golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= -golang.org/x/image v0.9.0/go.mod h1:jtrku+n79PfroUbvDdeUWMAI+heR786BofxrbiSF+J0= golang.org/x/image v0.13.0 h1:3cge/F/QTkNLauhf2QoE9zp+7sr+ZcL4HnoZmdwg9sg= golang.org/x/image v0.13.0/go.mod h1:6mmbMOeV28HuMTgA6OSRkdXKYw/t5W9Uwn2Yv1r3Yxk= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -168,7 +159,6 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= @@ -189,32 +179,25 @@ golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -- 2.43.0
Set the window title using Vaxis at UI initialization. Signed-off-by: Tim Culverhouse <tim@timculverhouse.com> --- go.mod | 1 - go.sum | 3 --- lib/ui/ui.go | 1 + main.go | 28 ---------------------------- 4 files changed, 1 insertion(+), 32 deletions(-) diff --git a/go.mod b/go.mod index 26eaf474cb22..fb81a2dc6a90 100644 --- a/go.mod +++ b/go.mod @@ -30,7 +30,6 @@ require ( github.com/riywo/loginshell v0.0.0-20200815045211-7d26008be1ab github.com/stretchr/testify v1.8.4 github.com/syndtr/goleveldb v1.0.0 - github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e golang.org/x/image v0.13.0 golang.org/x/oauth2 v0.7.0 golang.org/x/sys v0.13.0 diff --git a/go.sum b/go.sum index 0ec530333815..2705424e07d5 100644 --- a/go.sum +++ b/go.sum @@ -145,10 +145,7 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= -github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= -github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc= golang.org/x/image v0.13.0 h1:3cge/F/QTkNLauhf2QoE9zp+7sr+ZcL4HnoZmdwg9sg= golang.org/x/image v0.13.0/go.mod h1:6mmbMOeV28HuMTgA6OSRkdXKYw/t5W9Uwn2Yv1r3Yxk= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= diff --git a/lib/ui/ui.go b/lib/ui/ui.go index a7233f49da4e..ce0a7e930106 100644 --- a/lib/ui/ui.go +++ b/lib/ui/ui.go @@ -63,6 +63,7 @@ func Initialize(content DrawableInteractive) error { state.content = content state.vx = vx state.ctx = NewContext(state.vx, onPopover) + vx.SetTitle("aerc") Invalidate() if beeper, ok := content.(DrawableInteractiveBeeper); ok { diff --git a/main.go b/main.go index f592189adce2..77227e5b37c6 100644 --- a/main.go +++ b/main.go @@ -1,7 +1,6 @@ package main import ( - "bytes" "encoding/base64" "errors" "fmt" @@ -13,8 +12,6 @@ import ( "time" "git.sr.ht/~rjarry/go-opt" - "github.com/mattn/go-isatty" - "github.com/xo/terminfo" "git.sr.ht/~rjarry/aerc/app" "git.sr.ht/~rjarry/aerc/commands" @@ -184,27 +181,6 @@ func buildInfo() string { return info } -func setWindowTitle() { - log.Tracef("Parsing terminfo") - ti, err := terminfo.LoadFromEnv() - if err != nil { - log.Warnf("Cannot get terminfo: %v", err) - return - } - - if !ti.Has(terminfo.HasStatusLine) { - log.Infof("Terminal does not have status line support") - return - } - - log.Debugf("Setting terminal title") - buf := new(bytes.Buffer) - ti.Fprintf(buf, terminfo.ToStatusLine) - fmt.Fprint(buf, "aerc") - ti.Fprintf(buf, terminfo.FromStatusLine) - os.Stderr.Write(buf.Bytes()) -} - type Opts struct { Help bool `opt:"-h" action:"ShowHelp"` Version bool `opt:"-v" action:"ShowVersion"` @@ -324,10 +300,6 @@ func main() { } } - if isatty.IsTerminal(os.Stderr.Fd()) { - setWindowTitle() - } - go func() { defer log.PanicHandler() err := hooks.RunHook(&hooks.AercStartup{Version: Version}) -- 2.43.0
Update color value reference in documentation Signed-off-by: Tim Culverhouse <tim@timculverhouse.com> --- doc/aerc-stylesets.7.scd | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/aerc-stylesets.7.scd b/doc/aerc-stylesets.7.scd index 8083888b14b4..a5871756fd76 100644 --- a/doc/aerc-stylesets.7.scd +++ b/doc/aerc-stylesets.7.scd @@ -327,8 +327,7 @@ since *msglist_\** also applies to *msglist\_marked*. # COLORS -The color values are set using the values accepted by the tcell library. -The values can be one of the following. +The color values are set using any of the following methods: _default_ The color is set as per the system or terminal default. -- 2.43.0
v0.5.1 brings many perf improvements to the terminal widget as well as a few fixes to it. There are also some keybind related fixes for matching keys Signed-off-by: Tim Culverhouse <tim@timculverhouse.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index fb81a2dc6a90..61ea0dca9db6 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.18 require ( git.sr.ht/~rjarry/go-opt v1.3.0 git.sr.ht/~rockorager/go-jmap v0.3.0 - git.sr.ht/~rockorager/vaxis v0.4.7 + git.sr.ht/~rockorager/vaxis v0.5.1 github.com/ProtonMail/go-crypto v0.0.0-20230417170513-8ee5748c52b5 github.com/arran4/golang-ical v0.0.0-20230318005454-19abf92700cc github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 diff --git a/go.sum b/go.sum index 2705424e07d5..5ed2fee01b1c 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,8 @@ git.sr.ht/~rjarry/go-opt v1.3.0 h1:9BLOcXi5OhDYVzH3Td48i2uM/byMGNqXY7YhBzvEZg8= git.sr.ht/~rjarry/go-opt v1.3.0/go.mod h1:oEPZUTJKGn1FVye0znaLoeskE/QTuyoJw5q+fjusdM4= git.sr.ht/~rockorager/go-jmap v0.3.0 h1:h2WuPcNyXRYFg9+W2HGf/mzIqC6ISy9EaS/BGa7Z5RY= git.sr.ht/~rockorager/go-jmap v0.3.0/go.mod h1:aOTCtwpZSINpDDSOkLGpHU0Kbbm5lcSDMcobX3ZtOjY= -git.sr.ht/~rockorager/vaxis v0.4.7 h1:9VlkBBF9dxy62AMHnKAU8GqEs2/mUTlke/ZJ9o/6luk= -git.sr.ht/~rockorager/vaxis v0.4.7/go.mod h1:h94aKek3frIV1hJbdXjqnBqaLkbWXvV+UxAsQHg9bns= +git.sr.ht/~rockorager/vaxis v0.5.1 h1:5b2v3YXPKlLFl2Xu+6ZHTfT6cw2ZziVfQDpvGBcpORs= +git.sr.ht/~rockorager/vaxis v0.5.1/go.mod h1:h94aKek3frIV1hJbdXjqnBqaLkbWXvV+UxAsQHg9bns= github.com/ProtonMail/crypto v0.0.0-20200420072808-71bec3603bf3 h1:JW27/kGLQzeM1Fxg5YQhdkTEAU7HIAHMgSag35zVTnY= github.com/ProtonMail/crypto v0.0.0-20200420072808-71bec3603bf3/go.mod h1:Pxr7w4gA2ikI4sWyYwEffm+oew1WAJHzG1SiDpQMkrI= github.com/ProtonMail/go-crypto v0.0.0-20211112122917-428f8eabeeb3/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= -- 2.43.0
builds.sr.ht <builds@sr.ht>aerc/patches: SUCCESS in 5m0s [Replace tcell][0] v3 from [Tim Culverhouse][1] [0]: https://lists.sr.ht/~rjarry/aerc-devel/patches/48376 [1]: mailto:tim@timculverhouse.com ✓ #1129000 SUCCESS aerc/patches/alpine-edge.yml https://builds.sr.ht/~rjarry/job/1129000 ✓ #1129001 SUCCESS aerc/patches/openbsd.yml https://builds.sr.ht/~rjarry/job/1129001