refactor: merge Application with Layout
This commit is contained in:
parent
1769fa97e1
commit
9d05f2999c
8 changed files with 153 additions and 166 deletions
|
|
@ -1,68 +0,0 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
|
||||
"github.com/gdamore/tcell/v2"
|
||||
"github.com/rivo/tview"
|
||||
)
|
||||
|
||||
type Application struct {
|
||||
*tview.Application
|
||||
}
|
||||
|
||||
func newApplication() *Application {
|
||||
app := &Application{
|
||||
Application: tview.NewApplication(),
|
||||
}
|
||||
|
||||
app.EnableMouse(cfg.Mouse)
|
||||
app.SetInputCapture(app.onInputCapture)
|
||||
return app
|
||||
}
|
||||
|
||||
func (app *Application) onInputCapture(event *tcell.EventKey) *tcell.EventKey {
|
||||
switch event.Name() {
|
||||
case cfg.Keys.Quit:
|
||||
app.Stop()
|
||||
case "Ctrl+C":
|
||||
// https://github.com/rivo/tview/blob/a64fc48d7654432f71922c8b908280cdb525805c/application.go#L153
|
||||
return tcell.NewEventKey(tcell.KeyCtrlC, 0, tcell.ModNone)
|
||||
}
|
||||
|
||||
return event
|
||||
}
|
||||
|
||||
func (app *Application) show(token string) error {
|
||||
if token == "" {
|
||||
loginForm := newLoginForm(func(token string, err error) {
|
||||
if err != nil {
|
||||
slog.Error("failed to login", "err", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := app.show(token); err != nil {
|
||||
slog.Error("failed to show app", "err", err)
|
||||
}
|
||||
})
|
||||
app.SetRoot(loginForm, true)
|
||||
} else {
|
||||
// mainFlex must be initialized before opening a new state.
|
||||
mainFlex = newMainFlex()
|
||||
if err := openState(token); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
app.SetRoot(mainFlex, true)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (app *Application) Run(token string) error {
|
||||
if err := app.show(token); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return app.Application.Run()
|
||||
}
|
||||
|
|
@ -14,13 +14,14 @@ import (
|
|||
|
||||
type GuildsTree struct {
|
||||
*tview.TreeView
|
||||
|
||||
app *tview.Application
|
||||
selectedChannelID discord.ChannelID
|
||||
}
|
||||
|
||||
func newGuildsTree() *GuildsTree {
|
||||
func newGuildsTree(app *tview.Application) *GuildsTree {
|
||||
gt := &GuildsTree{
|
||||
TreeView: tview.NewTreeView(),
|
||||
app: app,
|
||||
}
|
||||
|
||||
root := tview.NewTreeNode("")
|
||||
|
|
@ -209,7 +210,7 @@ func (gt *GuildsTree) onSelected(n *tview.TreeNode) {
|
|||
mainFlex.messagesText.SetTitle(gt.channelToString(*c))
|
||||
|
||||
gt.selectedChannelID = ref
|
||||
app.SetFocus(mainFlex.messageInput)
|
||||
gt.app.SetFocus(mainFlex.messageInput)
|
||||
case nil: // Direct messages
|
||||
cs, err := discordState.PrivateChannels()
|
||||
if err != nil {
|
||||
|
|
|
|||
134
cmd/layout.go
Normal file
134
cmd/layout.go
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
|
||||
"github.com/ayn2op/discordo/internal/constants"
|
||||
"github.com/gdamore/tcell/v2"
|
||||
"github.com/rivo/tview"
|
||||
"github.com/zalando/go-keyring"
|
||||
)
|
||||
|
||||
type Layout struct {
|
||||
app *tview.Application
|
||||
flex *tview.Flex
|
||||
|
||||
guildsTree *GuildsTree
|
||||
messagesText *MessagesText
|
||||
messageInput *MessageInput
|
||||
}
|
||||
|
||||
func newLayout() *Layout {
|
||||
app := tview.NewApplication()
|
||||
l := &Layout{
|
||||
app: app,
|
||||
flex: tview.NewFlex(),
|
||||
|
||||
guildsTree: newGuildsTree(app),
|
||||
messagesText: newMessagesText(app),
|
||||
messageInput: newMessageInput(app),
|
||||
}
|
||||
|
||||
l.init()
|
||||
|
||||
l.app.EnableMouse(cfg.Mouse)
|
||||
l.app.SetInputCapture(l.onAppInputCapture)
|
||||
|
||||
l.flex.SetInputCapture(l.onFlexInputCapture)
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *Layout) show(token string) error {
|
||||
if token == "" {
|
||||
loginForm := newLoginForm(func(token string, err error) {
|
||||
if err != nil {
|
||||
slog.Error("failed to login", "err", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := l.show(token); err != nil {
|
||||
slog.Error("failed to show app", "err", err)
|
||||
}
|
||||
})
|
||||
l.app.SetRoot(loginForm, true)
|
||||
} else {
|
||||
if err := openState(token, l.app); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
l.app.SetRoot(l.flex, true)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *Layout) run(token string) error {
|
||||
if err := l.show(token); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return l.app.Run()
|
||||
}
|
||||
|
||||
func (l *Layout) init() {
|
||||
l.flex.Clear()
|
||||
|
||||
right := tview.NewFlex()
|
||||
right.SetDirection(tview.FlexRow)
|
||||
right.AddItem(l.messagesText, 0, 1, false)
|
||||
right.AddItem(l.messageInput, 3, 1, false)
|
||||
// The guilds tree is always focused first at start-up.
|
||||
l.flex.AddItem(l.guildsTree, 0, 1, true)
|
||||
l.flex.AddItem(right, 0, 4, false)
|
||||
}
|
||||
|
||||
func (l *Layout) onAppInputCapture(event *tcell.EventKey) *tcell.EventKey {
|
||||
switch event.Name() {
|
||||
case cfg.Keys.Quit:
|
||||
l.app.Stop()
|
||||
case "Ctrl+C":
|
||||
// https://github.com/rivo/tview/blob/a64fc48d7654432f71922c8b908280cdb525805c/application.go#L153
|
||||
return tcell.NewEventKey(tcell.KeyCtrlC, 0, tcell.ModNone)
|
||||
}
|
||||
|
||||
return event
|
||||
}
|
||||
|
||||
func (l *Layout) onFlexInputCapture(event *tcell.EventKey) *tcell.EventKey {
|
||||
switch event.Name() {
|
||||
case cfg.Keys.FocusGuildsTree:
|
||||
l.app.SetFocus(l.guildsTree)
|
||||
return nil
|
||||
case cfg.Keys.FocusMessagesText:
|
||||
l.app.SetFocus(l.messagesText)
|
||||
return nil
|
||||
case cfg.Keys.FocusMessageInput:
|
||||
l.app.SetFocus(l.messageInput)
|
||||
return nil
|
||||
case cfg.Keys.Logout:
|
||||
l.app.Stop()
|
||||
|
||||
if err := keyring.Delete(constants.Name, "token"); err != nil {
|
||||
slog.Error("failed to delete token from keyring", "err", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
case cfg.Keys.ToggleGuildsTree:
|
||||
// The guilds tree is visible if the numbers of items is two.
|
||||
if l.flex.GetItemCount() == 2 {
|
||||
l.flex.RemoveItem(l.guildsTree)
|
||||
|
||||
if l.guildsTree.HasFocus() {
|
||||
l.app.SetFocus(l.flex)
|
||||
}
|
||||
} else {
|
||||
l.init()
|
||||
l.app.SetFocus(l.guildsTree)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return event
|
||||
}
|
||||
|
|
@ -1,83 +0,0 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
|
||||
"github.com/ayn2op/discordo/internal/constants"
|
||||
"github.com/gdamore/tcell/v2"
|
||||
"github.com/rivo/tview"
|
||||
"github.com/zalando/go-keyring"
|
||||
)
|
||||
|
||||
type MainFlex struct {
|
||||
*tview.Flex
|
||||
|
||||
guildsTree *GuildsTree
|
||||
messagesText *MessagesText
|
||||
messageInput *MessageInput
|
||||
}
|
||||
|
||||
func newMainFlex() *MainFlex {
|
||||
mf := &MainFlex{
|
||||
Flex: tview.NewFlex(),
|
||||
|
||||
guildsTree: newGuildsTree(),
|
||||
messagesText: newMessagesText(),
|
||||
messageInput: newMessageInput(),
|
||||
}
|
||||
|
||||
mf.init()
|
||||
mf.SetInputCapture(mf.onInputCapture)
|
||||
return mf
|
||||
}
|
||||
|
||||
func (mf *MainFlex) init() {
|
||||
mf.Clear()
|
||||
|
||||
right := tview.NewFlex()
|
||||
right.SetDirection(tview.FlexRow)
|
||||
right.AddItem(mf.messagesText, 0, 1, false)
|
||||
right.AddItem(mf.messageInput, 3, 1, false)
|
||||
// The guilds tree is always focused first at start-up.
|
||||
mf.AddItem(mf.guildsTree, 0, 1, true)
|
||||
mf.AddItem(right, 0, 4, false)
|
||||
}
|
||||
|
||||
func (mf *MainFlex) onInputCapture(event *tcell.EventKey) *tcell.EventKey {
|
||||
switch event.Name() {
|
||||
case cfg.Keys.FocusGuildsTree:
|
||||
app.SetFocus(mf.guildsTree)
|
||||
return nil
|
||||
case cfg.Keys.FocusMessagesText:
|
||||
app.SetFocus(mf.messagesText)
|
||||
return nil
|
||||
case cfg.Keys.FocusMessageInput:
|
||||
app.SetFocus(mf.messageInput)
|
||||
return nil
|
||||
case cfg.Keys.Logout:
|
||||
app.Stop()
|
||||
|
||||
if err := keyring.Delete(constants.Name, "token"); err != nil {
|
||||
slog.Error("failed to delete token from keyring", "err", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
case cfg.Keys.ToggleGuildsTree:
|
||||
// The guilds tree is visible if the numbers of items is two.
|
||||
if mf.GetItemCount() == 2 {
|
||||
mf.RemoveItem(mf.guildsTree)
|
||||
|
||||
if mf.guildsTree.HasFocus() {
|
||||
app.SetFocus(mf)
|
||||
}
|
||||
} else {
|
||||
mf.init()
|
||||
app.SetFocus(mf.guildsTree)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return event
|
||||
}
|
||||
|
|
@ -17,12 +17,14 @@ import (
|
|||
|
||||
type MessageInput struct {
|
||||
*tview.TextArea
|
||||
app *tview.Application
|
||||
replyMessageID discord.MessageID
|
||||
}
|
||||
|
||||
func newMessageInput() *MessageInput {
|
||||
func newMessageInput(app *tview.Application) *MessageInput {
|
||||
mi := &MessageInput{
|
||||
TextArea: tview.NewTextArea(),
|
||||
app: app,
|
||||
}
|
||||
|
||||
mi.SetTextStyle(tcell.StyleDefault.Background(tcell.GetColor(cfg.Theme.BackgroundColor)))
|
||||
|
|
@ -131,7 +133,7 @@ func (mi *MessageInput) editor() {
|
|||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
app.Suspend(func() {
|
||||
mi.app.Suspend(func() {
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
slog.Error("failed to run command", "err", err, "command", cmd)
|
||||
|
|
|
|||
|
|
@ -21,13 +21,14 @@ import (
|
|||
|
||||
type MessagesText struct {
|
||||
*tview.TextView
|
||||
|
||||
app *tview.Application
|
||||
selectedMessageID discord.MessageID
|
||||
}
|
||||
|
||||
func newMessagesText() *MessagesText {
|
||||
func newMessagesText(app *tview.Application) *MessagesText {
|
||||
mt := &MessagesText{
|
||||
TextView: tview.NewTextView(),
|
||||
app: app,
|
||||
}
|
||||
|
||||
mt.SetDynamicColors(true)
|
||||
|
|
@ -345,7 +346,7 @@ func (mt *MessagesText) reply(mention bool) {
|
|||
title += msg.Author.Tag()
|
||||
mainFlex.messageInput.SetTitle(title)
|
||||
mainFlex.messageInput.replyMessageID = mt.selectedMessageID
|
||||
app.SetFocus(mainFlex.messageInput)
|
||||
mt.app.SetFocus(mainFlex.messageInput)
|
||||
}
|
||||
|
||||
func (mt *MessagesText) delete() {
|
||||
|
|
|
|||
|
|
@ -9,8 +9,7 @@ var (
|
|||
discordState *State
|
||||
|
||||
cfg *config.Config
|
||||
app *Application
|
||||
mainFlex *MainFlex
|
||||
mainFlex *Layout
|
||||
)
|
||||
|
||||
func Run(token string) error {
|
||||
|
|
@ -24,7 +23,6 @@ func Run(token string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// app must be initialized after configuration is loaded
|
||||
app = newApplication()
|
||||
return app.Run(token)
|
||||
mainFlex = newLayout()
|
||||
return mainFlex.run(token)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,11 +27,13 @@ func init() {
|
|||
|
||||
type State struct {
|
||||
*ningen.State
|
||||
app *tview.Application
|
||||
}
|
||||
|
||||
func openState(token string) error {
|
||||
func openState(token string, app *tview.Application) error {
|
||||
discordState = &State{
|
||||
State: ningen.New(token),
|
||||
app: app,
|
||||
}
|
||||
|
||||
// Handlers
|
||||
|
|
@ -78,7 +80,7 @@ func (s *State) onReady(r *gateway.ReadyEvent) {
|
|||
}
|
||||
|
||||
mainFlex.guildsTree.SetCurrentNode(root)
|
||||
app.SetFocus(mainFlex.guildsTree)
|
||||
s.app.SetFocus(mainFlex.guildsTree)
|
||||
}
|
||||
|
||||
func (s *State) onMessageCreate(m *gateway.MessageCreateEvent) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue