diff options
author | Koni Marti <koni.marti@gmail.com> | 2022-05-07 05:29:43 +0200 |
---|---|---|
committer | Robin Jarry <robin@jarry.cc> | 2022-05-08 18:36:49 +0200 |
commit | 4a4050ee0f3344824ee99c53747b68afeadcaceb (patch) | |
tree | e1928f47ae179848fd7cff153932f4a2a3b4bea9 | |
parent | b57fceaad4bfcbd4ca3022e013b73eff72079c0b (diff) | |
download | aerc-4a4050ee0f3344824ee99c53747b68afeadcaceb.zip |
ui: fix panic in selector when resizing terminal
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.go | 68 |
1 files changed, 63 insertions, 5 deletions
diff --git a/widgets/selector.go b/widgets/selector.go index e773c93..46a026d 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 + } } } |