parent
76a91813d8
commit
caad1b2c06
48 changed files with 326 additions and 1087 deletions
|
@ -10,7 +10,6 @@ import (
|
|||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/go-ini/ini"
|
||||
|
@ -76,21 +75,21 @@ type AccountWizard struct {
|
|||
|
||||
func NewAccountWizard(conf *config.AercConfig, aerc *Aerc) *AccountWizard {
|
||||
wizard := &AccountWizard{
|
||||
accountName: ui.NewTextInput("", conf.Ui).Prompt("> "),
|
||||
accountName: ui.NewTextInput("").Prompt("> "),
|
||||
aerc: aerc,
|
||||
conf: conf,
|
||||
temporary: false,
|
||||
copySent: true,
|
||||
email: ui.NewTextInput("", conf.Ui).Prompt("> "),
|
||||
fullName: ui.NewTextInput("", conf.Ui).Prompt("> "),
|
||||
imapPassword: ui.NewTextInput("", conf.Ui).Prompt("] ").Password(true),
|
||||
imapServer: ui.NewTextInput("", conf.Ui).Prompt("> "),
|
||||
imapStr: ui.NewText("imaps://", conf.Ui.GetStyle(config.STYLE_DEFAULT)),
|
||||
imapUsername: ui.NewTextInput("", conf.Ui).Prompt("> "),
|
||||
smtpPassword: ui.NewTextInput("", conf.Ui).Prompt("] ").Password(true),
|
||||
smtpServer: ui.NewTextInput("", conf.Ui).Prompt("> "),
|
||||
smtpStr: ui.NewText("smtps://", conf.Ui.GetStyle(config.STYLE_DEFAULT)),
|
||||
smtpUsername: ui.NewTextInput("", conf.Ui).Prompt("> "),
|
||||
email: ui.NewTextInput("").Prompt("> "),
|
||||
fullName: ui.NewTextInput("").Prompt("> "),
|
||||
imapPassword: ui.NewTextInput("").Prompt("] ").Password(true),
|
||||
imapServer: ui.NewTextInput("").Prompt("> "),
|
||||
imapStr: ui.NewText("imaps://"),
|
||||
imapUsername: ui.NewTextInput("").Prompt("> "),
|
||||
smtpPassword: ui.NewTextInput("").Prompt("] ").Password(true),
|
||||
smtpServer: ui.NewTextInput("").Prompt("> "),
|
||||
smtpStr: ui.NewText("smtps://"),
|
||||
smtpUsername: ui.NewTextInput("").Prompt("> "),
|
||||
}
|
||||
|
||||
// Autofill some stuff for the user
|
||||
|
@ -151,36 +150,33 @@ func NewAccountWizard(conf *config.AercConfig, aerc *Aerc) *AccountWizard {
|
|||
{ui.SIZE_WEIGHT, 1},
|
||||
})
|
||||
basics.AddChild(
|
||||
ui.NewText("\nWelcome to aerc! Let's configure your account.\n\n"+
|
||||
"This wizard supports basic IMAP & SMTP configuration.\n"+
|
||||
"For other configurations, use <Ctrl+q> to exit and read the "+
|
||||
"aerc-config(5) man page.\n"+
|
||||
"Press <Tab> and <Shift+Tab> to cycle between each field in this form, "+
|
||||
"or <Ctrl+j> and <Ctrl+k>.",
|
||||
conf.Ui.GetStyle(config.STYLE_DEFAULT)))
|
||||
ui.NewText("\nWelcome to aerc! Let's configure your account.\n\n" +
|
||||
"This wizard supports basic IMAP & SMTP configuration.\n" +
|
||||
"For other configurations, use <Ctrl+q> to exit and read the " +
|
||||
"aerc-config(5) man page.\n" +
|
||||
"Press <Tab> and <Shift+Tab> to cycle between each field in this form, or <Ctrl+j> and <Ctrl+k>."))
|
||||
basics.AddChild(
|
||||
ui.NewText("Name for this account? (e.g. 'Personal' or 'Work')",
|
||||
conf.Ui.GetStyle(config.STYLE_HEADER))).
|
||||
ui.NewText("Name for this account? (e.g. 'Personal' or 'Work')").
|
||||
Bold(true)).
|
||||
At(1, 0)
|
||||
basics.AddChild(wizard.accountName).
|
||||
At(2, 0)
|
||||
basics.AddChild(ui.NewFill(' ')).
|
||||
At(3, 0)
|
||||
basics.AddChild(
|
||||
ui.NewText("Full name for outgoing emails? (e.g. 'John Doe')",
|
||||
conf.Ui.GetStyle(config.STYLE_HEADER))).
|
||||
ui.NewText("Full name for outgoing emails? (e.g. 'John Doe')").
|
||||
Bold(true)).
|
||||
At(4, 0)
|
||||
basics.AddChild(wizard.fullName).
|
||||
At(5, 0)
|
||||
basics.AddChild(ui.NewFill(' ')).
|
||||
At(6, 0)
|
||||
basics.AddChild(
|
||||
ui.NewText("Your email address? (e.g. 'john@example.org')",
|
||||
conf.Ui.GetStyle(config.STYLE_HEADER))).
|
||||
ui.NewText("Your email address? (e.g. 'john@example.org')").Bold(true)).
|
||||
At(7, 0)
|
||||
basics.AddChild(wizard.email).
|
||||
At(8, 0)
|
||||
selecter := NewSelecter([]string{"Next"}, 0, conf.Ui).
|
||||
selecter := NewSelecter([]string{"Next"}, 0).
|
||||
OnChoose(func(option string) {
|
||||
email := wizard.email.String()
|
||||
if strings.ContainsRune(email, '@') {
|
||||
|
@ -231,19 +227,16 @@ func NewAccountWizard(conf *config.AercConfig, aerc *Aerc) *AccountWizard {
|
|||
}).Columns([]ui.GridSpec{
|
||||
{ui.SIZE_WEIGHT, 1},
|
||||
})
|
||||
incoming.AddChild(ui.NewText("\nConfigure incoming mail (IMAP)",
|
||||
conf.Ui.GetStyle(config.STYLE_DEFAULT)))
|
||||
incoming.AddChild(ui.NewText("\nConfigure incoming mail (IMAP)"))
|
||||
incoming.AddChild(
|
||||
ui.NewText("Username",
|
||||
conf.Ui.GetStyle(config.STYLE_HEADER))).
|
||||
ui.NewText("Username").Bold(true)).
|
||||
At(1, 0)
|
||||
incoming.AddChild(wizard.imapUsername).
|
||||
At(2, 0)
|
||||
incoming.AddChild(ui.NewFill(' ')).
|
||||
At(3, 0)
|
||||
incoming.AddChild(
|
||||
ui.NewText("Password",
|
||||
conf.Ui.GetStyle(config.STYLE_HEADER))).
|
||||
ui.NewText("Password").Bold(true)).
|
||||
At(4, 0)
|
||||
incoming.AddChild(wizard.imapPassword).
|
||||
At(5, 0)
|
||||
|
@ -251,22 +244,20 @@ func NewAccountWizard(conf *config.AercConfig, aerc *Aerc) *AccountWizard {
|
|||
At(6, 0)
|
||||
incoming.AddChild(
|
||||
ui.NewText("Server address "+
|
||||
"(e.g. 'mail.example.org' or 'mail.example.org:1313')",
|
||||
conf.Ui.GetStyle(config.STYLE_HEADER))).
|
||||
"(e.g. 'mail.example.org' or 'mail.example.org:1313')").Bold(true)).
|
||||
At(7, 0)
|
||||
incoming.AddChild(wizard.imapServer).
|
||||
At(8, 0)
|
||||
incoming.AddChild(ui.NewFill(' ')).
|
||||
At(9, 0)
|
||||
incoming.AddChild(
|
||||
ui.NewText("Connection mode",
|
||||
conf.Ui.GetStyle(config.STYLE_HEADER))).
|
||||
ui.NewText("Connection mode").Bold(true)).
|
||||
At(10, 0)
|
||||
imapMode := NewSelecter([]string{
|
||||
"IMAP over SSL/TLS",
|
||||
"IMAP with STARTTLS",
|
||||
"Insecure IMAP",
|
||||
}, 0, conf.Ui).Chooser(true).OnSelect(func(option string) {
|
||||
}, 0).Chooser(true).OnSelect(func(option string) {
|
||||
switch option {
|
||||
case "IMAP over SSL/TLS":
|
||||
wizard.imapMode = IMAP_OVER_TLS
|
||||
|
@ -278,7 +269,7 @@ func NewAccountWizard(conf *config.AercConfig, aerc *Aerc) *AccountWizard {
|
|||
wizard.imapUri()
|
||||
})
|
||||
incoming.AddChild(imapMode).At(11, 0)
|
||||
selecter = NewSelecter([]string{"Previous", "Next"}, 1, conf.Ui).
|
||||
selecter = NewSelecter([]string{"Previous", "Next"}, 1).
|
||||
OnChoose(wizard.advance)
|
||||
incoming.AddChild(ui.NewFill(' ')).At(12, 0)
|
||||
incoming.AddChild(wizard.imapStr).At(13, 0)
|
||||
|
@ -313,19 +304,16 @@ func NewAccountWizard(conf *config.AercConfig, aerc *Aerc) *AccountWizard {
|
|||
}).Columns([]ui.GridSpec{
|
||||
{ui.SIZE_WEIGHT, 1},
|
||||
})
|
||||
outgoing.AddChild(ui.NewText("\nConfigure outgoing mail (SMTP)",
|
||||
conf.Ui.GetStyle(config.STYLE_DEFAULT)))
|
||||
outgoing.AddChild(ui.NewText("\nConfigure outgoing mail (SMTP)"))
|
||||
outgoing.AddChild(
|
||||
ui.NewText("Username",
|
||||
conf.Ui.GetStyle(config.STYLE_HEADER))).
|
||||
ui.NewText("Username").Bold(true)).
|
||||
At(1, 0)
|
||||
outgoing.AddChild(wizard.smtpUsername).
|
||||
At(2, 0)
|
||||
outgoing.AddChild(ui.NewFill(' ')).
|
||||
At(3, 0)
|
||||
outgoing.AddChild(
|
||||
ui.NewText("Password",
|
||||
conf.Ui.GetStyle(config.STYLE_HEADER))).
|
||||
ui.NewText("Password").Bold(true)).
|
||||
At(4, 0)
|
||||
outgoing.AddChild(wizard.smtpPassword).
|
||||
At(5, 0)
|
||||
|
@ -333,22 +321,20 @@ func NewAccountWizard(conf *config.AercConfig, aerc *Aerc) *AccountWizard {
|
|||
At(6, 0)
|
||||
outgoing.AddChild(
|
||||
ui.NewText("Server address "+
|
||||
"(e.g. 'mail.example.org' or 'mail.example.org:1313')",
|
||||
conf.Ui.GetStyle(config.STYLE_HEADER))).
|
||||
"(e.g. 'mail.example.org' or 'mail.example.org:1313')").Bold(true)).
|
||||
At(7, 0)
|
||||
outgoing.AddChild(wizard.smtpServer).
|
||||
At(8, 0)
|
||||
outgoing.AddChild(ui.NewFill(' ')).
|
||||
At(9, 0)
|
||||
outgoing.AddChild(
|
||||
ui.NewText("Connection mode",
|
||||
conf.Ui.GetStyle(config.STYLE_HEADER))).
|
||||
ui.NewText("Connection mode").Bold(true)).
|
||||
At(10, 0)
|
||||
smtpMode := NewSelecter([]string{
|
||||
"SMTP over SSL/TLS",
|
||||
"SMTP with STARTTLS",
|
||||
"Insecure SMTP",
|
||||
}, 0, conf.Ui).Chooser(true).OnSelect(func(option string) {
|
||||
}, 0).Chooser(true).OnSelect(func(option string) {
|
||||
switch option {
|
||||
case "SMTP over SSL/TLS":
|
||||
wizard.smtpMode = SMTP_OVER_TLS
|
||||
|
@ -360,15 +346,15 @@ func NewAccountWizard(conf *config.AercConfig, aerc *Aerc) *AccountWizard {
|
|||
wizard.smtpUri()
|
||||
})
|
||||
outgoing.AddChild(smtpMode).At(11, 0)
|
||||
selecter = NewSelecter([]string{"Previous", "Next"}, 1, conf.Ui).
|
||||
selecter = NewSelecter([]string{"Previous", "Next"}, 1).
|
||||
OnChoose(wizard.advance)
|
||||
outgoing.AddChild(ui.NewFill(' ')).At(12, 0)
|
||||
outgoing.AddChild(wizard.smtpStr).At(13, 0)
|
||||
outgoing.AddChild(ui.NewFill(' ')).At(14, 0)
|
||||
outgoing.AddChild(
|
||||
ui.NewText("Copy sent messages to 'Sent' folder?",
|
||||
conf.Ui.GetStyle(config.STYLE_HEADER))).At(15, 0)
|
||||
copySent := NewSelecter([]string{"Yes", "No"}, 0, conf.Ui).
|
||||
ui.NewText("Copy sent messages to 'Sent' folder?").Bold(true)).
|
||||
At(15, 0)
|
||||
copySent := NewSelecter([]string{"Yes", "No"}, 0).
|
||||
Chooser(true).OnChoose(func(option string) {
|
||||
switch option {
|
||||
case "Yes":
|
||||
|
@ -394,16 +380,15 @@ func NewAccountWizard(conf *config.AercConfig, aerc *Aerc) *AccountWizard {
|
|||
{ui.SIZE_WEIGHT, 1},
|
||||
})
|
||||
complete.AddChild(ui.NewText(
|
||||
"\nConfiguration complete!\n\n"+
|
||||
"You can go back and double check your settings, or choose 'Finish' to\n"+
|
||||
"save your settings to accounts.conf.\n\n"+
|
||||
"To add another account in the future, run ':new-account'.",
|
||||
conf.Ui.GetStyle(config.STYLE_DEFAULT)))
|
||||
"\nConfiguration complete!\n\n" +
|
||||
"You can go back and double check your settings, or choose 'Finish' to\n" +
|
||||
"save your settings to accounts.conf.\n\n" +
|
||||
"To add another account in the future, run ':new-account'."))
|
||||
selecter = NewSelecter([]string{
|
||||
"Previous",
|
||||
"Finish & open tutorial",
|
||||
"Finish",
|
||||
}, 1, conf.Ui).OnChoose(func(option string) {
|
||||
}, 1).OnChoose(func(option string) {
|
||||
switch option {
|
||||
case "Previous":
|
||||
wizard.advance("Previous")
|
||||
|
@ -429,7 +414,7 @@ func (wizard *AccountWizard) ConfigureTemporaryAccount(temporary bool) {
|
|||
|
||||
func (wizard *AccountWizard) errorFor(d ui.Interactive, err error) {
|
||||
if d == nil {
|
||||
wizard.aerc.PushError(" "+err.Error(), 10*time.Second)
|
||||
wizard.aerc.PushError(" " + err.Error())
|
||||
wizard.Invalidate()
|
||||
return
|
||||
}
|
||||
|
@ -444,7 +429,7 @@ func (wizard *AccountWizard) errorFor(d ui.Interactive, err error) {
|
|||
wizard.step = step
|
||||
wizard.focus = focus
|
||||
wizard.Focus(true)
|
||||
wizard.aerc.PushError(" "+err.Error(), 10*time.Second)
|
||||
wizard.aerc.PushError(" " + err.Error())
|
||||
wizard.Invalidate()
|
||||
return
|
||||
}
|
||||
|
@ -555,7 +540,7 @@ func (wizard *AccountWizard) finish(tutorial bool) {
|
|||
term.OnClose = func(err error) {
|
||||
wizard.aerc.RemoveTab(term)
|
||||
if err != nil {
|
||||
wizard.aerc.PushError(" "+err.Error(), 10*time.Second)
|
||||
wizard.aerc.PushError(" " + err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/gdamore/tcell"
|
||||
|
||||
|
@ -55,7 +54,8 @@ func NewAccountView(aerc *Aerc, conf *config.AercConfig, acct *config.AccountCon
|
|||
|
||||
worker, err := worker.NewWorker(acct.Source, logger)
|
||||
if err != nil {
|
||||
host.SetError(fmt.Sprintf("%s: %s", acct.Name, err))
|
||||
host.SetStatus(fmt.Sprintf("%s: %s", acct.Name, err)).
|
||||
Color(tcell.ColorDefault, tcell.ColorRed)
|
||||
return &AccountView{
|
||||
acct: acct,
|
||||
aerc: aerc,
|
||||
|
@ -67,7 +67,7 @@ func NewAccountView(aerc *Aerc, conf *config.AercConfig, acct *config.AccountCon
|
|||
|
||||
dirlist := NewDirectoryList(conf, acct, logger, worker)
|
||||
if acctUiConf.SidebarWidth > 0 {
|
||||
grid.AddChild(ui.NewBordered(dirlist, ui.BORDER_RIGHT, acctUiConf))
|
||||
grid.AddChild(ui.NewBordered(dirlist, ui.BORDER_RIGHT))
|
||||
}
|
||||
|
||||
msglist := NewMessageList(conf, logger, aerc)
|
||||
|
@ -280,7 +280,8 @@ func (acct *AccountView) onMessage(msg types.WorkerMessage) {
|
|||
acct.labels = msg.Labels
|
||||
case *types.Error:
|
||||
acct.logger.Printf("%v", msg.Error)
|
||||
acct.host.SetError(fmt.Sprintf("%v", msg.Error))
|
||||
acct.host.SetStatus(fmt.Sprintf("%v", msg.Error)).
|
||||
Color(tcell.ColorDefault, tcell.ColorRed)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -290,7 +291,7 @@ func (acct *AccountView) getSortCriteria() []*types.SortCriterion {
|
|||
}
|
||||
criteria, err := sort.GetSortCriteria(acct.UiConfig().Sort)
|
||||
if err != nil {
|
||||
acct.aerc.PushError(" ui.sort: "+err.Error(), 10*time.Second)
|
||||
acct.aerc.PushError(" ui.sort: " + err.Error())
|
||||
return nil
|
||||
}
|
||||
return criteria
|
||||
|
|
|
@ -51,8 +51,8 @@ func NewAerc(conf *config.AercConfig, logger *log.Logger,
|
|||
|
||||
tabs := ui.NewTabs(&conf.Ui)
|
||||
|
||||
statusbar := ui.NewStack(conf.Ui)
|
||||
statusline := NewStatusLine(conf.Ui)
|
||||
statusbar := ui.NewStack()
|
||||
statusline := NewStatusLine()
|
||||
statusbar.Push(statusline)
|
||||
|
||||
grid := ui.NewGrid().Rows([]ui.GridSpec{
|
||||
|
@ -76,7 +76,7 @@ func NewAerc(conf *config.AercConfig, logger *log.Logger,
|
|||
logger: logger,
|
||||
statusbar: statusbar,
|
||||
statusline: statusline,
|
||||
prompts: ui.NewStack(conf.Ui),
|
||||
prompts: ui.NewStack(),
|
||||
tabs: tabs,
|
||||
}
|
||||
|
||||
|
@ -382,20 +382,12 @@ func (aerc *Aerc) SetStatus(status string) *StatusMessage {
|
|||
return aerc.statusline.Set(status)
|
||||
}
|
||||
|
||||
func (aerc *Aerc) SetError(status string) *StatusMessage {
|
||||
return aerc.statusline.SetError(status)
|
||||
}
|
||||
|
||||
func (aerc *Aerc) PushStatus(text string, expiry time.Duration) *StatusMessage {
|
||||
return aerc.statusline.Push(text, expiry)
|
||||
}
|
||||
|
||||
func (aerc *Aerc) PushError(text string, expiry time.Duration) *StatusMessage {
|
||||
return aerc.statusline.PushError(text, expiry)
|
||||
}
|
||||
|
||||
func (aerc *Aerc) PushSuccess(text string, expiry time.Duration) *StatusMessage {
|
||||
return aerc.statusline.PushSuccess(text, expiry)
|
||||
func (aerc *Aerc) PushError(text string) {
|
||||
aerc.PushStatus(text, 10*time.Second).Color(tcell.ColorDefault, tcell.ColorRed)
|
||||
}
|
||||
|
||||
func (aerc *Aerc) focus(item ui.Interactive) {
|
||||
|
@ -424,11 +416,11 @@ func (aerc *Aerc) BeginExCommand(cmd string) {
|
|||
exline := NewExLine(aerc.conf, cmd, func(cmd string) {
|
||||
parts, err := shlex.Split(cmd)
|
||||
if err != nil {
|
||||
aerc.PushError(" "+err.Error(), 10*time.Second)
|
||||
aerc.PushError(" " + err.Error())
|
||||
}
|
||||
err = aerc.cmd(parts)
|
||||
if err != nil {
|
||||
aerc.PushError(" "+err.Error(), 10*time.Second)
|
||||
aerc.PushError(" " + err.Error())
|
||||
}
|
||||
// only add to history if this is an unsimulated command,
|
||||
// ie one not executed from a keybinding
|
||||
|
@ -452,7 +444,7 @@ func (aerc *Aerc) RegisterPrompt(prompt string, cmd []string) {
|
|||
}
|
||||
err := aerc.cmd(cmd)
|
||||
if err != nil {
|
||||
aerc.PushError(" "+err.Error(), 10*time.Second)
|
||||
aerc.PushError(" " + err.Error())
|
||||
}
|
||||
}, func(cmd string) []string {
|
||||
return nil // TODO: completions
|
||||
|
@ -479,7 +471,7 @@ func (aerc *Aerc) RegisterChoices(choices []Choice) {
|
|||
}
|
||||
err := aerc.cmd(cmd)
|
||||
if err != nil {
|
||||
aerc.PushError(" "+err.Error(), 10*time.Second)
|
||||
aerc.PushError(" " + err.Error())
|
||||
}
|
||||
}, func(cmd string) []string {
|
||||
return nil // TODO: completions
|
||||
|
@ -560,10 +552,11 @@ func (aerc *Aerc) CloseDialog() {
|
|||
return
|
||||
}
|
||||
|
||||
|
||||
func (aerc *Aerc) GetPassword(title string, prompt string) (chText chan string, chErr chan error) {
|
||||
chText = make(chan string, 1)
|
||||
chErr = make(chan error, 1)
|
||||
getPasswd := NewGetPasswd(title, prompt, aerc.conf, func(pw string, err error) {
|
||||
getPasswd := NewGetPasswd(title, prompt, func(pw string, err error) {
|
||||
defer func() {
|
||||
close(chErr)
|
||||
close(chText)
|
||||
|
|
|
@ -72,11 +72,10 @@ func NewComposer(aerc *Aerc, acct *AccountView, conf *config.AercConfig,
|
|||
|
||||
templateData := templates.ParseTemplateData(defaults, original)
|
||||
cmpl := completer.New(conf.Compose.AddressBookCmd, func(err error) {
|
||||
aerc.PushError(
|
||||
fmt.Sprintf("could not complete header: %v", err), 10*time.Second)
|
||||
aerc.PushError(fmt.Sprintf("could not complete header: %v", err))
|
||||
worker.Logger.Printf("could not complete header: %v", err)
|
||||
}, aerc.Logger())
|
||||
layout, editors, focusable := buildComposeHeader(aerc, cmpl, defaults)
|
||||
layout, editors, focusable := buildComposeHeader(conf, cmpl, defaults)
|
||||
|
||||
email, err := ioutil.TempFile("", "aerc-compose-*.eml")
|
||||
if err != nil {
|
||||
|
@ -113,21 +112,21 @@ func NewComposer(aerc *Aerc, acct *AccountView, conf *config.AercConfig,
|
|||
return c, nil
|
||||
}
|
||||
|
||||
func buildComposeHeader(aerc *Aerc, cmpl *completer.Completer,
|
||||
func buildComposeHeader(conf *config.AercConfig, cmpl *completer.Completer,
|
||||
defaults map[string]string) (
|
||||
newLayout HeaderLayout,
|
||||
editors map[string]*headerEditor,
|
||||
focusable []ui.MouseableDrawableInteractive,
|
||||
) {
|
||||
layout := aerc.conf.Compose.HeaderLayout
|
||||
layout := conf.Compose.HeaderLayout
|
||||
editors = make(map[string]*headerEditor)
|
||||
focusable = make([]ui.MouseableDrawableInteractive, 0)
|
||||
|
||||
for _, row := range layout {
|
||||
for _, h := range row {
|
||||
e := newHeaderEditor(h, "", aerc.SelectedAccount().UiConfig())
|
||||
if aerc.conf.Ui.CompletionPopovers {
|
||||
e.input.TabComplete(cmpl.ForHeader(h), aerc.SelectedAccount().UiConfig().CompletionDelay)
|
||||
e := newHeaderEditor(h, "")
|
||||
if conf.Ui.CompletionPopovers {
|
||||
e.input.TabComplete(cmpl.ForHeader(h), conf.Ui.CompletionDelay)
|
||||
}
|
||||
editors[h] = e
|
||||
switch h {
|
||||
|
@ -144,9 +143,9 @@ func buildComposeHeader(aerc *Aerc, cmpl *completer.Completer,
|
|||
for _, h := range []string{"Cc", "Bcc"} {
|
||||
if val, ok := defaults[h]; ok && val != "" {
|
||||
if _, ok := editors[h]; !ok {
|
||||
e := newHeaderEditor(h, "", aerc.SelectedAccount().UiConfig())
|
||||
if aerc.conf.Ui.CompletionPopovers {
|
||||
e.input.TabComplete(cmpl.ForHeader(h), aerc.SelectedAccount().UiConfig().CompletionDelay)
|
||||
e := newHeaderEditor(h, "")
|
||||
if conf.Ui.CompletionPopovers {
|
||||
e.input.TabComplete(cmpl.ForHeader(h), conf.Ui.CompletionDelay)
|
||||
}
|
||||
editors[h] = e
|
||||
focusable = append(focusable, e)
|
||||
|
@ -260,9 +259,7 @@ func (c *Composer) readSignatureFromFile() []byte {
|
|||
}
|
||||
signature, err := ioutil.ReadFile(sigFile)
|
||||
if err != nil {
|
||||
c.aerc.PushError(
|
||||
fmt.Sprintf(" Error loading signature from file: %v", sigFile),
|
||||
10*time.Second)
|
||||
c.aerc.PushError(fmt.Sprintf(" Error loading signature from file: %v", sigFile))
|
||||
return nil
|
||||
}
|
||||
return signature
|
||||
|
@ -651,7 +648,7 @@ func (c *Composer) AddEditor(header string, value string, appendHeader bool) {
|
|||
}
|
||||
return
|
||||
}
|
||||
e := newHeaderEditor(header, value, c.aerc.SelectedAccount().UiConfig())
|
||||
e := newHeaderEditor(header, value)
|
||||
if c.config.Ui.CompletionPopovers {
|
||||
e.input.TabComplete(c.completer.ForHeader(header), c.config.Ui.CompletionDelay)
|
||||
}
|
||||
|
@ -705,27 +702,23 @@ func (c *Composer) reloadEmail() error {
|
|||
}
|
||||
|
||||
type headerEditor struct {
|
||||
name string
|
||||
focused bool
|
||||
input *ui.TextInput
|
||||
uiConfig config.UIConfig
|
||||
name string
|
||||
focused bool
|
||||
input *ui.TextInput
|
||||
}
|
||||
|
||||
func newHeaderEditor(name string, value string, uiConfig config.UIConfig) *headerEditor {
|
||||
func newHeaderEditor(name string, value string) *headerEditor {
|
||||
return &headerEditor{
|
||||
input: ui.NewTextInput(value, uiConfig),
|
||||
name: name,
|
||||
uiConfig: uiConfig,
|
||||
input: ui.NewTextInput(value),
|
||||
name: name,
|
||||
}
|
||||
}
|
||||
|
||||
func (he *headerEditor) Draw(ctx *ui.Context) {
|
||||
name := he.name + " "
|
||||
size := runewidth.StringWidth(name)
|
||||
defaultStyle := he.uiConfig.GetStyle(config.STYLE_DEFAULT)
|
||||
headerStyle := he.uiConfig.GetStyle(config.STYLE_HEADER)
|
||||
ctx.Fill(0, 0, size, ctx.Height(), ' ', defaultStyle)
|
||||
ctx.Printf(0, 0, headerStyle, "%s", name)
|
||||
ctx.Fill(0, 0, size, ctx.Height(), ' ', tcell.StyleDefault)
|
||||
ctx.Printf(0, 0, tcell.StyleDefault.Bold(true), "%s", name)
|
||||
he.input.Draw(ctx.Subcontext(size, 0, ctx.Width()-size, 1))
|
||||
}
|
||||
|
||||
|
@ -786,25 +779,21 @@ func newReviewMessage(composer *Composer, err error) *reviewMessage {
|
|||
{ui.SIZE_WEIGHT, 1},
|
||||
})
|
||||
|
||||
uiConfig := composer.config.Ui
|
||||
|
||||
if err != nil {
|
||||
grid.AddChild(ui.NewText(err.Error(), uiConfig.GetStyle(config.STYLE_ERROR)))
|
||||
grid.AddChild(ui.NewText("Press [q] to close this tab.",
|
||||
uiConfig.GetStyle(config.STYLE_DEFAULT))).At(1, 0)
|
||||
grid.AddChild(ui.NewText(err.Error()).
|
||||
Color(tcell.ColorRed, tcell.ColorDefault))
|
||||
grid.AddChild(ui.NewText("Press [q] to close this tab.")).At(1, 0)
|
||||
} else {
|
||||
// TODO: source this from actual keybindings?
|
||||
grid.AddChild(ui.NewText("Send this email? [y]es/[n]o/[e]dit/[a]ttach",
|
||||
uiConfig.GetStyle(config.STYLE_DEFAULT))).At(0, 0)
|
||||
grid.AddChild(ui.NewText("Attachments:",
|
||||
uiConfig.GetStyle(config.STYLE_TITLE))).At(1, 0)
|
||||
grid.AddChild(ui.NewText(
|
||||
"Send this email? [y]es/[n]o/[p]ostpone/[e]dit/[a]ttach")).At(0, 0)
|
||||
grid.AddChild(ui.NewText("Attachments:").
|
||||
Reverse(true)).At(1, 0)
|
||||
if len(composer.attachments) == 0 {
|
||||
grid.AddChild(ui.NewText("(none)",
|
||||
uiConfig.GetStyle(config.STYLE_DEFAULT))).At(2, 0)
|
||||
grid.AddChild(ui.NewText("(none)")).At(2, 0)
|
||||
} else {
|
||||
for i, a := range composer.attachments {
|
||||
grid.AddChild(ui.NewText(a, uiConfig.GetStyle(config.STYLE_DEFAULT))).
|
||||
At(i+2, 0)
|
||||
grid.AddChild(ui.NewText(a)).At(i+2, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -194,8 +194,7 @@ func (dirlist *DirectoryList) getRUEString(name string) string {
|
|||
}
|
||||
|
||||
func (dirlist *DirectoryList) Draw(ctx *ui.Context) {
|
||||
ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ',
|
||||
dirlist.UiConfig().GetStyle(config.STYLE_DIRLIST_DEFAULT))
|
||||
ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', tcell.StyleDefault)
|
||||
|
||||
if dirlist.spinner.IsRunning() {
|
||||
dirlist.spinner.Draw(ctx)
|
||||
|
@ -203,7 +202,7 @@ func (dirlist *DirectoryList) Draw(ctx *ui.Context) {
|
|||
}
|
||||
|
||||
if len(dirlist.dirs) == 0 {
|
||||
style := dirlist.UiConfig().GetStyle(config.STYLE_DIRLIST_DEFAULT)
|
||||
style := tcell.StyleDefault
|
||||
ctx.Printf(0, 0, style, dirlist.UiConfig().EmptyDirlist)
|
||||
return
|
||||
}
|
||||
|
@ -213,9 +212,12 @@ func (dirlist *DirectoryList) Draw(ctx *ui.Context) {
|
|||
if row >= ctx.Height() {
|
||||
break
|
||||
}
|
||||
style := dirlist.UiConfig().GetStyle(config.STYLE_DIRLIST_DEFAULT)
|
||||
style := tcell.StyleDefault
|
||||
if name == dirlist.selected {
|
||||
style = dirlist.UiConfig().GetStyleSelected(config.STYLE_DIRLIST_DEFAULT)
|
||||
style = style.Reverse(true)
|
||||
} else if name == dirlist.selecting {
|
||||
style = style.Reverse(true)
|
||||
style = style.Foreground(tcell.ColorGray)
|
||||
}
|
||||
ctx.Fill(0, row, ctx.Width(), 1, ' ', style)
|
||||
|
||||
|
|
|
@ -15,14 +15,13 @@ type ExLine struct {
|
|||
tabcomplete func(cmd string) []string
|
||||
cmdHistory lib.History
|
||||
input *ui.TextInput
|
||||
conf *config.AercConfig
|
||||
}
|
||||
|
||||
func NewExLine(conf *config.AercConfig, cmd string, commit func(cmd string), finish func(),
|
||||
tabcomplete func(cmd string) []string,
|
||||
cmdHistory lib.History) *ExLine {
|
||||
|
||||
input := ui.NewTextInput("", conf.Ui).Prompt(":").Set(cmd)
|
||||
input := ui.NewTextInput("").Prompt(":").Set(cmd)
|
||||
if conf.Ui.CompletionPopovers {
|
||||
input.TabComplete(tabcomplete, conf.Ui.CompletionDelay)
|
||||
}
|
||||
|
@ -32,7 +31,6 @@ func NewExLine(conf *config.AercConfig, cmd string, commit func(cmd string), fin
|
|||
tabcomplete: tabcomplete,
|
||||
cmdHistory: cmdHistory,
|
||||
input: input,
|
||||
conf: conf,
|
||||
}
|
||||
input.OnInvalidate(func(d ui.Drawable) {
|
||||
exline.Invalidate()
|
||||
|
@ -43,7 +41,7 @@ func NewExLine(conf *config.AercConfig, cmd string, commit func(cmd string), fin
|
|||
func NewPrompt(conf *config.AercConfig, prompt string, commit func(text string),
|
||||
tabcomplete func(cmd string) []string) *ExLine {
|
||||
|
||||
input := ui.NewTextInput("", conf.Ui).Prompt(prompt)
|
||||
input := ui.NewTextInput("").Prompt(prompt)
|
||||
if conf.Ui.CompletionPopovers {
|
||||
input.TabComplete(tabcomplete, conf.Ui.CompletionDelay)
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
|
||||
"github.com/gdamore/tcell"
|
||||
|
||||
"git.sr.ht/~sircmpwn/aerc/config"
|
||||
"git.sr.ht/~sircmpwn/aerc/lib/ui"
|
||||
)
|
||||
|
||||
|
@ -15,16 +14,14 @@ type GetPasswd struct {
|
|||
title string
|
||||
prompt string
|
||||
input *ui.TextInput
|
||||
conf *config.AercConfig
|
||||
}
|
||||
|
||||
func NewGetPasswd(title string, prompt string, conf *config.AercConfig, cb func(string, error)) *GetPasswd {
|
||||
func NewGetPasswd(title string, prompt string, cb func(string, error)) *GetPasswd {
|
||||
getpasswd := &GetPasswd{
|
||||
callback: cb,
|
||||
title: title,
|
||||
prompt: prompt,
|
||||
conf: conf,
|
||||
input: ui.NewTextInput("", conf.Ui).Password(true).Prompt("Password: "),
|
||||
input: ui.NewTextInput("").Password(true).Prompt("Password: "),
|
||||
}
|
||||
getpasswd.input.OnInvalidate(func(_ ui.Drawable) {
|
||||
getpasswd.Invalidate()
|
||||
|
@ -34,13 +31,10 @@ func NewGetPasswd(title string, prompt string, conf *config.AercConfig, cb func(
|
|||
}
|
||||
|
||||
func (gp *GetPasswd) Draw(ctx *ui.Context) {
|
||||
defaultStyle := gp.conf.Ui.GetStyle(config.STYLE_DEFAULT)
|
||||
titleStyle := gp.conf.Ui.GetStyle(config.STYLE_TITLE)
|
||||
|
||||
ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', defaultStyle)
|
||||
ctx.Fill(0, 0, ctx.Width(), 1, ' ', titleStyle)
|
||||
ctx.Printf(1, 0, titleStyle, "%s", gp.title)
|
||||
ctx.Printf(1, 1, defaultStyle, gp.prompt)
|
||||
ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', tcell.StyleDefault)
|
||||
ctx.Fill(0, 0, ctx.Width(), 1, ' ', tcell.StyleDefault.Reverse(true))
|
||||
ctx.Printf(1, 0, tcell.StyleDefault.Reverse(true), "%s", gp.title)
|
||||
ctx.Printf(1, 1, tcell.StyleDefault, gp.prompt)
|
||||
gp.input.Draw(ctx.Subcontext(1, 3, ctx.Width()-2, 1))
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@ package widgets
|
|||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/mattn/go-runewidth"
|
||||
|
@ -50,8 +49,7 @@ func (ml *MessageList) Invalidate() {
|
|||
|
||||
func (ml *MessageList) Draw(ctx *ui.Context) {
|
||||
ml.height = ctx.Height()
|
||||
ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ',
|
||||
ml.aerc.SelectedAccount().UiConfig().GetStyle(config.STYLE_MSGLIST_DEFAULT))
|
||||
ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', tcell.StyleDefault)
|
||||
|
||||
store := ml.Store()
|
||||
if store == nil {
|
||||
|
@ -86,50 +84,34 @@ func (ml *MessageList) Draw(ctx *ui.Context) {
|
|||
continue
|
||||
}
|
||||
|
||||
style := tcell.StyleDefault
|
||||
|
||||
// current row
|
||||
if row == ml.store.SelectedIndex()-ml.scroll {
|
||||
style = style.Reverse(true)
|
||||
}
|
||||
// deleted message
|
||||
if _, ok := store.Deleted[msg.Uid]; ok {
|
||||
style = style.Foreground(tcell.ColorGray)
|
||||
}
|
||||
// unread message
|
||||
seen := false
|
||||
for _, flag := range msg.Flags {
|
||||
if flag == models.SeenFlag {
|
||||
seen = true
|
||||
}
|
||||
}
|
||||
if !seen {
|
||||
style = style.Bold(true)
|
||||
}
|
||||
|
||||
ctx.Fill(0, row, ctx.Width(), 1, ' ', style)
|
||||
uiConfig := ml.conf.GetUiConfig(map[config.ContextType]string{
|
||||
config.UI_CONTEXT_ACCOUNT: ml.aerc.SelectedAccount().AccountConfig().Name,
|
||||
config.UI_CONTEXT_FOLDER: ml.aerc.SelectedAccount().Directories().Selected(),
|
||||
config.UI_CONTEXT_SUBJECT: msg.Envelope.Subject,
|
||||
})
|
||||
|
||||
so := config.STYLE_MSGLIST_DEFAULT
|
||||
|
||||
// deleted message
|
||||
if _, ok := store.Deleted[msg.Uid]; ok {
|
||||
so = config.STYLE_MSGLIST_DELETED
|
||||
}
|
||||
// unread message
|
||||
seen := false
|
||||
flaged := false
|
||||
for _, flag := range msg.Flags {
|
||||
switch flag {
|
||||
case models.SeenFlag:
|
||||
seen = true
|
||||
case models.FlaggedFlag:
|
||||
flaged = true
|
||||
}
|
||||
}
|
||||
if !seen {
|
||||
so = config.STYLE_MSGLIST_UNREAD
|
||||
}
|
||||
|
||||
if flaged {
|
||||
so = config.STYLE_MSGLIST_FLAGGED
|
||||
}
|
||||
|
||||
// marked message
|
||||
if store.IsMarked(msg.Uid) {
|
||||
so = config.STYLE_MSGLIST_MARKED
|
||||
}
|
||||
|
||||
style := uiConfig.GetStyle(so)
|
||||
|
||||
// current row
|
||||
if row == ml.store.SelectedIndex()-ml.scroll {
|
||||
style = uiConfig.GetStyleSelected(so)
|
||||
}
|
||||
|
||||
ctx.Fill(0, row, ctx.Width(), 1, ' ', style)
|
||||
fmtStr, args, err := format.ParseMessageFormat(
|
||||
ml.aerc.SelectedAccount().acct.From,
|
||||
uiConfig.IndexFormat,
|
||||
|
@ -186,7 +168,7 @@ func (ml *MessageList) MouseEvent(localX int, localY int, event tcell.Event) {
|
|||
lib.NewMessageStoreView(msg, store, ml.aerc.DecryptKeys,
|
||||
func(view lib.MessageView, err error) {
|
||||
if err != nil {
|
||||
ml.aerc.PushError(err.Error(), 10*time.Second)
|
||||
ml.aerc.PushError(err.Error())
|
||||
return
|
||||
}
|
||||
viewer := NewMessageViewer(acct, ml.aerc.Config(), view)
|
||||
|
@ -306,8 +288,7 @@ func (ml *MessageList) Scroll() {
|
|||
}
|
||||
|
||||
func (ml *MessageList) drawEmptyMessage(ctx *ui.Context) {
|
||||
uiConfig := ml.aerc.SelectedAccount().UiConfig()
|
||||
msg := uiConfig.EmptyMessage
|
||||
msg := ml.aerc.SelectedAccount().UiConfig().EmptyMessage
|
||||
ctx.Printf((ctx.Width()/2)-(len(msg)/2), 0,
|
||||
uiConfig.GetStyle(config.STYLE_MSGLIST_DEFAULT), "%s", msg)
|
||||
tcell.StyleDefault, "%s", msg)
|
||||
}
|
||||
|
|
|
@ -32,7 +32,6 @@ type MessageViewer struct {
|
|||
grid *ui.Grid
|
||||
switcher *PartSwitcher
|
||||
msg lib.MessageView
|
||||
uiConfig config.UIConfig
|
||||
}
|
||||
|
||||
type PartSwitcher struct {
|
||||
|
@ -62,11 +61,9 @@ func NewMessageViewer(acct *AccountView,
|
|||
header, headerHeight := layout.grid(
|
||||
func(header string) ui.Drawable {
|
||||
return &HeaderView{
|
||||
conf: conf,
|
||||
Name: header,
|
||||
Value: fmtHeader(msg.MessageInfo(), header,
|
||||
acct.UiConfig().TimestampFormat),
|
||||
uiConfig: acct.UiConfig(),
|
||||
}
|
||||
},
|
||||
)
|
||||
|
@ -96,16 +93,15 @@ func NewMessageViewer(acct *AccountView,
|
|||
err := createSwitcher(acct, switcher, conf, msg)
|
||||
if err != nil {
|
||||
return &MessageViewer{
|
||||
err: err,
|
||||
grid: grid,
|
||||
msg: msg,
|
||||
uiConfig: acct.UiConfig(),
|
||||
err: err,
|
||||
grid: grid,
|
||||
msg: msg,
|
||||
}
|
||||
}
|
||||
|
||||
grid.AddChild(header).At(0, 0)
|
||||
if msg.PGPDetails() != nil {
|
||||
grid.AddChild(NewPGPInfo(msg.PGPDetails(), acct.UiConfig())).At(1, 0)
|
||||
grid.AddChild(NewPGPInfo(msg.PGPDetails())).At(1, 0)
|
||||
grid.AddChild(ui.NewFill(' ')).At(2, 0)
|
||||
grid.AddChild(switcher).At(3, 0)
|
||||
} else {
|
||||
|
@ -119,7 +115,6 @@ func NewMessageViewer(acct *AccountView,
|
|||
grid: grid,
|
||||
msg: msg,
|
||||
switcher: switcher,
|
||||
uiConfig: acct.UiConfig(),
|
||||
}
|
||||
switcher.mv = mv
|
||||
|
||||
|
@ -228,9 +223,8 @@ func createSwitcher(acct *AccountView, switcher *PartSwitcher,
|
|||
|
||||
func (mv *MessageViewer) Draw(ctx *ui.Context) {
|
||||
if mv.err != nil {
|
||||
style := mv.acct.UiConfig().GetStyle(config.STYLE_DEFAULT)
|
||||
ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', style)
|
||||
ctx.Printf(0, 0, style, "%s", mv.err.Error())
|
||||
ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', tcell.StyleDefault)
|
||||
ctx.Printf(0, 0, tcell.StyleDefault, "%s", mv.err.Error())
|
||||
return
|
||||
}
|
||||
mv.grid.Draw(ctx)
|
||||
|
@ -352,10 +346,7 @@ func (ps *PartSwitcher) Draw(ctx *ui.Context) {
|
|||
ps.height = ctx.Height()
|
||||
y := ctx.Height() - height
|
||||
for i, part := range ps.parts {
|
||||
style := ps.mv.uiConfig.GetStyle(config.STYLE_DEFAULT)
|
||||
if ps.selected == i {
|
||||
style = ps.mv.uiConfig.GetStyleSelected(config.STYLE_DEFAULT)
|
||||
}
|
||||
style := tcell.StyleDefault.Reverse(ps.selected == i)
|
||||
ctx.Fill(0, y+i, ctx.Width(), 1, ' ', style)
|
||||
name := fmt.Sprintf("%s/%s",
|
||||
strings.ToLower(part.part.MIMEType),
|
||||
|
@ -444,7 +435,6 @@ func (mv *MessageViewer) Focus(focus bool) {
|
|||
|
||||
type PartViewer struct {
|
||||
ui.Invalidatable
|
||||
conf *config.AercConfig
|
||||
err error
|
||||
fetched bool
|
||||
filter *exec.Cmd
|
||||
|
@ -459,7 +449,6 @@ type PartViewer struct {
|
|||
term *Terminal
|
||||
selecter *Selecter
|
||||
grid *ui.Grid
|
||||
uiConfig config.UIConfig
|
||||
}
|
||||
|
||||
func NewPartViewer(acct *AccountView, conf *config.AercConfig,
|
||||
|
@ -529,8 +518,7 @@ func NewPartViewer(acct *AccountView, conf *config.AercConfig,
|
|||
{ui.SIZE_WEIGHT, 1},
|
||||
})
|
||||
|
||||
selecter := NewSelecter([]string{"Save message", "Pipe to command"},
|
||||
0, acct.UiConfig()).
|
||||
selecter := NewSelecter([]string{"Save message", "Pipe to command"}, 0).
|
||||
OnChoose(func(option string) {
|
||||
switch option {
|
||||
case "Save message":
|
||||
|
@ -543,7 +531,6 @@ func NewPartViewer(acct *AccountView, conf *config.AercConfig,
|
|||
grid.AddChild(selecter).At(2, 0)
|
||||
|
||||
pv := &PartViewer{
|
||||
conf: conf,
|
||||
filter: filter,
|
||||
index: index,
|
||||
msg: msg,
|
||||
|
@ -555,7 +542,6 @@ func NewPartViewer(acct *AccountView, conf *config.AercConfig,
|
|||
term: term,
|
||||
selecter: selecter,
|
||||
grid: grid,
|
||||
uiConfig: acct.UiConfig(),
|
||||
}
|
||||
|
||||
if term != nil {
|
||||
|
@ -653,16 +639,14 @@ func (pv *PartViewer) Invalidate() {
|
|||
}
|
||||
|
||||
func (pv *PartViewer) Draw(ctx *ui.Context) {
|
||||
style := pv.uiConfig.GetStyle(config.STYLE_DEFAULT)
|
||||
styleError := pv.uiConfig.GetStyle(config.STYLE_ERROR)
|
||||
if pv.filter == nil {
|
||||
// TODO: Let them download it directly or something
|
||||
ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', style)
|
||||
ctx.Printf(0, 0, styleError,
|
||||
ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', tcell.StyleDefault)
|
||||
ctx.Printf(0, 0, tcell.StyleDefault.Foreground(tcell.ColorRed),
|
||||
"No filter configured for this mimetype ('%s/%s')",
|
||||
pv.part.MIMEType, pv.part.MIMESubType,
|
||||
)
|
||||
ctx.Printf(0, 2, style,
|
||||
ctx.Printf(0, 2, tcell.StyleDefault,
|
||||
"You can still :save the message or :pipe it to an external command")
|
||||
pv.selecter.Focus(true)
|
||||
pv.grid.Draw(ctx)
|
||||
|
@ -673,8 +657,8 @@ func (pv *PartViewer) Draw(ctx *ui.Context) {
|
|||
pv.fetched = true
|
||||
}
|
||||
if pv.err != nil {
|
||||
ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', style)
|
||||
ctx.Printf(0, 0, style, "%s", pv.err.Error())
|
||||
ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', tcell.StyleDefault)
|
||||
ctx.Printf(0, 0, tcell.StyleDefault, "%s", pv.err.Error())
|
||||
return
|
||||
}
|
||||
pv.term.Draw(ctx)
|
||||
|
@ -696,10 +680,8 @@ func (pv *PartViewer) Event(event tcell.Event) bool {
|
|||
|
||||
type HeaderView struct {
|
||||
ui.Invalidatable
|
||||
conf *config.AercConfig
|
||||
Name string
|
||||
Value string
|
||||
uiConfig config.UIConfig
|
||||
Name string
|
||||
Value string
|
||||
}
|
||||
|
||||
func (hv *HeaderView) Draw(ctx *ui.Context) {
|
||||
|
@ -707,15 +689,18 @@ func (hv *HeaderView) Draw(ctx *ui.Context) {
|
|||
size := runewidth.StringWidth(name)
|
||||
lim := ctx.Width() - size - 1
|
||||
value := runewidth.Truncate(" "+hv.Value, lim, "…")
|
||||
|
||||
vstyle := hv.uiConfig.GetStyle(config.STYLE_DEFAULT)
|
||||
hstyle := hv.uiConfig.GetStyle(config.STYLE_HEADER)
|
||||
|
||||
var (
|
||||
hstyle tcell.Style
|
||||
vstyle tcell.Style
|
||||
)
|
||||
// TODO: Make this more robust and less dumb
|
||||
if hv.Name == "PGP" {
|
||||
vstyle = hv.uiConfig.GetStyle(config.STYLE_SUCCESS)
|
||||
vstyle = tcell.StyleDefault.Foreground(tcell.ColorGreen)
|
||||
hstyle = tcell.StyleDefault.Bold(true)
|
||||
} else {
|
||||
vstyle = tcell.StyleDefault
|
||||
hstyle = tcell.StyleDefault.Bold(true)
|
||||
}
|
||||
|
||||
ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', vstyle)
|
||||
ctx.Printf(0, 0, hstyle, "%s", name)
|
||||
ctx.Printf(size, 0, vstyle, "%s", value)
|
||||
|
|
|
@ -3,40 +3,40 @@ package widgets
|
|||
import (
|
||||
"errors"
|
||||
|
||||
"git.sr.ht/~sircmpwn/aerc/config"
|
||||
"git.sr.ht/~sircmpwn/aerc/lib/ui"
|
||||
|
||||
"github.com/gdamore/tcell"
|
||||
"golang.org/x/crypto/openpgp"
|
||||
pgperrors "golang.org/x/crypto/openpgp/errors"
|
||||
)
|
||||
|
||||
type PGPInfo struct {
|
||||
ui.Invalidatable
|
||||
details *openpgp.MessageDetails
|
||||
uiConfig config.UIConfig
|
||||
details *openpgp.MessageDetails
|
||||
}
|
||||
|
||||
func NewPGPInfo(details *openpgp.MessageDetails, uiConfig config.UIConfig) *PGPInfo {
|
||||
return &PGPInfo{details: details, uiConfig: uiConfig}
|
||||
func NewPGPInfo(details *openpgp.MessageDetails) *PGPInfo {
|
||||
return &PGPInfo{details: details}
|
||||
}
|
||||
|
||||
func (p *PGPInfo) DrawSignature(ctx *ui.Context) {
|
||||
errorStyle := p.uiConfig.GetStyle(config.STYLE_ERROR)
|
||||
warningStyle := p.uiConfig.GetStyle(config.STYLE_WARNING)
|
||||
validStyle := p.uiConfig.GetStyle(config.STYLE_SUCCESS)
|
||||
defaultStyle := p.uiConfig.GetStyle(config.STYLE_DEFAULT)
|
||||
errorStyle := tcell.StyleDefault.Background(tcell.ColorRed).
|
||||
Foreground(tcell.ColorWhite).Bold(true)
|
||||
softErrorStyle := tcell.StyleDefault.Foreground(tcell.ColorYellow).Bold(true)
|
||||
validStyle := tcell.StyleDefault.Foreground(tcell.ColorGreen).Bold(true)
|
||||
|
||||
// TODO: Nicer prompt for TOFU, fetch from keyserver, etc
|
||||
if errors.Is(p.details.SignatureError, pgperrors.ErrUnknownIssuer) ||
|
||||
p.details.SignedBy == nil {
|
||||
|
||||
x := ctx.Printf(0, 0, warningStyle, "*")
|
||||
x += ctx.Printf(x, 0, defaultStyle,
|
||||
x := ctx.Printf(0, 0, softErrorStyle, "*")
|
||||
x += ctx.Printf(x, 0, tcell.StyleDefault,
|
||||
" Signed with unknown key (%8X); authenticity unknown",
|
||||
p.details.SignedByKeyId)
|
||||
} else if p.details.SignatureError != nil {
|
||||
x := ctx.Printf(0, 0, errorStyle, "Invalid signature!")
|
||||
x += ctx.Printf(x, 0, errorStyle,
|
||||
x += ctx.Printf(x, 0, tcell.StyleDefault.
|
||||
Foreground(tcell.ColorRed).Bold(true),
|
||||
" This message may have been tampered with! (%s)",
|
||||
p.details.SignatureError.Error())
|
||||
} else {
|
||||
|
@ -44,26 +44,24 @@ func (p *PGPInfo) DrawSignature(ctx *ui.Context) {
|
|||
ident := entity.PrimaryIdentity()
|
||||
|
||||
x := ctx.Printf(0, 0, validStyle, "✓ Authentic ")
|
||||
x += ctx.Printf(x, 0, defaultStyle,
|
||||
x += ctx.Printf(x, 0, tcell.StyleDefault,
|
||||
"Signature from %s (%8X)",
|
||||
ident.Name, p.details.SignedByKeyId)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *PGPInfo) DrawEncryption(ctx *ui.Context, y int) {
|
||||
validStyle := p.uiConfig.GetStyle(config.STYLE_SUCCESS)
|
||||
defaultStyle := p.uiConfig.GetStyle(config.STYLE_DEFAULT)
|
||||
validStyle := tcell.StyleDefault.Foreground(tcell.ColorGreen).Bold(true)
|
||||
entity := p.details.DecryptedWith.Entity
|
||||
ident := entity.PrimaryIdentity()
|
||||
|
||||
x := ctx.Printf(0, y, validStyle, "✓ Encrypted ")
|
||||
x += ctx.Printf(x, y, defaultStyle,
|
||||
x += ctx.Printf(x, y, tcell.StyleDefault,
|
||||
"To %s (%8X) ", ident.Name, p.details.DecryptedWith.PublicKey.KeyId)
|
||||
}
|
||||
|
||||
func (p *PGPInfo) Draw(ctx *ui.Context) {
|
||||
defaultStyle := p.uiConfig.GetStyle(config.STYLE_DEFAULT)
|
||||
ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', defaultStyle)
|
||||
ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', tcell.StyleDefault)
|
||||
if p.details.IsSigned && p.details.IsEncrypted {
|
||||
p.DrawSignature(ctx)
|
||||
p.DrawEncryption(ctx, 1)
|
||||
|
|
|
@ -3,27 +3,24 @@ package widgets
|
|||
import (
|
||||
"github.com/gdamore/tcell"
|
||||
|
||||
"git.sr.ht/~sircmpwn/aerc/config"
|
||||
"git.sr.ht/~sircmpwn/aerc/lib/ui"
|
||||
)
|
||||
|
||||
type Selecter struct {
|
||||
ui.Invalidatable
|
||||
chooser bool
|
||||
focused bool
|
||||
focus int
|
||||
options []string
|
||||
uiConfig config.UIConfig
|
||||
chooser bool
|
||||
focused bool
|
||||
focus int
|
||||
options []string
|
||||
|
||||
onChoose func(option string)
|
||||
onSelect func(option string)
|
||||
}
|
||||
|
||||
func NewSelecter(options []string, focus int, uiConfig config.UIConfig) *Selecter {
|
||||
func NewSelecter(options []string, focus int) *Selecter {
|
||||
return &Selecter{
|
||||
focus: focus,
|
||||
options: options,
|
||||
uiConfig: uiConfig,
|
||||
focus: focus,
|
||||
options: options,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,16 +34,15 @@ func (sel *Selecter) Invalidate() {
|
|||
}
|
||||
|
||||
func (sel *Selecter) Draw(ctx *ui.Context) {
|
||||
ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ',
|
||||
sel.uiConfig.GetStyle(config.STYLE_SELECTER_DEFAULT))
|
||||
ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', tcell.StyleDefault)
|
||||
x := 2
|
||||
for i, option := range sel.options {
|
||||
style := sel.uiConfig.GetStyle(config.STYLE_SELECTER_DEFAULT)
|
||||
style := tcell.StyleDefault
|
||||
if sel.focus == i {
|
||||
if sel.focused {
|
||||
style = sel.uiConfig.GetStyle(config.STYLE_SELECTER_FOCUSED)
|
||||
style = style.Reverse(true)
|
||||
} else if sel.chooser {
|
||||
style = sel.uiConfig.GetStyle(config.STYLE_SELECTER_CHOOSER)
|
||||
style = style.Bold(true)
|
||||
}
|
||||
}
|
||||
x += ctx.Printf(x, 1, style, "[%s]", option)
|
||||
|
|
|
@ -16,7 +16,6 @@ type Spinner struct {
|
|||
frame int64 // access via atomic
|
||||
frames []string
|
||||
stop chan struct{}
|
||||
style tcell.Style
|
||||
}
|
||||
|
||||
func NewSpinner(uiConf *config.UIConfig) *Spinner {
|
||||
|
@ -24,7 +23,6 @@ func NewSpinner(uiConf *config.UIConfig) *Spinner {
|
|||
stop: make(chan struct{}),
|
||||
frame: -1,
|
||||
frames: strings.Split(uiConf.Spinner, uiConf.SpinnerDelimiter),
|
||||
style: uiConf.GetStyle(config.STYLE_SPINNER),
|
||||
}
|
||||
return &spinner
|
||||
}
|
||||
|
@ -72,9 +70,9 @@ func (s *Spinner) Draw(ctx *ui.Context) {
|
|||
|
||||
cur := int(atomic.LoadInt64(&s.frame) % int64(len(s.frames)))
|
||||
|
||||
ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', s.style)
|
||||
ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', tcell.StyleDefault)
|
||||
col := ctx.Width()/2 - len(s.frames[0])/2 + 1
|
||||
ctx.Printf(col, 0, s.style, "%s", s.frames[cur])
|
||||
ctx.Printf(col, 0, tcell.StyleDefault, "%s", s.frames[cur])
|
||||
}
|
||||
|
||||
func (s *Spinner) Invalidate() {
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
"github.com/gdamore/tcell"
|
||||
"github.com/mattn/go-runewidth"
|
||||
|
||||
"git.sr.ht/~sircmpwn/aerc/config"
|
||||
"git.sr.ht/~sircmpwn/aerc/lib/ui"
|
||||
)
|
||||
|
||||
|
@ -15,21 +14,21 @@ type StatusLine struct {
|
|||
stack []*StatusMessage
|
||||
fallback StatusMessage
|
||||
aerc *Aerc
|
||||
uiConfig config.UIConfig
|
||||
}
|
||||
|
||||
type StatusMessage struct {
|
||||
style tcell.Style
|
||||
bg tcell.Color
|
||||
fg tcell.Color
|
||||
message string
|
||||
}
|
||||
|
||||
func NewStatusLine(uiConfig config.UIConfig) *StatusLine {
|
||||
func NewStatusLine() *StatusLine {
|
||||
return &StatusLine{
|
||||
fallback: StatusMessage{
|
||||
style: uiConfig.GetStyle(config.STYLE_STATUSLINE_DEFAULT),
|
||||
bg: tcell.ColorDefault,
|
||||
fg: tcell.ColorDefault,
|
||||
message: "Idle",
|
||||
},
|
||||
uiConfig: uiConfig,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,7 +41,9 @@ func (status *StatusLine) Draw(ctx *ui.Context) {
|
|||
if len(status.stack) != 0 {
|
||||
line = status.stack[len(status.stack)-1]
|
||||
}
|
||||
ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', line.style)
|
||||
style := tcell.StyleDefault.
|
||||
Background(line.bg).Foreground(line.fg).Reverse(true)
|
||||
ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', style)
|
||||
pendingKeys := ""
|
||||
if status.aerc != nil {
|
||||
for _, pendingKey := range status.aerc.pendingKeys {
|
||||
|
@ -50,21 +51,13 @@ func (status *StatusLine) Draw(ctx *ui.Context) {
|
|||
}
|
||||
}
|
||||
message := runewidth.FillRight(line.message, ctx.Width()-len(pendingKeys)-5)
|
||||
ctx.Printf(0, 0, line.style, "%s%s", message, pendingKeys)
|
||||
ctx.Printf(0, 0, style, "%s%s", message, pendingKeys)
|
||||
}
|
||||
|
||||
func (status *StatusLine) Set(text string) *StatusMessage {
|
||||
status.fallback = StatusMessage{
|
||||
style: status.uiConfig.GetStyle(config.STYLE_STATUSLINE_DEFAULT),
|
||||
message: text,
|
||||
}
|
||||
status.Invalidate()
|
||||
return &status.fallback
|
||||
}
|
||||
|
||||
func (status *StatusLine) SetError(text string) *StatusMessage {
|
||||
status.fallback = StatusMessage{
|
||||
style: status.uiConfig.GetStyle(config.STYLE_STATUSLINE_ERROR),
|
||||
bg: tcell.ColorDefault,
|
||||
fg: tcell.ColorDefault,
|
||||
message: text,
|
||||
}
|
||||
status.Invalidate()
|
||||
|
@ -73,7 +66,8 @@ func (status *StatusLine) SetError(text string) *StatusMessage {
|
|||
|
||||
func (status *StatusLine) Push(text string, expiry time.Duration) *StatusMessage {
|
||||
msg := &StatusMessage{
|
||||
style: status.uiConfig.GetStyle(config.STYLE_STATUSLINE_DEFAULT),
|
||||
bg: tcell.ColorDefault,
|
||||
fg: tcell.ColorDefault,
|
||||
message: text,
|
||||
}
|
||||
status.stack = append(status.stack, msg)
|
||||
|
@ -90,18 +84,6 @@ func (status *StatusLine) Push(text string, expiry time.Duration) *StatusMessage
|
|||
return msg
|
||||
}
|
||||
|
||||
func (status *StatusLine) PushError(text string, expiry time.Duration) *StatusMessage {
|
||||
msg := status.Push(text, expiry)
|
||||
msg.Color(status.uiConfig.GetStyle(config.STYLE_STATUSLINE_ERROR))
|
||||
return msg
|
||||
}
|
||||
|
||||
func (status *StatusLine) PushSuccess(text string, expiry time.Duration) *StatusMessage {
|
||||
msg := status.Push(text, expiry)
|
||||
msg.Color(status.uiConfig.GetStyle(config.STYLE_STATUSLINE_SUCCESS))
|
||||
return msg
|
||||
}
|
||||
|
||||
func (status *StatusLine) Expire() {
|
||||
status.stack = nil
|
||||
}
|
||||
|
@ -110,6 +92,7 @@ func (status *StatusLine) SetAerc(aerc *Aerc) {
|
|||
status.aerc = aerc
|
||||
}
|
||||
|
||||
func (msg *StatusMessage) Color(style tcell.Style) {
|
||||
msg.style = style
|
||||
func (msg *StatusMessage) Color(bg tcell.Color, fg tcell.Color) {
|
||||
msg.bg = bg
|
||||
msg.fg = fg
|
||||
}
|
||||
|
|
|
@ -7,9 +7,6 @@ import (
|
|||
type TabHost interface {
|
||||
BeginExCommand(cmd string)
|
||||
SetStatus(status string) *StatusMessage
|
||||
SetError(err string) *StatusMessage
|
||||
PushStatus(text string, expiry time.Duration) *StatusMessage
|
||||
PushError(text string, expiry time.Duration) *StatusMessage
|
||||
PushSuccess(text string, expiry time.Duration) *StatusMessage
|
||||
Beep()
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue