moonmath/tui/tui.go

100 lines
2.3 KiB
Go

package tui
import (
"fmt"
"code.humancabbage.net/sam/moonmath/coindesk"
"code.humancabbage.net/sam/moonmath/config"
"code.humancabbage.net/sam/moonmath/tui/asset"
"code.humancabbage.net/sam/moonmath/tui/perf"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
)
type Model struct {
assets []asset.Model
stats perf.Model
displayStats bool
}
func New(assets []coindesk.Asset, cfg config.Root, displayStats bool) (m Model) {
m.stats = perf.New()
m.displayStats = displayStats
// construct models for each asset, but remove dupes
seen := map[coindesk.Asset]struct{}{}
for _, a := range assets {
if _, ok := seen[a]; ok {
continue
}
assetCfg := cfg.ForAsset(a)
assetModel := asset.New(assetCfg)
m.assets = append(m.assets, assetModel)
seen[a] = struct{}{}
}
return
}
func (m Model) Init() tea.Cmd {
// initialize child models, collecting their commands,
// then return them all in a batch
var inits []tea.Cmd
for i := range m.assets {
cmd := m.assets[i].Init()
inits = append(inits, cmd)
}
return tea.Batch(inits...)
}
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.stats.AddUpdate()
switch msg := msg.(type) {
// handle keys for quitting
case tea.KeyMsg:
switch msg.String() {
case "ctrl+c", "q", "esc":
return m, tea.Quit
}
// forward asset messages to the appropriate model
case asset.Msg:
cmd := m.forward(msg.Asset, msg)
return m, cmd
// forward any other message to each child model.
// typically, this is for animation.
default:
var commands []tea.Cmd
for i := range m.assets {
var cmd tea.Cmd
m.assets[i], cmd = m.assets[i].Update(msg)
commands = append(commands, cmd)
}
return m, tea.Batch(commands...)
}
return m, nil
}
func (m Model) View() string {
m.stats.AddView()
var ss []string
for i := range m.assets {
s := m.assets[i].View()
ss = append(ss, s)
}
if m.displayStats {
ss = append(ss, m.stats.View())
}
r := lipgloss.JoinVertical(lipgloss.Center, ss...)
return r
}
func (m Model) forward(a coindesk.Asset, msg tea.Msg) (cmd tea.Cmd) {
// O(n) is fine when n is small
for i := range m.assets {
if !m.assets[i].Handles(a) {
continue
}
m.assets[i], cmd = m.assets[i].Update(msg)
return
}
panic(fmt.Errorf("rogue message: %v", msg))
}