msgviewer: show available bindings when filter not configured

Show what command can be used to achieve the described effect so users
can add it as a binding.

Fixes: https://todo.sr.ht/~rjarry/aerc/40
Signed-off-by: kt programs <ktprograms@gmail.com>
Tested-by: Koni Marti <koni.marti@gmail.com>
Acked-by: Robin Jarry <robin@jarry.cc>
This commit is contained in:
kt programs 2022-04-20 20:21:06 +08:00 committed by Robin Jarry
parent 8db09d2c73
commit 5e5d5a0d1f
1 changed files with 59 additions and 27 deletions

View File

@ -482,6 +482,7 @@ func (mv *MessageViewer) Focus(focus bool) {
type PartViewer struct { type PartViewer struct {
ui.Invalidatable ui.Invalidatable
conf *config.AercConfig conf *config.AercConfig
acctConfig *config.AccountConfig
err error err error
fetched bool fetched bool
filter *exec.Cmd filter *exec.Cmd
@ -494,7 +495,6 @@ type PartViewer struct {
sink io.WriteCloser sink io.WriteCloser
source io.Reader source io.Reader
term *Terminal term *Terminal
selector *Selector
grid *ui.Grid grid *ui.Grid
uiConfig config.UIConfig uiConfig config.UIConfig
} }
@ -562,27 +562,14 @@ func NewPartViewer(acct *AccountView, conf *config.AercConfig,
grid := ui.NewGrid().Rows([]ui.GridSpec{ grid := ui.NewGrid().Rows([]ui.GridSpec{
{Strategy: ui.SIZE_EXACT, Size: ui.Const(3)}, // Message {Strategy: ui.SIZE_EXACT, Size: ui.Const(3)}, // Message
{Strategy: ui.SIZE_EXACT, Size: ui.Const(1)}, // Selector
{Strategy: ui.SIZE_WEIGHT, Size: ui.Const(1)}, {Strategy: ui.SIZE_WEIGHT, Size: ui.Const(1)},
}).Columns([]ui.GridSpec{ }).Columns([]ui.GridSpec{
{Strategy: ui.SIZE_WEIGHT, Size: ui.Const(1)}, {Strategy: ui.SIZE_WEIGHT, Size: ui.Const(1)},
}) })
selector := NewSelector([]string{"Save message", "Pipe to command"},
0, acct.UiConfig()).
OnChoose(func(option string) {
switch option {
case "Save message":
acct.aerc.BeginExCommand("save ")
case "Pipe to command":
acct.aerc.BeginExCommand("pipe ")
}
})
grid.AddChild(selector).At(2, 0)
pv := &PartViewer{ pv := &PartViewer{
conf: conf, conf: conf,
acctConfig: acct.AccountConfig(),
filter: filter, filter: filter,
index: index, index: index,
msg: msg, msg: msg,
@ -592,7 +579,6 @@ func NewPartViewer(acct *AccountView, conf *config.AercConfig,
showHeaders: conf.Viewer.ShowHeaders, showHeaders: conf.Viewer.ShowHeaders,
sink: pipe, sink: pipe,
term: term, term: term,
selector: selector,
grid: grid, grid: grid,
uiConfig: acct.UiConfig(), uiConfig: acct.UiConfig(),
} }
@ -716,24 +702,70 @@ func (pv *PartViewer) copySourceToSinkStripAnsi() {
} }
} }
var noFilterConfiguredCommands = [][]string{
{":open<enter>", "Open using the system handler"},
{":save<space>", "Save to file"},
{":pipe<space>", "Pipe to shell command"},
}
func newNoFilterConfigured(pv *PartViewer) *ui.Grid {
bindings := pv.conf.MergeContextualBinds(
pv.conf.Bindings.MessageView,
config.BIND_CONTEXT_ACCOUNT,
pv.acctConfig.Name,
"view",
)
var actions []string
for _, command := range noFilterConfiguredCommands {
cmd := command[0]
name := command[1]
strokes, _ := config.ParseKeyStrokes(cmd)
var inputs []string
for _, input := range bindings.GetReverseBindings(strokes) {
inputs = append(inputs, config.FormatKeyStrokes(input))
}
actions = append(actions, fmt.Sprintf(" %-6s %-29s %s",
strings.Join(inputs[:], ", "), name, cmd))
}
spec := []ui.GridSpec{
{Strategy: ui.SIZE_EXACT, Size: ui.Const(2)},
}
for i := 0; i < len(actions)-1; i++ {
spec = append(spec, ui.GridSpec{Strategy: ui.SIZE_EXACT, Size: ui.Const(1)})
}
// make the last element fill remaining space
spec = append(spec, ui.GridSpec{Strategy: ui.SIZE_WEIGHT, Size: ui.Const(1)})
grid := ui.NewGrid().Rows(spec).Columns([]ui.GridSpec{
{Strategy: ui.SIZE_WEIGHT, Size: ui.Const(1)},
})
uiConfig := pv.conf.Ui
noFilter := fmt.Sprintf(`No filter configured for this mimetype ('%s/%s')
What would you like to do?`, pv.part.MIMEType, pv.part.MIMESubType)
grid.AddChild(ui.NewText(noFilter,
uiConfig.GetStyle(config.STYLE_TITLE))).At(0, 0)
for i, action := range actions {
grid.AddChild(ui.NewText(action,
uiConfig.GetStyle(config.STYLE_DEFAULT))).At(i+1, 0)
}
return grid
}
func (pv *PartViewer) Invalidate() { func (pv *PartViewer) Invalidate() {
pv.DoInvalidate(pv) pv.DoInvalidate(pv)
} }
func (pv *PartViewer) Draw(ctx *ui.Context) { func (pv *PartViewer) Draw(ctx *ui.Context) {
style := pv.uiConfig.GetStyle(config.STYLE_DEFAULT) style := pv.uiConfig.GetStyle(config.STYLE_DEFAULT)
styleError := pv.uiConfig.GetStyle(config.STYLE_ERROR)
if pv.filter == nil { if pv.filter == nil {
// TODO: Let them download it directly or something
ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', style) ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', style)
ctx.Printf(0, 0, styleError, newNoFilterConfigured(pv).Draw(ctx)
"No filter configured for this mimetype ('%s/%s')",
pv.part.MIMEType, pv.part.MIMESubType,
)
ctx.Printf(0, 2, style,
"You can still :save the message or :pipe it to an external command")
pv.selector.Focus(true)
pv.grid.Draw(ctx)
return return
} }
if !pv.fetched { if !pv.fetched {
@ -759,7 +791,7 @@ func (pv *PartViewer) Event(event tcell.Event) bool {
if pv.term != nil { if pv.term != nil {
return pv.term.Event(event) return pv.term.Event(event)
} }
return pv.selector.Event(event) return false
} }
type HeaderView struct { type HeaderView struct {