/
events.go
1282 lines (1173 loc) · 35.6 KB
/
events.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Copyright (c) 2018, The GoKi Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gi
import (
"fmt"
"image"
"log"
"sort"
"sync"
"time"
"github.com/chewxy/math32"
"github.com/goki/gi/oswin"
"github.com/goki/gi/oswin/dnd"
"github.com/goki/gi/oswin/key"
"github.com/goki/gi/oswin/mimedata"
"github.com/goki/gi/oswin/mouse"
"github.com/goki/ki/ki"
)
//go:generate stringer -type=EventPris
// EventPris for different queues of event signals, processed in priority order
type EventPris int32
const (
// HiPri = high priority -- event receivers processed first -- can be used
// to override default behavior
HiPri EventPris = iota
// RegPri = default regular priority -- most should be here
RegPri
// LowPri = low priority -- processed last -- typically for containers /
// dialogs etc
LowPri
// LowRawPri = unfiltered (raw) low priority -- ignores whether the event
// was already processed.
LowRawPri
EventPrisN
// AllPris = -1 = all priorities (for delete cases only)
AllPris EventPris = -1
)
const (
// Popups means include popups
Popups = true
// NoPopups means exclude popups
NoPopups = false
)
// EventMgr is an event manager that handles distributing events to nodes.
// It relies on the EventMaster for a few things outside of its scope.
type EventMgr struct {
Master EventMaster `desc:"master of this event mangager -- handles broader scope issues"`
EventSigs [oswin.EventTypeN][EventPrisN]ki.Signal `desc:"signals for communicating each type of event, organized by priority"`
EventMu sync.Mutex `desc:"mutex that protects event sending"`
TimerMu sync.Mutex `desc:"mutex that protects timer variable updates (e.g., hover AfterFunc's)"`
Dragging ki.Ki `desc:"node receiving mouse dragging events -- not for DND but things like sliders -- anchor to same"`
Scrolling ki.Ki `desc:"node receiving mouse scrolling events -- anchor to same"`
DNDStage DNDStages `desc:"stage of DND process"`
DNDData mimedata.Mimes `desc:"drag-n-drop data -- if non-nil, then DND is taking place"`
DNDSource ki.Ki `desc:"drag-n-drop source node"`
DNDFinalEvent *dnd.Event `desc:"final event for DND which is sent if a finalize is received"`
DNDDropMod dnd.DropMods `desc:"modifier in place at time of drop event (DropMove or DropCopy)"`
Focus ki.Ki `desc:"node receiving keyboard events -- use SetFocus, CurFocus"`
FocusMu sync.RWMutex `desc:"mutex that protects focus updating"`
FocusStack []ki.Ki `desc:"stack of focus"`
StartFocus ki.Ki `desc:"node to focus on at start when no other focus has been set yet -- use SetStartFocus"`
LastModBits int32 `desc:"Last modifier key bits from most recent Mouse, Keyboard events"`
LastSelMode mouse.SelectModes `desc:"Last Select Mode from most recent Mouse, Keyboard events"`
LastMousePos image.Point `desc:"Last mouse position from most recent Mouse events"`
LagSkipDeltaPos image.Point `desc:"change in position accumulated from skipped-over laggy mouse move events"`
LagLastSkipped bool `desc:"true if last event was skipped due to lag"`
startDrag *mouse.DragEvent
dragStarted bool
startDND *mouse.DragEvent
dndStarted bool
startHover *mouse.MoveEvent
curHover *mouse.HoverEvent
hoverStarted bool
hoverTimer *time.Timer
startDNDHover *mouse.DragEvent
curDNDHover *mouse.DragEvent
dndHoverStarted bool
dndHoverTimer *time.Timer
}
// WinEventRecv is used to hold info about widgets receiving event signals to
// given function, used for sorting and delayed sending.
type WinEventRecv struct {
Recv ki.Ki
Func ki.RecvFunc
Data int
}
// Set sets the recv and fun
func (we *WinEventRecv) Set(r ki.Ki, f ki.RecvFunc, data int) {
we.Recv = r
we.Func = f
we.Data = data
}
// Call calls the function on the recv with the args
func (we *WinEventRecv) Call(send ki.Ki, sig int64, data interface{}) {
if EventTrace {
fmt.Printf("calling event: %v method on: %v\n", data, we.Recv.Path())
}
we.Func(we.Recv, send, sig, data)
}
type WinEventRecvList []WinEventRecv
func (wl *WinEventRecvList) Add(recv ki.Ki, fun ki.RecvFunc, data int) {
rr := WinEventRecv{recv, fun, data}
*wl = append(*wl, rr)
}
func (wl *WinEventRecvList) AddDepth(recv ki.Ki, fun ki.RecvFunc, par ki.Ki) {
wl.Add(recv, fun, recv.ParentLevel(par))
}
// ConnectEvent adds a Signal connection for given event type and
// priority to given receiver
func (em *EventMgr) ConnectEvent(recv ki.Ki, et oswin.EventType, pri EventPris, fun ki.RecvFunc) {
if et >= oswin.EventTypeN {
log.Printf("EventMgr ConnectEvent type: %v is not a known event type\n", et)
return
}
em.EventSigs[et][pri].Connect(recv, fun)
}
// DisconnectEvent removes Signal connection for given event type to given
// receiver -- pri is priority -- pass AllPris for all priorities
func (em *EventMgr) DisconnectEvent(recv ki.Ki, et oswin.EventType, pri EventPris) {
if et >= oswin.EventTypeN {
log.Printf("EventMgr DisconnectEvent type: %v is not a known event type\n", et)
return
}
if pri == AllPris {
for p := HiPri; p < EventPrisN; p++ {
em.EventSigs[et][p].Disconnect(recv)
}
} else {
em.EventSigs[et][pri].Disconnect(recv)
}
}
// DisconnectAllEvents disconnect node from all event signals -- pri is
// priority -- pass AllPris for all priorities
func (em *EventMgr) DisconnectAllEvents(recv ki.Ki, pri EventPris) {
if pri == AllPris {
for et := oswin.EventType(0); et < oswin.EventTypeN; et++ {
for p := HiPri; p < EventPrisN; p++ {
em.EventSigs[et][p].Disconnect(recv)
}
}
} else {
for et := oswin.EventType(0); et < oswin.EventTypeN; et++ {
em.EventSigs[et][pri].Disconnect(recv)
}
}
}
// SendEventSignal sends given event signal to all receivers that want it --
// note that because there is a different EventSig for each event type, we are
// ONLY looking at nodes that have registered to receive that type of event --
// the further filtering is just to ensure that they are in the right position
// to receive the event (focus, popup filtering, etc). If popup is true, then
// only items on popup are in scope, otherwise items NOT on popup are in scope
// (if no popup, everything is in scope).
func (em *EventMgr) SendEventSignal(evi oswin.Event, popup bool) {
et := evi.Type()
if et > oswin.EventTypeN || et < 0 {
return // can't handle other types of events here due to EventSigs[et] size
}
em.EventMu.Lock()
send := em.Master.EventTopNode()
// fmt.Printf("got event type: %v\n", et)
for pri := HiPri; pri < EventPrisN; pri++ {
if pri != LowRawPri && evi.IsProcessed() { // someone took care of it
continue
}
// we take control of signal process to sort elements by depth, and
// dispatch to inner-most one first
rvs := make(WinEventRecvList, 0, 10)
esig := &em.EventSigs[et][pri]
esig.ConsFunc(func(recv ki.Ki, fun ki.RecvFunc) bool {
if recv.IsDeleted() {
return ki.Continue
}
cont := em.SendEventSignalFunc(evi, popup, &rvs, recv, fun)
return cont // false = break
})
if len(rvs) == 0 {
continue
}
// deepest first
sort.Slice(rvs, func(i, j int) bool {
return rvs[i].Data > rvs[j].Data
})
for _, rr := range rvs {
switch evi.(type) {
case *mouse.DragEvent:
if em.Dragging == nil {
rr.Recv.SetFlag(int(NodeDragging)) // PROVISIONAL!
}
}
em.EventMu.Unlock()
rr.Call(send, int64(et), evi) // could call further event loops..
em.EventMu.Lock()
if pri != LowRawPri && evi.IsProcessed() { // someone took care of it
switch evi.(type) { // only grab events if processed
case *mouse.DragEvent:
if em.Dragging == nil {
em.Dragging = rr.Recv
rr.Recv.SetFlag(int(NodeDragging))
}
case *mouse.ScrollEvent:
if em.Scrolling == nil {
em.Scrolling = rr.Recv
}
}
break
} else {
switch evi.(type) {
case *mouse.DragEvent:
if em.Dragging == nil {
rr.Recv.ClearFlag(int(NodeDragging)) // clear provisional
}
}
}
}
}
em.EventMu.Unlock()
}
// SendEventSignalFunc is the inner loop of the SendEventSignal -- needed to deal with
// map iterator locking logic in a cleaner way. Returns true to continue, false to break
func (em *EventMgr) SendEventSignalFunc(evi oswin.Event, popup bool, rvs *WinEventRecvList, recv ki.Ki, fun ki.RecvFunc) bool {
if !em.Master.IsInScope(recv, popup) {
return ki.Continue
}
nii, ni := KiToNode2D(recv)
if ni != nil {
if evi.OnFocus() {
if !nii.HasFocus2D() { // note: HasFocus2D is a separate interface method, containers also set to true
return ki.Continue
}
if EventTrace && recv == em.CurFocus() {
fmt.Printf("Event: cur focus: %v\n", recv.Path())
}
if !em.Master.IsFocusActive() { // reactivate on keyboard input
em.Master.SetFocusActiveState(true)
if EventTrace {
fmt.Printf("Event: set focus active, was not: %v\n", ni.Path())
}
nii.FocusChanged2D(FocusActive)
}
}
}
top := em.Master.EventTopNode()
// remainder is done using generic node interface, for 2D and 3D
gni := recv.(Node)
gn := gni.AsGiNode()
if evi.HasPos() {
pos := evi.Pos()
switch evi.(type) {
case *mouse.DragEvent:
if em.Dragging != nil {
if em.Dragging == gn.This() {
if EventTrace {
fmt.Printf("Event: dragging top pri: %v\n", recv.Path())
}
rvs.Add(recv, fun, 10000)
return ki.Break
} else {
return ki.Continue
}
} else {
if gn.PosInWinBBox(pos) {
rvs.AddDepth(recv, fun, top)
return ki.Break
}
return ki.Continue
}
case *mouse.ScrollEvent:
if em.Scrolling != nil {
if em.Scrolling == gn.This() {
if EventTrace {
fmt.Printf("Event: scrolling top pri: %v\n", recv.Path())
}
rvs.Add(recv, fun, 10000)
} else {
return ki.Continue
}
} else {
if gn.PosInWinBBox(pos) {
rvs.AddDepth(recv, fun, top)
return ki.Break
}
return ki.Continue
}
default:
if em.Dragging == gn.This() { // dragger always gets it
if EventTrace {
fmt.Printf("Event: dragging, non drag top pri: %v\n", recv.Path())
}
rvs.Add(recv, fun, 10000) // top priority -- can't steal!
return ki.Break
}
if !gn.PosInWinBBox(pos) {
return ki.Continue
}
}
}
rvs.AddDepth(recv, fun, top)
return ki.Continue
}
// SendSig directly calls SendSig from given recv, sender for given event
// across all priorities.
func (em *EventMgr) SendSig(recv, sender ki.Ki, evi oswin.Event) {
et := evi.Type()
for pri := HiPri; pri < EventPrisN; pri++ {
em.EventSigs[et][pri].SendSig(recv, sender, int64(et), evi)
}
}
///////////////////////////////////////////////////////////////////////////
// Mouse event processing
// MouseEvents processes mouse drag and move events
func (em *EventMgr) MouseEvents(evi oswin.Event) {
et := evi.Type()
if et == oswin.MouseDragEvent {
em.MouseDragEvents(evi)
} else if et != oswin.KeyEvent { // allow modifier keypress
em.ResetMouseDrag()
}
if et == oswin.MouseMoveEvent {
em.MouseMoveEvents(evi)
} else {
em.ResetMouseMove()
}
if et == oswin.MouseEvent {
me := evi.(*mouse.Event)
em.LastModBits = me.Modifiers
em.LastSelMode = me.SelectMode()
em.LastMousePos = me.Pos()
}
if et == oswin.KeyChordEvent {
ke := evi.(*key.ChordEvent)
em.LastModBits = ke.Modifiers
em.LastSelMode = mouse.SelectModeBits(ke.Modifiers)
}
}
// MouseEventReset resets state for "catch" events (Dragging, Scrolling)
func (em *EventMgr) MouseEventReset(evi oswin.Event) {
et := evi.Type()
if em.Dragging != nil && et != oswin.MouseDragEvent {
em.Dragging.ClearFlag(int(NodeDragging))
em.Dragging = nil
}
if em.Scrolling != nil && et != oswin.MouseScrollEvent {
em.Scrolling = nil
}
}
// MouseDragEvents processes MouseDragEvent to Detect start of drag and DND.
// These require timing and delays, e.g., due to minor wiggles when pressing
// the mouse button
func (em *EventMgr) MouseDragEvents(evi oswin.Event) {
me := evi.(*mouse.DragEvent)
em.LastModBits = me.Modifiers
em.LastSelMode = me.SelectMode()
em.LastMousePos = me.Pos()
now := time.Now()
if !em.dragStarted {
if em.startDrag == nil {
em.startDrag = me
} else {
if em.DoInstaDrag(em.startDrag, !em.Master.CurPopupIsTooltip()) {
em.dragStarted = true
em.startDrag = nil
} else {
delayMs := int(now.Sub(em.startDrag.Time()) / time.Millisecond)
if delayMs >= DragStartMSec {
dst := int(math32.Hypot(float32(em.startDrag.Where.X-me.Pos().X), float32(em.startDrag.Where.Y-me.Pos().Y)))
if dst >= DragStartPix {
em.dragStarted = true
em.startDrag = nil
}
}
}
}
}
if em.Dragging == nil && !em.dndStarted {
if em.startDND == nil {
em.startDND = me
} else {
delayMs := int(now.Sub(em.startDND.Time()) / time.Millisecond)
if delayMs >= DNDStartMSec {
dst := int(math32.Hypot(float32(em.startDND.Where.X-me.Pos().X), float32(em.startDND.Where.Y-me.Pos().Y)))
if dst >= DNDStartPix {
em.dndStarted = true
em.DNDStartEvent(em.startDND)
em.startDND = nil
}
}
}
} else { // em.dndStarted
em.TimerMu.Lock()
if !em.dndHoverStarted {
em.dndHoverStarted = true
em.startDNDHover = me
em.curDNDHover = em.startDNDHover
em.dndHoverTimer = time.AfterFunc(time.Duration(HoverStartMSec)*time.Millisecond, func() {
em.TimerMu.Lock()
hoe := em.curDNDHover
em.dndHoverStarted = false
em.startDNDHover = nil
em.curDNDHover = nil
em.dndHoverTimer = nil
em.TimerMu.Unlock()
em.SendDNDHoverEvent(hoe)
})
} else {
dst := int(math32.Hypot(float32(em.startDNDHover.Where.X-me.Pos().X), float32(em.startDNDHover.Where.Y-me.Pos().Y)))
if dst > HoverMaxPix {
em.dndHoverTimer.Stop()
em.dndHoverStarted = false
em.startDNDHover = nil
em.dndHoverTimer = nil
} else {
em.curDNDHover = me
}
}
em.TimerMu.Unlock()
}
}
// ResetMouseDrag resets all the mouse dragging variables after last drag
func (em *EventMgr) ResetMouseDrag() {
em.dragStarted = false
em.startDrag = nil
em.dndStarted = false
em.startDND = nil
em.TimerMu.Lock()
em.dndHoverStarted = false
em.startDNDHover = nil
em.curDNDHover = nil
if em.dndHoverTimer != nil {
em.dndHoverTimer.Stop()
em.dndHoverTimer = nil
}
em.TimerMu.Unlock()
}
// MouseMoveEvents processes MouseMoveEvent to detect start of hover events.
// These require timing and delays
func (em *EventMgr) MouseMoveEvents(evi oswin.Event) {
me := evi.(*mouse.MoveEvent)
em.LastModBits = me.Modifiers
em.LastSelMode = me.SelectMode()
em.LastMousePos = me.Pos()
em.TimerMu.Lock()
if !em.hoverStarted {
em.hoverStarted = true
em.startHover = me
em.curHover = &mouse.HoverEvent{Event: me.Event}
em.hoverTimer = time.AfterFunc(time.Duration(HoverStartMSec)*time.Millisecond, func() {
em.TimerMu.Lock()
hoe := em.curHover
em.hoverStarted = false
em.startHover = nil
em.curHover = nil
em.hoverTimer = nil
em.TimerMu.Unlock()
if hoe != nil {
em.SendHoverEvent(hoe)
}
})
} else {
dst := int(math32.Hypot(float32(em.startHover.Where.X-me.Pos().X), float32(em.startHover.Where.Y-me.Pos().Y)))
if dst > HoverMaxPix {
em.hoverTimer.Stop()
em.hoverStarted = false
em.startHover = nil
em.hoverTimer = nil
em.Master.DeleteTooltip()
} else {
em.curHover = &mouse.HoverEvent{Event: me.Event}
}
}
em.TimerMu.Unlock()
}
// ResetMouseMove resets all the mouse moving variables after last move
func (em *EventMgr) ResetMouseMove() {
em.TimerMu.Lock()
em.hoverStarted = false
em.startHover = nil
em.curHover = nil
if em.hoverTimer != nil {
em.hoverTimer.Stop()
em.hoverTimer = nil
}
em.TimerMu.Unlock()
}
// GenMouseFocusEvents processes mouse.MoveEvent to generate mouse.FocusEvent
// events -- returns true if any such events were sent. If popup is true,
// then only items on popup are in scope, otherwise items NOT on popup are in
// scope (if no popup, everything is in scope).
func (em *EventMgr) GenMouseFocusEvents(mev *mouse.MoveEvent, popup bool) bool {
fe := mouse.FocusEvent{Event: mev.Event}
pos := mev.Pos()
ftyp := oswin.MouseFocusEvent
updated := false
updt := false
send := em.Master.EventTopNode()
for pri := HiPri; pri < EventPrisN; pri++ {
em.EventSigs[ftyp][pri].EmitFiltered(send, int64(ftyp), &fe, func(k ki.Ki) bool {
if k.IsDeleted() { // destroyed is filtered upstream
return ki.Break
}
if !em.Master.IsInScope(k, popup) {
return ki.Break
}
_, ni := KiToNode2D(k)
if ni != nil {
in := ni.PosInWinBBox(pos)
if in {
if !ni.HasFlag(int(MouseHasEntered)) {
fe.Action = mouse.Enter
ni.SetFlag(int(MouseHasEntered))
if !updated {
updt = em.Master.EventTopUpdateStart()
updated = true
}
return ki.Continue // send event
} else {
return ki.Break // already in
}
} else { // mouse not in object
if ni.HasFlag(int(MouseHasEntered)) {
fe.Action = mouse.Exit
ni.ClearFlag(int(MouseHasEntered))
if !updated {
updt = em.Master.EventTopUpdateStart()
updated = true
}
return ki.Continue // send event
} else {
return ki.Break // already out
}
}
} else {
// 3D
return ki.Break
}
})
}
if updated {
em.Master.EventTopUpdateEnd(updt)
}
return updated
}
// DoInstaDrag tests whether the given mouse DragEvent is on a widget marked
// with InstaDrag
func (em *EventMgr) DoInstaDrag(me *mouse.DragEvent, popup bool) bool {
et := me.Type()
for pri := HiPri; pri < EventPrisN; pri++ {
esig := &em.EventSigs[et][pri]
gotOne := false
esig.ConsFunc(func(recv ki.Ki, fun ki.RecvFunc) bool {
if recv.IsDeleted() {
return ki.Continue
}
if !em.Master.IsInScope(recv, popup) {
return ki.Continue
}
_, ni := KiToNode2D(recv)
if ni != nil {
pos := me.Pos()
if ni.PosInWinBBox(pos) {
if ni.IsInstaDrag() {
em.Dragging = ni.This()
ni.SetFlag(int(NodeDragging))
gotOne = true
return ki.Break
}
}
}
return ki.Continue
})
if gotOne {
return ki.Continue
}
}
return ki.Break
}
// SendHoverEvent sends mouse hover event, based on last mouse move event
func (em *EventMgr) SendHoverEvent(he *mouse.HoverEvent) {
he.ClearProcessed()
he.Action = mouse.Hover
em.SendEventSignal(he, Popups)
}
//////////////////////////////////////////////////////////////////////
// Drag-n-Drop = DND
//go:generate stringer -type=DNDStages
// DNDStages indicates stage of DND process
type DNDStages int32
const (
// DNDNotStarted = nothing happening
DNDNotStarted DNDStages = iota
// DNDStartSent means that the Start event was sent out, but receiver has
// not yet started the DND on its end by calling StartDragNDrop
DNDStartSent
// DNDStarted means that a node called StartDragNDrop
DNDStarted
// DNDDropped means that drop event has been sent
DNDDropped
DNDStagesN
)
// DNDTrace can be set to true to get a trace of the DND process
var DNDTrace = false
// DNDStartEvent handles drag-n-drop start events.
func (em *EventMgr) DNDStartEvent(e *mouse.DragEvent) {
de := dnd.Event{EventBase: e.EventBase, Where: e.Where, Modifiers: e.Modifiers}
de.ClearProcessed()
de.Action = dnd.Start
de.DefaultMod() // based on current key modifiers
em.DNDStage = DNDStartSent
if DNDTrace {
fmt.Printf("\nDNDStartSent\n")
}
em.SendEventSignal(&de, NoPopups)
// now up to receiver to call StartDragNDrop if they want to..
}
// DNDStart is driven by node responding to start event, actually starts DND
func (em *EventMgr) DNDStart(src ki.Ki, data mimedata.Mimes) {
em.DNDStage = DNDStarted
em.DNDSource = src
em.DNDData = data
if DNDTrace {
fmt.Printf("DNDStarted on: %v\n", src.Path())
}
}
// DNDIsInternalSrc returns true if the source of the DND operation is internal to GoGi
// system -- otherwise it originated from external OS source.
func (em *EventMgr) DNDIsInternalSrc() bool {
return em.DNDSource != nil
}
// SendDNDHoverEvent sends DND hover event, based on last mouse move event
func (em *EventMgr) SendDNDHoverEvent(e *mouse.DragEvent) {
if e == nil {
return
}
he := dnd.FocusEvent{Event: dnd.Event{EventBase: e.EventBase, Where: e.Where, Modifiers: e.Modifiers}}
he.ClearProcessed()
he.Action = dnd.Hover
em.SendEventSignal(&he, NoPopups)
}
// SendDNDMoveEvent sends DND move event
func (em *EventMgr) SendDNDMoveEvent(e *mouse.DragEvent) *dnd.MoveEvent {
// todo: when e.Where goes negative, transition to OS DND
// todo: send move / enter / exit events to anyone listening
de := &dnd.MoveEvent{Event: dnd.Event{EventBase: e.Event.EventBase, Where: e.Event.Where, Modifiers: e.Event.Modifiers}, From: e.From, LastTime: e.LastTime}
de.ClearProcessed()
de.DefaultMod() // based on current key modifiers
de.Action = dnd.Move
em.SendEventSignal(de, NoPopups)
em.GenDNDFocusEvents(de, NoPopups)
return de
}
// SendDNDDropEvent sends DND drop event -- returns false if drop event was not processed
// in which case the event should be cleared (by the Window)
func (em *EventMgr) SendDNDDropEvent(e *mouse.Event) bool {
de := dnd.Event{EventBase: e.EventBase, Where: e.Where, Modifiers: e.Modifiers}
de.ClearProcessed()
de.DefaultMod()
de.Action = dnd.DropOnTarget
de.Data = em.DNDData
de.Source = em.DNDSource
em.DNDSource.ClearFlag(int(NodeDragging))
em.Dragging = nil
em.DNDFinalEvent = &de
em.DNDDropMod = de.Mod
em.DNDStage = DNDDropped
if DNDTrace {
fmt.Printf("DNDDropped\n")
}
e.SetProcessed()
em.SendEventSignal(&de, NoPopups)
return de.IsProcessed()
}
// ClearDND clears DND state
func (em *EventMgr) ClearDND() {
em.DNDStage = DNDNotStarted
em.DNDSource = nil
em.DNDData = nil
em.Dragging = nil
em.DNDFinalEvent = nil
if DNDTrace {
fmt.Printf("DNDCleared\n")
}
}
// GenDNDFocusEvents processes mouse.MoveEvent to generate dnd.FocusEvent
// events -- returns true if any such events were sent. If popup is true,
// then only items on popup are in scope, otherwise items NOT on popup are in
// scope (if no popup, everything is in scope). Extra work is done to ensure
// that Exit from prior widget is always sent before Enter to next one.
func (em *EventMgr) GenDNDFocusEvents(mev *dnd.MoveEvent, popup bool) bool {
fe := dnd.FocusEvent{Event: mev.Event}
pos := mev.Pos()
ftyp := oswin.DNDFocusEvent
// first pass is just to get all the ins and outs
var ins, outs WinEventRecvList
send := em.Master.EventTopNode()
for pri := HiPri; pri < EventPrisN; pri++ {
esig := &em.EventSigs[ftyp][pri]
esig.ConsFunc(func(recv ki.Ki, fun ki.RecvFunc) bool {
if recv.IsDeleted() {
return ki.Continue
}
if !em.Master.IsInScope(recv, popup) {
return ki.Continue
}
_, ni := KiToNode2D(recv)
if ni != nil {
in := ni.PosInWinBBox(pos)
if in {
if !ni.HasFlag(int(DNDHasEntered)) {
ni.SetFlag(int(DNDHasEntered))
ins.Add(recv, fun, 0)
}
} else { // mouse not in object
if ni.HasFlag(int(DNDHasEntered)) {
ni.ClearFlag(int(DNDHasEntered))
outs.Add(recv, fun, 0)
}
}
} else {
// 3D
}
return ki.Continue
})
}
if len(outs)+len(ins) > 0 {
updt := em.Master.EventTopUpdateStart()
// now send all the exits before the enters..
fe.Action = dnd.Exit
for i := range outs {
outs[i].Call(send, int64(ftyp), &fe)
}
fe.Action = dnd.Enter
for i := range ins {
ins[i].Call(send, int64(ftyp), &fe)
}
em.Master.EventTopUpdateEnd(updt)
return ki.Continue
}
return ki.Break
}
///////////////////////////////////////////////////////////////////
// Key events
// SendKeyChordEvent sends a KeyChord event with given values. If popup is
// true, then only items on popup are in scope, otherwise items NOT on popup
// are in scope (if no popup, everything is in scope).
func (em *EventMgr) SendKeyChordEvent(popup bool, r rune, mods ...key.Modifiers) {
ke := key.ChordEvent{}
ke.SetTime()
ke.SetModifiers(mods...)
ke.Rune = r
ke.Action = key.Press
em.SendEventSignal(&ke, popup)
}
// SendKeyFunEvent sends a KeyChord event with params from the given KeyFun.
// If popup is true, then only items on popup are in scope, otherwise items
// NOT on popup are in scope (if no popup, everything is in scope).
func (em *EventMgr) SendKeyFunEvent(kf KeyFuns, popup bool) {
chord := ActiveKeyMap.ChordForFun(kf)
if chord == "" {
return
}
r, mods, err := chord.Decode()
if err != nil {
return
}
ke := key.ChordEvent{}
ke.SetTime()
ke.Modifiers = mods
ke.Rune = r
ke.Action = key.Press
em.SendEventSignal(&ke, popup)
}
// CurFocus gets the current focus node under mutex protection
func (em *EventMgr) CurFocus() ki.Ki {
em.FocusMu.RLock()
defer em.FocusMu.RUnlock()
return em.Focus
}
// setFocusPtr JUST sets the focus pointer under mutex protection --
// use SetFocus for end-user setting of focus
func (em *EventMgr) setFocusPtr(k ki.Ki) {
em.FocusMu.Lock()
em.Focus = k
em.FocusMu.Unlock()
}
// SetFocus sets focus to given item -- returns true if focus changed.
// If item is nil, then nothing has focus.
func (em *EventMgr) SetFocus(k ki.Ki) bool {
cfoc := em.CurFocus()
if cfoc == k {
if k != nil {
_, ni := KiToNode2D(k)
if ni != nil && ni.This() != nil {
ni.SetFocusState(true) // ensure focus flag always set
}
}
return false
}
updt := em.Master.EventTopUpdateStart()
defer em.Master.EventTopUpdateEnd(updt)
if cfoc != nil {
nii, ni := KiToNode2D(cfoc)
if ni != nil && ni.This() != nil {
ni.SetFocusState(false)
// fmt.Printf("clear foc: %v\n", ni.Path())
nii.FocusChanged2D(FocusLost)
}
}
em.setFocusPtr(k)
if k == nil {
return true
}
nii, ni := KiToNode2D(k)
if ni == nil || ni.This() == nil { // only 2d for now
em.setFocusPtr(nil)
return false
}
ni.SetFocusState(true)
em.Master.SetFocusActiveState(true)
// fmt.Printf("set foc: %v\n", ni.Path())
em.ClearNonFocus(k) // shouldn't need this but actually sometimes do
nii.FocusChanged2D(FocusGot)
return true
}
// FocusNext sets the focus on the next item that can accept focus after the
// given item (can be nil) -- returns true if a focus item found.
func (em *EventMgr) FocusNext(foc ki.Ki) bool {
gotFocus := false
focusNext := false // get the next guy
if foc == nil {
focusNext = true
}
focRoot := em.Master.FocusTopNode()
for i := 0; i < 2; i++ {
focRoot.FuncDownMeFirst(0, focRoot, func(k ki.Ki, level int, d interface{}) bool {
if gotFocus {
return ki.Break
}
_, ni := KiToNode2D(k)
if ni == nil || ni.This() == nil {
return ki.Continue
}
if foc == k { // current focus can be a non-can-focus item
focusNext = true
return ki.Continue
}
if !focusNext {
return ki.Continue
}
if !ni.CanFocus() {
return ki.Continue
}
em.SetFocus(k)
gotFocus = true
return ki.Break // done
})
if gotFocus {
return true
}
focusNext = true // this time around, just get the first one
}
return gotFocus
}
// FocusOnOrNext sets the focus on the given item, or the next one that can
// accept focus -- returns true if a new focus item found.
func (em *EventMgr) FocusOnOrNext(foc ki.Ki) bool {
cfoc := em.CurFocus()
if cfoc == foc {
return true
}
_, ni := KiToNode2D(foc)
if ni == nil || ni.This() == nil {
return false
}
if ni.CanFocus() {
em.SetFocus(foc)
return true
}
return em.FocusNext(foc)
}
// FocusOnOrPrev sets the focus on the given item, or the previous one that can
// accept focus -- returns true if a new focus item found.
func (em *EventMgr) FocusOnOrPrev(foc ki.Ki) bool {
cfoc := em.CurFocus()
if cfoc == foc {
return true
}
_, ni := KiToNode2D(foc)
if ni == nil || ni.This() == nil {
return false
}
if ni.CanFocus() {
em.SetFocus(foc)
return true
}
return em.FocusPrev(foc)
}
// FocusPrev sets the focus on the previous item before the given item (can be nil)
func (em *EventMgr) FocusPrev(foc ki.Ki) bool {
if foc == nil { // must have a current item here
em.FocusLast()
return false
}
gotFocus := false
var prevItem ki.Ki
focRoot := em.Master.FocusTopNode()
focRoot.FuncDownMeFirst(0, focRoot, func(k ki.Ki, level int, d interface{}) bool {
if gotFocus {
return ki.Break
}
_, ni := KiToNode2D(k)
if ni == nil || ni.This() == nil {
return ki.Continue
}
if foc == k {
gotFocus = true
return ki.Break