1
0
mirror of https://github.com/taigrr/wtf synced 2026-04-02 02:28:55 -07:00

Revert "Update dependencies"

This commit is contained in:
Chris Cummer
2018-11-13 12:39:57 -08:00
committed by GitHub
parent 53efd9c18f
commit 705993dd65
96 changed files with 1866 additions and 2551 deletions

View File

@@ -1,34 +1,25 @@
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.
screen tcell.Screen
// Indicates whether the application's screen is currently active. This is
// false during suspended mode.
// Indicates whether the application's screen is currently active.
running bool
// The primitive which currently has the keyboard focus.
@@ -53,23 +44,13 @@ type Application struct {
// was drawn.
afterDraw func(screen tcell.Screen)
// 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()
// A channel which signals the end of the suspended mode.
suspendToken chan struct{}
// Halts the event loop during suspended mode.
suspendMutex sync.Mutex
}
// NewApplication creates and returns a new application.
func NewApplication() *Application {
return &Application{
events: make(chan tcell.Event, queueSize),
updates: make(chan func(), queueSize),
suspendToken: make(chan struct{}, 1),
}
return &Application{}
}
// SetInputCapture sets a function which captures all key events before they are
@@ -153,105 +134,65 @@ func (a *Application) Run() error {
// Draw the screen for the first time.
a.Unlock()
a.draw()
// Separate loop to wait for screen events.
var wg sync.WaitGroup
wg.Add(1)
a.suspendToken <- struct{}{} // We need this to get started.
go func() {
defer wg.Done()
for range a.suspendToken {
for {
a.RLock()
screen := a.screen
a.RUnlock()
if screen == nil {
// We have no screen. We might need to stop.
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).
a.RLock()
running := a.running
a.RUnlock()
if running {
// The application was stopped. End the event loop.
a.QueueEvent(nil)
return
}
// We're in suspended mode (running is false). Pause and wait for new
// token.
break
}
}
}()
a.Draw()
// Start event loop.
EventLoop:
for {
select {
case event := <-a.events:
if event == nil {
break EventLoop
// 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.
}
}
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 {
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()
screen.Clear()
a.draw()
// Ctrl-C closes the application.
if event.Key() == tcell.KeyCtrlC {
a.Stop()
}
// If we have updates, now is the time to execute them.
case updater := <-a.updates:
updater()
// 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()
screen.Clear()
a.Draw()
}
}
a.running = false
close(a.suspendToken)
wg.Wait()
return nil
}
@@ -259,13 +200,12 @@ EventLoop:
func (a *Application) Stop() {
a.Lock()
defer a.Unlock()
screen := a.screen
if screen == nil {
if a.screen == nil {
return
}
a.screen.Fini()
a.screen = nil
screen.Fini()
// a.running is still true, the main loop will clean up.
a.running = false
}
// Suspend temporarily suspends the application by exiting terminal UI mode and
@@ -276,26 +216,32 @@ func (a *Application) Stop() {
// 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()
a.RLock()
screen := a.screen
if screen == nil {
if a.screen == nil {
// Screen has not yet been initialized.
a.Unlock()
a.RUnlock()
return false
}
// Enter suspended mode. Make a new screen here already so our event loop can
// continue.
a.screen = nil
a.running = false
screen.Fini()
a.Unlock()
// 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)
}
}()
// Wait for "f" to return.
f()
// Initialize our new screen and draw the contents.
// Make a new screen and redraw.
a.Lock()
var err error
a.screen, err = tcell.NewScreen()
@@ -309,26 +255,15 @@ func (a *Application) Suspend(f func()) bool {
}
a.running = true
a.Unlock()
a.draw()
a.suspendToken <- struct{}{}
// One key event will get lost, see https://github.com/gdamore/tcell/issues/194
a.Draw()
// Continue application loop.
return true
}
// 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.
// 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.QueueUpdate(func() {
a.draw()
})
return a
}
// draw actually does what Draw() promises to do.
func (a *Application) draw() *Application {
a.Lock()
defer a.Unlock()
@@ -469,35 +404,3 @@ 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
}