Browse Source

refactor text styling to line/segment APIs and fix list selection

ayn2op 2 months ago
parent
commit
01fe923a34

+ 1 - 1
go.mod

@@ -7,7 +7,7 @@ go 1.25.3
 require (
 	github.com/BurntSushi/toml v1.6.0
 	github.com/andybalholm/brotli v1.2.0
-	github.com/ayn2op/tview v0.0.0-20260208060151-4a4f3e6ac892
+	github.com/ayn2op/tview v0.0.0-20260208071935-3324c648db69
 	github.com/deckarep/gosx-notifier v0.0.0-20180201035817-e127226297fb
 	github.com/diamondburned/arikawa/v3 v3.6.1-0.20250928004212-a891a653eb26
 	github.com/diamondburned/ningen/v3 v3.0.1-0.20250920191746-98fbd92e134d

+ 2 - 2
go.sum

@@ -8,8 +8,8 @@ github.com/akavel/rsrc v0.10.2 h1:Zxm8V5eI1hW4gGaYsJQUhxpjkENuG91ki8B4zCrvEsw=
 github.com/akavel/rsrc v0.10.2/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
 github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
 github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
-github.com/ayn2op/tview v0.0.0-20260208060151-4a4f3e6ac892 h1:lQHJobFgud7g57eKr7ODWRqW0CWuuPwLtITSmVYbbhQ=
-github.com/ayn2op/tview v0.0.0-20260208060151-4a4f3e6ac892/go.mod h1:g6IOdF9SlnVZMDnRABANP8I0LFseKyYxWqEkzBSL5ho=
+github.com/ayn2op/tview v0.0.0-20260208071935-3324c648db69 h1:DU0vNRmldUajM4tY5feFG/WsxaLhiPUHuWaQotmCLH8=
+github.com/ayn2op/tview v0.0.0-20260208071935-3324c648db69/go.mod h1:g6IOdF9SlnVZMDnRABANP8I0LFseKyYxWqEkzBSL5ho=
 github.com/danieljoos/wincred v1.2.3 h1:v7dZC2x32Ut3nEfRH+vhoZGvN72+dQ/snVXo/vMFLdQ=
 github.com/danieljoos/wincred v1.2.3/go.mod h1:6qqX0WNrS4RzPZ1tnroDzq9kY3fu1KwE7MRLQK4X0bs=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=

+ 17 - 9
internal/ui/chat/guilds_tree.go

@@ -68,7 +68,7 @@ func (gt *guildsTree) createFolderNode(folder gateway.GuildFolder) {
 	folderNode := tview.NewTreeNode(name).SetExpanded(gt.cfg.Theme.GuildsTree.AutoExpandFolders)
 	if folder.Color != 0 {
 		folderStyle := tcell.StyleDefault.Foreground(tcell.NewHexColor(int32(folder.Color)))
-		folderNode.SetTextStyle(folderStyle)
+		gt.setNodeLineStyle(folderNode, folderStyle)
 	}
 	gt.GetRoot().AddChild(folderNode)
 
@@ -109,9 +109,8 @@ func (gt *guildsTree) getChannelNodeStyle(channelID discord.ChannelID) tcell.Sty
 }
 
 func (gt *guildsTree) createGuildNode(n *tview.TreeNode, guild discord.Guild) {
-	guildNode := tview.NewTreeNode(guild.Name).
-		SetReference(guild.ID).
-		SetTextStyle(gt.getGuildNodeStyle(guild.ID))
+	guildNode := tview.NewTreeNode(guild.Name).SetReference(guild.ID)
+	gt.setNodeLineStyle(guildNode, gt.getGuildNodeStyle(guild.ID))
 	n.AddChild(guildNode)
 	gt.guildNodeByID[guild.ID] = guildNode
 }
@@ -121,13 +120,20 @@ func (gt *guildsTree) createChannelNode(node *tview.TreeNode, channel discord.Ch
 		return
 	}
 
-	channelNode := tview.NewTreeNode(ui.ChannelToString(channel, gt.cfg.Icons)).
-		SetReference(channel.ID).
-		SetTextStyle(gt.getChannelNodeStyle(channel.ID))
+	channelNode := tview.NewTreeNode(ui.ChannelToString(channel, gt.cfg.Icons)).SetReference(channel.ID)
+	gt.setNodeLineStyle(channelNode, gt.getChannelNodeStyle(channel.ID))
 	node.AddChild(channelNode)
 	gt.channelNodeByID[channel.ID] = channelNode
 }
 
