refactor(config)!: move non-style theme options to [ui]

This commit is contained in:
ayn2op 2026-10-07 15:37:03 -04:00
parent 37cd42c811
commit 534c4e3c64
12 changed files with 228 additions and 197 deletions

View file

@ -69,6 +69,47 @@ arrows = ["◀", "▶"]
# Whether clicking an arrow selects the tab next to the active one.
clickable_arrows = true
[ui.title]
# "left", "center" or "right".
alignment = "left"
[ui.footer]
alignment = "left"
[ui.border]
enabled = true
padding = [0, 0, 1, 1] # [top, bottom, left, right]
# "hidden", "plain", "round", "thick" or "double".
normal_set = "round"
active_set = "round"
[ui.guilds_tree]
auto_expand_folders = true
# Give tree-like shape
graphics = true
[ui.members_tree]
# Give tree-like shape
graphics = true
[ui.scroll_bar]
visibility = "auto"
# "minimal", "box_drawing", "vertical" or "double_vertical"
symbol_set = "box_drawing"
[ui.messages_list]
reply_indicator = ">"
forwarded_indicator = "<"
[ui.mentions_list]
# Note: width and height are capped to the available space
# Minimum width
# 0 = make the list as wide as possible
min_width = 20
# Maximum height
# 0 = make the list as tall as needed
max_height = 0
[timestamps]
enabled = true
# https://pkg.go.dev/time#Layout
@ -259,31 +300,18 @@ select_bottom = "end"
# style = { foreground = "", background = "", attributes = "" or ["",""], underline = "", underline_color = "" }
[theme.title]
alignment = "left" # `"left"`, `"center"`, or `"right"`.
normal_style = { attributes = "dim" }
active_style = { foreground = "green", attributes = "bold" }
[theme.footer]
alignment = "left"
normal_style = { attributes = "dim" }
active_style = { foreground = "green", attributes = "bold" }
[theme.border]
enabled = true
padding = [0, 0, 1, 1] # [top, bottom, left, right]
normal_style = { attributes = "dim" }
active_style = { foreground = "green", attributes = "bold" }
# `"hidden"`, `"plain"`, `"round"`, `"thick"`, or `"double"`.
normal_set = "round"
active_set = "round"
[theme.guilds_tree]
auto_expand_folders = true
# Give tree-like shape
graphics = true
graphics_color = "default"
# Direct-message recipient presence styles.
@ -293,8 +321,6 @@ dnd_style = { foreground = "red" }
offline_style = { foreground = "gray" }
[theme.members_tree]
# Give tree-like shape
graphics = true
graphics_color = "default"
# Styles of the status dot before each member.
@ -304,16 +330,10 @@ dnd_style = { foreground = "red" }
offline_style = { foreground = "gray" }
[theme.scroll_bar]
visibility = "auto"
# "minimal", "box_drawing", "vertical", or "double_vertical"
symbol_set = "box_drawing"
track_style = { attributes = "dim" }
thumb_style = {}
[theme.messages_list]
reply_indicator = ">"
forwarded_indicator = "<"
mention_style = { foreground = "blue", attributes = "bold" }
emoji_style = { foreground = "green" }
url_style = { foreground = "blue" }
@ -336,14 +356,6 @@ footer_style = { attributes = ["dim", "italic"] }
url_style = { foreground = "blue", underline = "solid" }
[theme.mentions_list]
# Note: width and height are capped to the avaliable space
# Minimum width
# 0 = make the list as wide as possible
min_width = 20
# Maximum height
# 0 = make the list as tall as needed
max_height = 0
selected_style = { attributes = "reverse" }
[theme.dialog]

View file

