summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKoni Marti <koni.marti@gmail.com>2022-05-07 05:29:43 +0200
committerRobin Jarry <robin@jarry.cc>2022-05-08 18:36:49 +0200
commit4a4050ee0f3344824ee99c53747b68afeadcaceb (patch)
treee1928f47ae179848fd7cff153932f4a2a3b4bea9
parentb57fceaad4bfcbd4ca3022e013b73eff72079c0b (diff)
downloadaerc-4a4050ee0f3344824ee99c53747b68afeadcaceb.tar.gz
aerc-4a4050ee0f3344824ee99c53747b68afeadcaceb.zip
ui: fix panic in selector when resizing terminal0.10.0
Fix panic when resizing the terminal by dynamically adjusting the width of the option selector. The selector does not check the width of the terminal before printing. This can lead to a panic in the account wizard when reducing the terminal width. If the terminal width is not large enough, the space between the options is reduced. If this is still not enough, then the selector will only show the focused option and arrows indicating the alternatives. Fixes: https://todo.sr.ht/~rjarry/aerc/41 Reported-by: Omar Polo <op@omarpolo.com> Signed-off-by: Koni Marti <koni.marti@gmail.com> Acked-by: Robin Jarry <robin@jarry.cc>
-rw-r--r--widgets/selector.go68
1 files changed, 63 insertions, 5 deletions
diff --git a/widgets/selector.go b/widgets/selector.go
index e773c936..46a026d7 100644
--- a/widgets/selector.go
+++ b/widgets/selector.go
@@ -1,7 +1,10 @@
package widgets
import (
+ "fmt"
+
"github.com/gdamore/tcell/v2"
+ "github.com/mattn/go-runewidth"
"git.sr.ht/~rjarry/aerc/config"
"git.sr.ht/~rjarry/aerc/lib/ui"
@@ -37,11 +40,44 @@ func (sel *Selector) Invalidate() {
}
func (sel *Selector) Draw(ctx *ui.Context) {
- ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ',
- sel.uiConfig.GetStyle(config.STYLE_SELECTOR_DEFAULT))
+ defaultSelectorStyle := sel.uiConfig.GetStyle(config.STYLE_SELECTOR_DEFAULT)
+ w, h := ctx.Width(), ctx.Height()
+ ctx.Fill(0, 0, w, h, ' ', defaultSelectorStyle)
+
+ if w < 5 || h < 1 {
+ // if width and height are that small, don't even try to draw
+ // something
+ return
+ }
+
+ y := 1
+ if h == 1 {
+ y = 0
+ }
+
+ format := "[%s]"
+
+ calculateWidth := func(space int) int {
+ neededWidth := 2
+ for i, option := range sel.options {
+ neededWidth += runewidth.StringWidth(fmt.Sprintf(format, option))
+ if i < len(sel.options)-1 {
+ neededWidth += space
+ }
+ }
+ return neededWidth - space
+ }
+
+ space := 5
+ for ; space > 0; space-- {
+ if w > calculateWidth(space) {
+ break
+ }
+ }
+
x := 2
for i, option := range sel.options {
- style := sel.uiConfig.GetStyle(config.STYLE_SELECTOR_DEFAULT)
+ style := defaultSelectorStyle
if sel.focus == i {
if sel.focused {
style = sel.uiConfig.GetStyle(config.STYLE_SELECTOR_FOCUSED)
@@ -49,8 +85,30 @@ func (sel *Selector) Draw(ctx *ui.Context) {
style = sel.uiConfig.GetStyle(config.STYLE_SELECTOR_CHOOSER)
}
}
- x += ctx.Printf(x, 1, style, "[%s]", option)
- x += 5
+
+ if space == 0 {
+ if sel.focus == i {
+ leftArrow, rightArrow := ' ', ' '
+ if i > 0 {
+ leftArrow = '❮'
+ }
+ if i < len(sel.options)-1 {
+ rightArrow = '❯'
+ }
+
+ s := runewidth.Truncate(option,
+ w-runewidth.RuneWidth(leftArrow)-runewidth.RuneWidth(rightArrow)-runewidth.StringWidth(fmt.Sprintf(format, "")),
+ "…")
+
+ nextPos := 0
+ nextPos += ctx.Printf(nextPos, y, defaultSelectorStyle, "%c", leftArrow)
+ nextPos += ctx.Printf(nextPos, y, style, format, s)
+ ctx.Printf(nextPos, y, defaultSelectorStyle, "%c", rightArrow)
+ }
+ } else {
+ x += ctx.Printf(x, y, style, format, option)
+ x += space
+ }
}
}