diff options
author | Koni Marti <koni.marti@gmail.com> | 2022-03-18 22:35:33 +0100 |
---|---|---|
committer | Robin Jarry <robin@jarry.cc> | 2022-03-18 23:42:07 +0100 |
commit | 2512c0403fa42b19c3e87fed44240da045ec902f (patch) | |
tree | a258d6a9c70e79cf316985363efcdc9529c91924 | |
parent | 807870ea3542f2fcb00e7e0451af37c224041dfe (diff) | |
download | aerc-2512c0403fa42b19c3e87fed44240da045ec902f.zip |
statusline: implement per-account status
Implement a statusline state for each account. Keep the ex line and the
push notifications global. Add account name prefix to push
notifications. Prefix status line with account name when multiple
accounts are available.
Use account-specific status line for each tab where an account is
defined.
Handle threading, filter/search, viewer passthrough and connection
status.
Signed-off-by: Koni Marti <koni.marti@gmail.com>
Acked-by: Robin Jarry <robin@jarry.cc>
-rw-r--r-- | commands/account/clear.go | 4 | ||||
-rw-r--r-- | commands/account/connection.go | 12 | ||||
-rw-r--r-- | commands/account/search.go | 11 | ||||
-rw-r--r-- | commands/msg/toggle-threads.go | 2 | ||||
-rw-r--r-- | commands/msgview/toggle-key-passthrough.go | 7 | ||||
-rw-r--r-- | commands/next-tab.go | 1 | ||||
-rw-r--r-- | lib/statusline/state.go | 133 | ||||
-rw-r--r-- | widgets/account.go | 41 | ||||
-rw-r--r-- | widgets/aerc.go | 14 | ||||
-rw-r--r-- | widgets/status.go | 16 |
10 files changed, 196 insertions, 45 deletions
diff --git a/commands/account/clear.go b/commands/account/clear.go index 259a9de..64e7012 100644 --- a/commands/account/clear.go +++ b/commands/account/clear.go @@ -3,6 +3,7 @@ package account import ( "errors" + "git.sr.ht/~rjarry/aerc/lib/statusline" "git.sr.ht/~rjarry/aerc/widgets" ) @@ -30,6 +31,7 @@ func (Clear) Execute(aerc *widgets.Aerc, args []string) error { return errors.New("Cannot perform action. Messages still loading") } store.ApplyClear() - aerc.ClearExtraStatus() + acct.SetStatus(statusline.SearchFilterClear()) + return nil } diff --git a/commands/account/connection.go b/commands/account/connection.go index a87993b..52b569c 100644 --- a/commands/account/connection.go +++ b/commands/account/connection.go @@ -3,6 +3,7 @@ package account import ( "errors" + "git.sr.ht/~rjarry/aerc/lib/statusline" "git.sr.ht/~rjarry/aerc/widgets" "git.sr.ht/~rjarry/aerc/worker/types" ) @@ -26,12 +27,15 @@ func (Connection) Execute(aerc *widgets.Aerc, args []string) error { if acct == nil { return errors.New("No account selected") } + cb := func(msg types.WorkerMessage) { + acct.SetStatus(statusline.ConnectionActivity("")) + } if args[0] == "connect" { - acct.Worker().PostAction(&types.Connect{}, nil) - acct.SetStatus("Connecting...") + acct.Worker().PostAction(&types.Connect{}, cb) + acct.SetStatus(statusline.ConnectionActivity("Connecting...")) } else { - acct.Worker().PostAction(&types.Disconnect{}, nil) - acct.SetStatus("Disconnecting...") + acct.Worker().PostAction(&types.Disconnect{}, cb) + acct.SetStatus(statusline.ConnectionActivity("Disconnecting...")) } return nil } diff --git a/commands/account/search.go b/commands/account/search.go index 86d9dea..eeee7bd 100644 --- a/commands/account/search.go +++ b/commands/account/search.go @@ -2,8 +2,9 @@ package account import ( "errors" - "fmt" + "strings" + "git.sr.ht/~rjarry/aerc/lib/statusline" "git.sr.ht/~rjarry/aerc/widgets" ) @@ -33,16 +34,16 @@ func (SearchFilter) Execute(aerc *widgets.Aerc, args []string) error { var cb func([]uint32) if args[0] == "filter" { - aerc.SetExtraStatus("Filtering...") + acct.SetStatus(statusline.FilterActivity("Filtering..."), statusline.Search("")) cb = func(uids []uint32) { - aerc.SetExtraStatus(fmt.Sprintf("%s", args)) + acct.SetStatus(statusline.FilterResult(strings.Join(args, " "))) acct.Logger().Printf("Filter results: %v", uids) store.ApplyFilter(uids) } } else { - aerc.SetExtraStatus("Searching...") + acct.SetStatus(statusline.Search("Searching...")) cb = func(uids []uint32) { - aerc.SetExtraStatus(fmt.Sprintf("%s", args)) + acct.SetStatus(statusline.Search(strings.Join(args, " "))) acct.Logger().Printf("Search results: %v", uids) store.ApplySearch(uids) // TODO: Remove when stores have multiple OnUpdate handlers diff --git a/commands/msg/toggle-threads.go b/commands/msg/toggle-threads.go index e93cb42..79d515c 100644 --- a/commands/msg/toggle-threads.go +++ b/commands/msg/toggle-threads.go @@ -3,6 +3,7 @@ package msg import ( "errors" + "git.sr.ht/~rjarry/aerc/lib/statusline" "git.sr.ht/~rjarry/aerc/widgets" ) @@ -34,6 +35,7 @@ func (ToggleThreads) Execute(aerc *widgets.Aerc, args []string) error { return err } store.SetBuildThreads(!store.BuildThreads()) + acct.SetStatus(statusline.Threading(store.BuildThreads())) acct.Messages().Invalidate() return nil } diff --git a/commands/msgview/toggle-key-passthrough.go b/commands/msgview/toggle-key-passthrough.go index 6cd575b..1ac370e 100644 --- a/commands/msgview/toggle-key-passthrough.go +++ b/commands/msgview/toggle-key-passthrough.go @@ -3,6 +3,7 @@ package msgview import ( "errors" + "git.sr.ht/~rjarry/aerc/lib/statusline" "git.sr.ht/~rjarry/aerc/widgets" ) @@ -26,10 +27,8 @@ func (ToggleKeyPassthrough) Execute(aerc *widgets.Aerc, args []string) error { } mv, _ := aerc.SelectedTab().(*widgets.MessageViewer) keyPassthroughEnabled := mv.ToggleKeyPassthrough() - if keyPassthroughEnabled { - aerc.SetExtraStatus("[passthrough]") - } else { - aerc.ClearExtraStatus() + if acct := mv.SelectedAccount(); acct != nil { + acct.SetStatus(statusline.Passthrough(keyPassthroughEnabled)) } return nil } diff --git a/commands/next-tab.go b/commands/next-tab.go index 9d6a09b..854353f 100644 --- a/commands/next-tab.go +++ b/commands/next-tab.go @@ -42,6 +42,7 @@ func (NextPrevTab) Execute(aerc *widgets.Aerc, args []string) error { aerc.NextTab() } } + aerc.UpdateStatus() return nil } diff --git a/lib/statusline/state.go b/lib/statusline/state.go new file mode 100644 index 0000000..30029c1 --- /dev/null +++ b/lib/statusline/state.go @@ -0,0 +1,133 @@ +package statusline + +import ( + "fmt" + "strings" +) + +type State struct { + Name string + Multiple bool + Separator string + + Connection string + ConnActivity string + Connected bool + + Search string + Filter string + FilterActivity string + + Threading string + Passthrough string +} + +func NewState(name string, multipleAccts bool, sep string) *State { + return &State{Name: name, Multiple: multipleAccts, Separator: sep} +} + +func (s *State) String() string { + var line []string + if s.Connection != "" || s.ConnActivity != "" { + conn := s.Connection + if s.ConnActivity != "" { + conn = s.ConnActivity + } + if s.Multiple { + line = append(line, fmt.Sprintf("[%s] %s", s.Name, conn)) + } else { + line = append(line, conn) + } + } + if s.Connected { + if s.FilterActivity != "" { + line = append(line, s.FilterActivity) + } else { + if s.Filter != "" { + line = append(line, s.Filter) + } + } + if s.Search != "" { + line = append(line, s.Search) + } + if s.Threading != "" { + line = append(line, s.Threading) + } + if s.Passthrough != "" { + line = append(line, s.Passthrough) + } + } + return strings.Join(line, s.Separator) +} + +type SetStateFunc func(s *State) + +func Connected(state bool) SetStateFunc { + return func(s *State) { + s.ConnActivity = "" + s.Connected = state + if state { + s.Connection = "Connected" + } else { + s.Connection = "Disconnected" + } + } +} + +func ConnectionActivity(desc string) SetStateFunc { + return func(s *State) { + s.ConnActivity = desc + } +} + +func SearchFilterClear() SetStateFunc { + return func(s *State) { + s.Search = "" + s.FilterActivity = "" + s.Filter = "" + } +} + +func FilterActivity(str string) SetStateFunc { + return func(s *State) { + s.FilterActivity = str + } +} + +func FilterResult(str string) SetStateFunc { + return func(s *State) { + s.FilterActivity = "" + s.Filter = concatFilters(s.Filter, str) + } +} + +func concatFilters(existing, next string) string { + if existing == "" { + return next + } + return fmt.Sprintf("%s && %s", existing, next) +} + +func Search(desc string) SetStateFunc { + return func(s *State) { + s.Search = desc + } +} + +func Threading(on bool) SetStateFunc { + return func(s *State) { + s.Threading = "" + if on { + s.Threading = "threading" + } + } +} + +func Passthrough(on bool) SetStateFunc { + return func(s *State) { + s.Passthrough = "" + if on { + s.Passthrough = "passthrough" + } + } +} diff --git a/widgets/account.go b/widgets/account.go index 87a8cef..647a3ae 100644 --- a/widgets/account.go +++ b/widgets/account.go @@ -4,12 +4,14 @@ import ( "errors" "fmt" "log" + "time" "github.com/gdamore/tcell/v2" "git.sr.ht/~rjarry/aerc/config" "git.sr.ht/~rjarry/aerc/lib" "git.sr.ht/~rjarry/aerc/lib/sort" + "git.sr.ht/~rjarry/aerc/lib/statusline" "git.sr.ht/~rjarry/aerc/lib/ui" "git.sr.ht/~rjarry/aerc/models" "git.sr.ht/~rjarry/aerc/worker" @@ -29,6 +31,7 @@ type AccountView struct { logger *log.Logger msglist *MessageList worker *types.Worker + state *statusline.State } func (acct *AccountView) UiConfig() config.UIConfig { @@ -55,6 +58,7 @@ func NewAccountView(aerc *Aerc, conf *config.AercConfig, acct *config.AccountCon conf: conf, host: host, logger: logger, + state: statusline.NewState(acct.Name, len(conf.Accounts) > 1, " | "), } view.grid = ui.NewGrid().Rows([]ui.GridSpec{ @@ -86,7 +90,7 @@ func NewAccountView(aerc *Aerc, conf *config.AercConfig, acct *config.AccountCon worker.PostAction(&types.Configure{Config: acct}, nil) worker.PostAction(&types.Connect{}, nil) - host.SetStatus("Connecting...") + view.SetStatus(statusline.ConnectionActivity("Connecting...")) return view, nil } @@ -105,8 +109,22 @@ func (acct *AccountView) Tick() bool { } } -func (acct *AccountView) SetStatus(msg string) { - acct.host.SetStatus(msg) +func (acct *AccountView) SetStatus(setters ...statusline.SetStateFunc) { + for _, fn := range setters { + fn(acct.state) + } +} + +func (acct *AccountView) UpdateStatus() { + acct.host.SetStatus(acct.state.String()) +} + +func (acct *AccountView) PushStatus(status string, expiry time.Duration) { + acct.aerc.PushStatus(fmt.Sprintf("%s: %v", acct.acct.Name, status), expiry) +} + +func (acct *AccountView) PushError(err error) { + acct.aerc.PushError(fmt.Sprintf("%s: %v", acct.acct.Name, err)) } func (acct *AccountView) AccountConfig() *config.AccountConfig { @@ -140,6 +158,7 @@ func (acct *AccountView) Invalidate() { } func (acct *AccountView) Draw(ctx *ui.Context) { + acct.UpdateStatus() acct.grid.Draw(ctx) } @@ -203,7 +222,7 @@ func (acct *AccountView) onMessage(msg types.WorkerMessage) { case *types.Done: switch msg.InResponseTo().(type) { case *types.Connect, *types.Reconnect: - acct.host.SetStatus("Listing mailboxes...") + acct.SetStatus(statusline.ConnectionActivity("Listing mailboxes...")) acct.logger.Println("Listing mailboxes...") acct.dirlist.UpdateList(func(dirs []string) { var dir string @@ -221,13 +240,13 @@ func (acct *AccountView) onMessage(msg types.WorkerMessage) { } acct.msglist.SetInitDone() acct.logger.Println("Connected.") - acct.host.SetStatus("Connected.") + acct.SetStatus(statusline.Connected(true)) }) case *types.Disconnect: acct.dirlist.UpdateList(nil) acct.msglist.SetStore(nil) acct.logger.Println("Disconnected.") - acct.host.SetStatus("Disconnected.") + acct.SetStatus(statusline.Connected(false)) case *types.OpenDirectory: if store, ok := acct.dirlist.SelectedMsgStore(); ok { // If we've opened this dir before, we can re-render it from @@ -289,14 +308,14 @@ func (acct *AccountView) onMessage(msg types.WorkerMessage) { case *types.LabelList: acct.labels = msg.Labels case *types.ConnError: - acct.logger.Printf("connection error: %v", msg.Error) - acct.host.SetStatus("Disconnected.") - acct.aerc.PushError(fmt.Sprintf("%v", msg.Error)) + acct.logger.Printf("connection error: [%s] %v", acct.acct.Name, msg.Error) + acct.SetStatus(statusline.Connected(false)) + acct.PushError(msg.Error) acct.msglist.SetStore(nil) acct.worker.PostAction(&types.Reconnect{}, nil) case *types.Error: acct.logger.Printf("%v", msg.Error) - acct.aerc.PushError(fmt.Sprintf("%v", msg.Error)) + acct.PushError(msg.Error) } } @@ -306,7 +325,7 @@ func (acct *AccountView) getSortCriteria() []*types.SortCriterion { } criteria, err := sort.GetSortCriteria(acct.UiConfig().Sort) if err != nil { - acct.aerc.PushError(" ui.sort: " + err.Error()) + acct.PushError(fmt.Errorf("ui sort: %v", err)) return nil } return criteria diff --git a/widgets/aerc.go b/widgets/aerc.go index 3a8f47f..a8b23fe 100644 --- a/widgets/aerc.go +++ b/widgets/aerc.go @@ -337,6 +337,7 @@ func (aerc *Aerc) NumTabs() int { func (aerc *Aerc) NewTab(clickable ui.Drawable, name string) *ui.Tab { tab := aerc.tabs.Add(clickable, name) aerc.tabs.Select(len(aerc.tabs.Tabs) - 1) + aerc.UpdateStatus() return tab } @@ -400,17 +401,20 @@ func (aerc *Aerc) SelectPreviousTab() bool { return aerc.tabs.SelectPrevious() } -// TODO: Use per-account status lines, but a global ex line func (aerc *Aerc) SetStatus(status string) *StatusMessage { return aerc.statusline.Set(status) } -func (aerc *Aerc) SetExtraStatus(status string) { - aerc.statusline.SetExtra(status) +func (aerc *Aerc) UpdateStatus() { + if acct := aerc.SelectedAccount(); acct != nil { + acct.UpdateStatus() + } else { + aerc.ClearStatus() + } } -func (aerc *Aerc) ClearExtraStatus() { - aerc.statusline.ClearExtra() +func (aerc *Aerc) ClearStatus() { + aerc.statusline.Set("") } func (aerc *Aerc) SetError(status string) *StatusMessage { diff --git a/widgets/status.go b/widgets/status.go index 960f244..c70d215 100644 --- a/widgets/status.go +++ b/widgets/status.go @@ -14,7 +14,6 @@ type StatusLine struct { ui.Invalidatable stack []*StatusMessage fallback StatusMessage - extra string aerc *Aerc uiConfig config.UIConfig } @@ -30,7 +29,6 @@ func NewStatusLine(uiConfig config.UIConfig) *StatusLine { style: uiConfig.GetStyle(config.STYLE_STATUSLINE_DEFAULT), message: "Idle", }, - extra: "", uiConfig: uiConfig, } } @@ -51,11 +49,7 @@ func (status *StatusLine) Draw(ctx *ui.Context) { pendingKeys += string(pendingKey.Rune) } } - text := line.message - if status.extra != "" { - text += " " + status.extra - } - message := runewidth.FillRight(text, ctx.Width()-len(pendingKeys)-5) + message := runewidth.FillRight(line.message, ctx.Width()-len(pendingKeys)-5) ctx.Printf(0, 0, line.style, "%s%s", message, pendingKeys) } @@ -109,14 +103,6 @@ func (status *StatusLine) PushSuccess(text string) *StatusMessage { return msg } -func (status *StatusLine) SetExtra(text string) { - status.extra = text -} - -func (status *StatusLine) ClearExtra() { - status.extra = "" -} - func (status *StatusLine) Expire() { status.stack = nil } |