diff options
-rw-r--r-- | app/msglist.go | 17 | ||||
-rw-r--r-- | app/scrollable.go | 24 | ||||
-rw-r--r-- | commands/account/align.go | 60 | ||||
-rw-r--r-- | config/binds.conf | 4 | ||||
-rw-r--r-- | doc/aerc.1.scd | 7 |
5 files changed, 112 insertions, 0 deletions
diff --git a/app/msglist.go b/app/msglist.go index a5e5fbb6..38970123 100644 --- a/app/msglist.go +++ b/app/msglist.go @@ -51,6 +51,23 @@ type messageRowParams struct { headers *mail.Header } +// AlignMessage aligns the selected message to position pos. +func (ml *MessageList) AlignMessage(pos AlignPosition) { + store := ml.Store() + if store == nil { + return + } + idx := 0 + iter := store.UidsIterator() + for i := 0; iter.Next(); i++ { + if store.SelectedUid() == iter.Value().(uint32) { + idx = i + break + } + } + ml.Align(idx, pos) +} + func (ml *MessageList) Draw(ctx *ui.Context) { ml.height = ctx.Height() ml.width = ctx.Width() diff --git a/app/scrollable.go b/app/scrollable.go index 3a0555fe..04436375 100644 --- a/app/scrollable.go +++ b/app/scrollable.go @@ -62,6 +62,10 @@ func (s *Scrollable) EnsureScroll(idx int) { s.scroll = idx + s.offset - s.height + 1 } + s.checkBounds() +} + +func (s *Scrollable) checkBounds() { maxScroll := s.elems - s.height if maxScroll < 0 { maxScroll = 0 @@ -75,3 +79,23 @@ func (s *Scrollable) EnsureScroll(idx int) { s.scroll = 0 } } + +type AlignPosition uint + +const ( + AlignTop AlignPosition = iota + AlignCenter + AlignBottom +) + +func (s *Scrollable) Align(idx int, pos AlignPosition) { + switch pos { + case AlignTop: + s.scroll = idx + case AlignCenter: + s.scroll = idx - s.height/2 + case AlignBottom: + s.scroll = idx - s.height + 1 + } + s.checkBounds() +} diff --git a/commands/account/align.go b/commands/account/align.go new file mode 100644 index 00000000..7f835b71 --- /dev/null +++ b/commands/account/align.go @@ -0,0 +1,60 @@ +package account + +import ( + "errors" + + "git.sr.ht/~rjarry/aerc/app" + "git.sr.ht/~rjarry/aerc/commands" + "git.sr.ht/~rjarry/aerc/lib/ui" +) + +type Align struct { + Pos app.AlignPosition `opt:"pos" metavar:"top|center|bottom" action:"ParsePos" complete:"CompletePos"` +} + +func init() { + commands.Register(Align{}) +} + +var posNames []string = []string{"top", "center", "bottom"} + +func (a *Align) ParsePos(arg string) error { + switch arg { + case "top": + a.Pos = app.AlignTop + case "center": + a.Pos = app.AlignCenter + case "bottom": + a.Pos = app.AlignBottom + default: + return errors.New("invalid alignment") + } + return nil +} + +func (a *Align) CompletePos(arg string) []string { + return commands.FilterList(posNames, arg, commands.QuoteSpace) +} + +func (Align) Context() commands.CommandContext { + return commands.ACCOUNT +} + +func (Align) Aliases() []string { + return []string{"align"} +} + +func (a Align) Execute(args []string) error { + acct := app.SelectedAccount() + if acct == nil { + return errors.New("no account selected") + } + msgList := acct.Messages() + if msgList == nil { + return errors.New("no message list available") + } + msgList.AlignMessage(a.Pos) + ui.Invalidate() + + return nil +} diff --git a/config/binds.conf b/config/binds.conf index 011d86ee..125a906b 100644 --- a/config/binds.conf +++ b/config/binds.conf @@ -51,6 +51,10 @@ zM = :fold -a<Enter> zR = :unfold -a<Enter> <tab> = :fold -t<Enter> +zz = :align center<Enter> +zt = :align top<Enter> +zb = :align bottom<Enter> + <Enter> = :view<Enter> d = :prompt 'Really delete this message?' 'delete-message'<Enter> D = :delete<Enter> diff --git a/doc/aerc.1.scd b/doc/aerc.1.scd index 74cb0dd3..e3079090 100644 --- a/doc/aerc.1.scd +++ b/doc/aerc.1.scd @@ -452,6 +452,13 @@ message list, the message in the message viewer, etc). ## MESSAGE LIST COMMANDS +*:align* _top|center|bottom_ + Aligns the selected message. The available positions are: + + _top_: Top of the message list.++ + _center_: Center of the message list.++ + _bottom_: Bottom of the message list. + *:disconnect*++ *:connect* Disconnect or reconnect the current account. This only applies to |