aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTim Culverhouse <tim@timculverhouse.com>2023-02-03 13:23:08 -0600
committerRobin Jarry <robin@jarry.cc>2023-02-12 00:39:21 +0100
commit2a0da301c51a8862c4e8a73b141adc0f416833a5 (patch)
treedabf9adbef3a4732daae91acb11be941ff3c47f9
parent2df3a079a060bb56aaa4d94df066936cb7b73f9d (diff)
downloadaerc-2a0da301c51a8862c4e8a73b141adc0f416833a5.tar.gz
tabs: use template for compose tabs
Use a template for compose tabs. Other available values: Account string Subject string To []*mail.Address From []*mail.Address Cc []*mail.Address Bcc []*mail.Address OriginalFrom []*mail.Address When you use To, From, CC, BCC, or OriginalFrom the title will only be updated when an editing field has lost focus. This is so we don't end up calling "PrepareHeader" on every keystroke, which will likely error out anyways since it will be an invalid header. Subject still updates every keystroke. Signed-off-by: Tim Culverhouse <tim@timculverhouse.com> Acked-by: Robin Jarry <robin@jarry.cc>
-rw-r--r--commands/account/compose.go11
-rw-r--r--commands/account/recover.go7
-rw-r--r--commands/msg/forward.go11
-rw-r--r--commands/msg/invite.go11
-rw-r--r--commands/msg/recall.go11
-rw-r--r--commands/msg/reply.go11
-rw-r--r--commands/msg/unsubscribe.go11
-rw-r--r--config/ui.go13
-rw-r--r--doc/aerc-config.5.scd6
-rw-r--r--widgets/aerc.go10
-rw-r--r--widgets/compose.go54
11 files changed, 80 insertions, 76 deletions
diff --git a/commands/account/compose.go b/commands/account/compose.go
index 5695bb7d..a8b16791 100644
--- a/commands/account/compose.go
+++ b/commands/account/compose.go
@@ -11,7 +11,6 @@ import (
"github.com/emersion/go-message/mail"
"git.sr.ht/~rjarry/aerc/config"
- "git.sr.ht/~rjarry/aerc/lib/ui"
"git.sr.ht/~rjarry/aerc/log"
"git.sr.ht/~rjarry/aerc/widgets"
"git.sr.ht/~sircmpwn/getopt"
@@ -58,15 +57,7 @@ func (Compose) Execute(aerc *widgets.Aerc, args []string) error {
if err != nil {
return err
}
- tab := aerc.NewTab(composer, "New email")
- composer.OnHeaderChange("Subject", func(subject string) {
- if subject == "" {
- tab.Name = "New email"
- } else {
- tab.Name = subject
- }
- ui.Invalidate()
- })
+ composer.Tab = aerc.NewTab(composer, "New email")
go func() {
defer log.PanicHandler()
diff --git a/commands/account/recover.go b/commands/account/recover.go
index cef0720f..cccadf8d 100644
--- a/commands/account/recover.go
+++ b/commands/account/recover.go
@@ -8,7 +8,6 @@ import (
"path/filepath"
"git.sr.ht/~rjarry/aerc/commands"
- "git.sr.ht/~rjarry/aerc/lib/ui"
"git.sr.ht/~rjarry/aerc/log"
"git.sr.ht/~rjarry/aerc/widgets"
"git.sr.ht/~sircmpwn/getopt"
@@ -107,11 +106,7 @@ func (Recover) Execute(aerc *widgets.Aerc, args []string) error {
return err
}
- tab := aerc.NewTab(composer, "Recovered")
- composer.OnHeaderChange("Subject", func(subject string) {
- tab.Name = subject
- ui.Invalidate()
- })
+ composer.Tab = aerc.NewTab(composer, "Recovered")
go func() {
defer log.PanicHandler()
diff --git a/commands/msg/forward.go b/commands/msg/forward.go
index 90c32ec2..92575abd 100644
--- a/commands/msg/forward.go
+++ b/commands/msg/forward.go
@@ -15,7 +15,6 @@ import (
"git.sr.ht/~rjarry/aerc/config"
"git.sr.ht/~rjarry/aerc/lib"
"git.sr.ht/~rjarry/aerc/lib/format"
- "git.sr.ht/~rjarry/aerc/lib/ui"
"git.sr.ht/~rjarry/aerc/log"
"git.sr.ht/~rjarry/aerc/models"
"git.sr.ht/~rjarry/aerc/widgets"
@@ -107,20 +106,12 @@ func (forward) Execute(aerc *widgets.Aerc, args []string) error {
return nil, err
}
- tab := aerc.NewTab(composer, subject)
+ composer.Tab = aerc.NewTab(composer, subject)
if !h.Has("to") {
composer.FocusEditor("to")
} else {
composer.FocusTerminal()
}
- composer.OnHeaderChange("Subject", func(subject string) {
- if subject == "" {
- tab.Name = "New email"
- } else {
- tab.Name = subject
- }
- ui.Invalidate()
- })
return composer, nil
}
diff --git a/commands/msg/invite.go b/commands/msg/invite.go
index 1528abea..774c4a69 100644
--- a/commands/msg/invite.go
+++ b/commands/msg/invite.go
@@ -9,7 +9,6 @@ import (
"git.sr.ht/~rjarry/aerc/lib"
"git.sr.ht/~rjarry/aerc/lib/calendar"
"git.sr.ht/~rjarry/aerc/lib/format"
- "git.sr.ht/~rjarry/aerc/lib/ui"
"git.sr.ht/~rjarry/aerc/log"
"git.sr.ht/~rjarry/aerc/models"
"git.sr.ht/~rjarry/aerc/widgets"
@@ -152,15 +151,7 @@ func (invite) Execute(aerc *widgets.Aerc, args []string) error {
}
composer.FocusTerminal()
- tab := aerc.NewTab(composer, subject)
- composer.OnHeaderChange("Subject", func(subject string) {
- if subject == "" {
- tab.Name = "New email"
- } else {
- tab.Name = subject
- }
- ui.Invalidate()
- })
+ composer.Tab = aerc.NewTab(composer, subject)
composer.OnClose(func(c *widgets.Composer) {
if c.Sent() {
diff --git a/commands/msg/recall.go b/commands/msg/recall.go
index 2ef468e0..4eda8339 100644
--- a/commands/msg/recall.go
+++ b/commands/msg/recall.go
@@ -13,7 +13,6 @@ import (
"github.com/pkg/errors"
"git.sr.ht/~rjarry/aerc/lib"
- "git.sr.ht/~rjarry/aerc/lib/ui"
"git.sr.ht/~rjarry/aerc/log"
"git.sr.ht/~rjarry/aerc/models"
"git.sr.ht/~rjarry/aerc/widgets"
@@ -87,15 +86,7 @@ func (Recall) Execute(aerc *widgets.Aerc, args []string) error {
if subject == "" {
subject = "Recalled email"
}
- tab := aerc.NewTab(composer, subject)
- composer.OnHeaderChange("Subject", func(subject string) {
- if subject == "" {
- tab.Name = "New email"
- } else {
- tab.Name = subject
- }
- ui.Invalidate()
- })
+ composer.Tab = aerc.NewTab(composer, subject)
composer.OnClose(func(composer *widgets.Composer) {
worker := composer.Worker()
uids := []uint32{msgInfo.Uid}
diff --git a/commands/msg/reply.go b/commands/msg/reply.go
index f8cde644..8a244fed 100644
--- a/commands/msg/reply.go
+++ b/commands/msg/reply.go
@@ -16,7 +16,6 @@ import (
"git.sr.ht/~rjarry/aerc/lib"
"git.sr.ht/~rjarry/aerc/lib/crypto"
"git.sr.ht/~rjarry/aerc/lib/format"
- "git.sr.ht/~rjarry/aerc/lib/ui"
"git.sr.ht/~rjarry/aerc/log"
"git.sr.ht/~rjarry/aerc/models"
"git.sr.ht/~rjarry/aerc/widgets"
@@ -190,15 +189,7 @@ func (reply) Execute(aerc *widgets.Aerc, args []string) error {
composer.FocusTerminal()
}
- tab := aerc.NewTab(composer, subject)
- composer.OnHeaderChange("Subject", func(subject string) {
- if subject == "" {
- tab.Name = "New email"
- } else {
- tab.Name = subject
- }
- ui.Invalidate()
- })
+ composer.Tab = aerc.NewTab(composer, subject)
composer.OnClose(func(c *widgets.Composer) {
switch {
diff --git a/commands/msg/unsubscribe.go b/commands/msg/unsubscribe.go
index 37ed707f..46ff5d1f 100644
--- a/commands/msg/unsubscribe.go
+++ b/commands/msg/unsubscribe.go
@@ -9,7 +9,6 @@ import (
"time"
"git.sr.ht/~rjarry/aerc/lib"
- "git.sr.ht/~rjarry/aerc/lib/ui"
"git.sr.ht/~rjarry/aerc/log"
"git.sr.ht/~rjarry/aerc/widgets"
"github.com/emersion/go-message/mail"
@@ -160,15 +159,7 @@ func unsubscribeMailto(aerc *widgets.Aerc, u *url.URL) error {
return err
}
composer.SetContents(strings.NewReader(u.Query().Get("body")))
- tab := aerc.NewTab(composer, "unsubscribe")
- composer.OnHeaderChange("Subject", func(subject string) {
- if subject == "" {
- tab.Name = "unsubscribe"
- } else {
- tab.Name = subject
- }
- ui.Invalidate()
- })
+ composer.Tab = aerc.NewTab(composer, "unsubscribe")
composer.FocusTerminal()
return nil
}
diff --git a/config/ui.go b/config/ui.go
index 6c880293..db596d97 100644
--- a/config/ui.go
+++ b/config/ui.go
@@ -73,7 +73,8 @@ type UIConfig struct {
SortThreadSiblings bool `ini:"sort-thread-siblings"`
// Tab Templates
- TabTitleAccount *template.Template `ini:"-"`
+ TabTitleAccount *template.Template `ini:"-"`
+ TabTitleComposer *template.Template `ini:"-"`
// private
contextualUis []*UiConfigContext
@@ -106,6 +107,7 @@ func defaultUiConfig() *UIConfig {
flags, _ := templates.ParseTemplate("column-flags", `{{.Flags | join ""}}`)
subject, _ := templates.ParseTemplate("column-subject", "{{.Subject}}")
tabTitleAccount, _ := templates.ParseTemplate("tab-title-account", "{{.Account}}")
+ tabTitleComposer, _ := templates.ParseTemplate("tab-title-composer", "{{.Subject}}")
return &UIConfig{
IndexFormat: "", // deprecated
IndexColumns: []*ColumnDef{
@@ -148,6 +150,7 @@ func defaultUiConfig() *UIConfig {
ClientThreadsDelay: 50 * time.Millisecond,
NewMessageBell: true,
TabTitleAccount: tabTitleAccount,
+ TabTitleComposer: tabTitleComposer,
FuzzyComplete: false,
Spinner: "[..] , [..] , [..] , [..] , [..], [..] , [..] , [..] ",
SpinnerDelimiter: ",",
@@ -362,6 +365,14 @@ index-format will be removed in aerc 0.17.
}
config.TabTitleAccount = tmpl
}
+ if key, err := section.GetKey("tab-title-composer"); err == nil {
+ val := key.Value()
+ tmpl, err := templates.ParseTemplate("tab-title-composer", val)
+ if err != nil {
+ return err
+ }
+ config.TabTitleComposer = tmpl
+ }
return nil
}
diff --git a/doc/aerc-config.5.scd b/doc/aerc-config.5.scd
index 4939cf3d..3669d26e 100644
--- a/doc/aerc-config.5.scd
+++ b/doc/aerc-config.5.scd
@@ -172,6 +172,12 @@ These options are configured in the *[ui]* section of _aerc.conf_.
Default: _{{.Account}}_
+*tab-title-composer* = _<go_template>_
+ The template to use for composer tab titles. See *aerc-templates*(7) for
+ available field names.
+
+ Default: _{{.Subject}}_
+
*pinned-tab-marker* = _"<string>"_
Marker to show before a pinned tab's name.
diff --git a/widgets/aerc.go b/widgets/aerc.go
index e8d65539..8eec8823 100644
--- a/widgets/aerc.go
+++ b/widgets/aerc.go
@@ -748,15 +748,7 @@ func (aerc *Aerc) Mailto(addr *url.URL) error {
if to == nil {
composer.FocusEditor("to")
}
- tab := aerc.NewTab(composer, title)
- composer.OnHeaderChange("Subject", func(subject string) {
- if subject == "" {
- tab.Name = "New email"
- } else {
- tab.Name = subject
- }
- ui.Invalidate()
- })
+ composer.Tab = aerc.NewTab(composer, title)
for _, file := range attachments {
composer.AddAttachment(file)
diff --git a/widgets/compose.go b/widgets/compose.go
index 5795ccba..94fd79d5 100644
--- a/widgets/compose.go
+++ b/widgets/compose.go
@@ -63,6 +63,7 @@ type Composer struct {
width int
textParts []*lib.Part
+ Tab *ui.Tab
}
func NewComposer(
@@ -235,6 +236,15 @@ func (c *Composer) buildComposeHeader(aerc *Aerc, cmpl *completer.Completer) {
default:
c.focusable = append(c.focusable, e)
}
+ e.OnChange(func() {
+ c.setTitle()
+ ui.Invalidate()
+ })
+ e.OnFocusLost(func() {
+ c.PrepareHeader() //nolint:errcheck // tab title only, fine if it's not valid yet
+ c.setTitle()
+ ui.Invalidate()
+ })
}
}
@@ -606,6 +616,7 @@ func (c *Composer) OnClose(fn func(composer *Composer)) {
}
func (c *Composer) Draw(ctx *ui.Context) {
+ c.setTitle()
c.width = ctx.Width()
c.grid.Draw(ctx)
}
@@ -1517,3 +1528,46 @@ func (c *Composer) checkEncryptionKeys(_ string) bool {
}
return true
}
+
+// setTitle executes the title template and sets the tab title
+func (c *Composer) setTitle() {
+ if c.Tab == nil {
+ return
+ }
+ data := struct {
+ Account string
+ Subject string
+ To []*mail.Address
+ From []*mail.Address
+ Cc []*mail.Address
+ Bcc []*mail.Address
+ OriginalFrom []*mail.Address
+ }{}
+ data.Account = c.acct.Name()
+ // Get subject direct from the textinput
+ subject, ok := c.editors["subject"]
+ if ok {
+ data.Subject = subject.input.String()
+ }
+ if data.Subject == "" {
+ data.Subject = "New Email"
+ }
+ // Get address fields from header, which gets updated on focus lost of
+ // any headerEditor field
+ data.From, _ = c.header.AddressList("from")
+ data.To, _ = c.header.AddressList("to")
+ data.Cc, _ = c.header.AddressList("cc")
+ data.Bcc, _ = c.header.AddressList("bcc")
+
+ if c.parent != nil && c.parent.RFC822Headers != nil {
+ data.OriginalFrom, _ = c.parent.RFC822Headers.AddressList("from")
+ }
+
+ buf := bytes.NewBuffer(nil)
+ err := c.acct.UiConfig().TabTitleComposer.Execute(buf, data)
+ if err != nil {
+ c.acct.PushError(err)
+ return
+ }
+ c.Tab.SetTitle(buf.String())
+}