application.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729
  1. package tview
  2. import (
  3. "sync"
  4. "time"
  5. "github.com/gdamore/tcell"
  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(). The "done" channel receives exactly one element
  40. // 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. // Note that this also affects the default event handling of the application
  115. // itself: Such a handler can intercept the Ctrl-C event which closes the
  116. // application.
  117. func (a *Application) SetInputCapture(capture func(event *tcell.EventKey) *tcell.EventKey) *Application {
  118. a.inputCapture = capture
  119. return a
  120. }
  121. // GetInputCapture returns the function installed with SetInputCapture() or nil
  122. // if no such function has been installed.
  123. func (a *Application) GetInputCapture() func(event *tcell.EventKey) *tcell.EventKey {
  124. return a.inputCapture
  125. }
  126. // SetMouseCapture sets a function which captures mouse events (consisting of
  127. // the original tcell mouse event and the semantic mouse action) before they are
  128. // forwarded to the appropriate mouse event handler. This function can then
  129. // choose to forward that event (or a different one) by returning it or stop
  130. // the event processing by returning a nil mouse event.
  131. func (a *Application) SetMouseCapture(capture func(event *tcell.EventMouse, action MouseAction) (*tcell.EventMouse, MouseAction)) *Application {
  132. a.mouseCapture = capture
  133. return a
  134. }
  135. // GetMouseCapture returns the function installed with SetMouseCapture() or nil
  136. // if no such function has been installed.
  137. func (a *Application) GetMouseCapture() func(event *tcell.EventMouse, action MouseAction) (*tcell.EventMouse, MouseAction) {
  138. return a.mouseCapture
  139. }
  140. // SetScreen allows you to provide your own tcell.Screen object. For most
  141. // applications, this is not needed and you should be familiar with
  142. // tcell.Screen when using this function.
  143. //
  144. // This function is typically called before the first call to Run(). Init() need
  145. // not be called on the screen.
  146. func (a *Application) SetScreen(screen tcell.Screen) *Application {
  147. if screen == nil {
  148. return a // Invalid input. Do nothing.
  149. }
  150. a.Lock()
  151. if a.screen == nil {
  152. // Run() has not been called yet.
  153. a.screen = screen
  154. a.Unlock()
  155. return a
  156. }
  157. // Run() is already in progress. Exchange screen.
  158. oldScreen := a.screen
  159. a.Unlock()
  160. oldScreen.Fini()
  161. a.screenReplacement <- screen
  162. return a
  163. }
  164. // EnableMouse enables mouse events.
  165. func (a *Application) EnableMouse(enable bool) *Application {
  166. a.Lock()
  167. defer a.Unlock()
  168. if enable != a.enableMouse && a.screen != nil {
  169. if enable {
  170. a.screen.EnableMouse()
  171. } else {
  172. a.screen.DisableMouse()
  173. }
  174. }
  175. a.enableMouse = enable
  176. return a
  177. }
  178. // Run starts the application and thus the event loop. This function returns
  179. // when Stop() was called.
  180. func (a *Application) Run() error {
  181. var (
  182. err error
  183. width, height int // The current size of the screen.
  184. lastRedraw time.Time // The time the screen was last redrawn.
  185. redrawTimer *time.Timer // A timer to schedule the next redraw.
  186. )
  187. a.Lock()
  188. // Make a screen if there is none yet.
  189. if a.screen == nil {
  190. a.screen, err = tcell.NewScreen()
  191. if err != nil {
  192. a.Unlock()
  193. return err
  194. }
  195. if err = a.screen.Init(); err != nil {
  196. a.Unlock()
  197. return err
  198. }
  199. if a.enableMouse {
  200. a.screen.EnableMouse()
  201. }
  202. }
  203. // We catch panics to clean up because they mess up the terminal.
  204. defer func() {
  205. if p := recover(); p != nil {
  206. if a.screen != nil {
  207. a.screen.Fini()
  208. }
  209. panic(p)
  210. }
  211. }()
  212. // Draw the screen for the first time.
  213. a.Unlock()
  214. a.draw()
  215. // Separate loop to wait for screen events.
  216. var wg sync.WaitGroup
  217. wg.Add(1)
  218. go func() {
  219. defer wg.Done()
  220. for {
  221. a.RLock()
  222. screen := a.screen
  223. a.RUnlock()
  224. if screen == nil {
  225. // We have no screen. Let's stop.
  226. a.QueueEvent(nil)
  227. break
  228. }
  229. // Wait for next event and queue it.
  230. event := screen.PollEvent()
  231. if event != nil {
  232. // Regular event. Queue.
  233. a.QueueEvent(event)
  234. continue
  235. }
  236. // A screen was finalized (event is nil). Wait for a new scren.
  237. screen = <-a.screenReplacement
  238. if screen == nil {
  239. // No new screen. We're done.
  240. a.QueueEvent(nil)
  241. return
  242. }
  243. // We have a new screen. Keep going.
  244. a.Lock()
  245. a.screen = screen
  246. a.Unlock()
  247. // Initialize and draw this screen.
  248. if err := screen.Init(); err != nil {
  249. panic(err)
  250. }
  251. a.draw()
  252. }
  253. }()
  254. // Start event loop.
  255. EventLoop:
  256. for {
  257. select {
  258. case event := <-a.events:
  259. if event == nil {
  260. break EventLoop
  261. }
  262. switch event := event.(type) {
  263. case *tcell.EventKey:
  264. a.RLock()
  265. p := a.focus
  266. inputCapture := a.inputCapture
  267. a.RUnlock()
  268. // Intercept keys.
  269. if inputCapture != nil {
  270. event = inputCapture(event)
  271. if event == nil {
  272. a.draw()
  273. continue // Don't forward event.
  274. }
  275. }
  276. // Ctrl-C closes the application.
  277. if event.Key() == tcell.KeyCtrlC {
  278. a.Stop()
  279. }
  280. // Pass other key events to the currently focused primitive.
  281. if p != nil {
  282. if handler := p.InputHandler(); handler != nil {
  283. handler(event, func(p Primitive) {
  284. a.SetFocus(p)
  285. })
  286. a.draw()
  287. }
  288. }
  289. case *tcell.EventResize:
  290. if time.Since(lastRedraw) < redrawPause {
  291. if redrawTimer != nil {
  292. redrawTimer.Stop()
  293. }
  294. redrawTimer = time.AfterFunc(redrawPause, func() {
  295. a.events <- event
  296. })
  297. }
  298. a.RLock()
  299. screen := a.screen
  300. a.RUnlock()
  301. if screen == nil {
  302. continue
  303. }
  304. newWidth, newHeight := screen.Size()
  305. if newWidth == width && newHeight == height {
  306. continue
  307. }
  308. width, height = newWidth, newHeight
  309. lastRedraw = time.Now()
  310. screen.Clear()
  311. a.draw()
  312. case *tcell.EventMouse:
  313. consumed, isMouseDownAction := a.fireMouseActions(event)
  314. if consumed {
  315. a.draw()
  316. }
  317. a.lastMouseButtons = event.Buttons()
  318. if isMouseDownAction {
  319. a.mouseDownX, a.mouseDownY = event.Position()
  320. }
  321. }
  322. // If we have updates, now is the time to execute them.
  323. case update := <-a.updates:
  324. update.f()
  325. update.done <- struct{}{}
  326. }
  327. }
  328. // Wait for the event loop to finish.
  329. wg.Wait()
  330. a.screen = nil
  331. return nil
  332. }
  333. // fireMouseActions analyzes the provided mouse event, derives mouse actions
  334. // from it and then forwards them to the corresponding primitives.
  335. func (a *Application) fireMouseActions(event *tcell.EventMouse) (consumed, isMouseDownAction bool) {
  336. // We want to relay follow-up events to the same target primitive.
  337. var targetPrimitive Primitive
  338. // Helper function to fire a mouse action.
  339. fire := func(action MouseAction) {
  340. switch action {
  341. case MouseLeftDown, MouseMiddleDown, MouseRightDown:
  342. isMouseDownAction = true
  343. }
  344. // Intercept event.
  345. if a.mouseCapture != nil {
  346. event, action = a.mouseCapture(event, action)
  347. if event == nil {
  348. consumed = true
  349. return // Don't forward event.
  350. }
  351. }
  352. // Determine the target primitive.
  353. var primitive, capturingPrimitive Primitive
  354. if a.mouseCapturingPrimitive != nil {
  355. primitive = a.mouseCapturingPrimitive
  356. targetPrimitive = a.mouseCapturingPrimitive
  357. } else if targetPrimitive != nil {
  358. primitive = targetPrimitive
  359. } else {
  360. primitive = a.root
  361. }
  362. if primitive != nil {
  363. if handler := primitive.MouseHandler(); handler != nil {
  364. var wasConsumed bool
  365. wasConsumed, capturingPrimitive = handler(action, event, func(p Primitive) {
  366. a.SetFocus(p)
  367. })
  368. if wasConsumed {
  369. consumed = true
  370. }
  371. }
  372. }
  373. a.mouseCapturingPrimitive = capturingPrimitive
  374. }
  375. x, y := event.Position()
  376. buttons := event.Buttons()
  377. clickMoved := x != a.mouseDownX || y != a.mouseDownY
  378. buttonChanges := buttons ^ a.lastMouseButtons
  379. if x != a.lastMouseX || y != a.lastMouseY {
  380. fire(MouseMove)
  381. a.lastMouseX = x
  382. a.lastMouseY = y
  383. }
  384. for _, buttonEvent := range []struct {
  385. button tcell.ButtonMask
  386. down, up, click, dclick MouseAction
  387. }{
  388. {tcell.Button1, MouseLeftDown, MouseLeftUp, MouseLeftClick, MouseLeftDoubleClick},
  389. {tcell.Button2, MouseMiddleDown, MouseMiddleUp, MouseMiddleClick, MouseMiddleDoubleClick},
  390. {tcell.Button3, MouseRightDown, MouseRightUp, MouseRightClick, MouseRightDoubleClick},
  391. } {
  392. if buttonChanges&buttonEvent.button != 0 {
  393. if buttons&buttonEvent.button != 0 {
  394. fire(buttonEvent.down)
  395. } else {
  396. fire(buttonEvent.up)
  397. if !clickMoved {
  398. if a.lastMouseClick.Add(DoubleClickInterval).Before(time.Now()) {
  399. fire(buttonEvent.click)
  400. a.lastMouseClick = time.Now()
  401. } else {
  402. fire(buttonEvent.dclick)
  403. a.lastMouseClick = time.Time{} // reset
  404. }
  405. }
  406. }
  407. }
  408. }
  409. for _, wheelEvent := range []struct {
  410. button tcell.ButtonMask
  411. action MouseAction
  412. }{
  413. {tcell.WheelUp, MouseScrollUp},
  414. {tcell.WheelDown, MouseScrollDown},
  415. {tcell.WheelLeft, MouseScrollLeft},
  416. {tcell.WheelRight, MouseScrollRight}} {
  417. if buttons&wheelEvent.button != 0 {
  418. fire(wheelEvent.action)
  419. }
  420. }
  421. return consumed, isMouseDownAction
  422. }
  423. // Stop stops the application, causing Run() to return.
  424. func (a *Application) Stop() {
  425. a.Lock()
  426. defer a.Unlock()
  427. screen := a.screen
  428. if screen == nil {
  429. return
  430. }
  431. a.screen = nil
  432. screen.Fini()
  433. a.screenReplacement <- nil
  434. }
  435. // Suspend temporarily suspends the application by exiting terminal UI mode and
  436. // invoking the provided function "f". When "f" returns, terminal UI mode is
  437. // entered again and the application resumes.
  438. //
  439. // A return value of true indicates that the application was suspended and "f"
  440. // was called. If false is returned, the application was already suspended,
  441. // terminal UI mode was not exited, and "f" was not called.
  442. func (a *Application) Suspend(f func()) bool {
  443. a.RLock()
  444. screen := a.screen
  445. a.RUnlock()
  446. if screen == nil {
  447. return false // Screen has not yet been initialized.
  448. }
  449. // Enter suspended mode.
  450. screen.Fini()
  451. // Wait for "f" to return.
  452. f()
  453. // Make a new screen.
  454. var err error
  455. screen, err = tcell.NewScreen()
  456. if err != nil {
  457. panic(err)
  458. }
  459. a.screenReplacement <- screen
  460. // One key event will get lost, see https://github.com/gdamore/tcell/issues/194
  461. // Continue application loop.
  462. return true
  463. }
  464. // Draw refreshes the screen (during the next update cycle). It calls the Draw()
  465. // function of the application's root primitive and then syncs the screen
  466. // buffer. It is almost never necessary to call this function. Please see
  467. // https://github.com/rivo/tview/wiki/Concurrency for details.
  468. func (a *Application) Draw() *Application {
  469. a.QueueUpdate(func() {
  470. a.draw()
  471. })
  472. return a
  473. }
  474. // ForceDraw refreshes the screen immediately. Use this function with caution as
  475. // it may lead to race conditions with updates to primitives in other
  476. // goroutines. It is always preferrable to use Draw() instead. Never call this
  477. // function from a goroutine.
  478. //
  479. // It is safe to call this function during queued updates and direct event
  480. // handling.
  481. func (a *Application) ForceDraw() *Application {
  482. return a.draw()
  483. }
  484. // draw actually does what Draw() promises to do.
  485. func (a *Application) draw() *Application {
  486. a.Lock()
  487. defer a.Unlock()
  488. screen := a.screen
  489. root := a.root
  490. fullscreen := a.rootFullscreen
  491. before := a.beforeDraw
  492. after := a.afterDraw
  493. // Maybe we're not ready yet or not anymore.
  494. if screen == nil || root == nil {
  495. return a
  496. }
  497. // Resize if requested.
  498. if fullscreen && root != nil {
  499. width, height := screen.Size()
  500. root.SetRect(0, 0, width, height)
  501. }
  502. // Call before handler if there is one.
  503. if before != nil {
  504. if before(screen) {
  505. screen.Show()
  506. return a
  507. }
  508. }
  509. // Draw all primitives.
  510. root.Draw(screen)
  511. // Call after handler if there is one.
  512. if after != nil {
  513. after(screen)
  514. }
  515. // Sync screen.
  516. screen.Show()
  517. return a
  518. }
  519. // SetBeforeDrawFunc installs a callback function which is invoked just before
  520. // the root primitive is drawn during screen updates. If the function returns
  521. // true, drawing will not continue, i.e. the root primitive will not be drawn
  522. // (and an after-draw-handler will not be called).
  523. //
  524. // Note that the screen is not cleared by the application. To clear the screen,
  525. // you may call screen.Clear().
  526. //
  527. // Provide nil to uninstall the callback function.
  528. func (a *Application) SetBeforeDrawFunc(handler func(screen tcell.Screen) bool) *Application {
  529. a.beforeDraw = handler
  530. return a
  531. }
  532. // GetBeforeDrawFunc returns the callback function installed with
  533. // SetBeforeDrawFunc() or nil if none has been installed.
  534. func (a *Application) GetBeforeDrawFunc() func(screen tcell.Screen) bool {
  535. return a.beforeDraw
  536. }
  537. // SetAfterDrawFunc installs a callback function which is invoked after the root
  538. // primitive was drawn during screen updates.
  539. //
  540. // Provide nil to uninstall the callback function.
  541. func (a *Application) SetAfterDrawFunc(handler func(screen tcell.Screen)) *Application {
  542. a.afterDraw = handler
  543. return a
  544. }
  545. // GetAfterDrawFunc returns the callback function installed with
  546. // SetAfterDrawFunc() or nil if none has been installed.
  547. func (a *Application) GetAfterDrawFunc() func(screen tcell.Screen) {
  548. return a.afterDraw
  549. }
  550. // SetRoot sets the root primitive for this application. If "fullscreen" is set
  551. // to true, the root primitive's position will be changed to fill the screen.
  552. //
  553. // This function must be called at least once or nothing will be displayed when
  554. // the application starts.
  555. //
  556. // It also calls SetFocus() on the primitive.
  557. func (a *Application) SetRoot(root Primitive, fullscreen bool) *Application {
  558. a.Lock()
  559. a.root = root
  560. a.rootFullscreen = fullscreen
  561. if a.screen != nil {
  562. a.screen.Clear()
  563. }
  564. a.Unlock()
  565. a.SetFocus(root)
  566. return a
  567. }
  568. // ResizeToFullScreen resizes the given primitive such that it fills the entire
  569. // screen.
  570. func (a *Application) ResizeToFullScreen(p Primitive) *Application {
  571. a.RLock()
  572. width, height := a.screen.Size()
  573. a.RUnlock()
  574. p.SetRect(0, 0, width, height)
  575. return a
  576. }
  577. // SetFocus sets the focus on a new primitive. All key events will be redirected
  578. // to that primitive. Callers must ensure that the primitive will handle key
  579. // events.
  580. //
  581. // Blur() will be called on the previously focused primitive. Focus() will be
  582. // called on the new primitive.
  583. func (a *Application) SetFocus(p Primitive) *Application {
  584. a.Lock()
  585. if a.focus != nil {
  586. a.focus.Blur()
  587. }
  588. a.focus = p
  589. if a.screen != nil {
  590. a.screen.HideCursor()
  591. }
  592. a.Unlock()
  593. if p != nil {
  594. p.Focus(func(p Primitive) {
  595. a.SetFocus(p)
  596. })
  597. }
  598. return a
  599. }
  600. // GetFocus returns the primitive which has the current focus. If none has it,
  601. // nil is returned.
  602. func (a *Application) GetFocus() Primitive {
  603. a.RLock()
  604. defer a.RUnlock()
  605. return a.focus
  606. }
  607. // QueueUpdate is used to synchronize access to primitives from non-main
  608. // goroutines. The provided function will be executed as part of the event loop
  609. // and thus will not cause race conditions with other such update functions or
  610. // the Draw() function.
  611. //
  612. // Note that Draw() is not implicitly called after the execution of f as that
  613. // may not be desirable. You can call Draw() from f if the screen should be
  614. // refreshed after each update. Alternatively, use QueueUpdateDraw() to follow
  615. // up with an immediate refresh of the screen.
  616. //
  617. // This function returns after f has executed.
  618. func (a *Application) QueueUpdate(f func()) *Application {
  619. ch := make(chan struct{})
  620. a.updates <- queuedUpdate{f: f, done: ch}
  621. <-ch
  622. return a
  623. }
  624. // QueueUpdateDraw works like QueueUpdate() except it refreshes the screen
  625. // immediately after executing f.
  626. func (a *Application) QueueUpdateDraw(f func()) *Application {
  627. a.QueueUpdate(func() {
  628. f()
  629. a.draw()
  630. })
  631. return a
  632. }
  633. // QueueEvent sends an event to the Application event loop.
  634. //
  635. // It is not recommended for event to be nil.
  636. func (a *Application) QueueEvent(event tcell.Event) *Application {
  637. a.events <- event
  638. return a
  639. }