mirror of
https://github.com/taigrr/wtf
synced 2026-04-02 09:28:47 -07:00
Update dependencies to latest versions
This commit is contained in:
337
vendor/github.com/rivo/tview/application.go
generated
vendored
337
vendor/github.com/rivo/tview/application.go
generated
vendored
@@ -1,27 +1,34 @@
|
||||
package tview
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/gdamore/tcell"
|
||||
)
|
||||
|
||||
// The size of the event/update/redraw channels.
|
||||
const queueSize = 100
|
||||
|
||||
// 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.
|
||||
//
|
||||
// The following command displays a primitive p on the screen until Ctrl-C is
|
||||
// pressed:
|
||||
//
|
||||
// if err := tview.NewApplication().SetRoot(p, true).Run(); err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
type Application struct {
|
||||
sync.RWMutex
|
||||
|
||||
// The application's screen.
|
||||
// The application's screen. Apart from Run(), this variable should never be
|
||||
// set directly. Always use the screenReplacement channel after calling
|
||||
// Fini(), to set a new screen (or nil to stop the application).
|
||||
screen tcell.Screen
|
||||
|
||||
// Indicates whether the application's screen is currently active.
|
||||
running bool
|
||||
|
||||
// The primitive which currently has the keyboard focus.
|
||||
focus Primitive
|
||||
|
||||
@@ -44,13 +51,26 @@ type Application struct {
|
||||
// was drawn.
|
||||
afterDraw func(screen tcell.Screen)
|
||||
|
||||
// Halts the event loop during suspended mode.
|
||||
suspendMutex sync.Mutex
|
||||
// Used to send screen events from separate goroutine to main event loop
|
||||
events chan tcell.Event
|
||||
|
||||
// Functions queued from goroutines, used to serialize updates to primitives.
|
||||
updates chan func()
|
||||
|
||||
// An object that the screen variable will be set to after Fini() was called.
|
||||
// Use this channel to set a new screen object for the application
|
||||
// (screen.Init() and draw() will be called implicitly). A value of nil will
|
||||
// stop the application.
|
||||
screenReplacement chan tcell.Screen
|
||||
}
|
||||
|
||||
// NewApplication creates and returns a new application.
|
||||
func NewApplication() *Application {
|
||||
return &Application{}
|
||||
return &Application{
|
||||
events: make(chan tcell.Event, queueSize),
|
||||
updates: make(chan func(), queueSize),
|
||||
screenReplacement: make(chan tcell.Screen, 1),
|
||||
}
|
||||
}
|
||||
|
||||
// SetInputCapture sets a function which captures all key events before they are
|
||||
@@ -75,29 +95,29 @@ func (a *Application) GetInputCapture() func(event *tcell.EventKey) *tcell.Event
|
||||
|
||||
// SetScreen allows you to provide your own tcell.Screen object. For most
|
||||
// applications, this is not needed and you should be familiar with
|
||||
// tcell.Screen when using this function. Run() will call Init() and Fini() on
|
||||
// the provided screen object.
|
||||
// tcell.Screen when using this function.
|
||||
//
|
||||
// This function is typically called before calling Run(). Calling it while an
|
||||
// application is running will switch the application to the new screen. Fini()
|
||||
// will be called on the old screen and Init() on the new screen (errors
|
||||
// returned by Init() will lead to a panic).
|
||||
//
|
||||
// Note that calling Suspend() will invoke Fini() on your screen object and it
|
||||
// will not be restored when suspended mode ends. Instead, a new default screen
|
||||
// object will be created.
|
||||
// This function is typically called before the first call to Run(). Init() need
|
||||
// not be called on the screen.
|
||||
func (a *Application) SetScreen(screen tcell.Screen) *Application {
|
||||
if screen == nil {
|
||||
return a // Invalid input. Do nothing.
|
||||
}
|
||||
|
||||
a.Lock()
|
||||
defer a.Unlock()
|
||||
if a.running {
|
||||
a.screen.Fini()
|
||||
}
|
||||
a.screen = screen
|
||||
if a.running {
|
||||
if err := a.screen.Init(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if a.screen == nil {
|
||||
// Run() has not been called yet.
|
||||
a.screen = screen
|
||||
a.Unlock()
|
||||
return a
|
||||
}
|
||||
|
||||
// Run() is already in progress. Exchange screen.
|
||||
oldScreen := a.screen
|
||||
a.Unlock()
|
||||
oldScreen.Fini()
|
||||
a.screenReplacement <- screen
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
@@ -114,12 +134,11 @@ func (a *Application) Run() error {
|
||||
a.Unlock()
|
||||
return err
|
||||
}
|
||||
if err = a.screen.Init(); err != nil {
|
||||
a.Unlock()
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err = a.screen.Init(); err != nil {
|
||||
a.Unlock()
|
||||
return err
|
||||
}
|
||||
a.running = true
|
||||
|
||||
// We catch panics to clean up because they mess up the terminal.
|
||||
defer func() {
|
||||
@@ -127,72 +146,118 @@ func (a *Application) Run() error {
|
||||
if a.screen != nil {
|
||||
a.screen.Fini()
|
||||
}
|
||||
a.running = false
|
||||
panic(p)
|
||||
}
|
||||
}()
|
||||
|
||||
// Draw the screen for the first time.
|
||||
a.Unlock()
|
||||
a.Draw()
|
||||
a.draw()
|
||||
|
||||
// Start event loop.
|
||||
for {
|
||||
// Do not poll events during suspend mode
|
||||
a.suspendMutex.Lock()
|
||||
a.RLock()
|
||||
screen := a.screen
|
||||
a.RUnlock()
|
||||
if screen == nil {
|
||||
a.suspendMutex.Unlock()
|
||||
break
|
||||
}
|
||||
|
||||
// Wait for next event.
|
||||
event := a.screen.PollEvent()
|
||||
a.suspendMutex.Unlock()
|
||||
if event == nil {
|
||||
// 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.
|
||||
}
|
||||
}
|
||||
|
||||
// Ctrl-C closes the application.
|
||||
if event.Key() == tcell.KeyCtrlC {
|
||||
a.Stop()
|
||||
}
|
||||
|
||||
// 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.EventResize:
|
||||
// Separate loop to wait for screen events.
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for {
|
||||
a.RLock()
|
||||
screen := a.screen
|
||||
a.RUnlock()
|
||||
screen.Clear()
|
||||
a.Draw()
|
||||
if screen == nil {
|
||||
// We have no screen. Let's stop.
|
||||
a.QueueEvent(nil)
|
||||
break
|
||||
}
|
||||
|
||||
// Wait for next event and queue it.
|
||||
event := screen.PollEvent()
|
||||
if event != nil {
|
||||
// Regular event. Queue.
|
||||
a.QueueEvent(event)
|
||||
continue
|
||||
}
|
||||
|
||||
// A screen was finalized (event is nil). Wait for a new scren.
|
||||
screen = <-a.screenReplacement
|
||||
if screen == nil {
|
||||
// No new screen. We're done.
|
||||
a.QueueEvent(nil)
|
||||
return
|
||||
}
|
||||
|
||||
// We have a new screen. Keep going.
|
||||
a.Lock()
|
||||
a.screen = screen
|
||||
a.Unlock()
|
||||
|
||||
// Initialize and draw this screen.
|
||||
if err := screen.Init(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
a.draw()
|
||||
}
|
||||
}()
|
||||
|
||||
// Start event loop.
|
||||
EventLoop:
|
||||
for {
|
||||
select {
|
||||
case event := <-a.events:
|
||||
if event == nil {
|
||||
break EventLoop
|
||||
}
|
||||
|
||||
switch event := event.(type) {
|
||||
case *tcell.EventKey:
|
||||
a.RLock()
|
||||
p := a.focus
|
||||
inputCapture := a.inputCapture
|
||||
a.RUnlock()
|
||||
|
||||
// Intercept keys.
|
||||
if inputCapture != nil {
|
||||
event = inputCapture(event)
|
||||
if event == nil {
|
||||
a.draw()
|
||||
continue // Don't forward event.
|
||||
}
|
||||
}
|
||||
|
||||
// Ctrl-C closes the application.
|
||||
if event.Key() == tcell.KeyCtrlC {
|
||||
a.Stop()
|
||||
}
|
||||
|
||||
// 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.EventResize:
|
||||
a.RLock()
|
||||
screen := a.screen
|
||||
a.RUnlock()
|
||||
if screen == nil {
|
||||
continue
|
||||
}
|
||||
screen.Clear()
|
||||
a.draw()
|
||||
}
|
||||
|
||||
// If we have updates, now is the time to execute them.
|
||||
case updater := <-a.updates:
|
||||
updater()
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for the event loop to finish.
|
||||
wg.Wait()
|
||||
a.screen = nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -200,12 +265,13 @@ func (a *Application) Run() error {
|
||||
func (a *Application) Stop() {
|
||||
a.Lock()
|
||||
defer a.Unlock()
|
||||
if a.screen == nil {
|
||||
screen := a.screen
|
||||
if screen == nil {
|
||||
return
|
||||
}
|
||||
a.screen.Fini()
|
||||
a.screen = nil
|
||||
a.running = false
|
||||
screen.Fini()
|
||||
a.screenReplacement <- nil
|
||||
}
|
||||
|
||||
// Suspend temporarily suspends the application by exiting terminal UI mode and
|
||||
@@ -217,53 +283,54 @@ func (a *Application) Stop() {
|
||||
// terminal UI mode was not exited, and "f" was not called.
|
||||
func (a *Application) Suspend(f func()) bool {
|
||||
a.RLock()
|
||||
|
||||
if a.screen == nil {
|
||||
// Screen has not yet been initialized.
|
||||
a.RUnlock()
|
||||
return false
|
||||
screen := a.screen
|
||||
a.RUnlock()
|
||||
if screen == nil {
|
||||
return false // Screen has not yet been initialized.
|
||||
}
|
||||
|
||||
// Enter suspended mode.
|
||||
a.suspendMutex.Lock()
|
||||
defer a.suspendMutex.Unlock()
|
||||
a.RUnlock()
|
||||
a.Stop()
|
||||
|
||||
// Deal with panics during suspended mode. Exit the program.
|
||||
defer func() {
|
||||
if p := recover(); p != nil {
|
||||
fmt.Println(p)
|
||||
os.Exit(1)
|
||||
}
|
||||
}()
|
||||
screen.Fini()
|
||||
|
||||
// Wait for "f" to return.
|
||||
f()
|
||||
|
||||
// Make a new screen and redraw.
|
||||
a.Lock()
|
||||
// Make a new screen.
|
||||
var err error
|
||||
a.screen, err = tcell.NewScreen()
|
||||
screen, err = tcell.NewScreen()
|
||||
if err != nil {
|
||||
a.Unlock()
|
||||
panic(err)
|
||||
}
|
||||
if err = a.screen.Init(); err != nil {
|
||||
a.Unlock()
|
||||
panic(err)
|
||||
}
|
||||
a.running = true
|
||||
a.Unlock()
|
||||
a.Draw()
|
||||
a.screenReplacement <- screen
|
||||
// One key event will get lost, see https://github.com/gdamore/tcell/issues/194
|
||||
|
||||
// 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.
|
||||
// Draw refreshes the screen (during the next update cycle). It calls the Draw()
|
||||
// function of the application's root primitive and then syncs the screen
|
||||
// buffer.
|
||||
func (a *Application) Draw() *Application {
|
||||
a.QueueUpdate(func() {
|
||||
a.draw()
|
||||
})
|
||||
return a
|
||||
}
|
||||
|
||||
// ForceDraw refreshes the screen immediately. Use this function with caution as
|
||||
// it may lead to race conditions with updates to primitives in other
|
||||
// goroutines. It is always preferrable to use Draw() instead. Never call this
|
||||
// function from a goroutine.
|
||||
//
|
||||
// It is safe to call this function during queued updates and direct event
|
||||
// handling.
|
||||
func (a *Application) ForceDraw() *Application {
|
||||
return a.draw()
|
||||
}
|
||||
|
||||
// draw actually does what Draw() promises to do.
|
||||
func (a *Application) draw() *Application {
|
||||
a.Lock()
|
||||
defer a.Unlock()
|
||||
|
||||
@@ -404,3 +471,35 @@ func (a *Application) GetFocus() Primitive {
|
||||
defer a.RUnlock()
|
||||
return a.focus
|
||||
}
|
||||
|
||||
// QueueUpdate is used to synchronize access to primitives from non-main
|
||||
// goroutines. The provided function will be executed as part of the event loop
|
||||
// and thus will not cause race conditions with other such update functions or
|
||||
// the Draw() function.
|
||||
//
|
||||
// Note that Draw() is not implicitly called after the execution of f as that
|
||||
// may not be desirable. You can call Draw() from f if the screen should be
|
||||
// refreshed after each update. Alternatively, use QueueUpdateDraw() to follow
|
||||
// up with an immediate refresh of the screen.
|
||||
func (a *Application) QueueUpdate(f func()) *Application {
|
||||
a.updates <- f
|
||||
return a
|
||||
}
|
||||
|
||||
// QueueUpdateDraw works like QueueUpdate() except it refreshes the screen
|
||||
// immediately after executing f.
|
||||
func (a *Application) QueueUpdateDraw(f func()) *Application {
|
||||
a.QueueUpdate(func() {
|
||||
f()
|
||||
a.draw()
|
||||
})
|
||||
return a
|
||||
}
|
||||
|
||||
// QueueEvent sends an event to the Application event loop.
|
||||
//
|
||||
// It is not recommended for event to be nil.
|
||||
func (a *Application) QueueEvent(event tcell.Event) *Application {
|
||||
a.events <- event
|
||||
return a
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user