From aa2e1e24971e85a9c996dcac535fef5c69a9b024 Mon Sep 17 00:00:00 2001 From: ayn2op Date: Mon, 2 Mar 2026 00:05:39 -0500 Subject: [PATCH] refactor(ui/chat): adapt UI handlers to unified HandleEvent API --- go.mod | 7 +- go.sum | 4 +- internal/ui/chat/guilds_tree.go | 57 +++++------ internal/ui/chat/message_input.go | 154 +++++++++++++++++------------- internal/ui/chat/messages_list.go | 130 +++++++++++++------------ internal/ui/chat/view.go | 83 ++++++++-------- internal/ui/login/qr.go | 18 ++-- internal/ui/root/view.go | 43 ++++----- pkg/picker/picker.go | 54 ++++++----- 9 files changed, 293 insertions(+), 257 deletions(-) diff --git a/go.mod b/go.mod index 4a7ce9f..84f9600 100644 --- a/go.mod +++ b/go.mod @@ -2,11 +2,16 @@ module github.com/ayn2op/discordo go 1.26.0 +// replace ( +// github.com/ayn2op/tview => ../tview +// github.com/diamondburned/arikawa/v3 => ../arikawa +// ) + require ( github.com/BurntSushi/toml v1.6.0 github.com/alecthomas/chroma/v2 v2.23.1 github.com/andybalholm/brotli v1.2.0 - github.com/ayn2op/tview v0.0.0-20260301025618-69fc2518f451 + github.com/ayn2op/tview v0.0.0-20260302045935-716714ee7442 github.com/deckarep/gosx-notifier v0.0.0-20180201035817-e127226297fb github.com/diamondburned/arikawa/v3 v3.6.1-0.20260226015332-783a3e8e8e86 github.com/diamondburned/ningen/v3 v3.0.1-0.20260226220604-93f1e60c3cdb diff --git a/go.sum b/go.sum index e9b3dbf..3ef9872 100644 --- a/go.sum +++ b/go.sum @@ -14,8 +14,8 @@ github.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs github.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= 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-20260301025618-69fc2518f451 h1:DfZZiNSwERJNZ9f33hsxWIFFiAgGY2T+Fi7unZ6Ka7A= -github.com/ayn2op/tview v0.0.0-20260301025618-69fc2518f451/go.mod h1:lZ8RdOegQWBQafTOasGE7Ps1/Ymy4jmXoPt5vz2QsS0= +github.com/ayn2op/tview v0.0.0-20260302045935-716714ee7442 h1:5CN5nE/5s7zbIp51hiH1CY6hpDgvzfvh4CF38FXJCGk= +github.com/ayn2op/tview v0.0.0-20260302045935-716714ee7442/go.mod h1:lZ8RdOegQWBQafTOasGE7Ps1/Ymy4jmXoPt5vz2QsS0= 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= diff --git a/internal/ui/chat/guilds_tree.go b/internal/ui/chat/guilds_tree.go index 82fff9a..a05e78b 100644 --- a/internal/ui/chat/guilds_tree.go +++ b/internal/ui/chat/guilds_tree.go @@ -393,36 +393,39 @@ func (gt *guildsTree) collapseParentNode(node *tview.TreeNode) { }) } -func (gt *guildsTree) InputHandler(event *tcell.EventKey) tview.Command { - consume := tview.ConsumeEventCommand{} - consumeRedraw := tview.BatchCommand{tview.RedrawCommand{}, tview.ConsumeEventCommand{}} - handler := gt.TreeView.InputHandler +func (gt *guildsTree) HandleEvent(event tcell.Event) tview.Command { + switch event := event.(type) { + case *tview.KeyEvent: + consume := tview.ConsumeEventCommand{} + consumeRedraw := tview.BatchCommand{tview.RedrawCommand{}, tview.ConsumeEventCommand{}} + handler := gt.TreeView.HandleEvent - switch { - case keybind.Matches(event, gt.cfg.Keybinds.GuildsTree.CollapseParentNode.Keybind): - gt.collapseParentNode(gt.GetCurrentNode()) - return consumeRedraw - case keybind.Matches(event, gt.cfg.Keybinds.GuildsTree.MoveToParentNode.Keybind): - return handler(tcell.NewEventKey(tcell.KeyRune, "K", tcell.ModNone)) - case keybind.Matches(event, gt.cfg.Keybinds.GuildsTree.Up.Keybind): - return handler(tcell.NewEventKey(tcell.KeyUp, "", tcell.ModNone)) - case keybind.Matches(event, gt.cfg.Keybinds.GuildsTree.Down.Keybind): - return handler(tcell.NewEventKey(tcell.KeyDown, "", tcell.ModNone)) - case keybind.Matches(event, gt.cfg.Keybinds.GuildsTree.Top.Keybind): - gt.Move(gt.GetRowCount() * -1) - return consumeRedraw - case keybind.Matches(event, gt.cfg.Keybinds.GuildsTree.Bottom.Keybind): - gt.Move(gt.GetRowCount()) - return consumeRedraw - case keybind.Matches(event, gt.cfg.Keybinds.GuildsTree.SelectCurrent.Keybind): - return handler(tcell.NewEventKey(tcell.KeyEnter, "", tcell.ModNone)) - case keybind.Matches(event, gt.cfg.Keybinds.GuildsTree.YankID.Keybind): - gt.yankID() + switch { + case keybind.Matches(event, gt.cfg.Keybinds.GuildsTree.CollapseParentNode.Keybind): + gt.collapseParentNode(gt.GetCurrentNode()) + return consumeRedraw + case keybind.Matches(event, gt.cfg.Keybinds.GuildsTree.MoveToParentNode.Keybind): + return handler(tcell.NewEventKey(tcell.KeyRune, "K", tcell.ModNone)) + case keybind.Matches(event, gt.cfg.Keybinds.GuildsTree.Up.Keybind): + return handler(tcell.NewEventKey(tcell.KeyUp, "", tcell.ModNone)) + case keybind.Matches(event, gt.cfg.Keybinds.GuildsTree.Down.Keybind): + return handler(tcell.NewEventKey(tcell.KeyDown, "", tcell.ModNone)) + case keybind.Matches(event, gt.cfg.Keybinds.GuildsTree.Top.Keybind): + gt.Move(gt.GetRowCount() * -1) + return consumeRedraw + case keybind.Matches(event, gt.cfg.Keybinds.GuildsTree.Bottom.Keybind): + gt.Move(gt.GetRowCount()) + return consumeRedraw + case keybind.Matches(event, gt.cfg.Keybinds.GuildsTree.SelectCurrent.Keybind): + return handler(tcell.NewEventKey(tcell.KeyEnter, "", tcell.ModNone)) + case keybind.Matches(event, gt.cfg.Keybinds.GuildsTree.YankID.Keybind): + gt.yankID() + return consume + } + // Do not fall through to TreeView defaults for unmatched keys. return consume } - - // Do not fall through to TreeView defaults for unmatched keys. - return consume + return gt.TreeView.HandleEvent(event) } func (gt *guildsTree) yankID() { diff --git a/internal/ui/chat/message_input.go b/internal/ui/chat/message_input.go index 4ae1ce7..c250add 100644 --- a/internal/ui/chat/message_input.go +++ b/internal/ui/chat/message_input.go @@ -92,81 +92,99 @@ func (mi *messageInput) stopTypingTimer() { } } -func (mi *messageInput) InputHandler(event *tcell.EventKey) tview.Command { - consume := tview.ConsumeEventCommand{} - consumeRedraw := tview.BatchCommand{tview.RedrawCommand{}, tview.ConsumeEventCommand{}} - handler := mi.TextArea.InputHandler - - switch { - case keybind.Matches(event, mi.cfg.Keybinds.MessageInput.Paste.Keybind): - mi.paste() - return handler(tcell.NewEventKey(tcell.KeyCtrlV, "", tcell.ModNone)) - case keybind.Matches(event, mi.cfg.Keybinds.MessageInput.Send.Keybind): - if mi.chatView.GetVisible(mentionsListLayerName) { - mi.tabComplete() - } else { - mi.send() +func (mi *messageInput) HandleEvent(event tcell.Event) tview.Command { + switch event := event.(type) { + case *tview.KeyEvent: + consume := tview.ConsumeEventCommand{} + consumeRedraw := tview.BatchCommand{tview.RedrawCommand{}, tview.ConsumeEventCommand{}} + handler := mi.TextArea.HandleEvent + switch { + case keybind.Matches(event, mi.cfg.Keybinds.MessageInput.Paste.Keybind): + mi.paste() + return handler(tcell.NewEventKey(tcell.KeyCtrlV, "", tcell.ModNone)) + case keybind.Matches(event, mi.cfg.Keybinds.MessageInput.Send.Keybind): + if mi.chatView.GetVisible(mentionsListLayerName) { + mi.tabComplete() + } else { + mi.send() + } + return consumeRedraw + case keybind.Matches(event, mi.cfg.Keybinds.MessageInput.OpenEditor.Keybind): + var cmds tview.BatchCommand + mi.stopTabCompletion(func(next tview.Command) { + if next != nil { + cmds = append(cmds, next) + } + }) + mi.editor() + cmds = append(cmds, consumeRedraw) + return cmds + case keybind.Matches(event, mi.cfg.Keybinds.MessageInput.OpenFilePicker.Keybind): + var cmds tview.BatchCommand + mi.stopTabCompletion(func(next tview.Command) { + if next != nil { + cmds = append(cmds, next) + } + }) + mi.openFilePicker() + cmds = append(cmds, consumeRedraw) + return cmds + case keybind.Matches(event, mi.cfg.Keybinds.MessageInput.Cancel.Keybind): + var cmds tview.BatchCommand + if mi.chatView.GetVisible(mentionsListLayerName) { + mi.stopTabCompletion(func(next tview.Command) { + if next != nil { + cmds = append(cmds, next) + } + }) + } else { + mi.reset() + } + cmds = append(cmds, consumeRedraw) + return cmds + case keybind.Matches(event, mi.cfg.Keybinds.MessageInput.TabComplete.Keybind): + go mi.chatView.app.QueueUpdateDraw(func() { mi.tabComplete() }) + return consume + case keybind.Matches(event, mi.cfg.Keybinds.MessageInput.Undo.Keybind): + return handler(tcell.NewEventKey(tcell.KeyCtrlZ, "", tcell.ModNone)) } - return consumeRedraw - case keybind.Matches(event, mi.cfg.Keybinds.MessageInput.OpenEditor.Keybind): - var cmd tview.Command - mi.stopTabCompletion(func(next tview.Command) { cmd = tview.AppendCommand(cmd, next) }) - mi.editor() - return tview.AppendCommand(cmd, consumeRedraw) - case keybind.Matches(event, mi.cfg.Keybinds.MessageInput.OpenFilePicker.Keybind): - var cmd tview.Command - mi.stopTabCompletion(func(next tview.Command) { cmd = tview.AppendCommand(cmd, next) }) - mi.openFilePicker() - return tview.AppendCommand(cmd, consumeRedraw) - case keybind.Matches(event, mi.cfg.Keybinds.MessageInput.Cancel.Keybind): - var cmd tview.Command - if mi.chatView.GetVisible(mentionsListLayerName) { - mi.stopTabCompletion(func(next tview.Command) { cmd = tview.AppendCommand(cmd, next) }) - } else { - mi.reset() - } - return tview.AppendCommand(cmd, consumeRedraw) - case keybind.Matches(event, mi.cfg.Keybinds.MessageInput.TabComplete.Keybind): - go mi.chatView.app.QueueUpdateDraw(func() { mi.tabComplete() }) - return consume - case keybind.Matches(event, mi.cfg.Keybinds.MessageInput.Undo.Keybind): - return handler(tcell.NewEventKey(tcell.KeyCtrlZ, "", tcell.ModNone)) - } - if mi.cfg.TypingIndicator.Send && mi.typingTimer == nil { - mi.typingTimer = time.AfterFunc(typingDuration, func() { - mi.typingTimerMu.Lock() - mi.typingTimer = nil - mi.typingTimerMu.Unlock() - }) + if mi.cfg.TypingIndicator.Send && mi.typingTimer == nil { + mi.typingTimer = time.AfterFunc(typingDuration, func() { + mi.typingTimerMu.Lock() + mi.typingTimer = nil + mi.typingTimerMu.Unlock() + }) - if selectedChannel := mi.chatView.SelectedChannel(); selectedChannel != nil { - go mi.chatView.state.Typing(selectedChannel.ID) - } - } - - if mi.cfg.AutocompleteLimit > 0 { - if mi.chatView.GetVisible(mentionsListLayerName) { - switch { - case keybind.Matches(event, mi.cfg.Keybinds.MentionsList.Up.Keybind): - mi.mentionsList.InputHandler(tcell.NewEventKey(tcell.KeyUp, "", tcell.ModNone)) - return consumeRedraw - case keybind.Matches(event, mi.cfg.Keybinds.MentionsList.Down.Keybind): - mi.mentionsList.InputHandler(tcell.NewEventKey(tcell.KeyDown, "", tcell.ModNone)) - return consumeRedraw - case keybind.Matches(event, mi.cfg.Keybinds.MentionsList.Top.Keybind): - mi.mentionsList.InputHandler(tcell.NewEventKey(tcell.KeyHome, "", tcell.ModNone)) - return consumeRedraw - case keybind.Matches(event, mi.cfg.Keybinds.MentionsList.Bottom.Keybind): - mi.mentionsList.InputHandler(tcell.NewEventKey(tcell.KeyEnd, "", tcell.ModNone)) - return consumeRedraw + if selectedChannel := mi.chatView.SelectedChannel(); selectedChannel != nil { + go mi.chatView.state.Typing(selectedChannel.ID) } } - go mi.chatView.app.QueueUpdateDraw(func() { mi.tabSuggestion() }) - } + if mi.cfg.AutocompleteLimit > 0 { + if mi.chatView.GetVisible(mentionsListLayerName) { + switch { + case keybind.Matches(event, mi.cfg.Keybinds.MentionsList.Up.Keybind): + mi.mentionsList.HandleEvent(tcell.NewEventKey(tcell.KeyUp, "", tcell.ModNone)) + return consumeRedraw + case keybind.Matches(event, mi.cfg.Keybinds.MentionsList.Down.Keybind): + mi.mentionsList.HandleEvent(tcell.NewEventKey(tcell.KeyDown, "", tcell.ModNone)) + return consumeRedraw + case keybind.Matches(event, mi.cfg.Keybinds.MentionsList.Top.Keybind): + mi.mentionsList.HandleEvent(tcell.NewEventKey(tcell.KeyHome, "", tcell.ModNone)) + return consumeRedraw + case keybind.Matches(event, mi.cfg.Keybinds.MentionsList.Bottom.Keybind): + mi.mentionsList.HandleEvent(tcell.NewEventKey(tcell.KeyEnd, "", tcell.ModNone)) + return consumeRedraw + } + } - return handler(event) + go mi.chatView.app.QueueUpdateDraw(func() { mi.tabSuggestion() }) + } + + return handler(event) + } + return mi.TextArea.HandleEvent(event) } func (mi *messageInput) paste() { diff --git a/internal/ui/chat/messages_list.go b/internal/ui/chat/messages_list.go index 2ca1d50..3ff4eb4 100644 --- a/internal/ui/chat/messages_list.go +++ b/internal/ui/chat/messages_list.go @@ -525,72 +525,76 @@ func (ml *messagesList) selectedMessage() (*discord.Message, error) { return &ml.messages[cursor], nil } -func (ml *messagesList) InputHandler(event *tcell.EventKey) tview.Command { - consume := tview.ConsumeEventCommand{} - consumeRedraw := tview.BatchCommand{tview.RedrawCommand{}, tview.ConsumeEventCommand{}} +func (ml *messagesList) HandleEvent(event tcell.Event) tview.Command { + switch event := event.(type) { + case *tview.KeyEvent: + consume := tview.ConsumeEventCommand{} + consumeRedraw := tview.BatchCommand{tview.RedrawCommand{}, tview.ConsumeEventCommand{}} - switch { - case keybind.Matches(event, ml.cfg.Keybinds.MessagesList.ScrollUp.Keybind): - ml.ScrollUp() - return consumeRedraw - case keybind.Matches(event, ml.cfg.Keybinds.MessagesList.ScrollDown.Keybind): - ml.ScrollDown() - return consumeRedraw - case keybind.Matches(event, ml.cfg.Keybinds.MessagesList.ScrollTop.Keybind): - ml.ScrollToStart() - return consumeRedraw - case keybind.Matches(event, ml.cfg.Keybinds.MessagesList.ScrollBottom.Keybind): - ml.ScrollToEnd() - return consumeRedraw - case keybind.Matches(event, ml.cfg.Keybinds.MessagesList.Cancel.Keybind): - ml.clearSelection() - return consumeRedraw - case keybind.Matches(event, ml.cfg.Keybinds.MessagesList.SelectUp.Keybind): - ml.selectUp() - return consumeRedraw - case keybind.Matches(event, ml.cfg.Keybinds.MessagesList.SelectDown.Keybind): - ml.selectDown() - return consumeRedraw - case keybind.Matches(event, ml.cfg.Keybinds.MessagesList.SelectTop.Keybind): - ml.selectTop() - return consumeRedraw - case keybind.Matches(event, ml.cfg.Keybinds.MessagesList.SelectBottom.Keybind): - ml.selectBottom() - return consumeRedraw - case keybind.Matches(event, ml.cfg.Keybinds.MessagesList.SelectReply.Keybind): - ml.selectReply() - return consumeRedraw - case keybind.Matches(event, ml.cfg.Keybinds.MessagesList.YankID.Keybind): - ml.yankID() + switch { + case keybind.Matches(event, ml.cfg.Keybinds.MessagesList.ScrollUp.Keybind): + ml.ScrollUp() + return consumeRedraw + case keybind.Matches(event, ml.cfg.Keybinds.MessagesList.ScrollDown.Keybind): + ml.ScrollDown() + return consumeRedraw + case keybind.Matches(event, ml.cfg.Keybinds.MessagesList.ScrollTop.Keybind): + ml.ScrollToStart() + return consumeRedraw + case keybind.Matches(event, ml.cfg.Keybinds.MessagesList.ScrollBottom.Keybind): + ml.ScrollToEnd() + return consumeRedraw + case keybind.Matches(event, ml.cfg.Keybinds.MessagesList.Cancel.Keybind): + ml.clearSelection() + return consumeRedraw + case keybind.Matches(event, ml.cfg.Keybinds.MessagesList.SelectUp.Keybind): + ml.selectUp() + return consumeRedraw + case keybind.Matches(event, ml.cfg.Keybinds.MessagesList.SelectDown.Keybind): + ml.selectDown() + return consumeRedraw + case keybind.Matches(event, ml.cfg.Keybinds.MessagesList.SelectTop.Keybind): + ml.selectTop() + return consumeRedraw + case keybind.Matches(event, ml.cfg.Keybinds.MessagesList.SelectBottom.Keybind): + ml.selectBottom() + return consumeRedraw + case keybind.Matches(event, ml.cfg.Keybinds.MessagesList.SelectReply.Keybind): + ml.selectReply() + return consumeRedraw + case keybind.Matches(event, ml.cfg.Keybinds.MessagesList.YankID.Keybind): + ml.yankID() + return consume + case keybind.Matches(event, ml.cfg.Keybinds.MessagesList.YankContent.Keybind): + ml.yankContent() + return consume + case keybind.Matches(event, ml.cfg.Keybinds.MessagesList.YankURL.Keybind): + ml.yankURL() + return consume + case keybind.Matches(event, ml.cfg.Keybinds.MessagesList.Open.Keybind): + ml.open() + return consume + case keybind.Matches(event, ml.cfg.Keybinds.MessagesList.Reply.Keybind): + ml.reply(false) + return consumeRedraw + case keybind.Matches(event, ml.cfg.Keybinds.MessagesList.ReplyMention.Keybind): + ml.reply(true) + return consumeRedraw + case keybind.Matches(event, ml.cfg.Keybinds.MessagesList.Edit.Keybind): + ml.edit() + return consumeRedraw + case keybind.Matches(event, ml.cfg.Keybinds.MessagesList.Delete.Keybind): + ml.delete() + return consumeRedraw + case keybind.Matches(event, ml.cfg.Keybinds.MessagesList.DeleteConfirm.Keybind): + ml.confirmDelete() + return consumeRedraw + } + + // Do not fall through to List defaults for unmatched keys. return consume - case keybind.Matches(event, ml.cfg.Keybinds.MessagesList.YankContent.Keybind): - ml.yankContent() - return consume - case keybind.Matches(event, ml.cfg.Keybinds.MessagesList.YankURL.Keybind): - ml.yankURL() - return consume - case keybind.Matches(event, ml.cfg.Keybinds.MessagesList.Open.Keybind): - ml.open() - return consume - case keybind.Matches(event, ml.cfg.Keybinds.MessagesList.Reply.Keybind): - ml.reply(false) - return consumeRedraw - case keybind.Matches(event, ml.cfg.Keybinds.MessagesList.ReplyMention.Keybind): - ml.reply(true) - return consumeRedraw - case keybind.Matches(event, ml.cfg.Keybinds.MessagesList.Edit.Keybind): - ml.edit() - return consumeRedraw - case keybind.Matches(event, ml.cfg.Keybinds.MessagesList.Delete.Keybind): - ml.delete() - return consumeRedraw - case keybind.Matches(event, ml.cfg.Keybinds.MessagesList.DeleteConfirm.Keybind): - ml.confirmDelete() - return consumeRedraw } - - // Do not fall through to List defaults for unmatched keys. - return consume + return ml.List.HandleEvent(event) } func (ml *messagesList) selectUp() { diff --git a/internal/ui/chat/view.go b/internal/ui/chat/view.go index 1dea66d..c96b58f 100644 --- a/internal/ui/chat/view.go +++ b/internal/ui/chat/view.go @@ -222,48 +222,53 @@ func (v *View) focusNext() { } } -func (v *View) InputHandler(event *tcell.EventKey) tview.Command { - consume := tview.BatchCommand{tview.RedrawCommand{}, tview.ConsumeEventCommand{}} +func (v *View) HandleEvent(event tcell.Event) tview.Command { + switch event := event.(type) { + case *tview.KeyEvent: + consume := tview.BatchCommand{tview.RedrawCommand{}, tview.ConsumeEventCommand{}} - switch { - case keybind.Matches(event, v.cfg.Keybinds.ToggleHelp.Keybind): - v.help.SetShowAll(!v.help.ShowAll()) - v.updateHelpHeight() - return consume - case keybind.Matches(event, v.cfg.Keybinds.FocusGuildsTree.Keybind): - v.messageInput.removeMentionsList() - v.focusGuildsTree() - return consume - case keybind.Matches(event, v.cfg.Keybinds.FocusMessagesList.Keybind): - v.messageInput.removeMentionsList() - v.app.SetFocus(v.messagesList) - return consume - case keybind.Matches(event, v.cfg.Keybinds.FocusMessageInput.Keybind): - v.focusMessageInput() - return consume - case keybind.Matches(event, v.cfg.Keybinds.FocusPrevious.Keybind): - v.focusPrevious() - return consume - case keybind.Matches(event, v.cfg.Keybinds.FocusNext.Keybind): - v.focusNext() - return consume - case keybind.Matches(event, v.cfg.Keybinds.Logout.Keybind): - if v.onLogout != nil { - v.onLogout() + switch { + case keybind.Matches(event, v.cfg.Keybinds.ToggleHelp.Keybind): + v.help.SetShowAll(!v.help.ShowAll()) + v.updateHelpHeight() + return consume + case keybind.Matches(event, v.cfg.Keybinds.FocusGuildsTree.Keybind): + v.messageInput.removeMentionsList() + v.focusGuildsTree() + return consume + case keybind.Matches(event, v.cfg.Keybinds.FocusMessagesList.Keybind): + v.messageInput.removeMentionsList() + v.app.SetFocus(v.messagesList) + return consume + case keybind.Matches(event, v.cfg.Keybinds.FocusMessageInput.Keybind): + v.focusMessageInput() + return consume + case keybind.Matches(event, v.cfg.Keybinds.FocusPrevious.Keybind): + v.focusPrevious() + return consume + case keybind.Matches(event, v.cfg.Keybinds.FocusNext.Keybind): + v.focusNext() + return consume + case keybind.Matches(event, v.cfg.Keybinds.Logout.Keybind): + if v.onLogout != nil { + v.onLogout() + } + if err := keyring.DeleteToken(); err != nil { + slog.Error("failed to delete token from keyring", "err", err) + } + return consume + case keybind.Matches(event, v.cfg.Keybinds.ToggleGuildsTree.Keybind): + v.toggleGuildsTree() + return consume + case keybind.Matches(event, v.cfg.Keybinds.ToggleChannelsPicker.Keybind): + v.togglePicker() + return consume } - if err := keyring.DeleteToken(); err != nil { - slog.Error("failed to delete token from keyring", "err", err) - } - return consume - case keybind.Matches(event, v.cfg.Keybinds.ToggleGuildsTree.Keybind): - v.toggleGuildsTree() - return consume - case keybind.Matches(event, v.cfg.Keybinds.ToggleChannelsPicker.Keybind): - v.togglePicker() - return consume + + cmd := v.Layers.HandleEvent(event) + return v.consumeLayerCommands(cmd) } - - cmd := v.Layers.InputHandler(event) + cmd := v.Layers.HandleEvent(event) return v.consumeLayerCommands(cmd) } diff --git a/internal/ui/login/qr.go b/internal/ui/login/qr.go index d998c9b..fa2e82b 100644 --- a/internal/ui/login/qr.go +++ b/internal/ui/login/qr.go @@ -63,15 +63,19 @@ func newQRLogin(app *tview.Application, cfg *config.Config, done func(token stri return q } -func (q *qrLogin) InputHandler(event *tcell.EventKey) tview.Command { - if event.Key() == tcell.KeyEsc { - q.stop() - if q.done != nil { - q.done("", nil) +func (q *qrLogin) HandleEvent(event tcell.Event) tview.Command { + switch event := event.(type) { + case *tview.KeyEvent: + if event.Key() == tcell.KeyEsc { + q.stop() + if q.done != nil { + q.done("", nil) + } + return tview.BatchCommand{tview.RedrawCommand{}, tview.ConsumeEventCommand{}} } - return tview.BatchCommand{tview.RedrawCommand{}, tview.ConsumeEventCommand{}} + return q.TextView.HandleEvent(event) } - return q.TextView.InputHandler(event) + return q.TextView.HandleEvent(event) } func (q *qrLogin) start() { diff --git a/internal/ui/root/view.go b/internal/ui/root/view.go index 23ba0a7..5ea2425 100644 --- a/internal/ui/root/view.go +++ b/internal/ui/root/view.go @@ -123,18 +123,25 @@ func (v *View) Draw(screen tcell.Screen) { v.inner.Draw(screen) } -func (v *View) InputHandler(event *tcell.EventKey) tview.Command { - switch { - case keybind.Matches(event, v.cfg.Keybinds.Suspend.Keybind): - v.suspend() - return tview.ConsumeEventCommand{} - case keybind.Matches(event, v.cfg.Keybinds.Quit.Keybind): - v.closeChatViewState() - return tview.QuitCommand{} - } +func (v *View) HandleEvent(event tcell.Event) tview.Command { + switch event := event.(type) { + case *tview.KeyEvent: + switch { + case keybind.Matches(event, v.cfg.Keybinds.Suspend.Keybind): + v.suspend() + return tview.ConsumeEventCommand{} + case keybind.Matches(event, v.cfg.Keybinds.Quit.Keybind): + v.closeChatViewState() + return tview.QuitCommand{} + } - if v.inner != nil { - return v.inner.InputHandler(event) + if v.inner != nil { + return v.inner.HandleEvent(event) + } + case *tview.MouseEvent, *tview.PasteEvent: + if v.inner != nil { + return v.inner.HandleEvent(event) + } } return nil } @@ -160,17 +167,3 @@ func (v *View) Blur() { } v.Box.Blur() } - -func (v *View) MouseHandler(action tview.MouseAction, event *tcell.EventMouse) (tview.Primitive, tview.Command) { - if v.inner == nil { - return nil, nil - } - return v.inner.MouseHandler(action, event) -} - -func (v *View) PasteHandler(text string) tview.Command { - if v.inner == nil { - return nil - } - return v.inner.PasteHandler(text) -} diff --git a/pkg/picker/picker.go b/pkg/picker/picker.go index 31f8f89..8a56832 100644 --- a/pkg/picker/picker.go +++ b/pkg/picker/picker.go @@ -148,33 +148,37 @@ func (p *Picker) onInputChanged(text string) { p.setFilteredItems(fuzzied) } -func (p *Picker) InputHandler(event *tcell.EventKey) tview.Command { - consume := tview.BatchCommand{tview.RedrawCommand{}, tview.ConsumeEventCommand{}} +func (p *Picker) HandleEvent(event tcell.Event) tview.Command { + switch event := event.(type) { + case *tview.KeyEvent: + consume := tview.BatchCommand{tview.RedrawCommand{}, tview.ConsumeEventCommand{}} - if p.keyMap != nil { - switch { - case keybind.Matches(event, p.keyMap.Up): - p.list.InputHandler(tcell.NewEventKey(tcell.KeyUp, "", tcell.ModNone)) - return consume - case keybind.Matches(event, p.keyMap.Down): - p.list.InputHandler(tcell.NewEventKey(tcell.KeyDown, "", tcell.ModNone)) - return consume - case keybind.Matches(event, p.keyMap.Top): - p.list.InputHandler(tcell.NewEventKey(tcell.KeyHome, "", tcell.ModNone)) - return consume - case keybind.Matches(event, p.keyMap.Bottom): - p.list.InputHandler(tcell.NewEventKey(tcell.KeyEnd, "", tcell.ModNone)) - return consume - case keybind.Matches(event, p.keyMap.Select): - p.onListSelected(p.list.Cursor()) - return consume - case keybind.Matches(event, p.keyMap.Cancel): - if p.onCancel != nil { - p.onCancel() + if p.keyMap != nil { + switch { + case keybind.Matches(event, p.keyMap.Up): + p.list.HandleEvent(tcell.NewEventKey(tcell.KeyUp, "", tcell.ModNone)) + return consume + case keybind.Matches(event, p.keyMap.Down): + p.list.HandleEvent(tcell.NewEventKey(tcell.KeyDown, "", tcell.ModNone)) + return consume + case keybind.Matches(event, p.keyMap.Top): + p.list.HandleEvent(tcell.NewEventKey(tcell.KeyHome, "", tcell.ModNone)) + return consume + case keybind.Matches(event, p.keyMap.Bottom): + p.list.HandleEvent(tcell.NewEventKey(tcell.KeyEnd, "", tcell.ModNone)) + return consume + case keybind.Matches(event, p.keyMap.Select): + p.onListSelected(p.list.Cursor()) + return consume + case keybind.Matches(event, p.keyMap.Cancel): + if p.onCancel != nil { + p.onCancel() + } + return consume } - return consume } - } - return p.Flex.InputHandler(event) + return p.Flex.HandleEvent(event) + } + return p.Flex.HandleEvent(event) }