浏览代码

feat(config): granular control over theme config (#564)

Ayyan 10 月之前
父节点
当前提交
7eff390646
共有 7 个文件被更改,包括 94 次插入66 次删除
  1. 9 5
      cmd/guilds_tree.go
  2. 2 2
      cmd/message_input.go
  3. 2 2
      cmd/messages_text.go
  4. 4 2
      cmd/state.go
  5. 14 15
      internal/config/config.toml
  6. 46 27
      internal/config/theme.go
  7. 17 13
      internal/ui/util.go

+ 9 - 5
cmd/guilds_tree.go

@@ -28,7 +28,7 @@ func newGuildsTree(cfg *config.Config) *guildsTree {
 		cfg:      cfg,
 	}
 
-	gt.Box = ui.NewConfiguredBox(gt.Box, &cfg.Theme)
+	gt.Box = ui.ConfigureBox(gt.Box, &cfg.Theme)
 
 	gt.
 		SetRoot(tview.NewTreeNode("")).
@@ -63,9 +63,11 @@ func (gt *guildsTree) createFolderNode(folder gateway.GuildFolder) {
 }
 
 func (gt *guildsTree) createGuildNode(n *tview.TreeNode, g discord.Guild) {
-	guildNode := tview.NewTreeNode(g.Name)
-	guildNode.SetReference(g.ID)
-	guildNode.SetColor(tcell.GetColor(gt.cfg.Theme.GuildsTree.GuildColor))
+	style := gt.cfg.Theme.GuildsTree.GuildStyle.Style
+	guildNode := tview.NewTreeNode(g.Name).
+		SetReference(g.ID).
+		SetTextStyle(style).
+		SetSelectedTextStyle(style.Reverse(true))
 	n.AddChild(guildNode)
 }
 
@@ -110,9 +112,11 @@ func (gt *guildsTree) createChannelNode(node *tview.TreeNode, channel discord.Ch
 		}
 	}
 
+	style := gt.cfg.Theme.GuildsTree.ChannelStyle.Style
 	channelNode := tview.NewTreeNode(gt.channelToString(channel)).
 		SetReference(channel.ID).
-		SetColor(tcell.GetColor(gt.cfg.Theme.GuildsTree.ChannelColor))
+		SetTextStyle(style).
+		SetSelectedTextStyle(style.Reverse(true))
 	node.AddChild(channelNode)
 }
 

+ 2 - 2
cmd/message_input.go

@@ -48,7 +48,7 @@ func newMessageInput(cfg *config.Config) *messageInput {
 		autocomplete: tview.NewList(),
 	}
 
