diff --git a/ui/toolkit/msg.go b/ui/toolkit/msg.go new file mode 100644 index 0000000..6514aea --- /dev/null +++ b/ui/toolkit/msg.go @@ -0,0 +1,96 @@ +package toolkit + +import ( + "strconv" + + "github.com/charmbracelet/bubbles/key" + tea "github.com/charmbracelet/bubbletea" + "github.com/mrusme/gobbs/ui/cmd" +) + +type MsgHandlingKeymapKey struct { + ID string + Handler func(m interface{}) (bool, []tea.Cmd) +} + +type MsgHandling struct { + OnKeymapKey []MsgHandlingKeymapKey + OnAnyNumberKey func(m interface{}, n int8) (bool, []tea.Cmd) + OnAnyUncaughtKey func(m interface{}, k tea.KeyMsg) (bool, []tea.Cmd) + OnViewResize func(m interface{}) (bool, []tea.Cmd) + OnWinOpenCmd func(m interface{}, c cmd.Command) (bool, []tea.Cmd) + OnWinRefreshDataCmd func(m interface{}, c cmd.Command) (bool, []tea.Cmd) + OnWinFreshDataCmd func(m interface{}, c cmd.Command) (bool, []tea.Cmd) +} + +func (tk *ToolKit) SetMsgHandling(mh MsgHandling) { + tk.mh = mh +} + +func (tk *ToolKit) HandleMsg(m interface{}, msg tea.Msg) (bool, []tea.Cmd) { + var cmds []tea.Cmd + + switch msg := msg.(type) { + case tea.KeyMsg: + for i := 0; i < len(tk.mh.OnKeymapKey); i++ { + if key.Matches(msg, tk.KeymapGet(tk.mh.OnKeymapKey[i].ID)) { + return tk.mh.OnKeymapKey[i].Handler(m) + } + } + + if tk.mh.OnAnyNumberKey != nil { + switch msg.String() { + case "1", "2", "3", "4", "5", "6", "7", "8", "9", "0": + n, _ := strconv.Atoi(msg.String()) + return tk.mh.OnAnyNumberKey(m, int8(n)) + } + } + + if tk.mh.OnAnyUncaughtKey != nil { + return tk.mh.OnAnyUncaughtKey(m, msg) + } + + case tea.WindowSizeMsg: + tk.wh[0] = msg.Width + tk.wh[1] = msg.Height + if tk.mh.OnViewResize != nil { + return tk.mh.OnViewResize(m) + } + return false, cmds + + case cmd.Command: + tk.logger.Debugf("got command: %v\n", msg) + switch msg.Call { + case cmd.WinFocus: + if msg.Target == tk.winID || + msg.Target == "*" { + tk.logger.Debug("got WinFocus") + tk.Focus(m) + } + tk.logger.Debugf("focused: %v", tk.focused) + return true, nil + case cmd.WinBlur: + if msg.Target == tk.winID || + msg.Target == "*" { + tk.logger.Debug("got WinBlur") + tk.Blur(m) + } + tk.logger.Debugf("focused: %v", tk.focused) + return true, nil + case cmd.WinOpen: + if tk.mh.OnWinOpenCmd != nil { + return tk.mh.OnWinOpenCmd(m, msg) + } + case cmd.WinRefreshData: + if tk.mh.OnWinRefreshDataCmd != nil { + return tk.mh.OnWinRefreshDataCmd(m, msg) + } + case cmd.WinFreshData: + if tk.mh.OnWinFreshDataCmd != nil { + return tk.mh.OnWinFreshDataCmd(m, msg) + } + } + + } + return false, cmds +} diff --git a/ui/toolkit/toolkit.go b/ui/toolkit/toolkit.go index da4013a..a0dbc14 100644 --- a/ui/toolkit/toolkit.go +++ b/ui/toolkit/toolkit.go @@ -2,8 +2,6 @@ package toolkit import ( "github.com/charmbracelet/bubbles/key" - tea "github.com/charmbracelet/bubbletea" - "github.com/mrusme/gobbs/ui/cmd" "github.com/mrusme/gobbs/ui/theme" "go.uber.org/zap" ) @@ -15,6 +13,8 @@ type ToolKit struct { theme *theme.Theme logger *zap.SugaredLogger + mh MsgHandling + m interface{} wh [2]int focused bool @@ -31,6 +31,8 @@ func New(winID string, t *theme.Theme, l *zap.SugaredLogger) *ToolKit { tk.theme = t tk.logger = l + tk.mh = MsgHandling{} + tk.wh = [2]int{0, 0} tk.focused = false @@ -90,37 +92,3 @@ func (tk *ToolKit) ViewWidth() int { func (tk *ToolKit) ViewHeight() int { return tk.wh[1] } - -func (tk *ToolKit) HandleMsg(m interface{}, msg tea.Msg) (bool, []tea.Cmd) { - var cmds []tea.Cmd - - switch msg := msg.(type) { - case tea.WindowSizeMsg: - tk.wh[0] = msg.Width - tk.wh[1] = msg.Height - return false, cmds - - case cmd.Command: - tk.logger.Debugf("got command: %v\n", msg) - switch msg.Call { - case cmd.WinFocus: - if msg.Target == tk.winID || - msg.Target == "*" { - tk.logger.Debug("got WinFocus") - tk.Focus(m) - } - tk.logger.Debugf("focused: %v", tk.focused) - return true, nil - case cmd.WinBlur: - if msg.Target == tk.winID || - msg.Target == "*" { - tk.logger.Debug("got WinBlur") - tk.Blur(m) - } - tk.logger.Debugf("focused: %v", tk.focused) - return true, nil - } - - } - return false, cmds -} diff --git a/ui/windows/postshow/postshow.go b/ui/windows/postshow/postshow.go index b3a7fb8..a4b61a1 100644 --- a/ui/windows/postshow/postshow.go +++ b/ui/windows/postshow/postshow.go @@ -5,7 +5,6 @@ import ( "strconv" "strings" - "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/viewport" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/glamour" @@ -69,9 +68,139 @@ func NewModel(c *ctx.Ctx) Model { m.tk.KeymapAdd("reply", "reply", "r") + m.tk.SetMsgHandling(toolkit.MsgHandling{ + OnKeymapKey: []toolkit.MsgHandlingKeymapKey{ + { + ID: "reply", + Handler: handleReply, + }, + }, + OnAnyNumberKey: handleNumberKeys, + OnAnyUncaughtKey: handleUncaughtKeys, + OnViewResize: handleViewResize, + OnWinOpenCmd: handleWinOpenCmd, + OnWinRefreshDataCmd: handleWinOpenCmd, + OnWinFreshDataCmd: handleWinFreshDataCmd, + }) + return m } +func handleReply(mi interface{}) (bool, []tea.Cmd) { + var m *Model = mi.(*Model) + var cmds []tea.Cmd + var replyToIdx int = 0 + var err error + + // m.viewcache = m.buildView(false) + m.tk.CacheView(m) + + if m.buffer != "" { + replyToIdx, err = strconv.Atoi(m.buffer) + + if err != nil { + // TODO: Handle error + } + + if replyToIdx >= len(m.replyIDs) { + // TODO: Handle error + } + } + + m.ctx.Logger.Debugf("replyToIdx: %d", replyToIdx) + var rtype cmd.Arg = cmd.Arg{Name: "replyTo"} + var rarg cmd.Arg + var ridx cmd.Arg = cmd.Arg{Name: "replyToIdx", Value: replyToIdx} + + if replyToIdx == 0 { + rtype.Value = "post" + rarg.Name = "post" + rarg.Value = *m.activePost + } else { + rtype.Value = "reply" + rarg.Name = "reply" + rarg.Value = *m.allReplies[(replyToIdx - 1)] + } + + cmd := cmd.New(cmd.WinOpen, postcreate.WIN_ID, rtype, rarg, ridx) + cmds = append(cmds, cmd.Tea()) + + m.ctx.Logger.Debugln("caching view") + m.ctx.Logger.Debugf("buffer: %s", m.buffer) + // m.viewcache = m.buildView(false) + + return true, cmds +} + +func handleNumberKeys(mi interface{}, n int8) (bool, []tea.Cmd) { + var m *Model = mi.(*Model) + var cmds []tea.Cmd + + m.buffer += strconv.Itoa(int(n)) + + return false, cmds +} + +func handleUncaughtKeys(mi interface{}, k tea.KeyMsg) (bool, []tea.Cmd) { + var m *Model = mi.(*Model) + var cmds []tea.Cmd + + m.buffer = "" + + return false, cmds +} + +func handleViewResize(mi interface{}) (bool, []tea.Cmd) { + var m *Model = mi.(*Model) + var cmds []tea.Cmd + + m.ctx.Logger.Debugf("received WindowSizeMsg: %vx%v\n", m.tk.ViewWidth(), m.tk.ViewHeight()) + viewportWidth := m.tk.ViewWidth() - 2 + viewportHeight := m.tk.ViewHeight() - 5 + + viewportStyle.Width(viewportWidth) + viewportStyle.Height(viewportHeight) + m.viewport = viewport.New(viewportWidth-4, viewportHeight-4) + m.viewport.Width = viewportWidth - 4 + m.viewport.Height = viewportHeight + 1 + // cmds = append(cmds, viewport.Sync(m.viewport)) + + return false, cmds +} + +func handleWinOpenCmd(mi interface{}, c cmd.Command) (bool, []tea.Cmd) { + var m *Model = mi.(*Model) + var cmds []tea.Cmd + + if c.Target == WIN_ID { + m.ctx.Logger.Debug("got own WinOpen command") + m.activePost = c.GetArg("post").(*post.Post) + m.viewport.SetContent(m.renderViewport(m.activePost)) + m.ctx.Logger.Debugf("loading post: %v", m.activePost.ID) + m.ctx.Loading = true + cmds = append(cmds, m.loadPost(m.activePost)) + return true, cmds + } + + return false, cmds +} + +func handleWinFreshDataCmd(mi interface{}, c cmd.Command) (bool, []tea.Cmd) { + var m *Model = mi.(*Model) + var cmds []tea.Cmd + + if c.Target == WIN_ID || + c.Target == "*" { + m.ctx.Logger.Debug("got *post.Post") + m.activePost = c.GetArg("post").(*post.Post) + m.viewport.SetContent(m.renderViewport(m.activePost)) + m.ctx.Loading = false + return true, cmds + } + + return false, cmds +} + func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { var cmds []tea.Cmd @@ -80,105 +209,6 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, tea.Batch(cmds...) } - switch msg := msg.(type) { - case tea.KeyMsg: - switch { - - case key.Matches(msg, m.tk.KeymapGet("reply")): - var replyToIdx int = 0 - var err error - - // m.viewcache = m.buildView(false) - m.tk.CacheView(&m) - - if m.buffer != "" { - replyToIdx, err = strconv.Atoi(m.buffer) - - if err != nil { - // TODO: Handle error - } - - if replyToIdx >= len(m.replyIDs) { - // TODO: Handle error - } - } - - m.ctx.Logger.Debugf("replyToIdx: %d", replyToIdx) - var rtype cmd.Arg = cmd.Arg{Name: "replyTo"} - var rarg cmd.Arg - var ridx cmd.Arg = cmd.Arg{Name: "replyToIdx", Value: replyToIdx} - - if replyToIdx == 0 { - rtype.Value = "post" - rarg.Name = "post" - rarg.Value = *m.activePost - } else { - rtype.Value = "reply" - rarg.Name = "reply" - rarg.Value = *m.allReplies[(replyToIdx - 1)] - } - - cmd := cmd.New(cmd.WinOpen, postcreate.WIN_ID, rtype, rarg, ridx) - cmds = append(cmds, cmd.Tea()) - - m.ctx.Logger.Debugln("caching view") - m.ctx.Logger.Debugf("buffer: %s", m.buffer) - // m.viewcache = m.buildView(false) - - return m, tea.Batch(cmds...) - - default: - switch msg.String() { - case "1", "2", "3", "4", "5", "6", "7", "8", "9", "0": - m.buffer += msg.String() - return m, nil - default: - m.buffer = "" - } - } - - case tea.WindowSizeMsg: - m.ctx.Logger.Debugf("received WindowSizeMsg: %vx%v\n", m.tk.ViewWidth(), m.tk.ViewHeight()) - viewportWidth := m.tk.ViewWidth() - 2 - viewportHeight := m.tk.ViewHeight() - 5 - - viewportStyle.Width(viewportWidth) - viewportStyle.Height(viewportHeight) - m.viewport = viewport.New(viewportWidth-4, viewportHeight-4) - m.viewport.Width = viewportWidth - 4 - m.viewport.Height = viewportHeight + 1 - // cmds = append(cmds, viewport.Sync(m.viewport)) - - case cmd.Command: - m.ctx.Logger.Debugf("got command: %v\n", msg) - switch msg.Call { - case cmd.WinOpen, cmd.WinRefreshData: - if msg.Target == WIN_ID { - m.ctx.Logger.Debug("got own WinOpen command") - m.activePost = msg.GetArg("post").(*post.Post) - m.viewport.SetContent(m.renderViewport(m.activePost)) - m.ctx.Logger.Debugf("loading post: %v", m.activePost.ID) - m.ctx.Loading = true - return m, m.loadPost(m.activePost) - } - return m, nil - case cmd.WinFreshData: - if msg.Target == WIN_ID || - msg.Target == "*" { - m.ctx.Logger.Debug("got *post.Post") - m.activePost = msg.GetArg("post").(*post.Post) - m.viewport.SetContent(m.renderViewport(m.activePost)) - m.ctx.Loading = false - return m, nil - } - default: - m.ctx.Logger.Debugf("received unhandled command: %v\n", msg) - } - - default: - m.ctx.Logger.Debugf("received unhandled msg: %v\n", msg) - } - var cmd tea.Cmd m.viewport, cmd = m.viewport.Update(msg)