aboutsummaryrefslogtreecommitdiff
path: root/vendor/maunium.net/go/tview/application.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/maunium.net/go/tview/application.go')
-rw-r--r--vendor/maunium.net/go/tview/application.go408
1 files changed, 408 insertions, 0 deletions
diff --git a/vendor/maunium.net/go/tview/application.go b/vendor/maunium.net/go/tview/application.go
new file mode 100644
index 0000000..f3d6328
--- /dev/null
+++ b/vendor/maunium.net/go/tview/application.go
@@ -0,0 +1,408 @@
+package tview
+
+import (
+ "fmt"
+ "os"
+ "sync"
+
+ "maunium.net/go/tcell"
+)
+
+// Application represents the top node of an application.
+//
+// It is not strictly required to use this class as none of the other classes
+// depend on it. However, it provides useful tools to set up an application and
+// plays nicely with all widgets.
+type Application struct {
+ sync.RWMutex
+
+ // The application's screen.
+ screen tcell.Screen
+
+ // The primitive which currently has the keyboard focus.
+ focus Primitive
+
+ // The root primitive to be seen on the screen.
+ root Primitive
+
+ // Whether or not the application resizes the root primitive.
+ rootFullscreen bool
+
+ // An optional capture function which receives a key event and returns the
+ // event to be forwarded to the default input handler (nil if nothing should
+ // be forwarded).
+ inputCapture func(event *tcell.EventKey) *tcell.EventKey
+
+ // An optional capture function which receives a mouse event and returns the
+ // event to be forwarded to the default input handler (nil if nothing should
+ // be forwarded).
+ mouseCapture func(event *tcell.EventMouse) *tcell.EventMouse
+
+ // An optional callback function which is invoked just before the root
+ // primitive is drawn.
+ beforeDraw func(screen tcell.Screen) bool
+
+ // An optional callback function which is invoked after the root primitive
+ // was drawn.
+ afterDraw func(screen tcell.Screen)
+
+ // If this value is true, the application has entered suspended mode.
+ suspended bool
+}
+
+// NewApplication creates and returns a new application.
+func NewApplication() *Application {
+ return &Application{}
+}
+
+// SetInputCapture sets a function which captures all key events before they are
+// forwarded to the key event handler of the primitive which currently has
+// focus. This function can then choose to forward that key event (or a
+// different one) by returning it or stop the key event processing by returning
+// nil.
+//
+// Note that this also affects the default event handling of the application
+// itself: Such a handler can intercept the Ctrl-C event which closes the
+// applicatoon.
+func (a *Application) SetInputCapture(capture func(event *tcell.EventKey) *tcell.EventKey) *Application {
+ a.inputCapture = capture
+ return a
+}
+
+// GetInputCapture returns the function installed with SetInputCapture() or nil
+// if no such function has been installed.
+func (a *Application) GetInputCapture() func(event *tcell.EventKey) *tcell.EventKey {
+ return a.inputCapture
+}
+
+// SetMouseCapture sets a function which captures all mouse events before they are
+// forwarded to the mouse event handler of the primitive which currently has
+// focus. This function can then choose to forward that mouse event (or a
+// different one) by returning it or stop the key event processing by returning
+// nil.
+func (a *Application) SetMouseCapture(capture func(event *tcell.EventMouse) *tcell.EventMouse) *Application {
+ a.mouseCapture = capture
+ return a
+}
+
+// GetMouseCapture returns the function installed with SetMouseCapture() or nil
+// if no such function has been installed.
+func (a *Application) GetMouseCapture() func(event *tcell.EventMouse) *tcell.EventMouse {
+ return a.mouseCapture
+}
+
+func (a *Application) GetScreen() tcell.Screen {
+ return a.screen
+}
+
+// Run starts the application and thus the event loop. This function returns
+// when Stop() was called.
+func (a *Application) Run() error {
+ var err error
+ a.Lock()
+
+ // Make a screen.
+ a.screen, err = tcell.NewScreen()
+ if err != nil {
+ a.Unlock()
+ return err
+ }
+ if err = a.screen.Init(); err != nil {
+ a.Unlock()
+ return err
+ }
+ a.screen.EnableMouse()
+
+ // Draw the screen for the first time.
+ a.Unlock()
+ a.Draw()
+
+ // Start event loop.
+ for {
+ a.Lock()
+ screen := a.screen
+ if a.suspended {
+ a.suspended = false // Clear previous suspended flag.
+ }
+ a.Unlock()
+ if screen == nil {
+ break
+ }
+
+ // Wait for next event.
+ event := a.screen.PollEvent()
+ if event == nil {
+ a.Lock()
+ if a.suspended {
+ // This screen was renewed due to suspended mode.
+ a.suspended = false
+ a.Unlock()
+ continue // Resume.
+ }
+ a.Unlock()
+
+ // The screen was finalized. Exit the loop.
+ break
+ }
+
+ switch event := event.(type) {
+ case *tcell.EventKey:
+ a.RLock()
+ p := a.focus
+ a.RUnlock()
+
+ // Intercept keys.
+ if a.inputCapture != nil {
+ event = a.inputCapture(event)
+ if event == nil {
+ break // Don't forward event.
+ }
+ }
+
+ // Pass other key events to the currently focused primitive.
+ if p != nil {
+ if handler := p.InputHandler(); handler != nil {
+ handler(event, func(p Primitive) {
+ a.SetFocus(p)
+ })
+ a.Draw()
+ }
+ }
+ case *tcell.EventMouse:
+ a.RLock()
+ p := a.focus
+ a.RUnlock()
+
+ // Intercept keys.
+ if a.mouseCapture != nil {
+ event = a.mouseCapture(event)
+ if event == nil {
+ break // Don't forward event.
+ }
+ }
+
+ // Pass other key events to the currently focused primitive.
+ if p != nil {
+ if handler := p.MouseHandler(); handler != nil {
+ handler(event, func(p Primitive) {
+ a.SetFocus(p)
+ })
+ //a.Draw()
+ }
+ }
+ case *tcell.EventResize:
+ a.Lock()
+ screen := a.screen
+ a.Unlock()
+ screen.Clear()
+ a.Draw()
+ }
+ }
+
+ return nil
+}
+
+// Stop stops the application, causing Run() to return.
+func (a *Application) Stop() {
+ a.RLock()
+ defer a.RUnlock()
+ if a.screen == nil {
+ return
+ }
+ a.screen.Fini()
+ a.screen = nil
+}
+
+// Suspend temporarily suspends the application by exiting terminal UI mode and
+// invoking the provided function "f". When "f" returns, terminal UI mode is
+// entered again and the application resumes.
+//
+// A return value of true indicates that the application was suspended and "f"
+// was called. If false is returned, the application was already suspended,
+// terminal UI mode was not exited, and "f" was not called.
+func (a *Application) Suspend(f func()) bool {
+ a.Lock()
+
+ if a.suspended || a.screen == nil {
+ // Application is already suspended.
+ a.Unlock()
+ return false
+ }
+
+ // Enter suspended mode.
+ a.suspended = true
+ a.Unlock()
+ a.Stop()
+
+ // Deal with panics during suspended mode. Exit the program.
+ defer func() {
+ if p := recover(); p != nil {
+ fmt.Println(p)
+ os.Exit(1)
+ }
+ }()
+
+ // Wait for "f" to return.
+ f()
+
+ // Make a new screen and redraw.
+ a.Lock()
+ var err error
+ a.screen, err = tcell.NewScreen()
+ if err != nil {
+ a.Unlock()
+ panic(err)
+ }
+ if err = a.screen.Init(); err != nil {
+ a.Unlock()
+ panic(err)
+ }
+ a.Unlock()
+ a.Draw()
+
+ // Continue application loop.
+ return true
+}
+
+// Draw refreshes the screen. It calls the Draw() function of the application's
+// root primitive and then syncs the screen buffer.
+func (a *Application) Draw() *Application {
+ a.RLock()
+ screen := a.screen
+ root := a.root
+ fullscreen := a.rootFullscreen
+ before := a.beforeDraw
+ after := a.afterDraw
+ a.RUnlock()
+
+ // Maybe we're not ready yet or not anymore.
+ if screen == nil || root == nil {
+ return a
+ }
+
+ // Resize if requested.
+ if fullscreen && root != nil {
+ width, height := screen.Size()
+ root.SetRect(0, 0, width, height)
+ }
+
+ // Call before handler if there is one.
+ if before != nil {
+ if before(screen) {
+ screen.Show()
+ return a
+ }
+ }
+
+ // Draw all primitives.
+ root.Draw(screen)
+
+ // Call after handler if there is one.
+ if after != nil {
+ after(screen)
+ }
+
+ // Sync screen.
+ screen.Show()
+
+ return a
+}
+
+// SetBeforeDrawFunc installs a callback function which is invoked just before
+// the root primitive is drawn during screen updates. If the function returns
+// true, drawing will not continue, i.e. the root primitive will not be drawn
+// (and an after-draw-handler will not be called).
+//
+// Note that the screen is not cleared by the application. To clear the screen,
+// you may call screen.Clear().
+//
+// Provide nil to uninstall the callback function.
+func (a *Application) SetBeforeDrawFunc(handler func(screen tcell.Screen) bool) *Application {
+ a.beforeDraw = handler
+ return a
+}
+
+// GetBeforeDrawFunc returns the callback function installed with
+// SetBeforeDrawFunc() or nil if none has been installed.
+func (a *Application) GetBeforeDrawFunc() func(screen tcell.Screen) bool {
+ return a.beforeDraw
+}
+
+// SetAfterDrawFunc installs a callback function which is invoked after the root
+// primitive was drawn during screen updates.
+//
+// Provide nil to uninstall the callback function.
+func (a *Application) SetAfterDrawFunc(handler func(screen tcell.Screen)) *Application {
+ a.afterDraw = handler
+ return a
+}
+
+// GetAfterDrawFunc returns the callback function installed with
+// SetAfterDrawFunc() or nil if none has been installed.
+func (a *Application) GetAfterDrawFunc() func(screen tcell.Screen) {
+ return a.afterDraw
+}
+
+// SetRoot sets the root primitive for this application. If "fullscreen" is set
+// to true, the root primitive's position will be changed to fill the screen.
+//
+// This function must be called at least once or nothing will be displayed when
+// the application starts.
+//
+// It also calls SetFocus() on the primitive.
+func (a *Application) SetRoot(root Primitive, fullscreen bool) *Application {
+ a.Lock()
+ a.root = root
+ a.rootFullscreen = fullscreen
+ if a.screen != nil {
+ a.screen.Clear()
+ }
+ a.Unlock()
+
+ a.SetFocus(root)
+
+ return a
+}
+
+// ResizeToFullScreen resizes the given primitive such that it fills the entire
+// screen.
+func (a *Application) ResizeToFullScreen(p Primitive) *Application {
+ a.RLock()
+ width, height := a.screen.Size()
+ a.RUnlock()
+ p.SetRect(0, 0, width, height)
+ return a
+}
+
+// SetFocus sets the focus on a new primitive. All key events will be redirected
+// to that primitive. Callers must ensure that the primitive will handle key
+// events.
+//
+// Blur() will be called on the previously focused primitive. Focus() will be
+// called on the new primitive.
+func (a *Application) SetFocus(p Primitive) *Application {
+ a.Lock()
+ if a.focus != nil {
+ a.focus.Blur()
+ }
+ a.focus = p
+ if a.screen != nil {
+ a.screen.HideCursor()
+ }
+ a.Unlock()
+ if p != nil {
+ p.Focus(func(p Primitive) {
+ a.SetFocus(p)
+ })
+ }
+
+ return a
+}
+
+// GetFocus returns the primitive which has the current focus. If none has it,
+// nil is returned.
+func (a *Application) GetFocus() Primitive {
+ a.RLock()
+ defer a.RUnlock()
+ return a.focus
+}