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 }