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:
parent
8db09d2c73
commit
5e5d5a0d1f
1 changed files with 59 additions and 27 deletions
|
@ -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 {
|
||||||
|
|
Loading…
Reference in a new issue