From 6a0489592ffec29a82178da12759d84ca22a51cd Mon Sep 17 00:00:00 2001 From: Christian Rocha Date: Sat, 18 Jan 2020 22:18:19 -0500 Subject: [PATCH] Programs should take an init type/function as the first argument This is in line with the way Elm works. Also update examples. --- examples/fullscreen/main.go | 6 +++++- examples/input/go.mod | 5 ----- examples/input/go.sum | 6 ------ examples/input/main.go | 12 ++++++++---- examples/simple/main.go | 6 +++++- examples/views/main.go | 8 +++++++- tea.go | 26 +++++++++++++++++++------- 7 files changed, 44 insertions(+), 25 deletions(-) delete mode 100644 examples/input/go.mod delete mode 100644 examples/input/go.sum diff --git a/examples/fullscreen/main.go b/examples/fullscreen/main.go index 780bebd..870be2f 100644 --- a/examples/fullscreen/main.go +++ b/examples/fullscreen/main.go @@ -17,12 +17,16 @@ type tickMsg struct{} func main() { tea.Fullscreen() defer tea.ExitFullscreen() - err := tea.NewProgram(model(5), update, view, []tea.Sub{tick}).Start() + err := tea.NewProgram(initialize, update, view, []tea.Sub{tick}).Start() if err != nil { log.Fatal(err) } } +func initialize() (tea.Model, tea.Cmd) { + return model(5), nil +} + func update(message tea.Msg, mdl tea.Model) (tea.Model, tea.Cmd) { m, _ := mdl.(model) diff --git a/examples/input/go.mod b/examples/input/go.mod deleted file mode 100644 index 3730734..0000000 --- a/examples/input/go.mod +++ /dev/null @@ -1,5 +0,0 @@ -module input-example - -go 1.13 - -require github.com/charmbracelet/teaparty v0.0.0-20200118155738-c83a0bee59b9 // indirect diff --git a/examples/input/go.sum b/examples/input/go.sum deleted file mode 100644 index 5de1746..0000000 --- a/examples/input/go.sum +++ /dev/null @@ -1,6 +0,0 @@ -github.com/charmbracelet/tea v0.0.0-20200118154546-df52853f9d94 h1:m2xhUqOw6OcefbPBR9Il0J0n0gB1663NoKU+vvkiLdU= -github.com/charmbracelet/tea v0.0.0-20200118154546-df52853f9d94/go.mod h1:lijy1lXOKNwMjBu/jTT/DvR8yE9PhtX2olGFsCz9/Vk= -github.com/charmbracelet/teaparty v0.0.0-20200118155738-c83a0bee59b9 h1:YQvJgppGVexnzIJ+KJlK9lBYA3+zXfdqZO/5Ngedtb0= -github.com/charmbracelet/teaparty v0.0.0-20200118155738-c83a0bee59b9/go.mod h1:z8JWtuxM0oA+dZfi7BkgBW2YGbyOTbWAixFs46W3SK4= -github.com/pkg/term v0.0.0-20190109203006-aa71e9d9e942 h1:A7GG7zcGjl3jqAqGPmcNjd/D9hzL95SuoOQAaFNdLU0= -github.com/pkg/term v0.0.0-20190109203006-aa71e9d9e942/go.mod h1:eCbImbZ95eXtAUIbLAuAVnBnwf83mjf6QIVH8SHYwqQ= diff --git a/examples/input/main.go b/examples/input/main.go index 626228b..9296ba6 100644 --- a/examples/input/main.go +++ b/examples/input/main.go @@ -22,10 +22,7 @@ func main() { tea.UseSysLog("tea") p := tea.NewProgram( - Model{ - Input: input.DefaultModel(), - Error: nil, - }, + initialize, update, view, []tea.Sub{ @@ -46,6 +43,13 @@ func main() { } } +func initialize() (tea.Model, tea.Cmd) { + return Model{ + Input: input.DefaultModel(), + Error: nil, + }, nil +} + func update(msg tea.Msg, model tea.Model) (tea.Model, tea.Cmd) { var cmd tea.Cmd m, ok := model.(Model) diff --git a/examples/simple/main.go b/examples/simple/main.go index 8d84abd..9baeb5e 100644 --- a/examples/simple/main.go +++ b/examples/simple/main.go @@ -21,12 +21,16 @@ type TickMsg struct{} func main() { // Initialize our program - p := tea.NewProgram(Model(5), update, view, []tea.Sub{tick}) + p := tea.NewProgram(initialize, update, view, []tea.Sub{tick}) if err := p.Start(); err != nil { log.Fatal(err) } } +func initialize() (tea.Model, tea.Cmd) { + return Model(5), nil +} + // Update is called when messages are recived. The idea is that you inspect // the message and update the model (or send back a new one) accordingly. You // can also return a commmand, which is a function that peforms I/O and diff --git a/examples/views/main.go b/examples/views/main.go index 9518a7b..af968cf 100644 --- a/examples/views/main.go +++ b/examples/views/main.go @@ -28,7 +28,7 @@ type frameMsg struct{} func main() { p := tea.NewProgram( - Model{0, false, 10, 0, 0, false}, + initialize, update, view, []tea.Sub{tick, frame}, @@ -38,6 +38,12 @@ func main() { } } +// INIT + +func initialize() (tea.Model, tea.Cmd) { + return Model{0, false, 10, 0, 0, false}, nil +} + // SUBSCRIPTIONS func tick(model tea.Model) tea.Msg { diff --git a/tea.go b/tea.go index c54af21..596f71c 100644 --- a/tea.go +++ b/tea.go @@ -22,6 +22,10 @@ type Cmd func() Msg // Sub is an event subscription. If it returns nil it's considered a no-op. type Sub func(Model) Msg +// Init is the first function that will be called. It returns your initial +// model and runs an optional command +type Init func() (Model, Cmd) + // Update is called when a message is received. It may update the model and/or // send a command. type Update func(Msg, Model) (Model, Cmd) @@ -31,7 +35,7 @@ type View func(Model) string // Program is a terminal user interface type Program struct { - model Model + init Init update Update view View subscriptions []Sub @@ -64,9 +68,9 @@ func Quit() Msg { type quitMsg struct{} // NewProgram creates a new Program -func NewProgram(model Model, update Update, view View, subs []Sub) *Program { +func NewProgram(init Init, update Update, view View, subs []Sub) *Program { return &Program{ - model: model, + init: init, update: update, view: view, subscriptions: subs, @@ -76,7 +80,7 @@ func NewProgram(model Model, update Update, view View, subs []Sub) *Program { // Start initializes the program func (p *Program) Start() error { var ( - model = p.model + model Model cmd Cmd cmds = make(chan Cmd) msgs = make(chan Msg) @@ -90,13 +94,21 @@ func (p *Program) Start() error { p.rw = tty tty.SetRaw() + hideCursor() defer func() { showCursor() tty.Restore() }() + // Initialize program + model, _ = p.init() + if cmd != nil { + go func() { + cmds <- cmd + }() + } + // Render initial view - hideCursor() p.render(model) // Subscribe to user input. We could move this out of here and offer it @@ -115,7 +127,7 @@ func (p *Program) Start() error { for _, sub := range p.subscriptions { go func(s Sub) { for { - msgs <- s(p.model) + msgs <- s(model) } }(sub) } @@ -148,9 +160,9 @@ func (p *Program) Start() error { } model, cmd = p.update(msg, model) + log.Println(model) cmds <- cmd // process command (if any) p.render(model) - p.model = model } } }