application.go 21 KB

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