-	mi.Box = ui.NewConfiguredBox(mi.Box, &cfg.Theme)
+	mi.Box = ui.ConfigureBox(mi.Box, &cfg.Theme)
 	mi.
 		SetTextStyle(tcell.StyleDefault.Background(tcell.GetColor(cfg.Theme.BackgroundColor))).
 		SetClipboard(func(s string) {
@@ -59,7 +59,7 @@ func newMessageInput(cfg *config.Config) *messageInput {
 		}).
 		SetInputCapture(mi.onInputCapture)
 
-	mi.autocomplete.Box = ui.NewConfiguredBox(mi.autocomplete.Box, &mi.cfg.Theme)
+	mi.autocomplete.Box = ui.ConfigureBox(mi.autocomplete.Box, &mi.cfg.Theme)
 	mi.autocomplete.
 		ShowSecondaryText(false).
 		SetSelectedStyle(tcell.StyleDefault.Background(tcell.ColorWhite).Foreground(tcell.ColorBlack)).

+ 2 - 2
cmd/messages_text.go

@@ -46,7 +46,7 @@ func newMessagesText(cfg *config.Config) *messagesText {
 		cfg:      cfg,
 	}
 
-	mt.Box = ui.NewConfiguredBox(mt.Box, &cfg.Theme)
+	mt.Box = ui.ConfigureBox(mt.Box, &cfg.Theme)
 	mt.
 		SetDynamicColors(true).
 		SetRegions(true).
@@ -455,7 +455,7 @@ func (mt *messagesText) showUrlSelector(urls []string, attachments []discord.Att
 		SetHighlightFullLine(true).
 		ShowSecondaryText(false).
 		SetDoneFunc(done)
-	list.Box = ui.NewConfiguredBox(list.Box, &mt.cfg.Theme)
+	list.Box = ui.ConfigureBox(list.Box, &mt.cfg.Theme)
 
 	list.
 		SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {

+ 4 - 2
cmd/state.go

@@ -12,7 +12,6 @@ import (
 	"github.com/diamondburned/arikawa/v3/utils/httputil/httpdriver"
 	"github.com/diamondburned/arikawa/v3/utils/ws"
 	"github.com/diamondburned/ningen/v3"
-	"github.com/gdamore/tcell/v2"
 )
 
 func openState(token string) error {
@@ -76,7 +75,10 @@ func onReady(r *gateway.ReadyEvent) {
 	root := app.guildsTree.GetRoot()
 	root.ClearChildren()
 
-	dmNode := tview.NewTreeNode("Direct Messages").SetColor(tcell.GetColor(app.cfg.Theme.GuildsTree.PrivateChannelColor))
+	style := app.cfg.Theme.GuildsTree.PrivateChannelStyle.Style
+	dmNode := tview.NewTreeNode("Direct Messages").
+		SetTextStyle(style).
+		SetSelectedTextStyle(style.Reverse(true))
 	root.AddChild(dmNode)
 
 	for _, folder := range r.UserSettings.GuildFolders {

+ 14 - 15
internal/config/config.toml

@@ -106,29 +106,28 @@ down = "Down"
 background_color = "default"
 
 [theme.title]
-# The values can be one of the following: `"left"`, `"center"`, and `"right"`.
+# `"left"`, `"center"`, or `"right"`.
 alignment = "left"
-# style supports foreground and background.
-style = { foreground = "default" }
-active_style = { foreground = "green" }
+normal_style = { foreground = "default", background = "default" }
+active_style = { foreground = "green", background = "default" }
 
 [theme.border]
 enabled = true
 # [top, bottom, left, right]
 padding = [0, 0, 1, 1]
-# The values can be one of the following: `"plain"`, `"round"`, `"thick"`, and `"double"`.
+# `"plain"`, `"round"`, `"thick"`, or `"double"`.
 set = "plain"
-style = { foreground = "default" }
-active_style = { foreground = "green" }
+normal_style = { foreground = "default", background = "default" }
+active_style = { foreground = "green", background = "default" }
 
 [theme.guilds_tree]
 auto_expand_folders = true
 # Give tree-like shape
 graphics = true
 graphics_color = "default"
-private_channel_color = "white"
-guild_color = "white"
-channel_color = "white"
+private_channel_style = { foreground = "white", background = "default" }
+guild_style = { foreground = "white", background = "default" }
+channel_style = { foreground = "white", background = "default" }
 
 [theme.messages_text]
 # Set to false to show messages with usernames instead of nicknames
@@ -138,11 +137,11 @@ show_user_colors = true
 reply_indicator = ">"
 forwarded_indicator = "<"
 
-author_style = { foreground = "aqua" }
-mention_style = { foreground = "blue" }
-emoji_style = { foreground = "green" }
-url_style = { foreground = "blue" }
-attachment_style = { foreground = "yellow" }
+author_style = { foreground = "aqua", background = "default" }
+mention_style = { foreground = "blue", background = "default" }
+emoji_style = { foreground = "green", background = "default" }
+url_style = { foreground = "blue", background = "default" }
+attachment_style = { foreground = "yellow", background = "default" }
 
 [theme.autocomplete]
 show_user_nicks = true

+ 46 - 27
internal/config/theme.go

@@ -1,14 +1,25 @@
 package config
 
 import (
+	"errors"
+
 	"github.com/ayn2op/tview"
 	"github.com/gdamore/tcell/v2"
 )
 
+var (
+	errInvalidType = errors.New("invalid type")
+)
+
 type BorderSetWrapper struct{ tview.BorderSet }
 
-func (bw *BorderSetWrapper) UnmarshalTOML(v any) error {
-	switch v.(string) {
+func (bw *BorderSetWrapper) UnmarshalTOML(val any) error {
+	s, ok := val.(string)
+	if !ok {
+		return errInvalidType
+	}
+
+	switch s {
 	case "plain":
 		bw.BorderSet = tview.BorderSetPlain()
 	case "round":
@@ -25,7 +36,12 @@ func (bw *BorderSetWrapper) UnmarshalTOML(v any) error {
 type AlignmentWrapper struct{ tview.Alignment }
 
 func (aw *AlignmentWrapper) UnmarshalTOML(v any) error {
-	switch v.(string) {
+	s, ok := v.(string)
+	if !ok {
+		return errInvalidType
+	}
+
+	switch s {
 	case "left":
 		aw.Alignment = tview.AlignmentLeft
 	case "center":
@@ -46,16 +62,16 @@ func NewStyleWrapper(style tcell.Style) StyleWrapper {
 func (sw *StyleWrapper) UnmarshalTOML(v any) error {
 	m, ok := v.(map[string]any)
 	if !ok {
-		return nil
+		return errInvalidType
 	}
 
-	for k, v := range m {
-		s, ok := v.(string)
+	for key, val := range m {
+		s, ok := val.(string)
 		if !ok {
 			continue
 		}
 
-		switch k {
+		switch key {
 		case "foreground":
 			color := tcell.GetColor(s)
 			sw.Style = sw.Foreground(color)
@@ -69,29 +85,21 @@ func (sw *StyleWrapper) UnmarshalTOML(v any) error {
 }
 
 type (
-	BorderTheme struct {
-		Enabled bool             `toml:"enabled"`
-		Padding [4]int           `toml:"padding"`
-		Set     BorderSetWrapper `toml:"set"`
-
-		Style       StyleWrapper `toml:"style"`
+	ThemeStyle struct {
+		NormalStyle StyleWrapper `toml:"normal_style"`
 		ActiveStyle StyleWrapper `toml:"active_style"`
 	}
 
 	TitleTheme struct {
-		Style       StyleWrapper     `toml:"style"`
-		ActiveStyle StyleWrapper     `toml:"active_style"`
-		Alignment   AlignmentWrapper `toml:"alignment"`
+		ThemeStyle
+		Alignment AlignmentWrapper `toml:"alignment"`
 	}
 
-	Theme struct {
-		BackgroundColor string `toml:"background_color"`
-
-		Title        TitleTheme        `toml:"title"`
-		Border       BorderTheme       `toml:"border"`
-		GuildsTree   GuildsTreeTheme   `toml:"guilds_tree"`
-		MessagesText MessagesTextTheme `toml:"messages_text"`
-		Autocomplete AutocompleteTheme `toml:"autocomplete"`
+	BorderTheme struct {
+		ThemeStyle
+		Enabled bool             `toml:"enabled"`
+		Padding [4]int           `toml:"padding"`
+		Set     BorderSetWrapper `toml:"set"`
 	}
 
 	GuildsTreeTheme struct {
@@ -100,9 +108,9 @@ type (
 		Graphics      bool   `toml:"graphics"`
 		GraphicsColor string `toml:"graphics_color"`
 
-		PrivateChannelColor string `toml:"private_channel_color"`
-		GuildColor          string `toml:"guild_color"`
-		ChannelColor        string `toml:"channel_color"`
+		PrivateChannelStyle StyleWrapper `toml:"private_channel_style"`
+		GuildStyle          StyleWrapper `toml:"guild_style"`
+		ChannelStyle        StyleWrapper `toml:"channel_style"`
 	}
 
 	MessagesTextTheme struct {
@@ -126,4 +134,15 @@ type (
 		MinWidth  uint `toml:"min_width"`
 		MaxHeight uint `toml:"max_height"`
 	}
+
+	Theme struct {
+		BackgroundColor string `toml:"background_color"`
+
+		Title  TitleTheme  `toml:"title"`
+		Border BorderTheme `toml:"border"`
+
+		GuildsTree   GuildsTreeTheme   `toml:"guilds_tree"`
+		MessagesText MessagesTextTheme `toml:"messages_text"`
+		Autocomplete AutocompleteTheme `toml:"autocomplete"`
+	}
 )

+ 17 - 13
internal/ui/util.go

@@ -5,32 +5,36 @@ import (
 	"github.com/ayn2op/tview"
 )
 
-func NewConfiguredBox(box *tview.Box, cfg *config.Theme) *tview.Box {
-	b := cfg.Border
-	t := cfg.Title
-	p := b.Padding
+// ConfigureBox configures the provided box according to the provided theme.
+func ConfigureBox(box *tview.Box, cfg *config.Theme) *tview.Box {
+	border := cfg.Border
+	title := cfg.Title
+	normalBorderStyle, activeBorderStyle := border.NormalStyle.Style, border.ActiveStyle.Style
+	normalTitleStyle, activeTitleStyle := title.NormalStyle.Style, title.ActiveStyle.Style
+	p := border.Padding
 	box.
-		SetBorderStyle(b.Style.Style).
-		SetBorderSet(b.Set.BorderSet).
+		SetBorderStyle(normalBorderStyle).
+		SetBorderSet(border.Set.BorderSet).
 		SetBorderPadding(p[0], p[1], p[2], p[3]).
-		SetTitleStyle(t.Style.Style).
-		SetTitleAlignment(t.Alignment.Alignment).
+		SetTitleStyle(normalTitleStyle).
+		SetTitleAlignment(title.Alignment.Alignment).
 		SetFocusFunc(func() {
-			box.SetBorderStyle(b.ActiveStyle.Style)
-			box.SetTitleStyle(t.ActiveStyle.Style)
+			box.SetBorderStyle(activeBorderStyle)
+			box.SetTitleStyle(activeTitleStyle)
 		}).
 		SetBlurFunc(func() {
-			box.SetBorderStyle(b.Style.Style)
-			box.SetTitleStyle(t.Style.Style)
+			box.SetBorderStyle(normalBorderStyle)
+			box.SetTitleStyle(normalTitleStyle)
 		})
 
-	if b.Enabled {
+	if border.Enabled {
 		box.SetBorders(tview.BordersAll)
 	}
 
 	return box
 }
 
+// Centered creates a new grid with provided primitive aligned in the center.
 func Centered(p tview.Primitive, width, height int) tview.Primitive {
 	return tview.NewGrid().
 		SetColumns(0, width, 0).