gemini-browser

A text-based gemini browser
git clone git://git.laack.co/gemini-browser.git
Log | Files | Refs | README

application.go (25173B)


      1 package tview
      2 
      3 import (
      4 	"strings"
      5 	"sync"
      6 	"time"
      7 
      8 	"github.com/gdamore/tcell/v2"
      9 )
     10 
     11 const (
     12 	// The size of the event/update/redraw channels.
     13 	queueSize = 100
     14 
     15 	// The minimum time between two consecutive redraws.
     16 	redrawPause = 50 * time.Millisecond
     17 )
     18 
     19 // DoubleClickInterval specifies the maximum time between clicks to register a
     20 // double click rather than click.
     21 var DoubleClickInterval = 500 * time.Millisecond
     22 
     23 // MouseAction indicates one of the actions the mouse is logically doing.
     24 type MouseAction int16
     25 
     26 // Available mouse actions.
     27 const (
     28 	MouseMove MouseAction = iota
     29 	MouseLeftDown
     30 	MouseLeftUp
     31 	MouseLeftClick
     32 	MouseLeftDoubleClick
     33 	MouseMiddleDown
     34 	MouseMiddleUp
     35 	MouseMiddleClick
     36 	MouseMiddleDoubleClick
     37 	MouseRightDown
     38 	MouseRightUp
     39 	MouseRightClick
     40 	MouseRightDoubleClick
     41 	MouseScrollUp
     42 	MouseScrollDown
     43 	MouseScrollLeft
     44 	MouseScrollRight
     45 
     46 	// The following special value will not be provided as a mouse action but
     47 	// indicate that an overridden mouse event was consumed. See
     48 	// [Box.SetMouseCapture] for details.
     49 	MouseConsumed
     50 )
     51 
     52 // queuedUpdate represented the execution of f queued by
     53 // Application.QueueUpdate(). If "done" is not nil, it receives exactly one
     54 // element after f has executed.
     55 type queuedUpdate struct {
     56 	f    func()
     57 	done chan struct{}
     58 }
     59 
     60 // Application represents the top node of an application.
     61 //
     62 // It is not strictly required to use this class as none of the other classes
     63 // depend on it. However, it provides useful tools to set up an application and
     64 // plays nicely with all widgets.
     65 //
     66 // The following command displays a primitive p on the screen until Ctrl-C is
     67 // pressed:
     68 //
     69 //	if err := tview.NewApplication().SetRoot(p, true).Run(); err != nil {
     70 //	    panic(err)
     71 //	}
     72 type Application struct {
     73 	sync.RWMutex
     74 
     75 	// The application's screen. Apart from Run(), this variable should never be
     76 	// set directly. Always use the screenReplacement channel after calling
     77 	// Fini(), to set a new screen (or nil to stop the application).
     78 	screen tcell.Screen
     79 
     80 	// The application's title. If not empty, it will be set on every new screen
     81 	// that is added.
     82 	title string
     83 
     84 	// The primitive which currently has the keyboard focus.
     85 	focus Primitive
     86 
     87 	// The root primitive to be seen on the screen.
     88 	root Primitive
     89 
     90 	// Whether or not the application resizes the root primitive.
     91 	rootFullscreen bool
     92 
     93 	// Set to true if mouse events are enabled.
     94 	enableMouse bool
     95 
     96 	// Set to true if paste events are enabled.
     97 	enablePaste bool
     98 
     99 	// An optional capture function which receives a key event and returns the
    100 	// event to be forwarded to the default input handler (nil if nothing should
    101 	// be forwarded).
    102 	inputCapture func(event *tcell.EventKey) *tcell.EventKey
    103 
    104 	// An optional callback function which is invoked just before the root
    105 	// primitive is drawn.
    106 	beforeDraw func(screen tcell.Screen) bool
    107 
    108 	// An optional callback function which is invoked after the root primitive
    109 	// was drawn.
    110 	afterDraw func(screen tcell.Screen)
    111 
    112 	// Used to send screen events from separate goroutine to main event loop
    113 	events chan tcell.Event
    114 
    115 	// Functions queued from goroutines, used to serialize updates to primitives.
    116 	updates chan queuedUpdate
    117 
    118 	// An object that the screen variable will be set to after Fini() was called.
    119 	// Use this channel to set a new screen object for the application
    120 	// (screen.Init() and draw() will be called implicitly). A value of nil will
    121 	// stop the application.
    122 	screenReplacement chan tcell.Screen
    123 
    124 	// An optional capture function which receives a mouse event and returns the
    125 	// event to be forwarded to the default mouse handler (nil if nothing should
    126 	// be forwarded).
    127 	mouseCapture func(event *tcell.EventMouse, action MouseAction) (*tcell.EventMouse, MouseAction)
    128 
    129 	mouseCapturingPrimitive Primitive        // A Primitive returned by a MouseHandler which will capture future mouse events.
    130 	lastMouseX, lastMouseY  int              // The last position of the mouse.
    131 	mouseDownX, mouseDownY  int              // The position of the mouse when its button was last pressed.
    132 	lastMouseClick          time.Time        // The time when a mouse button was last clicked.
    133 	lastMouseButtons        tcell.ButtonMask // The last mouse button state.
    134 }
    135 
    136 // NewApplication creates and returns a new application.
    137 func NewApplication() *Application {
    138 	return &Application{
    139 		events:            make(chan tcell.Event, queueSize),
    140 		updates:           make(chan queuedUpdate, queueSize),
    141 		screenReplacement: make(chan tcell.Screen, 1),
    142 	}
    143 }
    144 
    145 // SetInputCapture sets a function which captures all key events before they are
    146 // forwarded to the key event handler of the primitive which currently has
    147 // focus. This function can then choose to forward that key event (or a
    148 // different one) by returning it or stop the key event processing by returning
    149 // nil.
    150 //
    151 // The only default global key event is Ctrl-C which stops the application. It
    152 // requires special handling:
    153 //
    154 //   - If you do not wish to change the default behavior, return the original
    155 //     event object passed to your input capture function.
    156 //   - If you wish to block Ctrl-C from any functionality, return nil.
    157 //   - If you do not wish Ctrl-C to stop the application but still want to
    158 //     forward the Ctrl-C event to primitives down the hierarchy, return a new
    159 //     key event with the same key and modifiers, e.g.
    160 //     tcell.NewEventKey(tcell.KeyCtrlC, 0, tcell.ModNone).
    161 //
    162 // Pasted key events are not forwarded to the input capture function if pasting
    163 // is enabled (see [Application.EnablePaste]).
    164 func (a *Application) SetInputCapture(capture func(event *tcell.EventKey) *tcell.EventKey) *Application {
    165 	a.inputCapture = capture
    166 	return a
    167 }
    168 
    169 // GetInputCapture returns the function installed with SetInputCapture() or nil
    170 // if no such function has been installed.
    171 func (a *Application) GetInputCapture() func(event *tcell.EventKey) *tcell.EventKey {
    172 	return a.inputCapture
    173 }
    174 
    175 // SetMouseCapture sets a function which captures mouse events (consisting of
    176 // the original tcell mouse event and the semantic mouse action) before they are
    177 // forwarded to the appropriate mouse event handler. This function can then
    178 // choose to forward that event (or a different one) by returning it or stop
    179 // the event processing by returning a nil mouse event. In such a case, the
    180 // event is considered consumed and the screen will be redrawn.
    181 func (a *Application) SetMouseCapture(capture func(event *tcell.EventMouse, action MouseAction) (*tcell.EventMouse, MouseAction)) *Application {
    182 	a.mouseCapture = capture
    183 	return a
    184 }
    185 
    186 // GetMouseCapture returns the function installed with SetMouseCapture() or nil
    187 // if no such function has been installed.
    188 func (a *Application) GetMouseCapture() func(event *tcell.EventMouse, action MouseAction) (*tcell.EventMouse, MouseAction) {
    189 	return a.mouseCapture
    190 }
    191 
    192 // SetScreen allows you to provide your own tcell.Screen object. For most
    193 // applications, this is not needed and you should be familiar with
    194 // tcell.Screen when using this function. As the tcell.Screen interface may
    195 // change in the future, you may need to update your code when this package
    196 // updates to a new tcell version.
    197 //
    198 // This function is typically called before the first call to Run(). Init() need
    199 // not be called on the screen.
    200 func (a *Application) SetScreen(screen tcell.Screen) *Application {
    201 	if screen == nil {
    202 		return a // Invalid input. Do nothing.
    203 	}
    204 
    205 	a.Lock()
    206 	if a.screen == nil {
    207 		// Run() has not been called yet.
    208 		a.screen = screen
    209 		a.Unlock()
    210 		screen.Init()
    211 		return a
    212 	}
    213 
    214 	// Run() is already in progress. Exchange screen.
    215 	oldScreen := a.screen
    216 	a.Unlock()
    217 	oldScreen.Fini()
    218 	a.screenReplacement <- screen
    219 
    220 	return a
    221 }
    222 
    223 // SetTitle sets the title of the terminal window, to the extent that the
    224 // terminal supports it. A non-empty title will be set on every new tcell.Screen
    225 // that is created by or added to this application.
    226 func (a *Application) SetTitle(title string) *Application {
    227 	a.Lock()
    228 	defer a.Unlock()
    229 	a.title = title
    230 	if a.screen != nil {
    231 		a.screen.SetTitle(title)
    232 	}
    233 	return a
    234 }
    235 
    236 // EnableMouse enables mouse events or disables them (if "false" is provided).
    237 func (a *Application) EnableMouse(enable bool) *Application {
    238 	a.Lock()
    239 	defer a.Unlock()
    240 	if enable != a.enableMouse && a.screen != nil {
    241 		if enable {
    242 			a.screen.EnableMouse()
    243 		} else {
    244 			a.screen.DisableMouse()
    245 		}
    246 	}
    247 	a.enableMouse = enable
    248 	return a
    249 }
    250 
    251 // EnablePaste enables the capturing of paste events or disables them (if
    252 // "false" is provided). This must be supported by the terminal.
    253 //
    254 // Widgets won't interpret paste events for navigation or selection purposes.
    255 // Paste events are typically only used to insert a block of text into an
    256 // [InputField] or a [TextArea].
    257 func (a *Application) EnablePaste(enable bool) *Application {
    258 	a.Lock()
    259 	defer a.Unlock()
    260 	if enable != a.enablePaste && a.screen != nil {
    261 		if enable {
    262 			a.screen.EnablePaste()
    263 		} else {
    264 			a.screen.DisablePaste()
    265 		}
    266 	}
    267 	a.enablePaste = enable
    268 	return a
    269 }
    270 
    271 // Run starts the application and thus the event loop. This function returns
    272 // when [Application.Stop] was called.
    273 //
    274 // Note that while an application is running, it fully claims stdin, stdout, and
    275 // stderr. If you use these standard streams, they may not work as expected.
    276 // Consider stopping the application first or suspending it (using
    277 // [Application.Suspend]) if you have to interact with the standard streams, for
    278 // example when needing to print a call stack during a panic.
    279 func (a *Application) Run() error {
    280 	var (
    281 		err, appErr error
    282 		lastRedraw  time.Time   // The time the screen was last redrawn.
    283 		redrawTimer *time.Timer // A timer to schedule the next redraw.
    284 	)
    285 	a.Lock()
    286 
    287 	// Make a screen if there is none yet.
    288 	if a.screen == nil {
    289 		a.screen, err = tcell.NewScreen()
    290 		if err != nil {
    291 			a.Unlock()
    292 			return err
    293 		}
    294 		if err = a.screen.Init(); err != nil {
    295 			a.Unlock()
    296 			return err
    297 		}
    298 		if a.enableMouse {
    299 			a.screen.EnableMouse()
    300 		} else {
    301 			a.screen.DisableMouse()
    302 		}
    303 		if a.enablePaste {
    304 			a.screen.EnablePaste()
    305 		} else {
    306 			a.screen.DisablePaste()
    307 		}
    308 		if a.title != "" {
    309 			a.screen.SetTitle(a.title)
    310 		}
    311 	}
    312 
    313 	// We catch panics to clean up because they mess up the terminal.
    314 	defer func() {
    315 		if p := recover(); p != nil {
    316 			if a.screen != nil {
    317 				a.screen.Fini()
    318 			}
    319 			panic(p)
    320 		}
    321 	}()
    322 
    323 	// Draw the screen for the first time.
    324 	a.Unlock()
    325 	a.draw()
    326 
    327 	// Separate loop to wait for screen events.
    328 	var wg sync.WaitGroup
    329 	wg.Add(1)
    330 	go func() {
    331 		defer wg.Done()
    332 		for {
    333 			a.RLock()
    334 			screen := a.screen
    335 			a.RUnlock()
    336 			if screen == nil {
    337 				// We have no screen. Let's stop.
    338 				a.QueueEvent(nil)
    339 				break
    340 			}
    341 
    342 			// Wait for next event and queue it.
    343 			event := screen.PollEvent()
    344 			if event != nil {
    345 				// Regular event. Queue.
    346 				a.QueueEvent(event)
    347 				continue
    348 			}
    349 
    350 			// A screen was finalized (event is nil). Wait for a new screen.
    351 			screen = <-a.screenReplacement
    352 			if screen == nil {
    353 				// No new screen. We're done.
    354 				a.QueueEvent(nil) // Stop the event loop.
    355 				return
    356 			}
    357 
    358 			// We have a new screen. Keep going.
    359 			a.Lock()
    360 			a.screen = screen
    361 			enableMouse := a.enableMouse
    362 			enablePaste := a.enablePaste
    363 			a.Unlock()
    364 
    365 			// Initialize and draw this screen.
    366 			if err := screen.Init(); err != nil {
    367 				panic(err)
    368 			}
    369 			if enableMouse {
    370 				screen.EnableMouse()
    371 			} else {
    372 				screen.DisableMouse()
    373 			}
    374 			if enablePaste {
    375 				screen.EnablePaste()
    376 			} else {
    377 				screen.DisablePaste()
    378 			}
    379 			if a.title != "" {
    380 				screen.SetTitle(a.title)
    381 			}
    382 			a.draw()
    383 		}
    384 	}()
    385 
    386 	// Start event loop.
    387 	var (
    388 		pasteBuffer strings.Builder
    389 		pasting     bool // Set to true while we receive paste key events.
    390 	)
    391 EventLoop:
    392 	for {
    393 		select {
    394 		// If we received an event, handle it.
    395 		case event := <-a.events:
    396 			if event == nil {
    397 				break EventLoop
    398 			}
    399 
    400 			switch event := event.(type) {
    401 			case *tcell.EventKey:
    402 				// If we are pasting, collect runes, nothing else.
    403 				if pasting {
    404 					switch event.Key() {
    405 					case tcell.KeyRune:
    406 						pasteBuffer.WriteRune(event.Rune())
    407 					case tcell.KeyEnter:
    408 						pasteBuffer.WriteRune('\n')
    409 					case tcell.KeyTab:
    410 						pasteBuffer.WriteRune('\t')
    411 					}
    412 					break
    413 				}
    414 
    415 				a.RLock()
    416 				root := a.root
    417 				inputCapture := a.inputCapture
    418 				a.RUnlock()
    419 
    420 				// Intercept keys.
    421 				var draw bool
    422 				originalEvent := event
    423 				if inputCapture != nil {
    424 					event = inputCapture(event)
    425 					if event == nil {
    426 						a.draw()
    427 						break // Don't forward event.
    428 					}
    429 					draw = true
    430 				}
    431 
    432 				// Ctrl-C closes the application.
    433 				if event == originalEvent && event.Key() == tcell.KeyCtrlC {
    434 					a.Stop()
    435 					break
    436 				}
    437 
    438 				// Pass other key events to the root primitive.
    439 				if root != nil && root.HasFocus() {
    440 					if handler := root.InputHandler(); handler != nil {
    441 						handler(event, func(p Primitive) {
    442 							a.SetFocus(p)
    443 						})
    444 						draw = true
    445 					}
    446 				}
    447 
    448 				// Redraw.
    449 				if draw {
    450 					a.draw()
    451 				}
    452 			case *tcell.EventPaste:
    453 				if !a.enablePaste {
    454 					break
    455 				}
    456 				if event.Start() {
    457 					pasting = true
    458 					pasteBuffer.Reset()
    459 				} else if event.End() {
    460 					pasting = false
    461 					a.RLock()
    462 					root := a.root
    463 					a.RUnlock()
    464 					if root != nil && root.HasFocus() && pasteBuffer.Len() > 0 {
    465 						// Pass paste event to the root primitive.
    466 						if handler := root.PasteHandler(); handler != nil {
    467 							handler(pasteBuffer.String(), func(p Primitive) {
    468 								a.SetFocus(p)
    469 							})
    470 						}
    471 
    472 						// Redraw.
    473 						a.draw()
    474 					}
    475 				}
    476 			case *tcell.EventResize:
    477 				if time.Since(lastRedraw) < redrawPause {
    478 					if redrawTimer != nil {
    479 						redrawTimer.Stop()
    480 					}
    481 					redrawTimer = time.AfterFunc(redrawPause, func() {
    482 						a.events <- event
    483 					})
    484 				}
    485 				a.RLock()
    486 				screen := a.screen
    487 				a.RUnlock()
    488 				if screen == nil {
    489 					break
    490 				}
    491 				lastRedraw = time.Now()
    492 				screen.Clear()
    493 				a.draw()
    494 			case *tcell.EventMouse:
    495 				consumed, isMouseDownAction := a.fireMouseActions(event)
    496 				if consumed {
    497 					a.draw()
    498 				}
    499 				a.lastMouseButtons = event.Buttons()
    500 				if isMouseDownAction {
    501 					a.mouseDownX, a.mouseDownY = event.Position()
    502 				}
    503 			case *tcell.EventError:
    504 				appErr = event
    505 				a.Stop()
    506 			}
    507 
    508 		// If we have updates, now is the time to execute them.
    509 		case update := <-a.updates:
    510 			update.f()
    511 			if update.done != nil {
    512 				update.done <- struct{}{}
    513 			}
    514 		}
    515 	}
    516 
    517 	// Wait for the event loop to finish.
    518 	wg.Wait()
    519 	a.screen = nil
    520 
    521 	return appErr
    522 }
    523 
    524 // fireMouseActions analyzes the provided mouse event, derives mouse actions
    525 // from it and then forwards them to the corresponding primitives.
    526 func (a *Application) fireMouseActions(event *tcell.EventMouse) (consumed, isMouseDownAction bool) {
    527 	// We want to relay follow-up events to the same target primitive.
    528 	var targetPrimitive Primitive
    529 
    530 	// Helper function to fire a mouse action.
    531 	fire := func(action MouseAction) {
    532 		switch action {
    533 		case MouseLeftDown, MouseMiddleDown, MouseRightDown:
    534 			isMouseDownAction = true
    535 		}
    536 
    537 		// Intercept event.
    538 		if a.mouseCapture != nil {
    539 			event, action = a.mouseCapture(event, action)
    540 			if event == nil {
    541 				consumed = true
    542 				return // Don't forward event.
    543 			}
    544 		}
    545 
    546 		// Determine the target primitive.
    547 		var primitive, capturingPrimitive Primitive
    548 		if a.mouseCapturingPrimitive != nil {
    549 			primitive = a.mouseCapturingPrimitive
    550 			targetPrimitive = a.mouseCapturingPrimitive
    551 		} else if targetPrimitive != nil {
    552 			primitive = targetPrimitive
    553 		} else {
    554 			primitive = a.root
    555 		}
    556 		if primitive != nil {
    557 			if handler := primitive.MouseHandler(); handler != nil {
    558 				var wasConsumed bool
    559 				wasConsumed, capturingPrimitive = handler(action, event, func(p Primitive) {
    560 					a.SetFocus(p)
    561 				})
    562 				if wasConsumed {
    563 					consumed = true
    564 				}
    565 			}
    566 		}
    567 		a.mouseCapturingPrimitive = capturingPrimitive
    568 	}
    569 
    570 	x, y := event.Position()
    571 	buttons := event.Buttons()
    572 	clickMoved := x != a.mouseDownX || y != a.mouseDownY
    573 	buttonChanges := buttons ^ a.lastMouseButtons
    574 
    575 	if x != a.lastMouseX || y != a.lastMouseY {
    576 		fire(MouseMove)
    577 		a.lastMouseX = x
    578 		a.lastMouseY = y
    579 	}
    580 
    581 	for _, buttonEvent := range []struct {
    582 		button                  tcell.ButtonMask
    583 		down, up, click, dclick MouseAction
    584 	}{
    585 		{tcell.ButtonPrimary, MouseLeftDown, MouseLeftUp, MouseLeftClick, MouseLeftDoubleClick},
    586 		{tcell.ButtonMiddle, MouseMiddleDown, MouseMiddleUp, MouseMiddleClick, MouseMiddleDoubleClick},
    587 		{tcell.ButtonSecondary, MouseRightDown, MouseRightUp, MouseRightClick, MouseRightDoubleClick},
    588 	} {
    589 		if buttonChanges&buttonEvent.button != 0 {
    590 			if buttons&buttonEvent.button != 0 {
    591 				fire(buttonEvent.down)
    592 			} else {
    593 				fire(buttonEvent.up) // A user override might set event to nil.
    594 				if !clickMoved && event != nil {
    595 					if a.lastMouseClick.Add(DoubleClickInterval).Before(time.Now()) {
    596 						fire(buttonEvent.click)
    597 						a.lastMouseClick = time.Now()
    598 					} else {
    599 						fire(buttonEvent.dclick)
    600 						a.lastMouseClick = time.Time{} // reset
    601 					}
    602 				}
    603 			}
    604 		}
    605 	}
    606 
    607 	for _, wheelEvent := range []struct {
    608 		button tcell.ButtonMask
    609 		action MouseAction
    610 	}{
    611 		{tcell.WheelUp, MouseScrollUp},
    612 		{tcell.WheelDown, MouseScrollDown},
    613 		{tcell.WheelLeft, MouseScrollLeft},
    614 		{tcell.WheelRight, MouseScrollRight}} {
    615 		if buttons&wheelEvent.button != 0 {
    616 			fire(wheelEvent.action)
    617 		}
    618 	}
    619 
    620 	return consumed, isMouseDownAction
    621 }
    622 
    623 // Stop stops the application, causing Run() to return.
    624 func (a *Application) Stop() {
    625 	a.Lock()
    626 	defer a.Unlock()
    627 	screen := a.screen
    628 	if screen == nil {
    629 		return
    630 	}
    631 	a.screen = nil
    632 	screen.Fini()
    633 	a.screenReplacement <- nil
    634 }
    635 
    636 // Suspend temporarily suspends the application by exiting terminal UI mode and
    637 // invoking the provided function "f". When "f" returns, terminal UI mode is
    638 // entered again and the application resumes.
    639 //
    640 // A return value of true indicates that the application was suspended and "f"
    641 // was called. If false is returned, the application was already suspended,
    642 // terminal UI mode was not exited, and "f" was not called.
    643 func (a *Application) Suspend(f func()) bool {
    644 	a.RLock()
    645 	screen := a.screen
    646 	a.RUnlock()
    647 	if screen == nil {
    648 		return false // Screen has not yet been initialized.
    649 	}
    650 
    651 	// Enter suspended mode.
    652 	if err := screen.Suspend(); err != nil {
    653 		return false // Suspension failed.
    654 	}
    655 
    656 	// Wait for "f" to return.
    657 	f()
    658 
    659 	// If the screen object has changed in the meantime, we need to do more.
    660 	a.RLock()
    661 	defer a.RUnlock()
    662 	if a.screen != screen {
    663 		// Calling Stop() while in suspend mode currently still leads to a
    664 		// panic, see https://github.com/gdamore/tcell/issues/440.
    665 		screen.Fini()
    666 		if a.screen == nil {
    667 			return true // If stop was called (a.screen is nil), we're done already.
    668 		}
    669 	} else {
    670 		// It hasn't changed. Resume.
    671 		screen.Resume() // Not much we can do in case of an error.
    672 	}
    673 
    674 	// Continue application loop.
    675 	return true
    676 }
    677 
    678 // Draw refreshes the screen (during the next update cycle). It calls the Draw()
    679 // function of the application's root primitive and then syncs the screen
    680 // buffer. It is almost never necessary to call this function. It can actually
    681 // deadlock your application if you call it from the main thread (e.g. in a
    682 // callback function of a widget). Please see
    683 // https://github.com/rivo/tview/wiki/Concurrency for details.
    684 func (a *Application) Draw() *Application {
    685 	a.QueueUpdate(func() {
    686 		a.draw()
    687 	})
    688 	return a
    689 }
    690 
    691 // ForceDraw refreshes the screen immediately. Use this function with caution as
    692 // it may lead to race conditions with updates to primitives in other
    693 // goroutines. It is always preferable to call [Application.Draw] instead.
    694 // Never call this function from a goroutine.
    695 //
    696 // It is safe to call this function during queued updates and direct event
    697 // handling.
    698 func (a *Application) ForceDraw() *Application {
    699 	return a.draw()
    700 }
    701 
    702 // draw actually does what Draw() promises to do.
    703 func (a *Application) draw() *Application {
    704 	a.Lock()
    705 	defer a.Unlock()
    706 
    707 	screen := a.screen
    708 	root := a.root
    709 	fullscreen := a.rootFullscreen
    710 	before := a.beforeDraw
    711 	after := a.afterDraw
    712 
    713 	// Maybe we're not ready yet or not anymore.
    714 	if screen == nil || root == nil {
    715 		return a
    716 	}
    717 
    718 	// Resize if requested.
    719 	if fullscreen { // root is not nil here.
    720 		width, height := screen.Size()
    721 		root.SetRect(0, 0, width, height)
    722 	}
    723 
    724 	// Clear screen to remove unwanted artifacts from the previous cycle.
    725 	screen.Clear()
    726 
    727 	// Call before handler if there is one.
    728 	if before != nil {
    729 		if before(screen) {
    730 			screen.Show()
    731 			return a
    732 		}
    733 	}
    734 
    735 	// Draw all primitives.
    736 	root.Draw(screen)
    737 
    738 	// Call after handler if there is one.
    739 	if after != nil {
    740 		after(screen)
    741 	}
    742 
    743 	// Sync screen.
    744 	screen.Show()
    745 
    746 	return a
    747 }
    748 
    749 // Sync forces a full re-sync of the screen buffer with the actual screen during
    750 // the next event cycle. This is useful for when the terminal screen is
    751 // corrupted so you may want to offer your users a keyboard shortcut to refresh
    752 // the screen.
    753 func (a *Application) Sync() *Application {
    754 	a.updates <- queuedUpdate{f: func() {
    755 		a.RLock()
    756 		screen := a.screen
    757 		a.RUnlock()
    758 		if screen == nil {
    759 			return
    760 		}
    761 		screen.Sync()
    762 	}}
    763 	return a
    764 }
    765 
    766 // SetBeforeDrawFunc installs a callback function which is invoked just before
    767 // the root primitive is drawn during screen updates. If the function returns
    768 // true, drawing will not continue, i.e. the root primitive will not be drawn
    769 // (and an after-draw-handler will not be called).
    770 //
    771 // Note that the screen is not cleared by the application. To clear the screen,
    772 // you may call screen.Clear().
    773 //
    774 // Provide nil to uninstall the callback function.
    775 func (a *Application) SetBeforeDrawFunc(handler func(screen tcell.Screen) bool) *Application {
    776 	a.beforeDraw = handler
    777 	return a
    778 }
    779 
    780 // GetBeforeDrawFunc returns the callback function installed with
    781 // SetBeforeDrawFunc() or nil if none has been installed.
    782 func (a *Application) GetBeforeDrawFunc() func(screen tcell.Screen) bool {
    783 	return a.beforeDraw
    784 }
    785 
    786 // SetAfterDrawFunc installs a callback function which is invoked after the root
    787 // primitive was drawn during screen updates.
    788 //
    789 // Provide nil to uninstall the callback function.
    790 func (a *Application) SetAfterDrawFunc(handler func(screen tcell.Screen)) *Application {
    791 	a.afterDraw = handler
    792 	return a
    793 }
    794 
    795 // GetAfterDrawFunc returns the callback function installed with
    796 // SetAfterDrawFunc() or nil if none has been installed.
    797 func (a *Application) GetAfterDrawFunc() func(screen tcell.Screen) {
    798 	return a.afterDraw
    799 }
    800 
    801 // SetRoot sets the root primitive for this application. If "fullscreen" is set
    802 // to true, the root primitive's position will be changed to fill the screen.
    803 //
    804 // This function must be called at least once or nothing will be displayed when
    805 // the application starts.
    806 //
    807 // It also calls SetFocus() on the primitive.
    808 func (a *Application) SetRoot(root Primitive, fullscreen bool) *Application {
    809 	a.Lock()
    810 	a.root = root
    811 	a.rootFullscreen = fullscreen
    812 	if a.screen != nil {
    813 		a.screen.Clear()
    814 	}
    815 	a.Unlock()
    816 
    817 	a.SetFocus(root)
    818 
    819 	return a
    820 }
    821 
    822 // ResizeToFullScreen resizes the given primitive such that it fills the entire
    823 // screen.
    824 func (a *Application) ResizeToFullScreen(p Primitive) *Application {
    825 	a.RLock()
    826 	width, height := a.screen.Size()
    827 	a.RUnlock()
    828 	p.SetRect(0, 0, width, height)
    829 	return a
    830 }
    831 
    832 // SetFocus sets the focus to a new primitive. All key events will be directed
    833 // down the hierarchy (starting at the root) until a primitive handles them,
    834 // which per default goes towards the focused primitive.
    835 //
    836 // Blur() will be called on the previously focused primitive. Focus() will be
    837 // called on the new primitive.
    838 func (a *Application) SetFocus(p Primitive) *Application {
    839 	a.Lock()
    840 	if a.focus != nil {
    841 		a.focus.Blur()
    842 	}
    843 	a.focus = p
    844 	if a.screen != nil {
    845 		a.screen.HideCursor()
    846 	}
    847 	a.Unlock()
    848 	if p != nil {
    849 		p.Focus(func(p Primitive) {
    850 			a.SetFocus(p)
    851 		})
    852 	}
    853 
    854 	return a
    855 }
    856 
    857 // GetFocus returns the primitive which has the current focus. If none has it,
    858 // nil is returned.
    859 func (a *Application) GetFocus() Primitive {
    860 	a.RLock()
    861 	defer a.RUnlock()
    862 	return a.focus
    863 }
    864 
    865 // QueueUpdate is used to synchronize access to primitives from non-main
    866 // goroutines. The provided function will be executed as part of the event loop
    867 // and thus will not cause race conditions with other such update functions or
    868 // the Draw() function.
    869 //
    870 // Note that Draw() is not implicitly called after the execution of f as that
    871 // may not be desirable. You can call Draw() from f if the screen should be
    872 // refreshed after each update. Alternatively, use QueueUpdateDraw() to follow
    873 // up with an immediate refresh of the screen.
    874 //
    875 // This function returns after f has executed.
    876 func (a *Application) QueueUpdate(f func()) *Application {
    877 	ch := make(chan struct{})
    878 	a.updates <- queuedUpdate{f: f, done: ch}
    879 	<-ch
    880 	return a
    881 }
    882 
    883 // QueueUpdateDraw works like QueueUpdate() except it refreshes the screen
    884 // immediately after executing f.
    885 func (a *Application) QueueUpdateDraw(f func()) *Application {
    886 	a.QueueUpdate(func() {
    887 		f()
    888 		a.draw()
    889 	})
    890 	return a
    891 }
    892 
    893 // QueueEvent sends an event to the Application event loop.
    894 //
    895 // It is not recommended for event to be nil.
    896 func (a *Application) QueueEvent(event tcell.Event) *Application {
    897 	a.events <- event
    898 	return a
    899 }