@ -6,33 +6,11 @@ import (
"github.com/ayn2op/arikawa/v3/discord"
"github.com/ayn2op/tview"
"github.com/ayn2op/tview/list"
"github.com/ayn2op/tview/scrollbar"
"github.com/gdamore/tcell/v3"
)
var errInvalidType = errors.New("invalid type")
type AlignmentWrapper struct{ tview.Alignment }
func (aw *AlignmentWrapper) UnmarshalTOML(v any) error {
s, ok := v.(string)
if !ok {
return errInvalidType
}
switch s {
case "left":
aw.Alignment = tview.AlignmentLeft
case "center":
aw.Alignment = tview.AlignmentCenter
case "right":
aw.Alignment = tview.AlignmentRight
default:
return fmt.Errorf("unknown value: %q", s)
}
return nil
}
type StyleWrapper struct{ tview.Style }
func (sw *StyleWrapper) UnmarshalTOML(v any) error {
@ -113,72 +91,6 @@ func (sw *StyleWrapper) parseAttr(s string) {
}
}
type BorderSetWrapper struct{ tview.BorderSet }
func (bw *BorderSetWrapper) UnmarshalTOML(val any) error {
s, ok := val.(string)
if !ok {
return errInvalidType
}
switch s {
case "hidden":
bw.BorderSet = tview.BorderSetHidden()
case "plain":
bw.BorderSet = tview.BorderSetPlain()
case "round":
bw.BorderSet = tview.BorderSetRound()
case "thick":
bw.BorderSet = tview.BorderSetThick()
case "double":
bw.BorderSet = tview.BorderSetDouble()
default:
return fmt.Errorf("unknown value: %q", s)
}
return nil
}
type SymbolSetWrapper struct{ scrollbar.SymbolSet }
func (sw *SymbolSetWrapper) UnmarshalTOML(val any) error {
s, ok := val.(string)
if !ok {
return errInvalidType
}
switch s {
case "minimal":
sw.SymbolSet = scrollbar.SymbolSet{Thumb: "█", Begin: "▲", End: "▼"}
case "box_drawing", "boxdrawing", "box":
sw.SymbolSet = scrollbar.SymbolSet{Track: "│", Thumb: "█", Begin: "▲", End: "▼"}
case "vertical":
sw.SymbolSet = scrollbar.SymbolSetVertical()
case "double_vertical":
sw.SymbolSet = scrollbar.SymbolSetDoubleVertical()
default:
return fmt.Errorf("unknown value: %q", s)
}
return nil
}
type ScrollBarVisibilityWrapper struct{ list.ScrollBarVisibility }
func (vw *ScrollBarVisibilityWrapper) UnmarshalTOML(val any) error {
s, ok := val.(string)
if !ok {
return errInvalidType
}
switch s {
case "automatic", "auto":
vw.ScrollBarVisibility = list.ScrollBarVisibilityAutomatic
case "always":
vw.ScrollBarVisibility = list.ScrollBarVisibilityAlways
case "never", "hidden", "off":
vw.ScrollBarVisibility = list.ScrollBarVisibilityNever
default:
return fmt.Errorf("unknown value: %q", s)
}
return nil
}
type (
HelpTheme struct {
ShortKeyStyle StyleWrapper `toml:"short_key_style"`
@ -192,40 +104,14 @@ type (
ActiveStyle StyleWrapper `toml:"active_style"`
}
TitleTheme struct {
ThemeStyle
Alignment AlignmentWrapper `toml:"alignment"`
}
FooterTheme struct {
ThemeStyle
Alignment AlignmentWrapper `toml:"alignment"`
}
BorderTheme struct {
ThemeStyle
Enabled bool `toml:"enabled"`
Padding [4]int `toml:"padding"`
NormalSet BorderSetWrapper `toml:"normal_set"`
ActiveSet BorderSetWrapper `toml:"active_set"`
}
GuildsTreeTheme struct {
CommonTreeTheme
AutoExpandFolders bool `toml:"auto_expand_folders"`
}
MessagesListTheme struct {
ReplyIndicator string `toml:"reply_indicator"`
ForwardedIndicator string `toml:"forwarded_indicator"`
AuthorStyle StyleWrapper `toml:"author_style"`
MentionStyle StyleWrapper `toml:"mention_style"`
EmojiStyle StyleWrapper `toml:"emoji_style"`
URLStyle StyleWrapper `toml:"url_style"`
AttachmentStyle StyleWrapper `toml:"attachment_style"`
ReactionStyle StyleWrapper `toml:"reaction_style"`
OwnReactionStyle StyleWrapper `toml:"own_reaction_style"`
AuthorStyle StyleWrapper `toml:"author_style"`
MentionStyle StyleWrapper `toml:"mention_style"`
EmojiStyle StyleWrapper `toml:"emoji_style"`
URLStyle StyleWrapper `toml:"url_style"`
AttachmentStyle StyleWrapper `toml:"attachment_style"`
ReactionStyle StyleWrapper `toml:"reaction_style"`
OwnReactionStyle StyleWrapper `toml:"own_reaction_style"`
MessageStyle StyleWrapper `toml:"message_style"`
SelectedMessageStyle StyleWrapper `toml:"selected_message_style"`
@ -245,9 +131,6 @@ type (
}
MentionsListTheme struct {
MinWidth uint `toml:"min_width"`
MaxHeight uint `toml:"max_height"`
SelectedStyle StyleWrapper `toml:"selected_style"`
}
@ -257,17 +140,15 @@ type (
}
ScrollBarTheme struct {
Visibility ScrollBarVisibilityWrapper `toml:"visibility"`
SymbolSet SymbolSetWrapper `toml:"symbol_set"`
TrackStyle StyleWrapper `toml:"track_style"`
ThumbStyle StyleWrapper `toml:"thumb_style"`
TrackStyle StyleWrapper `toml:"track_style"`
ThumbStyle StyleWrapper `toml:"thumb_style"`
}
Theme struct {
Title TitleTheme `toml:"title"`
Footer FooterTheme `toml:"footer"`
Border BorderTheme `toml:"border"`
GuildsTree GuildsTreeTheme `toml:"guilds_tree"`
Title ThemeStyle `toml:"title"`
Footer ThemeStyle `toml:"footer"`
Border ThemeStyle `toml:"border"`
GuildsTree CommonTreeTheme `toml:"guilds_tree"`
MembersTree CommonTreeTheme `toml:"members_tree"`
ScrollBar ScrollBarTheme `toml:"scroll_bar"`
MessagesList MessagesListTheme `toml:"messages_list"`
@ -278,7 +159,6 @@ type (
)
type CommonTreeTheme struct {
Graphics bool `toml:"graphics"`
GraphicsColor string `toml:"graphics_color"`
OnlineStyle StyleWrapper `toml:"online_style"`

View file

@ -1,8 +1,110 @@
package config
import (
"fmt"
"github.com/ayn2op/tview"
"github.com/ayn2op/tview/list"
"github.com/ayn2op/tview/scrollbar"
)
type AlignmentWrapper struct{ tview.Alignment }
func (aw *AlignmentWrapper) UnmarshalTOML(v any) error {
s, ok := v.(string)
if !ok {
return errInvalidType
}
switch s {
case "left":
aw.Alignment = tview.AlignmentLeft
case "center":
aw.Alignment = tview.AlignmentCenter
case "right":
aw.Alignment = tview.AlignmentRight
default:
return fmt.Errorf("unknown value: %q", s)
}
return nil
}
type BorderSetWrapper struct{ tview.BorderSet }
func (bw *BorderSetWrapper) UnmarshalTOML(val any) error {
s, ok := val.(string)
if !ok {
return errInvalidType
}
switch s {
case "hidden":
bw.BorderSet = tview.BorderSetHidden()
case "plain":
bw.BorderSet = tview.BorderSetPlain()
case "round":
bw.BorderSet = tview.BorderSetRound()
case "thick":
bw.BorderSet = tview.BorderSetThick()
case "double":
bw.BorderSet = tview.BorderSetDouble()
default:
return fmt.Errorf("unknown value: %q", s)
}
return nil
}
type SymbolSetWrapper struct{ scrollbar.SymbolSet }
func (sw *SymbolSetWrapper) UnmarshalTOML(val any) error {
s, ok := val.(string)
if !ok {
return errInvalidType
}
switch s {
case "minimal":
sw.SymbolSet = scrollbar.SymbolSet{Thumb: "█", Begin: "▲", End: "▼"}
case "box_drawing", "boxdrawing", "box":
sw.SymbolSet = scrollbar.SymbolSet{Track: "│", Thumb: "█", Begin: "▲", End: "▼"}
case "vertical":
sw.SymbolSet = scrollbar.SymbolSetVertical()
case "double_vertical":
sw.SymbolSet = scrollbar.SymbolSetDoubleVertical()
default:
return fmt.Errorf("unknown value: %q", s)
}
return nil
}
type ScrollBarVisibilityWrapper struct{ list.ScrollBarVisibility }
func (vw *ScrollBarVisibilityWrapper) UnmarshalTOML(val any) error {
s, ok := val.(string)
if !ok {
return errInvalidType
}
switch s {
case "automatic", "auto":
vw.ScrollBarVisibility = list.ScrollBarVisibilityAutomatic
case "always":
vw.ScrollBarVisibility = list.ScrollBarVisibilityAlways
case "never", "hidden", "off":
vw.ScrollBarVisibility = list.ScrollBarVisibilityNever
default:
return fmt.Errorf("unknown value: %q", s)
}
return nil
}
type (
UIConfig struct {
Tabs TabsConfig `toml:"tabs"`
Tabs TabsConfig `toml:"tabs"`
Title TitleConfig `toml:"title"`
Footer TitleConfig `toml:"footer"`
Border BorderConfig `toml:"border"`
GuildsTree GuildsTreeConfig `toml:"guilds_tree"`
MembersTree TreeConfig `toml:"members_tree"`
ScrollBar ScrollBarConfig `toml:"scroll_bar"`
MessagesList MessagesListConfig `toml:"messages_list"`
MentionsList MentionsListConfig `toml:"mentions_list"`
}
TabsConfig struct {
@ -13,4 +115,39 @@ type (
Arrows [2]string `toml:"arrows"`
ClickableArrows bool `toml:"clickable_arrows"`
}
TitleConfig struct {
Alignment AlignmentWrapper `toml:"alignment"`
}
BorderConfig struct {
Enabled bool `toml:"enabled"`
Padding [4]int `toml:"padding"`
NormalSet BorderSetWrapper `toml:"normal_set"`
ActiveSet BorderSetWrapper `toml:"active_set"`
}
TreeConfig struct {
Graphics bool `toml:"graphics"`
}
GuildsTreeConfig struct {
TreeConfig
AutoExpandFolders bool `toml:"auto_expand_folders"`
}
ScrollBarConfig struct {
Visibility ScrollBarVisibilityWrapper `toml:"visibility"`
SymbolSet SymbolSetWrapper `toml:"symbol_set"`
}
MessagesListConfig struct {
ReplyIndicator string `toml:"reply_indicator"`
ForwardedIndicator string `toml:"forwarded_indicator"`
}
MentionsListConfig struct {
MinWidth uint `toml:"min_width"`
MaxHeight uint `toml:"max_height"`
}
)

View file

@ -152,7 +152,7 @@ func (m *Model) box(focused bool) box.Widget {
Keybind(m.editAction).
Focused(focused && !m.disabled).
OnChange(func(a textarea.Change) tview.Msg { return editMsg(a) })
return ui.Box(text, &m.cfg.Theme, focused).Title(m.title()).Footer(m.footer())
return ui.Box(text, m.cfg, focused).Title(m.title()).Footer(m.footer())
}
// title returns what the composer is doing: editing, replying, or nothing.
@ -523,16 +523,16 @@ func (p mentionsPopup) Handle(msg tview.Msg, area tview.Rectangle) tview.Msg {
func (p mentionsPopup) area(area tview.Rectangle) tview.Rectangle {
c := p.c
borders := 0
if c.cfg.Theme.Border.Enabled {
if c.cfg.UI.Border.Enabled {
borders = 1
}
x, _ := c.frame()
maxW, maxH := area.Width, area.Height-c.Height()
if t := int(c.cfg.Theme.MentionsList.MaxHeight); t != 0 {
if t := int(c.cfg.UI.MentionsList.MaxHeight); t != 0 {
maxH = min(maxH, t)
}
h := min(c.mentionsList.ItemCount()+borders, maxH) + borders + c.cfg.Theme.Border.Padding[1]
w := int(c.cfg.Theme.MentionsList.MinWidth)
h := min(c.mentionsList.ItemCount()+borders, maxH) + borders + c.cfg.UI.Border.Padding[1]
w := int(c.cfg.UI.MentionsList.MinWidth)
if w == 0 {
w = maxW
} else {

View file

@ -54,7 +54,8 @@ func (m Model) View(focused bool) tview.Widget {
m.root,
m.selectionState,
m.cfg,
m.cfg.Theme.GuildsTree.CommonTreeTheme,
m.cfg.Theme.GuildsTree,
m.cfg.UI.GuildsTree.Graphics,
m.cfg.Keybinds.GuildsTree.TreeKeybinds,
focused,
onChange,
@ -163,7 +164,7 @@ func (m Model) createFolderNode(folder gateway.GuildFolder, guildsByID map[disco
name = folder.Name
}
folderNode := tree.NewNode(name).SetExpanded(m.cfg.Theme.GuildsTree.AutoExpandFolders)
folderNode := tree.NewNode(name).SetExpanded(m.cfg.UI.GuildsTree.AutoExpandFolders)
if folder.Color != 0 {
folderStyle := tcell.StyleDefault.Foreground(tcell.NewHexColor(int32(folder.Color)))
m.setNodeLineStyle(folderNode, folderStyle)

View file

@ -85,7 +85,7 @@ func (m *Model) SetChannel(channel *discord.Channel) tview.Cmd {
func (m Model) View(focused bool) tview.Widget {
onChange := func(c tree.Change) tview.Msg { return Msg(c) }
onSelect := func(n *tree.Node) tview.Msg { return SelectedMsg{Node: n} }
return uitree.New(m.root, m.selectionState, m.cfg, m.cfg.Theme.MembersTree, m.cfg.Keybinds.MembersTree, focused, onChange, onSelect).Title("Members")
return uitree.New(m.root, m.selectionState, m.cfg, m.cfg.Theme.MembersTree, m.cfg.UI.MembersTree.Graphics, m.cfg.Keybinds.MembersTree, focused, onChange, onSelect).Title("Members")
}
// Update returns m changed in response to msg and a command to run, or nil.

View file

@ -22,9 +22,9 @@ func NewModel(cfg *config.Config) Model {
// View shows the list in a box whose bottom corners join the composer's border below it.
func (m Model) View() tview.Widget {
set := m.cfg.Theme.Border.NormalSet.BorderSet
set := m.cfg.UI.Border.NormalSet.BorderSet
set.BottomLeft, set.BottomRight = set.BottomT, set.BottomT
return ui.Box(m.listView(), &m.cfg.Theme, false).Title("Mentions").BorderSet(set)
return ui.Box(m.listView(), m.cfg, false).Title("Mentions").BorderSet(set)
}
func (Model) Init() tview.Cmd { return nil }

View file

@ -81,7 +81,7 @@ func NewModel(cfg *config.Config, state *ningen.State) Model {
// View shows the messages in a box titled with the channel and footed with footer, such as who is typing.
func (m Model) View(focused bool, footer string) tview.Widget {
return ui.Box(m.listView(focused), &m.cfg.Theme, focused).Title(m.title()).Footer(footer)
return ui.Box(m.listView(focused), m.cfg, focused).Title(m.title()).Footer(footer)
}
// title returns the selected channel and its topic, or "Messages" when none is selected.
@ -97,11 +97,11 @@ func (m Model) title() string {
}
func (m Model) listView(focused bool) list.Widget {
cfg := m.cfg.Theme.ScrollBar
cfg, theme := m.cfg.UI.ScrollBar, m.cfg.Theme.ScrollBar
bar := scrollbar.New().
SymbolSet(cfg.SymbolSet.SymbolSet).
Style(cfg.TrackStyle.Style).
ThumbStyle(cfg.ThumbStyle.Style)
Style(theme.TrackStyle.Style).
ThumbStyle(theme.ThumbStyle.Style)
return list.New(m.selectionState, len(m.items), m.buildItem).
SelectedStyle(m.cfg.Theme.MessagesList.SelectedMessageStyle.Style).
ScrollBar(bar, cfg.Visibility.ScrollBarVisibility).
@ -803,14 +803,14 @@ func (m Model) drawForwardedMessage(builder *richtext.Builder, message discord.M
dimStyle := baseStyle.Dim(true)
m.drawTimestamps(builder, message.Timestamp, baseStyle)
m.drawAuthor(builder, message, baseStyle)
builder.Write(m.cfg.Theme.MessagesList.ForwardedIndicator+" ", dimStyle)
builder.Write(m.cfg.UI.MessagesList.ForwardedIndicator+" ", dimStyle)
m.drawSnapshotContent(builder, message, message.MessageSnapshots[0].Message, baseStyle)
builder.Write(" ("+m.formatTimestamp(message.MessageSnapshots[0].Message.Timestamp)+") ", dimStyle)
}
func (m Model) drawReplyMessage(builder *richtext.Builder, message discord.Message, baseStyle tview.Style, width int) {
dimStyle := baseStyle.Dim(true)
builder.Write(m.cfg.Theme.MessagesList.ReplyIndicator+" ", dimStyle)
builder.Write(m.cfg.UI.MessagesList.ReplyIndicator+" ", dimStyle)
if reference := message.ReferencedMessage; reference != nil {
reference.GuildID = message.GuildID

View file

@ -65,7 +65,7 @@ func (m Model) View() tview.Widget {
Content(content).
Keybind(tabAction).
OnSelect(func(i int) tview.Msg { return selectTabMsg(i) })
return ui.Box(t, &m.cfg.Theme, false)
return ui.Box(t, m.cfg, false)
}
// tabs returns the login methods as tabs set up by the tabs config.

View file

@ -28,17 +28,17 @@ func (Model) Init() tview.Cmd { return nil }
func (m Model) View(onSelect func(picker.Item) tview.Msg, onCancel tview.Msg) tview.Widget {
cfg := m.cfg
bar := scrollbar.New().
SymbolSet(cfg.Theme.ScrollBar.SymbolSet.SymbolSet).
SymbolSet(cfg.UI.ScrollBar.SymbolSet.SymbolSet).
Style(cfg.Theme.ScrollBar.TrackStyle.Style).
ThumbStyle(cfg.Theme.ScrollBar.ThumbStyle.Style)
p := picker.New(m.items, &m.searchState).
ScrollBar(bar, cfg.Theme.ScrollBar.Visibility.ScrollBarVisibility).
ScrollBar(bar, cfg.UI.ScrollBar.Visibility.ScrollBarVisibility).
Keybind(bindKeys(cfg.Keybinds.Picker)).
ListKeybind(ui.SelectionKeybind(cfg.Keybinds.Picker.SelectionKeybinds)).
OnChange(func(c picker.Change) tview.Msg { return changeMsg(c) }).
OnSelect(onSelect).
OnCancel(onCancel)
return ui.Box(p, &cfg.Theme, true).Title(m.title)
return ui.Box(p, cfg, true).Title(m.title)
}
func (m Model) Update(msg tview.Msg) (Model, tview.Cmd) {

View file

@ -11,11 +11,11 @@ import (
"github.com/gdamore/tcell/v3"
)
// New returns a boxed tree of the nodes under root, drawn with theme and moved with kbs, that turns changes and selected nodes into the messages onChange and onSelect return.
func New(root *tree.Node, selectionState tree.SelectionState, cfg *config.Config, theme config.CommonTreeTheme, kbs config.TreeKeybinds, focused bool, onChange func(tree.Change) tview.Msg, onSelect func(*tree.Node) tview.Msg) box.Widget {
set := cfg.Theme.Border.NormalSet.BorderSet
// New returns a boxed tree of the nodes under root, drawn with theme and graphics and moved with kbs, that turns changes and selected nodes into the messages onChange and onSelect return.
func New(root *tree.Node, selectionState tree.SelectionState, cfg *config.Config, theme config.CommonTreeTheme, graphics bool, kbs config.TreeKeybinds, focused bool, onChange func(tree.Change) tview.Msg, onSelect func(*tree.Node) tview.Msg) box.Widget {
set := cfg.UI.Border.NormalSet.BorderSet
if focused {
set = cfg.Theme.Border.ActiveSet.BorderSet
set = cfg.UI.Border.ActiveSet.BorderSet
}
t := tree.New(root, selectionState).
TopLevel(1).
@ -24,14 +24,14 @@ func New(root *tree.Node, selectionState tree.SelectionState, cfg *config.Config
Collapsed: cfg.Sidebar.Markers.Collapsed,
Leaf: cfg.Sidebar.Markers.Leaf,
}).
Graphics(theme.Graphics).
Graphics(graphics).
GraphicsSet(set).
GraphicsStyle(tcell.StyleDefault.Foreground(tcell.GetColor(theme.GraphicsColor))).
Keybind(bindKeys(kbs)).
Focused(focused).
OnChange(onChange).
OnSelect(onSelect)
return ui.Box(t, &cfg.Theme, focused)
return ui.Box(t, cfg, focused)
}
func bindKeys(kbs config.TreeKeybinds) func(tview.KeyMsg) tree.Action {

View file

@ -12,24 +12,25 @@ import (
"github.com/ayn2op/tview/box"
)
// Box returns child in a box styled by the theme, highlighted while focused.
func Box(child tview.Widget, theme *config.Theme, focused bool) box.Widget {
padding := theme.Border.Padding
// Box returns child in a box laid out and styled by cfg, highlighted while focused.
func Box(child tview.Widget, cfg *config.Config, focused bool) box.Widget {
ui, theme := cfg.UI, cfg.Theme
padding := ui.Border.Padding
b := box.New(child).
Padding(padding[0], padding[1], padding[2], padding[3]).
TitleAlignment(theme.Title.Alignment.Alignment).
FooterAlignment(theme.Footer.Alignment.Alignment)
if theme.Border.Enabled {
TitleAlignment(ui.Title.Alignment.Alignment).
FooterAlignment(ui.Footer.Alignment.Alignment)
if ui.Border.Enabled {
b = b.Borders(tview.BordersAll)
}
if focused {
return b.BorderStyle(theme.Border.ActiveStyle.Style).
BorderSet(theme.Border.ActiveSet.BorderSet).
BorderSet(ui.Border.ActiveSet.BorderSet).
TitleStyle(theme.Title.ActiveStyle.Style).
FooterStyle(theme.Footer.ActiveStyle.Style)
}
return b.BorderStyle(theme.Border.NormalStyle.Style).
BorderSet(theme.Border.NormalSet.BorderSet).
BorderSet(ui.Border.NormalSet.BorderSet).
TitleStyle(theme.Title.NormalStyle.Style).
FooterStyle(theme.Footer.NormalStyle.Style)
}