Преглед на файлове

refactor(ui/chat): align discordo with latest tview list/picker API (#772)

ayn2op преди 1 месец
родител
ревизия
993cc53609

+ 1 - 1
go.mod

@@ -11,7 +11,7 @@ require (
 	github.com/alecthomas/chroma/v2 v2.23.1
 	github.com/andybalholm/brotli v1.2.0
 	github.com/ayn2op/clipboard v0.0.0-20260308203959-c5ad7df3fc97
-	github.com/ayn2op/tview v0.0.0-20260318010246-0993cedfe0c2
+	github.com/ayn2op/tview v0.0.0-20260318080250-5bddfb2ac264
 	github.com/deckarep/gosx-notifier v0.0.0-20180201035817-e127226297fb
 	github.com/diamondburned/arikawa/v3 v3.6.1-0.20260311205148-176ad9b9440f
 	github.com/diamondburned/ningen/v3 v3.0.1-0.20260306213430-5a08d3a709b4

+ 2 - 2
go.sum

@@ -16,8 +16,8 @@ github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwTo
 github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
 github.com/ayn2op/clipboard v0.0.0-20260308203959-c5ad7df3fc97 h1:WujETUV+v0DEJyZgjeLzQvihWyL80c0Tg4qf0dDo+Io=
 github.com/ayn2op/clipboard v0.0.0-20260308203959-c5ad7df3fc97/go.mod h1:3kFnpNCa3dF6WryzOMCDao7PfZ7DTCh+pievlfuwV80=
-github.com/ayn2op/tview v0.0.0-20260318010246-0993cedfe0c2 h1:AMOGrvQ1+TnDRuKC2Jnr+H1WL4bq8GSNd2GDxvy3dig=
-github.com/ayn2op/tview v0.0.0-20260318010246-0993cedfe0c2/go.mod h1:IXZrCv1VAZyWPdKDUJYs9ZYFiJ1GmCQUo9tLjQWoSnU=
+github.com/ayn2op/tview v0.0.0-20260318080250-5bddfb2ac264 h1:UolGESVzxrK9JyGkpoc1sLZeIWDiMtCwRA8sRckqmrA=
+github.com/ayn2op/tview v0.0.0-20260318080250-5bddfb2ac264/go.mod h1:xgkca32kuEswK7Kzkfk3Pk24WHcKPjqK4xGmDi9QqOY=
 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=

+ 9 - 6
internal/ui/chat/attachments_picker.go

@@ -3,10 +3,11 @@ package chat
 import (
 	"github.com/ayn2op/discordo/internal/config"
 	"github.com/ayn2op/discordo/internal/ui"
-	"github.com/ayn2op/discordo/pkg/picker"
 	"github.com/ayn2op/tview"
 	"github.com/ayn2op/tview/help"
 	"github.com/ayn2op/tview/keybind"
+	"github.com/ayn2op/tview/list"
+	"github.com/ayn2op/tview/picker"
 )
 
 type attachmentItem struct {
@@ -39,12 +40,14 @@ func newAttachmentsPicker(cfg *config.Config, chatView *Model) *attachmentsPicke
 		SetFooterStyle(cfg.Theme.Footer.ActiveStyle.Style)
 
 	ap.SetTitle("Attachments")
-	ap.SetKeyMap(&picker.KeyMap{
+	ap.SetKeybinds(picker.Keybinds{
 		Cancel: cfg.Keybinds.Picker.Cancel.Keybind,
-		Up:     cfg.Keybinds.Picker.Up.Keybind,
-		Down:   cfg.Keybinds.Picker.Down.Keybind,
-		Top:    cfg.Keybinds.Picker.Top.Keybind,
-		Bottom: cfg.Keybinds.Picker.Bottom.Keybind,
+		Keybinds: list.Keybinds{
+			SelectUp:     cfg.Keybinds.Picker.Up.Keybind,
+			SelectDown:   cfg.Keybinds.Picker.Down.Keybind,
+			SelectTop:    cfg.Keybinds.Picker.Top.Keybind,
+			SelectBottom: cfg.Keybinds.Picker.Bottom.Keybind,
+		},
 		Select: cfg.Keybinds.Picker.Select.Keybind,
 	})
 	ap.SetScrollBarVisibility(cfg.Theme.ScrollBar.Visibility.ScrollBarVisibility)

+ 9 - 6
internal/ui/chat/channels_picker.go

@@ -6,10 +6,11 @@ import (
 
 	"github.com/ayn2op/discordo/internal/config"
 	"github.com/ayn2op/discordo/internal/ui"
-	"github.com/ayn2op/discordo/pkg/picker"
 	"github.com/ayn2op/tview"
 	"github.com/ayn2op/tview/help"
 	"github.com/ayn2op/tview/keybind"
+	"github.com/ayn2op/tview/list"
+	"github.com/ayn2op/tview/picker"
 	"github.com/diamondburned/arikawa/v3/discord"
 )
 
@@ -40,12 +41,14 @@ func newChannelsPicker(cfg *config.Config, chatView *Model) *channelsPicker {
 		SetTrackStyle(cfg.Theme.ScrollBar.TrackStyle.Style).
 		SetThumbStyle(cfg.Theme.ScrollBar.ThumbStyle.Style).
 		SetGlyphSet(cfg.Theme.ScrollBar.GlyphSet.GlyphSet))
-	cp.SetKeyMap(&picker.KeyMap{
+	cp.SetKeybinds(picker.Keybinds{
 		Cancel: cfg.Keybinds.Picker.Cancel.Keybind,
-		Up:     cfg.Keybinds.Picker.Up.Keybind,
-		Down:   cfg.Keybinds.Picker.Down.Keybind,
-		Top:    cfg.Keybinds.Picker.Top.Keybind,
-		Bottom: cfg.Keybinds.Picker.Bottom.Keybind,
+		Keybinds: list.Keybinds{
+			SelectUp:     cfg.Keybinds.Picker.Up.Keybind,
+			SelectDown:   cfg.Keybinds.Picker.Down.Keybind,
+			SelectTop:    cfg.Keybinds.Picker.Top.Keybind,
+			SelectBottom: cfg.Keybinds.Picker.Bottom.Keybind,
+		},
 		Select: cfg.Keybinds.Picker.Select.Keybind,
 	})
 	return cp

+ 1 - 1
internal/ui/chat/guilds_tree.go

@@ -350,7 +350,7 @@ func (gt *guildsTree) onSelected(node *tview.TreeNode) {
 		gt.chat.messagesList.reset()
 		gt.chat.messagesList.setTitle(*channel)
 		gt.chat.messagesList.setMessages(messages)
-		gt.chat.messagesList.ScrollToEnd()
+		gt.chat.messagesList.ScrollBottom()
 
 		hasNoPerm := channel.Type != discord.DirectMessage && channel.Type != discord.GroupDM && !gt.chat.state.HasPermissions(channel.ID, discord.PermissionSendMessages)
 		gt.chat.messageInput.SetDisabled(hasNoPerm)

+ 4 - 0
internal/ui/chat/mentions_list.go

@@ -23,6 +23,10 @@ func newMentionsList(cfg *config.Config) *mentionsList {
 	m := &mentionsList{
 		Model: list.NewModel(),
 	}
+	m.SetKeybinds(list.Keybinds{
+		SelectUp:   cfg.Keybinds.MentionsList.Up.Keybind,
+		SelectDown: cfg.Keybinds.MentionsList.Down.Keybind,
+	})
 
 	m.Box = ui.ConfigureBox(m.Box, &cfg.Theme)
 	m.SetSnapToItems(true).SetTitle("Mentions")

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

@@ -169,16 +169,7 @@ func (mi *messageInput) HandleEvent(event tview.Event) tview.Command {
 
 		if mi.cfg.AutocompleteLimit > 0 {
 			if mi.chat.GetVisible(mentionsListLayerName) {
-				switch {
-				case keybind.Matches(event, mi.cfg.Keybinds.MentionsList.Up.Keybind):
-					return mi.mentionsList.HandleEvent(tcell.NewEventKey(tcell.KeyUp, "", tcell.ModNone))
-				case keybind.Matches(event, mi.cfg.Keybinds.MentionsList.Down.Keybind):
-					return mi.mentionsList.HandleEvent(tcell.NewEventKey(tcell.KeyDown, "", tcell.ModNone))
-				case keybind.Matches(event, mi.cfg.Keybinds.MentionsList.Top.Keybind):
-					return mi.mentionsList.HandleEvent(tcell.NewEventKey(tcell.KeyHome, "", tcell.ModNone))
-				case keybind.Matches(event, mi.cfg.Keybinds.MentionsList.Bottom.Keybind):
-					return mi.mentionsList.HandleEvent(tcell.NewEventKey(tcell.KeyEnd, "", tcell.ModNone))
-				}
+				mi.mentionsList.HandleEvent(event)
 			}
 
 			go mi.chat.app.QueueUpdateDraw(func() { mi.tabSuggestion() })
@@ -248,7 +239,7 @@ func (mi *messageInput) send() {
 	}
 	mi.reset()
 	mi.chat.messagesList.clearSelection()
-	mi.chat.messagesList.ScrollToEnd()
+	mi.chat.messagesList.ScrollBottom()
 }
 
 func (mi *messageInput) processText(channel *discord.Channel, src []byte) string {

+ 7 - 14
internal/ui/chat/messages_list.go

@@ -95,6 +95,12 @@ func newMessagesList(cfg *config.Config, chatView *Model) *messagesList {
 	ml.SetBuilder(ml.buildItem)
 	ml.SetChangedFunc(ml.onRowCursorChanged)
 	ml.SetTrackEnd(true)
+	ml.SetKeybinds(list.Keybinds{
+		ScrollUp:     cfg.Keybinds.MessagesList.ScrollUp.Keybind,
+		ScrollDown:   cfg.Keybinds.MessagesList.ScrollDown.Keybind,
+		ScrollTop:    cfg.Keybinds.MessagesList.ScrollTop.Keybind,
+		ScrollBottom: cfg.Keybinds.MessagesList.ScrollBottom.Keybind,
+	})
 	ml.SetScrollBarVisibility(cfg.Theme.ScrollBar.Visibility.ScrollBarVisibility)
 	ml.SetScrollBar(tview.NewScrollBar().
 		SetTrackStyle(cfg.Theme.ScrollBar.TrackStyle.Style).
@@ -842,18 +848,6 @@ func (ml *messagesList) HandleEvent(event tview.Event) tview.Command {
 	switch event := event.(type) {
 	case *tview.KeyEvent:
 		switch {
-		case keybind.Matches(event, ml.cfg.Keybinds.MessagesList.ScrollUp.Keybind):
-			ml.ScrollUp()
-			return nil
-		case keybind.Matches(event, ml.cfg.Keybinds.MessagesList.ScrollDown.Keybind):
-			ml.ScrollDown()
-			return nil
-		case keybind.Matches(event, ml.cfg.Keybinds.MessagesList.ScrollTop.Keybind):
-			ml.ScrollToStart()
-			return nil
-		case keybind.Matches(event, ml.cfg.Keybinds.MessagesList.ScrollBottom.Keybind):
-			ml.ScrollToEnd()
-			return nil
 		case keybind.Matches(event, ml.cfg.Keybinds.MessagesList.Cancel.Keybind):
 			ml.clearSelection()
 			return nil
@@ -896,8 +890,7 @@ func (ml *messagesList) HandleEvent(event tview.Event) tview.Command {
 			ml.confirmDelete()
 			return nil
 		}
-		// Do not fall through to List defaults for unmatched keys.
-		return nil
+		return ml.Model.HandleEvent(event)
 	}
 	return ml.Model.HandleEvent(event)
 }

+ 0 - 34
pkg/picker/events.go

@@ -1,34 +0,0 @@
-package picker
-
-import (
-	"github.com/ayn2op/tview"
-	"github.com/gdamore/tcell/v3"
-)
-
-type SelectedEvent struct {
-	tcell.EventTime
-	Item
-}
-
-func newSelectedEvent(item Item) *SelectedEvent {
-	return &SelectedEvent{Item: item}
-}
-
-func (m *Model) _select() tview.Command {
-	index := m.list.Cursor()
-	if index >= 0 && index < len(m.filtered) {
-		item := m.filtered[index]
-		return func() tview.Event {
-			return newSelectedEvent(item)
-		}
-	}
-	return nil
-}
-
-type CancelEvent struct{ tcell.EventTime }
-
-func cancel() tview.Command {
-	return func() tview.Event {
-		return &CancelEvent{}
-	}
-}

+ 0 - 17
pkg/picker/item.go

@@ -1,17 +0,0 @@
-package picker
-
-type Item struct {
-	Text       string
-	FilterText string
-	Reference  any
-}
-
-type Items []Item
-
-func (is Items) String(index int) string {
-	return is[index].FilterText
-}
-
-func (is Items) Len() int {
-	return len(is)
-}

+ 0 - 13
pkg/picker/keymap.go

@@ -1,13 +0,0 @@
-package picker
-
-import "github.com/ayn2op/tview/keybind"
-
-type KeyMap struct {
-	Cancel keybind.Keybind
-
-	Up     keybind.Keybind
-	Down   keybind.Keybind
-	Top    keybind.Keybind
-	Bottom keybind.Keybind
-	Select keybind.Keybind
-}

+ 0 - 159
pkg/picker/model.go

@@ -1,159 +0,0 @@
-package picker
-
-import (
-	"github.com/ayn2op/tview"
-	"github.com/ayn2op/tview/flex"
-	"github.com/ayn2op/tview/keybind"
-	"github.com/ayn2op/tview/list"
-	"github.com/gdamore/tcell/v3"
-	"github.com/sahilm/fuzzy"
-)
-
-// bottom border + value
-const inputHeight = 2
-
-type Model struct {
-	*flex.Model
-	input *tview.InputField
-	list  *list.Model
-
-	keyMap *KeyMap
-
-	items    Items
-	filtered Items
-}
-
-func NewModel() *Model {
-	m := &Model{
-		Model: flex.NewModel(),
-		input: tview.NewInputField(),
-		list:  list.NewModel(),
-	}
-
-	// Show a horizontal bottom border to visually separate input from list.
-	var borderSet tview.BorderSet
-	borderSet.Bottom = tview.BoxDrawingsLightHorizontal
-	borderSet.BottomLeft = borderSet.Bottom
-	borderSet.BottomRight = borderSet.Bottom
-
-	m.input.
-		SetChangedFunc(m.onInputChanged).
-		SetLabel("> ").
-		SetBorders(tview.BordersBottom).
-		SetBorderSet(borderSet).
-		SetBorderStyle(tcell.StyleDefault.Dim(true))
-
-	m.
-		SetDirection(flex.DirectionRow).
-		AddItem(m.input, inputHeight, 0, true).
-		AddItem(m.list, 0, 1, false)
-
-	m.Update()
-	return m
-}
-
-func (m *Model) setFilteredItems(filtered Items) {
-	m.filtered = filtered
-
-	m.list.SetBuilder(func(index int, cursor int) list.Item {
-		if index < 0 || index >= len(m.filtered) {
-			return nil
-		}
-		style := tcell.StyleDefault
-		if index == cursor {
-			style = style.Reverse(true)
-		}
-		return tview.NewTextView().
-			SetScrollable(false).
-			SetWrap(false).
-			SetWordWrap(false).
-			SetTextStyle(style).
-			SetLines([]tview.Line{{{Text: m.filtered[index].Text, Style: style}}})
-	})
-
-	if len(filtered) == 0 {
-		m.list.SetCursor(-1)
-	} else {
-		m.list.SetCursor(0)
-	}
-}
-
-func (m *Model) SetKeyMap(keyMap *KeyMap) {
-	m.keyMap = keyMap
-}
-
-// SetScrollBarVisibility sets when the picker's list scrollBar is rendered.
-func (m *Model) SetScrollBarVisibility(visibility list.ScrollBarVisibility) {
-	m.list.SetScrollBarVisibility(visibility)
-}
-
-// SetScrollBar sets the scrollBar primitive used by the picker's list.
-func (m *Model) SetScrollBar(scrollBar *tview.ScrollBar) {
-	m.list.SetScrollBar(scrollBar)
-}
-
-func (m *Model) ClearInput() {
-	m.input.SetText("")
-}
-
-func (m *Model) ClearList() {
-	m.filtered = nil
-	m.list.Clear()
-}
-
-func (m *Model) ClearItems() {
-	m.items = nil
-	m.filtered = nil
-}
-
-func (m *Model) AddItem(item Item) {
-	m.items = append(m.items, item)
-}
-
-func (m *Model) Update() {
-	m.ClearInput()
-	m.onInputChanged("")
-}
-
-func (m *Model) onInputChanged(text string) {
-	var fuzzied Items
-	if text == "" {
-		fuzzied = append(fuzzied, m.items...)
-	} else {
-		matches := fuzzy.FindFrom(text, m.items)
-		for _, match := range matches {
-			fuzzied = append(fuzzied, m.items[match.Index])
-		}
-	}
-	m.setFilteredItems(fuzzied)
-}
-
-func (m *Model) HandleEvent(event tview.Event) tview.Command {
-	switch event := event.(type) {
-	case *tview.KeyEvent:
-		if m.keyMap != nil {
-			switch {
-			case keybind.Matches(event, m.keyMap.Up):
-				m.list.HandleEvent(tcell.NewEventKey(tcell.KeyUp, "", tcell.ModNone))
-				return nil
-			case keybind.Matches(event, m.keyMap.Down):
-				m.list.HandleEvent(tcell.NewEventKey(tcell.KeyDown, "", tcell.ModNone))
-				return nil
-			case keybind.Matches(event, m.keyMap.Top):
-				m.list.HandleEvent(tcell.NewEventKey(tcell.KeyHome, "", tcell.ModNone))
-				return nil
-			case keybind.Matches(event, m.keyMap.Bottom):
-				m.list.HandleEvent(tcell.NewEventKey(tcell.KeyEnd, "", tcell.ModNone))
-				return nil
-
-			case keybind.Matches(event, m.keyMap.Select):
-				return m._select()
-			case keybind.Matches(event, m.keyMap.Cancel):
-				return cancel()
-			}
-		}
-
-		return m.Model.HandleEvent(event)
-	}
-	return m.Model.HandleEvent(event)
-}