github.com/jmigpin/editor@v1.6.0/util/uiutil/mousefilter/clickfilter.go (about) 1 package mousefilter 2 3 import ( 4 "image" 5 "time" 6 7 "github.com/jmigpin/editor/util/uiutil/event" 8 ) 9 10 // produce click/doubleclick/tripleclick events 11 type ClickFilter struct { 12 m map[event.MouseButton]*MultipleClick 13 emitEvFn func(interface{}, image.Point) 14 } 15 16 func NewClickFilter(emitEvFn func(interface{}, image.Point)) *ClickFilter { 17 return &ClickFilter{ 18 m: map[event.MouseButton]*MultipleClick{}, 19 emitEvFn: emitEvFn, 20 } 21 } 22 23 func (clickf *ClickFilter) Filter(ev interface{}) { 24 switch t := ev.(type) { 25 case *event.MouseDown: 26 clickf.down(t) 27 case *event.MouseUp: 28 clickf.up(t) 29 case *event.MouseMove: 30 clickf.move(t) 31 } 32 } 33 34 func (clickf *ClickFilter) down(ev *event.MouseDown) { 35 // initialize on demand 36 mc, ok := clickf.m[ev.Button] 37 if !ok { 38 mc = &MultipleClick{} 39 clickf.m[ev.Button] = mc 40 } 41 42 mc.prevDownPoint = mc.downPoint 43 mc.downPoint = ev.Point 44 } 45 46 func (clickf *ClickFilter) up(ev *event.MouseUp) { 47 mc, ok := clickf.m[ev.Button] 48 if !ok { 49 return 50 } 51 52 // update time 53 upTime0 := mc.upTime 54 mc.upTime = time.Now() 55 56 // must be clicked within a margin 57 if DetectMove(mc.downPoint, ev.Point) { 58 mc.action = MClickActionSingle // reset action 59 return 60 } 61 62 // if it takes too much time, it gets back to single click 63 d := mc.upTime.Sub(upTime0) 64 if d > 400*time.Millisecond { 65 mc.action = MClickActionSingle 66 } else { 67 if DetectMove(mc.prevDownPoint, ev.Point) { 68 mc.action = MClickActionSingle // reset action 69 } else { 70 // single, double, triple 71 mc.action = (mc.action + 1) % 3 72 } 73 } 74 75 // always run a click 76 ev2 := &event.MouseClick{ev.Point, ev.Button, ev.Buttons, ev.Mods} 77 clickf.emitEv(ev2, ev.Point) 78 79 switch mc.action { 80 case MClickActionDouble: 81 ev2 := &event.MouseDoubleClick{ev.Point, ev.Button, ev.Buttons, ev.Mods} 82 clickf.emitEv(ev2, ev.Point) 83 case MClickActionTriple: 84 ev2 := &event.MouseTripleClick{ev.Point, ev.Button, ev.Buttons, ev.Mods} 85 clickf.emitEv(ev2, ev.Point) 86 } 87 } 88 89 func (clickf *ClickFilter) move(ev *event.MouseMove) { 90 for b, mc := range clickf.m { 91 // clear if moved outside move detection margins 92 if DetectMove(mc.downPoint, ev.Point) { 93 delete(clickf.m, b) 94 } 95 } 96 } 97 98 //---------- 99 100 func (clickf *ClickFilter) emitEv(ev interface{}, p image.Point) { 101 clickf.emitEvFn(ev, p) 102 } 103 104 //---------- 105 106 type MultipleClick struct { 107 upTime time.Time 108 downPoint image.Point 109 prevDownPoint image.Point 110 action MClickAction 111 } 112 113 type MClickAction int 114 115 const ( 116 MClickActionSingle MClickAction = iota 117 MClickActionDouble 118 MClickActionTriple 119 )