From 673752658f7692a098b3292344cda3be6c44919e Mon Sep 17 00:00:00 2001 From: Christian Rocha Date: Fri, 19 Jun 2020 14:14:15 -0400 Subject: [PATCH] Comments and cleanup --- examples/pager/main.go | 28 +++++++++++++++++++++------- renderer.go | 19 +++++++++++++++++++ tea.go | 6 ++++-- 3 files changed, 44 insertions(+), 9 deletions(-) diff --git a/examples/pager/main.go b/examples/pager/main.go index a0f9072..7ac99e0 100644 --- a/examples/pager/main.go +++ b/examples/pager/main.go @@ -34,12 +34,14 @@ func main() { os.Exit(1) } - // Set PAGER_LOG to a path to log to a file. For example, + // Set PAGER_LOG to a path to log to a file. For example: // // export PAGER_LOG=debug.log // - if os.Getenv("PAGER_LOG") != "" { - p := os.Getenv("PAGER_LOG") + // This becomes handy when debugging stuff since you can't debug to stdout + // because the UI is occupying it! + p := os.Getenv("PAGER_LOG") + if p != "" { f, err := tea.LogToFile(p, "pager") if err != nil { fmt.Printf("Could not open file %s: %v", p, err) @@ -48,7 +50,7 @@ func main() { defer f.Close() } - // Use the full size of the terminal in its "Alternate Screen Buffer" + // Use the full size of the terminal in its "alternate screen buffer" tea.AltScreen() defer tea.ExitAltScreen() @@ -71,7 +73,9 @@ type model struct { func initialize(content string) func() (tea.Model, tea.Cmd) { return func() (tea.Model, tea.Cmd) { return model{ - content: content, // keep content in the model + // Store content in the model so we can hand it off to the viewport + // later. + content: content, }, nil } } @@ -86,6 +90,7 @@ func update(msg tea.Msg, mdl tea.Model) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: + // Ctrl+c exits if msg.Type == tea.KeyCtrlC { return m, tea.Quit } @@ -94,6 +99,11 @@ func update(msg tea.Msg, mdl tea.Model) (tea.Model, tea.Cmd) { verticalMargins := headerHeight + footerHeight if !m.ready { + // Since this program is using the full size of the viewport we need + // to wait until we've received the window dimensions before we + // can initialize the viewport. The initial dimensions come in + // quickly, though asynchronously, which is why we wait for them + // here. m.viewport = viewport.NewModel(msg.Width, msg.Height-verticalMargins) m.viewport.YPosition = headerHeight m.viewport.HighPerformanceRendering = useHighPerformanceRenderer @@ -104,8 +114,11 @@ func update(msg tea.Msg, mdl tea.Model) (tea.Model, tea.Cmd) { m.viewport.Height = msg.Height - verticalMargins } - // Render (or re-render) the whole viewport if useHighPerformanceRenderer { + // Render (or re-render) the whole viewport. Necessary both to + // initialize the viewport and when the window is resized. + // + // This is needed for high-performance rendering only. cmds = append(cmds, viewport.Sync(m.viewport)) } } @@ -115,6 +128,7 @@ func update(msg tea.Msg, mdl tea.Model) (tea.Model, tea.Cmd) { // // * Recieves messages from the Bubble Tea runtime // * Returns commands to the Bubble Tea runtime + // m.viewport, cmd = viewport.Update(msg, m.viewport) if useHighPerformanceRenderer { cmds = append(cmds, cmd) @@ -143,7 +157,7 @@ func view(mdl tea.Model) string { footerTop = strings.Repeat(" ", gapSize) + footerTop footerMid = strings.Repeat("─", gapSize) + footerMid footerBot = strings.Repeat(" ", gapSize) + footerBot - footer := footerTop + "\n" + footerMid + "\n" + footerBot + footer := fmt.Sprintf("%s\n%s\n%s", footerTop, footerMid, footerBot) return fmt.Sprintf("%s\n%s\n%s", header, viewport.View(m.viewport), footer) } diff --git a/renderer.go b/renderer.go index 65a6835..6cc86f6 100644 --- a/renderer.go +++ b/renderer.go @@ -61,6 +61,7 @@ func (r *renderer) stop() { r.done <- struct{}{} } +// listen waits for ticks on the ticker, or a signal to stop the renderer. func (r *renderer) listen() { for { select { @@ -279,6 +280,11 @@ type syncScrollAreaMsg struct { bottomBoundary int } +// SyncScrollArea performs a paint of the entire scroll area. This is required +// to initialize the scrollable region and should also be called on resize +// (WindowSizeMsg). +// +// For high-performance, scroll-based rendering only. func SyncScrollArea(lines []string, topBoundary int, bottomBoundary int) Cmd { return func() Msg { return syncScrollAreaMsg{ @@ -291,6 +297,10 @@ func SyncScrollArea(lines []string, topBoundary int, bottomBoundary int) Cmd { type clearScrollAreaMsg struct{} +// ClearScrollArea deallocates the scrollable region and returns the control of +// those lines to the main rendering routine. +// +// For high-performance, scroll-based rendering only. func ClearScrollArea() Msg { return clearScrollAreaMsg{} } @@ -301,6 +311,10 @@ type scrollUpMsg struct { bottomBoundary int } +// ScrollUp adds lines to the top of the scrollable region, pushing lines below +// down. Lines that are pushed out the scrollable region disappear from view. +// +// For high-performance, scroll-based rendering only. func ScrollUp(newLines []string, topBoundary, bottomBoundary int) Cmd { return func() Msg { return scrollUpMsg{ @@ -317,6 +331,11 @@ type scrollDownMsg struct { bottomBoundary int } +// ScrollDown adds lines to the bottom of the scrollable region, pushing lines +// above up. Lines that are pushed out of the scrollable region disappear from +// view. +// +// For high-performance, scroll-based rendering only. func ScrollDown(newLines []string, topBoundary, bottomBoundary int) Cmd { return func() Msg { return scrollDownMsg{ diff --git a/tea.go b/tea.go index b00358a..44fe716 100644 --- a/tea.go +++ b/tea.go @@ -8,13 +8,15 @@ import ( ) // Msg represents an action and is usually the result of an IO operation. It's -// triggers the Update function, and henceforth, the UI. +// triggers the Update function, and henceforth, the UI. type Msg interface{} // Model contains the program's state. type Model interface{} -// Cmd is an IO operation. If it's nil it's considered a no-op. +// Cmd is an IO operation. If it's nil it's considered a no-op. Keep in mind +// that there's almost never a need to use a command to send a message to +// another part of your program. type Cmd func() Msg // Batch peforms a bunch of commands concurrently with no ordering guarantees