1717//! Note: arguably this module should be renamed, perhaps we should use
1818//! "event" for this level and maybe "message" at the View level.
1919
20- use glazier:: { Modifiers , PointerButton , PointerButtons , PointerType } ;
20+ use glazier:: { Modifiers , PointerButton , PointerButtons } ;
2121use vello:: kurbo:: { Point , Rect , Vec2 } ;
2222
2323#[ derive( Debug , Clone ) ]
@@ -58,21 +58,17 @@ pub struct ViewContext {
5858 pub mouse_position : Option < Point > ,
5959}
6060
61- impl < ' a > From < & ' a glazier :: PointerEvent > for MouseEvent {
62- fn from ( src : & glazier :: PointerEvent ) -> MouseEvent {
61+ impl Default for MouseEvent {
62+ fn default ( ) -> Self {
6363 MouseEvent {
64- pos : src. pos ,
65- window_pos : src. pos ,
66- buttons : src. buttons ,
67- mods : src. modifiers ,
68- count : src. count ,
69- focus : src. focus ,
70- button : src. button ,
71- wheel_delta : if let PointerType :: Mouse ( ref info) = src. pointer_type {
72- info. wheel_delta
73- } else {
74- Vec2 :: ZERO
75- } ,
64+ pos : Point :: ZERO ,
65+ window_pos : Point :: ZERO ,
66+ buttons : PointerButtons :: new ( ) ,
67+ mods : Modifiers :: default ( ) ,
68+ count : 0 ,
69+ focus : false ,
70+ button : PointerButton :: None ,
71+ wheel_delta : Vec2 :: ZERO ,
7672 }
7773 }
7874}
@@ -87,3 +83,116 @@ impl ViewContext {
8783 }
8884 }
8985}
86+
87+ /// Crush all pointer events into a single pointer that counts clicks
88+ /// and attaches positions to events that don't contain them.
89+ #[ derive( Default ) ]
90+ pub struct PointerCrusher {
91+ e : MouseEvent ,
92+ counter : ClickCounter ,
93+ }
94+
95+ impl PointerCrusher {
96+ pub fn new ( ) -> Self {
97+ PointerCrusher :: default ( )
98+ }
99+
100+ pub fn mods ( & mut self , mods : Modifiers ) {
101+ self . e . mods = mods;
102+ }
103+
104+ pub fn pressed ( & mut self , button : PointerButton ) -> MouseEvent {
105+ self . e . wheel_delta = Vec2 :: ZERO ;
106+ self . e . buttons . insert ( button) ;
107+ self . e . count = self . counter . count_for_click ( self . e . pos ) ;
108+ self . e . button = button;
109+ self . e . clone ( )
110+ }
111+
112+ pub fn released ( & mut self , button : PointerButton ) -> MouseEvent {
113+ self . e . wheel_delta = Vec2 :: ZERO ;
114+ self . e . buttons . remove ( button) ;
115+ self . e . count = self . counter . count_for_click ( self . e . pos ) ;
116+ self . e . button = button;
117+ self . e . clone ( )
118+ }
119+
120+ pub fn moved ( & mut self , pos : Point ) -> MouseEvent {
121+ self . e . wheel_delta = Vec2 :: ZERO ;
122+ self . e . button = PointerButton :: None ;
123+ self . e . pos = pos;
124+ self . e . window_pos = pos;
125+ self . e . clone ( )
126+ }
127+
128+ pub fn wheel ( & mut self , wheel_delta : Vec2 ) -> MouseEvent {
129+ self . e . wheel_delta = wheel_delta;
130+ self . e . button = PointerButton :: None ;
131+ self . e . clone ( )
132+ }
133+ }
134+
135+ use instant:: Instant ;
136+ use std:: cell:: Cell ;
137+ use std:: time:: Duration ;
138+
139+ // This is the default timing on windows.
140+ const MULTI_CLICK_INTERVAL : Duration = Duration :: from_millis ( 500 ) ;
141+ // the max distance between two clicks for them to count as a multi-click
142+ const MULTI_CLICK_MAX_DISTANCE : f64 = 5.0 ;
143+
144+ /// A small helper for determining the click-count of a mouse-down event.
145+ ///
146+ /// Click-count is incremented if both the duration and distance between a pair
147+ /// of clicks are below some threshold.
148+ #[ derive( Debug , Clone ) ]
149+ struct ClickCounter {
150+ max_interval : Cell < Duration > ,
151+ max_distance : Cell < f64 > ,
152+ last_click : Cell < Instant > ,
153+ last_pos : Cell < Point > ,
154+ click_count : Cell < u8 > ,
155+ }
156+
157+ #[ allow( dead_code) ]
158+ impl ClickCounter {
159+ /// Create a new `ClickCounter` with the given interval and distance.
160+ pub fn new ( max_interval : Duration , max_distance : f64 ) -> ClickCounter {
161+ ClickCounter {
162+ max_interval : Cell :: new ( max_interval) ,
163+ max_distance : Cell :: new ( max_distance) ,
164+ last_click : Cell :: new ( Instant :: now ( ) ) ,
165+ click_count : Cell :: new ( 0 ) ,
166+ last_pos : Cell :: new ( Point :: new ( f64:: MAX , 0.0 ) ) ,
167+ }
168+ }
169+
170+ pub fn set_interval_ms ( & self , millis : u64 ) {
171+ self . max_interval . set ( Duration :: from_millis ( millis) ) ;
172+ }
173+
174+ pub fn set_distance ( & self , distance : f64 ) {
175+ self . max_distance . set ( distance) ;
176+ }
177+
178+ /// Return the click count for a click occurring now, at the provided position.
179+ pub fn count_for_click ( & self , click_pos : Point ) -> u8 {
180+ let click_time = Instant :: now ( ) ;
181+ let last_time = self . last_click . replace ( click_time) ;
182+ let last_pos = self . last_pos . replace ( click_pos) ;
183+ let elapsed = click_time - last_time;
184+ let distance = last_pos. distance ( click_pos) ;
185+ if elapsed > self . max_interval . get ( ) || distance > self . max_distance . get ( ) {
186+ self . click_count . set ( 0 ) ;
187+ }
188+ let click_count = self . click_count . get ( ) . saturating_add ( 1 ) ;
189+ self . click_count . set ( click_count) ;
190+ click_count
191+ }
192+ }
193+
194+ impl Default for ClickCounter {
195+ fn default ( ) -> Self {
196+ ClickCounter :: new ( MULTI_CLICK_INTERVAL , MULTI_CLICK_MAX_DISTANCE )
197+ }
198+ }
0 commit comments