style: customize vertical and horizontal border characters

New border-char-horizontal and border-char-vertical config settings in
aerc.conf allow users to modify border appearance from the default
1-wide/tall blank space. In stylesets, border.fg now affects the
foreground color when custom characters are defined.

Signed-off-by: Robin Jarry <robin@jarry.cc>
This commit is contained in:
Dian M Fay 2021-11-29 17:48:35 -05:00 committed by Robin Jarry
parent ad5f65b927
commit f776fb8246
7 changed files with 58 additions and 8 deletions

View file

@ -94,6 +94,12 @@ next-message-on-delete=true
# default: @SHAREDIR@/stylesets/ # default: @SHAREDIR@/stylesets/
stylesets-dirs=@SHAREDIR@/stylesets/ stylesets-dirs=@SHAREDIR@/stylesets/
# Uncomment to use box-drawing characters for vertical and horizontal borders.
#
# Default: spaces
# border-char-vertical=│
# border-char-horizontal=─
# Sets the styleset to use for the aerc ui elements. # Sets the styleset to use for the aerc ui elements.
# #
# Default: default # Default: default

View file

@ -53,6 +53,9 @@ type UIConfig struct {
StyleSetDirs []string `ini:"stylesets-dirs" delim:":"` StyleSetDirs []string `ini:"stylesets-dirs" delim:":"`
StyleSetName string `ini:"styleset-name"` StyleSetName string `ini:"styleset-name"`
style StyleSet style StyleSet
// customize border appearance
BorderCharVertical rune `ini:"-"`
BorderCharHorizontal rune `ini:"-"`
} }
type ContextType int type ContextType int
@ -358,6 +361,9 @@ func (config *AercConfig) LoadConfig(file *ini.File) error {
if err := ui.MapTo(&config.Ui); err != nil { if err := ui.MapTo(&config.Ui); err != nil {
return err return err
} }
if err := validateBorderChars(ui, &config.Ui); err != nil {
return err
}
} }
for _, sectionName := range file.SectionStrings() { for _, sectionName := range file.SectionStrings() {
if !strings.Contains(sectionName, "ui:") { if !strings.Contains(sectionName, "ui:") {
@ -372,6 +378,9 @@ func (config *AercConfig) LoadConfig(file *ini.File) error {
if err := uiSection.MapTo(&uiSubConfig); err != nil { if err := uiSection.MapTo(&uiSubConfig); err != nil {
return err return err
} }
if err := validateBorderChars(uiSection, &uiSubConfig); err != nil {
return err
}
contextualUi := contextualUi :=
UIConfigContext{ UIConfigContext{
UiConfig: uiSubConfig, UiConfig: uiSubConfig,
@ -461,6 +470,26 @@ func (config *AercConfig) LoadConfig(file *ini.File) error {
return nil return nil
} }
func validateBorderChars(section *ini.Section, config *UIConfig) error {
for key, val := range section.KeysHash() {
switch key {
case "border-char-vertical":
char := []rune(val)
if len(char) != 1 {
return fmt.Errorf("%v must be one and only one character", key)
}
config.BorderCharVertical = char[0]
case "border-char-horizontal":
char := []rune(val)
if len(char) != 1 {
return fmt.Errorf("%v must be one and only one character", key)
}
config.BorderCharHorizontal = char[0]
}
}
return nil
}
func LoadConfigFromFile(root *string, sharedir string) (*AercConfig, error) { func LoadConfigFromFile(root *string, sharedir string) (*AercConfig, error) {
if root == nil { if root == nil {
_root := path.Join(xdg.ConfigHome(), "aerc") _root := path.Join(xdg.ConfigHome(), "aerc")
@ -524,6 +553,9 @@ func LoadConfigFromFile(root *string, sharedir string) (*AercConfig, error) {
CompletionPopovers: true, CompletionPopovers: true,
StyleSetDirs: []string{path.Join(sharedir, "stylesets")}, StyleSetDirs: []string{path.Join(sharedir, "stylesets")},
StyleSetName: "default", StyleSetName: "default",
// border defaults
BorderCharVertical: ' ',
BorderCharHorizontal: ' ',
}, },
ContextualUis: []UIConfigContext{}, ContextualUis: []UIConfigContext{},

View file

@ -192,6 +192,13 @@ These options are configured in the *[ui]* section of aerc.conf.
Default: 250ms Default: 250ms
*border-char-vertical*
*border-char-horizontal*
Set stylable characters (via the 'border' element) for vertical and
horizontal borders.
Default: spaces
*stylesets-dirs* *stylesets-dirs*
The directories where the stylesets are stored. The config takes a The directories where the stylesets are stored. The config takes a
colon-separated list of dirs. colon-separated list of dirs.

View file

@ -128,7 +128,7 @@ styling.
| spinner | spinner
: The style for the loading spinner. : The style for the loading spinner.
| border | border
: The style used to draw borders. *Only the background color is used*. : The style used to draw borders. *Only the background color is used unless you customize border-char-vertical and/or border-char-horizontal in aerc.conf*.
| selector_default | selector_default
: The default style for the selector ui element. : The default style for the selector ui element.
| selector_focused | selector_focused

View file

@ -50,22 +50,25 @@ func (bordered *Bordered) Draw(ctx *Context) {
width := ctx.Width() width := ctx.Width()
height := ctx.Height() height := ctx.Height()
style := bordered.uiConfig.GetStyle(config.STYLE_BORDER) style := bordered.uiConfig.GetStyle(config.STYLE_BORDER)
verticalChar := bordered.uiConfig.BorderCharVertical
horizontalChar := bordered.uiConfig.BorderCharHorizontal
if bordered.borders&BORDER_LEFT != 0 { if bordered.borders&BORDER_LEFT != 0 {
ctx.Fill(0, 0, 1, ctx.Height(), ' ', style) ctx.Fill(0, 0, 1, ctx.Height(), verticalChar, style)
x += 1 x += 1
width -= 1 width -= 1
} }
if bordered.borders&BORDER_TOP != 0 { if bordered.borders&BORDER_TOP != 0 {
ctx.Fill(0, 0, ctx.Width(), 1, ' ', style) ctx.Fill(0, 0, ctx.Width(), 1, horizontalChar, style)
y += 1 y += 1
height -= 1 height -= 1
} }
if bordered.borders&BORDER_RIGHT != 0 { if bordered.borders&BORDER_RIGHT != 0 {
ctx.Fill(ctx.Width()-1, 0, 1, ctx.Height(), ' ', style) ctx.Fill(ctx.Width()-1, 0, 1, ctx.Height(), verticalChar, style)
width -= 1 width -= 1
} }
if bordered.borders&BORDER_BOTTOM != 0 { if bordered.borders&BORDER_BOTTOM != 0 {
ctx.Fill(0, ctx.Height()-1, ctx.Width(), 1, ' ', style) ctx.Fill(0, ctx.Height()-1, ctx.Width(), 1, horizontalChar, style)
height -= 1 height -= 1
} }
subctx := ctx.Subcontext(x, y, width, height) subctx := ctx.Subcontext(x, y, width, height)

View file

@ -679,9 +679,10 @@ func (c *Composer) updateGrid() {
c.grid.RemoveChild(c.heditors) c.grid.RemoveChild(c.heditors)
} }
borderStyle := c.config.Ui.GetStyle(config.STYLE_BORDER) borderStyle := c.config.Ui.GetStyle(config.STYLE_BORDER)
borderChar := c.config.Ui.BorderCharHorizontal
c.heditors = heditors c.heditors = heditors
c.grid.AddChild(c.heditors).At(0, 0) c.grid.AddChild(c.heditors).At(0, 0)
c.grid.AddChild(ui.NewFill(' ', borderStyle)).At(1, 0) c.grid.AddChild(ui.NewFill(borderChar, borderStyle)).At(1, 0)
} }
func (c *Composer) reloadEmail() error { func (c *Composer) reloadEmail() error {

View file

@ -106,14 +106,15 @@ func NewMessageViewer(acct *AccountView,
} }
borderStyle := acct.UiConfig().GetStyle(config.STYLE_BORDER) borderStyle := acct.UiConfig().GetStyle(config.STYLE_BORDER)
borderChar := acct.UiConfig().BorderCharHorizontal
grid.AddChild(header).At(0, 0) grid.AddChild(header).At(0, 0)
if msg.PGPDetails() != nil { if msg.PGPDetails() != nil {
grid.AddChild(NewPGPInfo(msg.PGPDetails(), acct.UiConfig())).At(1, 0) grid.AddChild(NewPGPInfo(msg.PGPDetails(), acct.UiConfig())).At(1, 0)
grid.AddChild(ui.NewFill(' ', borderStyle)).At(2, 0) grid.AddChild(ui.NewFill(borderChar, borderStyle)).At(2, 0)
grid.AddChild(switcher).At(3, 0) grid.AddChild(switcher).At(3, 0)
} else { } else {
grid.AddChild(ui.NewFill(' ', borderStyle)).At(1, 0) grid.AddChild(ui.NewFill(borderChar, borderStyle)).At(1, 0)
grid.AddChild(switcher).At(2, 0) grid.AddChild(switcher).At(2, 0)
} }