+func (gt *guildsTree) setNodeLineStyle(node *tview.TreeNode, style tcell.Style) {
+	line := node.GetLine()
+	for i := range line {
+		line[i].Style = style
+	}
+	node.SetLine(line)
+}
+
 func (gt *guildsTree) createChannelNodes(node *tview.TreeNode, channels []discord.Channel) {
 	// Preserve exact ordering semantics:
 	// 1) top-level non-categories (in input order),
@@ -243,14 +249,16 @@ func (gt *guildsTree) onSelected(node *tview.TreeNode) {
 
 		hasNoPerm := channel.Type != discord.DirectMessage && channel.Type != discord.GroupDM && !gt.chatView.state.HasPermissions(channel.ID, discord.PermissionSendMessages)
 		gt.chatView.messageInput.SetDisabled(hasNoPerm)
+		var text string
 		if hasNoPerm {
-			gt.chatView.messageInput.SetPlaceholder("You do not have permission to send messages in this channel.")
+			text = "You do not have permission to send messages in this channel."
 		} else {
-			gt.chatView.messageInput.SetPlaceholder("Message...")
+			text = "Message..."
 			if gt.cfg.AutoFocus {
 				gt.chatView.app.SetFocus(gt.chatView.messageInput)
 			}
 		}
+		gt.chatView.messageInput.SetPlaceholder(tview.NewLine(tview.NewSegment(text, tcell.StyleDefault.Dim(true))))
 	case dmNode: // Direct messages folder
 		channels, err := gt.chatView.state.PrivateChannels()
 		if err != nil {

+ 3 - 2
internal/ui/chat/mentions_list.go

@@ -51,15 +51,16 @@ func (m *mentionsList) rebuild() {
 		item := m.items[index]
 		style := item.style
 		if index == cursor {
-			style = style.Foreground(tview.Styles.PrimitiveBackgroundColor).Background(tview.Styles.PrimaryTextColor)
+			style = style.Reverse(true)
 		}
+		line := tview.NewLine(tview.NewSegment(item.displayText, style))
 
 		return tview.NewTextView().
 			SetScrollable(false).
 			SetWrap(false).
 			SetWordWrap(false).
 			SetTextStyle(style).
-			SetLines([]tview.Line{{{Text: item.displayText, Style: style}}})
+			SetLines([]tview.Line{line})
 	})
 
 	if len(m.items) == 0 {

+ 1 - 2
internal/ui/chat/message_input.go

@@ -65,8 +65,7 @@ func newMessageInput(cfg *config.Config, chatView *View) *messageInput {
 	mi.Box = ui.ConfigureBox(mi.Box, &cfg.Theme)
 	mi.SetInputCapture(mi.onInputCapture)
 	mi.
-		SetPlaceholder("Select a channel to start chatting").
-		SetPlaceholderStyle(tcell.StyleDefault.Dim(true)).
+		SetPlaceholder(tview.NewLine(tview.NewSegment("Select a channel to start chatting", tcell.StyleDefault.Dim(true)))).
 		SetClipboard(
 			func(s string) { clipboard.Write(clipboard.FmtText, []byte(s)) },
 			func() string { return string(clipboard.Read(clipboard.FmtText)) },

+ 2 - 2
internal/ui/chat/view.go

@@ -268,7 +268,7 @@ func (v *View) onReadUpdate(event *read.UpdateEvent) {
 	var updated bool
 	if event.GuildID.IsValid() {
 		if guildNode := v.guildsTree.findNodeByReference(event.GuildID); guildNode != nil {
-			guildNode.SetTextStyle(v.guildsTree.getGuildNodeStyle(event.GuildID))
+			v.guildsTree.setNodeLineStyle(guildNode, v.guildsTree.getGuildNodeStyle(event.GuildID))
 			updated = true
 		}
 	}
@@ -276,7 +276,7 @@ func (v *View) onReadUpdate(event *read.UpdateEvent) {
 	// Channel style is always updated for the target channel regardless of
 	// whether it's in a guild or DM.
 	if channelNode := v.guildsTree.findNodeByReference(event.ChannelID); channelNode != nil {
-		channelNode.SetTextStyle(v.guildsTree.getChannelNodeStyle(event.ChannelID))
+		v.guildsTree.setNodeLineStyle(channelNode, v.guildsTree.getChannelNodeStyle(event.ChannelID))
 		updated = true
 	}
 

+ 1 - 1
pkg/picker/picker.go

@@ -66,7 +66,7 @@ func (p *Picker) setFilteredItems(filtered Items) {
 		}
 		style := tcell.StyleDefault
 		if index == cursor {
-			style = style.Foreground(tview.Styles.PrimitiveBackgroundColor).Background(tview.Styles.PrimaryTextColor)
+			style = style.Reverse(true)
 		}
 		return tview.NewTextView().
 			SetScrollable(false).