1 package cview
2
3 import (
4 "fmt"
5 "sync"
6 "time"
7
8 "github.com/gdamore/tcell/v3"
9 )
10
11 const (
12
13 queueSize = 100
14
15
16 resizeEventThrottle = 50 * time.Millisecond
17 )
18
19
20
21
22
23
24
25
26
27
28
29
30
31 type Application struct {
32
33
34
35 screen tcell.Screen
36
37
38 width, height int
39
40
41 focus Primitive
42
43
44 root Primitive
45
46
47 rootFullscreen bool
48
49
50 enableBracketedPaste bool
51
52
53 enableMouse bool
54
55
56 passthroughCtrlC bool
57
58
59
60
61 inputCapture func(event *tcell.EventKey) *tcell.EventKey
62
63
64 lastResize time.Time
65
66
67 throttleResize *time.Timer
68
69
70
71
72
73 afterResize func(width int, height int)
74
75
76
77 beforeFocus func(p Primitive) bool
78
79
80
81 afterFocus func(p Primitive)
82
83
84
85 beforeDraw func(screen tcell.Screen) bool
86
87
88
89 afterDraw func(screen tcell.Screen)
90
91
92 events chan tcell.Event
93
94
95 updates chan func()
96
97
98
99
100
101 screenReplacement chan tcell.Screen
102
103
104
105
106 mouseCapture func(event *tcell.EventMouse, action MouseAction) (*tcell.EventMouse, MouseAction)
107
108
109
110 doubleClickInterval time.Duration
111
112 mouseCapturingPrimitive Primitive
113 lastMouseX, lastMouseY int
114 mouseDownX, mouseDownY int
115 lastMouseClick time.Time
116 lastMouseButtons tcell.ButtonMask
117
118 sync.RWMutex
119 }
120
121
122 func NewApplication() *Application {
123 return &Application{
124 enableBracketedPaste: true,
125 events: make(chan tcell.Event, queueSize),
126 updates: make(chan func(), queueSize),
127 screenReplacement: make(chan tcell.Screen, 1),
128 }
129 }
130
131
132
133
134
135
136
137 func (a *Application) HandlePanic() {
138 p := recover()
139 if p == nil {
140 return
141 }
142
143 a.finalizeScreen()
144
145 panic(p)
146 }
147
148
149
150
151
152
153
154
155
156
157 func (a *Application) SetInputCapture(capture func(event *tcell.EventKey) *tcell.EventKey) {
158 a.Lock()
159 defer a.Unlock()
160
161 a.inputCapture = capture
162
163 }
164
165
166
167 func (a *Application) GetInputCapture() func(event *tcell.EventKey) *tcell.EventKey {
168 a.RLock()
169 defer a.RUnlock()
170
171 return a.inputCapture
172 }
173
174
175
176
177
178
179 func (a *Application) SetMouseCapture(capture func(event *tcell.EventMouse, action MouseAction) (*tcell.EventMouse, MouseAction)) {
180 a.mouseCapture = capture
181
182 }
183
184
185
186 func (a *Application) GetMouseCapture() func(event *tcell.EventMouse, action MouseAction) (*tcell.EventMouse, MouseAction) {
187 return a.mouseCapture
188 }
189
190
191
192
193 func (a *Application) SetDoubleClickInterval(interval time.Duration) {
194 a.doubleClickInterval = interval
195 }
196
197
198
199
200
201
202
203 func (a *Application) SetScreen(screen tcell.Screen) {
204 if screen == nil {
205 return
206 }
207
208 a.Lock()
209 if a.screen == nil {
210
211 a.screen = screen
212 a.Unlock()
213 return
214 }
215
216
217 oldScreen := a.screen
218 a.Unlock()
219 oldScreen.Fini()
220 a.screenReplacement <- screen
221 }
222
223
224
225
226 func (a *Application) GetScreen() tcell.Screen {
227 a.RLock()
228 defer a.RUnlock()
229 return a.screen
230 }
231
232
233
234 func (a *Application) GetScreenSize() (width, height int) {
235 a.RLock()
236 defer a.RUnlock()
237 return a.width, a.height
238 }
239
240
241
242
243 func (a *Application) Init() error {
244 a.Lock()
245 defer a.Unlock()
246 return a.init()
247 }
248
249 func (a *Application) init() error {
250 if a.screen != nil {
251 return nil
252 }
253
254 var err error
255 a.screen, err = tcell.NewScreen()
256 if err != nil {
257 return err
258 }
259 if err = a.screen.Init(); err != nil {
260 return err
261 }
262 a.width, a.height = a.screen.Size()
263 if a.enableBracketedPaste {
264 a.screen.EnablePaste()
265 }
266 if a.enableMouse {
267 a.screen.EnableMouse()
268 }
269 return nil
270 }
271
272
273 func (a *Application) EnableBracketedPaste(enable bool) {
274 a.Lock()
275 defer a.Unlock()
276 if enable != a.enableBracketedPaste && a.screen != nil {
277 if enable {
278 a.screen.EnablePaste()
279 } else {
280 a.screen.DisablePaste()
281 }
282 }
283 a.enableBracketedPaste = enable
284 }
285
286
287 func (a *Application) EnableMouse(enable bool) {
288 a.Lock()
289 defer a.Unlock()
290 if enable != a.enableMouse && a.screen != nil {
291 if enable {
292 a.screen.EnableMouse()
293 } else {
294 a.screen.DisableMouse()
295 }
296 }
297 a.enableMouse = enable
298 }
299
300
301
302 func (a *Application) EnablePassthroughCtrlC(enable bool) {
303 a.Lock()
304 defer a.Unlock()
305 a.passthroughCtrlC = enable
306 }
307
308
309
310 func (a *Application) Run() error {
311 a.Lock()
312
313
314 err := a.init()
315 if err != nil {
316 a.Unlock()
317 return err
318 }
319
320 defer a.HandlePanic()
321
322
323 a.Unlock()
324 a.draw()
325
326
327 var wg sync.WaitGroup
328 wg.Add(1)
329 go func() {
330 defer a.HandlePanic()
331
332 defer wg.Done()
333 for {
334 a.RLock()
335 screen := a.screen
336 a.RUnlock()
337 if screen == nil {
338
339 a.QueueEvent(nil)
340 break
341 }
342
343
344 screen = <-a.screenReplacement
345 if screen == nil {
346
347 a.QueueEvent(nil)
348 return
349 }
350
351
352 a.Lock()
353 a.screen = screen
354 a.Unlock()
355
356
357 if err := screen.Init(); err != nil {
358 panic(err)
359 }
360 if a.enableBracketedPaste {
361 screen.EnablePaste()
362 }
363 if a.enableMouse {
364 screen.EnableMouse()
365 }
366
367 a.draw()
368 }
369 }()
370
371 handle := func(event interface{}) {
372 a.RLock()
373 p := a.focus
374 inputCapture := a.inputCapture
375 screen := a.screen
376 a.RUnlock()
377
378 switch event := event.(type) {
379 case *tcell.EventKey:
380
381 if inputCapture != nil {
382 event = inputCapture(event)
383 if event == nil {
384 a.draw()
385 return
386 }
387 }
388
389
390 if !a.passthroughCtrlC && event.Key() == tcell.KeyCtrlC {
391 a.Stop()
392 return
393 }
394
395
396 if p != nil {
397 if handler := p.InputHandler(); handler != nil {
398 handler(event, func(p Primitive) {
399 a.SetFocus(p)
400 })
401 a.draw()
402 }
403 }
404 case *tcell.EventResize:
405
406 if time.Since(a.lastResize) < resizeEventThrottle {
407
408 if a.throttleResize != nil && !a.throttleResize.Stop() {
409 select {
410 case <-a.throttleResize.C:
411 default:
412 }
413 }
414
415 event := event
416
417
418 a.throttleResize = time.AfterFunc(resizeEventThrottle, func() {
419 a.events <- event
420 })
421
422 return
423 }
424
425 a.lastResize = time.Now()
426
427 if screen == nil {
428 return
429 }
430
431 screen.Clear()
432 a.width, a.height = event.Size()
433
434
435 if a.afterResize != nil {
436 a.afterResize(a.width, a.height)
437 }
438
439 a.draw()
440 case *tcell.EventMouse:
441 consumed, isMouseDownAction := a.fireMouseActions(event)
442 if consumed {
443 a.draw()
444 }
445 a.lastMouseButtons = event.Buttons()
446 if isMouseDownAction {
447 a.mouseDownX, a.mouseDownY = event.Position()
448 }
449 }
450 }
451
452 semaphore := &sync.Mutex{}
453
454 go func() {
455 defer a.HandlePanic()
456
457 for update := range a.updates {
458 semaphore.Lock()
459 update()
460 semaphore.Unlock()
461 }
462 }()
463
464 go func() {
465 defer a.HandlePanic()
466
467 for event := range a.events {
468 semaphore.Lock()
469 handle(event)
470 semaphore.Unlock()
471 }
472 }()
473
474
475 a.Lock()
476 screen := a.screen
477 a.Unlock()
478 if screen != nil {
479 for event := range screen.EventQ() {
480 if event == nil {
481 break
482 }
483
484 semaphore.Lock()
485 handle(event)
486 semaphore.Unlock()
487 }
488 }
489
490
491 wg.Wait()
492 a.Lock()
493 a.screen = nil
494 a.Unlock()
495
496 return nil
497 }
498
499
500
501 func (a *Application) fireMouseActions(event *tcell.EventMouse) (consumed, isMouseDownAction bool) {
502
503 var targetPrimitive Primitive
504
505
506 fire := func(action MouseAction) {
507 switch action {
508 case MouseLeftDown, MouseMiddleDown, MouseRightDown:
509 isMouseDownAction = true
510 }
511
512
513 if a.mouseCapture != nil {
514 event, action = a.mouseCapture(event, action)
515 if event == nil {
516 consumed = true
517 return
518 }
519 }
520
521
522 var primitive, capturingPrimitive Primitive
523 if a.mouseCapturingPrimitive != nil {
524 primitive = a.mouseCapturingPrimitive
525 targetPrimitive = a.mouseCapturingPrimitive
526 } else if targetPrimitive != nil {
527 primitive = targetPrimitive
528 } else {
529 primitive = a.root
530 }
531 if primitive != nil {
532 if handler := primitive.MouseHandler(); handler != nil {
533 var wasConsumed bool
534 wasConsumed, capturingPrimitive = handler(action, event, func(p Primitive) {
535 a.SetFocus(p)
536 })
537 if wasConsumed {
538 consumed = true
539 }
540 }
541 }
542 a.mouseCapturingPrimitive = capturingPrimitive
543 }
544
545 x, y := event.Position()
546 buttons := event.Buttons()
547 clickMoved := x != a.mouseDownX || y != a.mouseDownY
548 buttonChanges := buttons ^ a.lastMouseButtons
549
550 if x != a.lastMouseX || y != a.lastMouseY {
551 fire(MouseMove)
552 a.lastMouseX = x
553 a.lastMouseY = y
554 }
555
556 for _, buttonEvent := range []struct {
557 button tcell.ButtonMask
558 down, up, click, dclick MouseAction
559 }{
560 {tcell.ButtonPrimary, MouseLeftDown, MouseLeftUp, MouseLeftClick, MouseLeftDoubleClick},
561 {tcell.ButtonMiddle, MouseMiddleDown, MouseMiddleUp, MouseMiddleClick, MouseMiddleDoubleClick},
562 {tcell.ButtonSecondary, MouseRightDown, MouseRightUp, MouseRightClick, MouseRightDoubleClick},
563 } {
564 if buttonChanges&buttonEvent.button != 0 {
565 if buttons&buttonEvent.button != 0 {
566 fire(buttonEvent.down)
567 } else {
568 fire(buttonEvent.up)
569 if !clickMoved {
570 if a.doubleClickInterval == 0 || a.lastMouseClick.Add(a.doubleClickInterval).Before(time.Now()) {
571 fire(buttonEvent.click)
572 a.lastMouseClick = time.Now()
573 } else {
574 fire(buttonEvent.dclick)
575 a.lastMouseClick = time.Time{}
576 }
577 }
578 }
579 }
580 }
581
582 for _, wheelEvent := range []struct {
583 button tcell.ButtonMask
584 action MouseAction
585 }{
586 {tcell.WheelUp, MouseScrollUp},
587 {tcell.WheelDown, MouseScrollDown},
588 {tcell.WheelLeft, MouseScrollLeft},
589 {tcell.WheelRight, MouseScrollRight}} {
590 if buttons&wheelEvent.button != 0 {
591 fire(wheelEvent.action)
592 }
593 }
594
595 return consumed, isMouseDownAction
596 }
597
598
599 func (a *Application) Stop() {
600 a.Lock()
601 defer a.Unlock()
602
603 a.finalizeScreen()
604 a.screenReplacement <- nil
605 }
606
607 func (a *Application) finalizeScreen() {
608 screen := a.screen
609 if screen == nil {
610 return
611 }
612
613 a.screen = nil
614 screen.Fini()
615 }
616
617
618
619
620
621
622
623
624 func (a *Application) Suspend(f func()) bool {
625 a.Lock()
626 if a.screen == nil {
627 a.Unlock()
628 return false
629 }
630 err := a.screen.Suspend()
631 a.Unlock()
632 if err != nil {
633 panic(err)
634 }
635
636
637 f()
638
639 a.Lock()
640 err = a.screen.Resume()
641 a.Unlock()
642 if err != nil {
643 panic(err)
644 }
645
646 return true
647 }
648
649
650
651
652
653
654
655
656
657
658
659 func (a *Application) Draw(p ...Primitive) {
660 a.QueueUpdate(func() {
661 if len(p) == 0 {
662 a.draw()
663 return
664 }
665
666 a.Lock()
667 if a.screen != nil {
668 for _, primitive := range p {
669 primitive.Draw(a.screen)
670 }
671 a.screen.Show()
672 }
673 a.Unlock()
674 })
675 }
676
677
678 func (a *Application) draw() {
679 a.Lock()
680
681 screen := a.screen
682 root := a.root
683 fullscreen := a.rootFullscreen
684 before := a.beforeDraw
685 after := a.afterDraw
686
687
688 if screen == nil || root == nil {
689 a.Unlock()
690 return
691 }
692
693
694 if fullscreen {
695 root.SetRect(0, 0, a.width, a.height)
696 }
697
698
699 if before != nil {
700 a.Unlock()
701 if before(screen) {
702 screen.Show()
703 return
704 }
705 } else {
706 a.Unlock()
707 }
708
709
710 root.Draw(screen)
711
712
713 if after != nil {
714 after(screen)
715 }
716
717
718 screen.Show()
719 }
720
721
722
723
724
725
726
727
728
729
730 func (a *Application) SetBeforeDrawFunc(handler func(screen tcell.Screen) bool) {
731 a.Lock()
732 defer a.Unlock()
733
734 a.beforeDraw = handler
735 }
736
737
738
739 func (a *Application) GetBeforeDrawFunc() func(screen tcell.Screen) bool {
740 a.RLock()
741 defer a.RUnlock()
742
743 return a.beforeDraw
744 }
745
746
747
748
749
750 func (a *Application) SetAfterDrawFunc(handler func(screen tcell.Screen)) {
751 a.Lock()
752 defer a.Unlock()
753
754 a.afterDraw = handler
755 }
756
757
758
759 func (a *Application) GetAfterDrawFunc() func(screen tcell.Screen) {
760 a.RLock()
761 defer a.RUnlock()
762
763 return a.afterDraw
764 }
765
766
767
768
769
770
771
772
773 func (a *Application) SetRoot(root Primitive, fullscreen bool) {
774 a.Lock()
775 a.root = root
776 a.rootFullscreen = fullscreen
777 if a.screen != nil {
778 a.screen.Clear()
779 }
780 a.Unlock()
781
782 a.SetFocus(root)
783
784 a.Draw()
785 }
786
787
788
789 func (a *Application) ResizeToFullScreen(p Primitive) {
790 a.RLock()
791 width, height := a.width, a.height
792 a.RUnlock()
793 p.SetRect(0, 0, width, height)
794 }
795
796
797
798
799
800
801
802 func (a *Application) SetAfterResizeFunc(handler func(width int, height int)) {
803 a.Lock()
804 defer a.Unlock()
805
806 a.afterResize = handler
807 }
808
809
810
811 func (a *Application) GetAfterResizeFunc() func(width int, height int) {
812 a.RLock()
813 defer a.RUnlock()
814
815 return a.afterResize
816 }
817
818
819
820
821
822
823
824 func (a *Application) SetFocus(p Primitive) {
825 a.Lock()
826
827 if a.beforeFocus != nil {
828 a.Unlock()
829 ok := a.beforeFocus(p)
830 if !ok {
831 return
832 }
833 a.Lock()
834 }
835
836 if a.focus != nil {
837 a.focus.Blur()
838 }
839
840 a.focus = p
841
842 if a.screen != nil {
843 a.screen.HideCursor()
844 }
845
846 if a.afterFocus != nil {
847 a.Unlock()
848
849 a.afterFocus(p)
850 } else {
851 a.Unlock()
852 }
853
854 if p != nil {
855 p.Focus(func(p Primitive) {
856 a.SetFocus(p)
857 })
858 }
859 }
860
861
862
863 func (a *Application) GetFocus() Primitive {
864 a.RLock()
865 defer a.RUnlock()
866
867 return a.focus
868 }
869
870
871
872
873
874 func (a *Application) SetBeforeFocusFunc(handler func(p Primitive) bool) {
875 a.Lock()
876 defer a.Unlock()
877
878 a.beforeFocus = handler
879 }
880
881
882
883
884
885 func (a *Application) SetAfterFocusFunc(handler func(p Primitive)) {
886 a.Lock()
887 defer a.Unlock()
888
889 a.afterFocus = handler
890 }
891
892
893
894
895
896
897
898 func (a *Application) QueueUpdate(f func()) {
899 a.updates <- f
900 }
901
902
903
904
905
906 func (a *Application) QueueUpdateDraw(f func(), p ...Primitive) {
907 a.QueueUpdate(func() {
908 f()
909
910 if len(p) == 0 {
911 a.draw()
912 return
913 }
914 a.Lock()
915 if a.screen != nil {
916 for _, primitive := range p {
917 primitive.Draw(a.screen)
918 }
919 a.screen.Show()
920 }
921 a.Unlock()
922 })
923 }
924
925
926
927
928 func (a *Application) QueueEvent(event tcell.Event) {
929 a.events <- event
930 }
931
932
933 func (a *Application) RingBell() {
934 a.QueueUpdate(func() {
935 fmt.Print(string(byte(7)))
936 })
937 }
938
View as plain text