Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(215)

Side by Side Diff: sky/specs/gestures.md

Issue 872523002: Specs: Gestures Mark III -- handle how to have a gesture claim that it (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 Gestures 1 Gestures
2 ======== 2 ========
3 3
4 TODO(ianh): make it possible for a Gesture to time out and cancel even
5 without having seen a pointer event, to handle double-tap events
6
7 TODO(ianh): even with that, we should keep track of finished-but-valid
8 candidates so that when double-tap cancels itself, the tap, which was
9 still valid even though it's done, can be accepted
10
4 ```javascript 11 ```javascript
5 12
6 callback GestureCallback void (Event event); 13 callback GestureCallback void (Event event);
7 14
8 dictionary GestureState { 15 dictionary GestureState {
9 Boolean valid = false; // if true, the event was part of the current gesture 16 Boolean valid = false; // if true, the event was part of the current gesture
17 Boolean forceCommit = false; // if true, the gesture thinks that other gestu res should give up
10 Boolean finished = true; // if true, we're ready for the next gesture to start 18 Boolean finished = true; // if true, we're ready for the next gesture to start
11 } 19 }
12 20
21 dictionary SendEventOptions {
22 Integer? coallesceGroup = null; // when queuing events, only the last event wi th each group is kept
23 Boolean precommit = false; // if true, event should just be sent right away, n ot queued
24 }
25
13 abstract class Gesture { 26 abstract class Gesture {
14 constructor (); 27 constructor ();
15 28
16 Boolean processEvent(EventTarget target, Event event); 29 attribute GestureCallback callback;
17 // - if this.ready=true, clear the sendEvent() buffer and forget 30 // set by GestureChooser to point to itself
18 // the last accept() callback, if any. 31
32 GestureState processEvent(EventTarget target, Event event);
33 // - if this.ready=true:
34 // - clear the sendEvent() buffer
35 // - set this.accepted = false
19 // - let returnValue = this.processEventInternal(...) 36 // - let returnValue = this.processEventInternal(...)
20 // - if this.discarding: 37 // - if this.discarding:
21 // - assert: returnValue.valid == false 38 // - assert: returnValue.valid == false
22 // - if !returnValue.valid, then clear the sendEvent() buffer and 39 // - if returnValue.valid == false
23 // forget the last accept() callback, if any 40 // - assert: returnValue.forceCommit == false
41 // - if !returnValue.valid, then:
42 // - clear the sendEvent() buffer
43 // - set this.accepted = false
44 // - set this.canceled = !returnValue.valid
24 // - set this.ready = returnValue.finished 45 // - set this.ready = returnValue.finished
25 // - set this.canceled = !returnValue.valid
26 // - set this.discarding = !returnValue.valid && !returnValue.finished 46 // - set this.discarding = !returnValue.valid && !returnValue.finished
27 // - set this.active = returnValue.valid && !returnValue.finished 47 // - set this.active = returnValue.valid && !returnValue.finished
28 // - return returnValue.valid 48 // - return returnValue
29
30 readonly attribute Boolean active; // defaults to false
31 // true if the last time processEvent was invoked, valid was true
32 // and finished was false, and we haven't been cancel()ed
33 49
34 readonly attribute Boolean canceled; // defaults to false 50 readonly attribute Boolean canceled; // defaults to false
35 // true if either the last time processEvent was invoked, valid was 51 // true if either the last time processEvent was invoked, valid was
36 // false, or, we have been cancel()ed 52 // false, or, we have been cancel()ed
37 53
54 readonly attribute Boolean ready; // defaults to true
55 // true if the last time processEvent was invoked, the gesture was
56 // over
57
38 readonly attribute Boolean discarding; // defaults to false 58 readonly attribute Boolean discarding; // defaults to false
39 // true if the last time processEvent was invoked, valid was false 59 // true if the last time processEvent was invoked, valid was false
60 // and finished was false, or, we have been cancel()ed
61 // (aka canceled && !ready)
62
63 readonly attribute Boolean active; // defaults to false
64 // true if the last time processEvent was invoked, valid was true
40 // and finished was false, and we haven't been cancel()ed 65 // and finished was false, and we haven't been cancel()ed
66 // (aka !canceled && !ready)
41 67
42 readonly attribute Boolean ready; // defaults to true 68 readonly attribute Boolean accepted; // defaults to false
43 // true if the last time processEvent was invoked, the gesture was 69 // true accept() was called and we haven't been cancel()ed since
44 // over, or, we have been cancel()ed
45 70
46 void accept(GestureCallback callback); 71 void accept();
47 // assert: this.canceled == false 72 // assert: this.canceled == false
48 // remember the giver accept callback, and send the buffered gesture 73 // set accepted = true
49 // events to that callback 74 // send the buffered gesture events to the callback
50 // - call this immediately after getting a positive result from 75 // - call this immediately after getting a positive result from
51 // processEvent() 76 // processEvent()
52 77
53 virtual void cancel(); 78 virtual void cancel();
54 // set active=false, canceled=true, discarding=false, ready=false 79 // called to indicate that this gesture isn't going to be chosen,
55 // clear the sendEvent() buffer and forget the last accept() 80 // or if it was chosen, that it is finished
56 // callback, if any 81 // - assert: this.canceled == false
57 // descendants may override this if they have more state to drop 82 // - set this.canceled = true
83 // - set this.discarding = !this.ready
84 // - set this.active = false
85 // - clear the sendEvent() buffer
86 // - set this.accepted = false
87 // - descendants may override this if they have more state to drop,
88 // or if they want to send an event to report that it's canceled,
89 // especially if this.accepted is true
90
91 virtual void reset();
92 // called immediately after the first pointer-down of a possible
93 // gesture is sent to processEvents() to indicate that the pointer
94 // wasn't captured so we are to forget anything ever happened (later
95 // pointer-downs are always captured)
96 // - set this.canceled = true
97 // - set this.ready = true
98 // - set this.discarding = false
99 // - set this.active = false
100 // - clear the sendEvent() buffer
101 // - set this.accepted = false
102 // - descendants may override this if they have more state to drop
58 103
59 // internal API: 104 // internal API:
60 105
61 virtual GestureState processEventInternal(EventTarget target, Event event); 106 virtual GestureState processEventInternal(EventTarget target, Event event);
62 // descendants override this 107 // descendants override this
63 // default implementation returns { } (defaults) 108 // default implementation returns { } (defaults)
64 // - if this.discarding == false, then: 109 // - if this.discarding == false, then:
65 // - optionally, call sendEvent() to fire gesture-specific 110 // - optionally, call sendEvent() to fire gesture-specific
66 // events 111 // events
67 // - as the events are received, they get examined to see if they 112 // - as the events are received, they get examined to see if they
68 // fit the pattern for the gesture; if they do, then return an 113 // fit the pattern for the gesture; if they do, then return an
69 // object with valid=true; if more events for this gesture could 114 // object with valid=true; if more events for this gesture could
70 // still come in, return finished=false. 115 // still come in, return finished=false.
71 // - if you returned valid=false finished=false, then the next call 116 // - if you returned valid=false finished=false, then the next call
72 // to this must not return valid=true 117 // to this must not return valid=true
73 // - doing anything with the event or target other than reading 118 // - doing anything with the event or target other than reading
74 // state is a contract violation 119 // state is a contract violation
75 // - you are allowed to call sendEvent() at any time during a 120 // - you are allowed to call sendEvent() at any time during a
76 // processEventInternal() call, or between calls to 121 // processEventInternal() call, or after a call to
77 // processEventInternal() assuming that the last such call 122 // processEventInternal(), assuming that the last such call
78 // returned either valid=true or finished=true. 123 // returned either valid=true or finished=true, until the next
124 // call to processEventInternal() or cancel().
125 // - set forceCommit=true on the return value if you are confident
126 // that this is the gesture the user meant, even if it's possible
127 // that another gesture is still claiming it's valid (e.g. a long
128 // press might forceCommit to override a scroll, if the user
129 // hasn't moved for a while)
130 // - if you send events, you can set precommit=true to send the
131 // event even before the gesture has been accepted
132 // - if you send precommit events, make sure to send corresponding
133 // "cancel" events if reset() or cancel() are called
79 134
80 void sendEvent(Event event); 135 void sendEvent(Event event, SendEventOptions options);
81 // used internally to queue up or send events 136 // used internally to queue up or send events
82 // - assert: this.discarding == false 137 // - assert: this.discarding == false
83 // - if accepted is true, then send the event straight to the 138 // - assert: options.precommit is false or options.coallesceGroup
84 // callback 139 // is null
85 // - otherwise, add it to the buffer 140 // - set event.gesture = this
86 141 // - if this.accepted is true or if options.precommit is true, then
142 // send the event straight to the callback
143 // - otherwise:
144 // - if the buffer has an entry with the same coallesceGroup
145 // identifier, drop it
146 // - add the event to the buffer
87 } 147 }
88 148
89 class GestureChooser : EventTarget { 149 class GestureChooser : EventTarget {
90 constructor (EventTarget? target = null, Array<Gesture> candidates = []); 150 constructor (EventTarget? target = null, Array<Gesture> candidates = []);
91 // throws if any of the candidates are active 151 // throws if any of the candidates are active
92 152
93 readonly attribute EventTarget? target; 153 readonly attribute EventTarget? target;
94 void setTarget(EventTarget? target); 154 void setTarget(EventTarget? target);
95 155
96 Array<Gesture> getGestures(); 156 Array<Gesture> getGestures();
97 void addGesture(Gesture candidate); 157 void addGesture(Gesture candidate);
98 // throw if candidates.active is true 158 // throw if candidates.active is true
99 void removeGesture(Gesture candidate); 159 void removeGesture(Gesture candidate);
100 // if active is true and candidate was the last Gesture in our list 160 // if active is true and candidate was the last Gesture in our list
101 // to be active, set active and accepted to false 161 // to be active, set active and accepted to false
102 162
103 // while target is not null and the list of candidates is not empty, 163 // while target is not null and the list of candidates is not empty,
104 // ensures that it is registered as an event listener for 164 // ensures that it is registered as an event listener for
105 // pointer-down, pointer-move, and pointer-up events on the target; 165 // pointer-down, pointer-move, and pointer-up events on the target;
106 // when the target changes, or when the list of candidates is 166 // when the target changes, or when the list of candidates is
107 // emptied, unregisters itself 167 // emptied, unregisters itself
108 168
109 readonly attribute Boolean active; // at least one of the gestures is active ( initially false) 169 readonly attribute Boolean active; // at least one of the gestures is active ( initially false)
110 readonly attribute Boolean accepted; // we accepted a gesture since the last t ime active was false (initially false) 170 readonly attribute Boolean accepted; // we accepted a gesture since the last t ime active was false (initially false)
111 171
172 // internal state:
173 // /candidates/ is a list of Gesture objects, initially empty
174 //
112 // any time one of the pointer events is received: 175 // any time one of the pointer events is received:
113 // - if it's pointer-down and it's already captured, ignore the 176 // - if it's pointer-down and it's already captured, ignore the
114 // event; otherwise: 177 // event and skip the remaining steps
115 // - let /candidates/ be a list of Gestures, initially empty 178 // - let captured be a boolean
116 // - if all of the registered Gestures have ready==true, then add 179 // - if /candidates/ is empty, then:
117 // all of them to /candidates/; otherwise, add all the Gestures 180 // - set captured to false
118 // with ready==false to /candidates/ 181 // - if accepted is true, call cancel() on whatever the last
182 // accepted candidate was, if any
183 // - add all the registered Gestures to /candidates/
184 // - otherwise:
185 // - set captured to true
186 // - if it's pointer-down, capture the event
119 // - call processEvent() with the event on all the Gestures in 187 // - call processEvent() with the event on all the Gestures in
120 // /candidates/ 188 // /candidates/, collecting their return values (GestureState
121 // - if it's pointer-down then: 189 // objects)
122 // - if at least one Gesture returned true, then capture the 190 // - set forcingAccept to false
123 // event 191 // - set willAccept to null
124 // - else send cancel() to all the gestures in /candidates/. 192 // - for each Gesture in /candidates/, in registration order:
125 // - if accepted is false, and exactly one of the processEvent() 193 // - if it returned valid==false, then
126 // methods returned true, then set accepted to true and call that 194 // - if it is our last accepted candidate, then:
127 // Gesture's accept() method, passing it a method that fires the 195 // - set this.accepted = false
128 // provided event on the current target (if not null) 196 // - if it returned valid==true:
129 // - if all the registered Gestures are now ready==true (regardless 197 // - if it's pointer-down, then:
130 // of the return values), then set active and accepted to false; 198 // - set captured to true
199 // - capture the event
200 // - if this.accepted == true:
201 // - assert: this Gesture is the last accepted candidate
202 // - if it returned forceCommit==true then:
203 // - assert that its accepted attribute is false
204 // - if forcingAccept is false, then:
205 // - set willAccept to this Gesture
206 // - set forcingAccept to true
207 // - otherwise:
208 // - if forcingAccept is false:
209 // - if willAccept is null:
210 // - set willAccept to this Gesture
211 // - otherwise:
212 // - set willAccept to 'undecided'
213 // - if it returned finished==true
214 // - remove the Gesture from /candidates/
215 // - if willAccept is set to a Gesture:
216 // - set this.accepted = true
217 // - call the Gesture's accept() method; this is now the last
218 // accepted candidate
219 // - call cancel() on all the other Gesture objects that returned
220 // valid==true
221 // - if captured is false:
222 // - call reset() on all the gestures in /candidates/, and then
223 // let /candidates/ be empty
224 // - if /candidates/ is now empty, then set active to false;
131 // otherwise, set active to true 225 // otherwise, set active to true
132 226
133 } 227 }
134 228
135 class TapGesture : Gesture { 229 class TapGesture : Gesture {
136 230
137 // internal state: 231 // internal state:
138 // Integer numButtons = 0; 232 // Integer numButtons = 0;
139 // Boolean primaryDown = false; 233 // Boolean primaryDown = false;
140 234
141 virtual Boolean internalProcessEvent(EventTarget target, Event event); 235 virtual Boolean internalProcessEvent(EventTarget target, Event event);
142 // - if the event is a pointer-down: 236 // - if the event is a pointer-down:
143 // - increment this.numButtons 237 // - increment this.numButtons
144 // - otherwise if it is a pointer-up: 238 // - otherwise if it is a pointer-up:
145 // - assert: this.numButtons > 0 239 // - assert: this.numButtons > 0
146 // - decrement this.numButtons 240 // - decrement this.numButtons
147 // - if this.discarding == true: 241 // - if this.discarding == true:
148 // return { valid: false, finished: this.numButtons == 0 } 242 // return { valid: false, finished: this.numButtons == 0 }
149 // - if EventTarget isn't an Element: 243 // - if EventTarget isn't an Element:
150 // - assert: event is a pointer-down 244 // - assert: event is a pointer-down
151 // - assert: this.numButtons > 0 245 // - assert: this.numButtons > 0
152 // - return { valid: false, finished: false } 246 // - return { valid: false, finished: false }
153 // - if the event is pointer-down: 247 // - if the event is pointer-down:
154 // - assert: this.numButtons > 0 248 // - assert: this.numButtons > 0
155 // - if it's primary: 249 // - if it's primary:
156 // - assert: this.ready==true // this is the first press 250 // - assert: this.ready==true // this is the first press
157 // - this.primaryDown = true 251 // - this.primaryDown = true
158 // - sendEvent() a tap-down event 252 // - sendEvent() a tap-down event, with precommit=true
159 // - return { valid: true, finished: false } 253 // - return { valid: true, finished: false }
160 // - otherwise: 254 // - otherwise:
161 // - if this.ready == false: 255 // - if this.ready == false:
162 // - // this is a right-click or similar 256 // - // this is a right-click or similar
163 // - return { valid: false, finished: false } 257 // - return { valid: false, finished: false }
164 // - otherwise, if this.canceled==false: 258 // - otherwise, if this.canceled==false:
165 // - assert: this.active==true 259 // - assert: this.active==true
166 // - // this is some bogus secondary press that we should ignore 260 // - // this is some bogus secondary press that we should ignore
167 // // but it doesn't invalidate the existing primary press 261 // // but it doesn't invalidate the existing primary press
168 // - return { valid true, finished: false } 262 // - return { valid true, finished: false }
169 // - otherwise: 263 // - otherwise:
170 // - // this is some secondary press but we don't have a first press 264 // - // this is some secondary press but we don't have a first press
171 // // we have to wait til it's done before we can start a 265 // // we have to wait til it's done before we can start a
172 // // tap gesture again 266 // // tap gesture again
173 // - return { valid: false, finished: false } 267 // - return { valid: false, finished: false }
174 // - otherwise: 268 // - otherwise:
175 // - assert: this.active 269 // - assert: this.active
176 // // if we're ready, forcibly the first event we'll see is a pointer-down , 270 // // if we're ready, forcibly the first event we'll see is a pointer-down ,
177 // // so this.ready will never be true here 271 // // so this.ready will never be true here
178 // // if we're cancelled, then we won't get to here 272 // // if we're cancelled, then we won't get to here
179 // - if the event is pointer-move: 273 // - if the event is pointer-move:
180 // - assert: this.numButtons > 0 274 // - assert: this.numButtons > 0
181 // // because otherwise we would have lost capture and thus not be gett ing the events 275 // // because otherwise we would have lost capture and thus not be gett ing the events
182 // - if it's primary: 276 // - if it's primary:
183 // - if it hit tests within target's bounding box: 277 // - if it hit tests within target's bounding box:
184 // - sendEvent() a tap-move event 278 // - sendEvent() a tap-move event, with precommit=true
185 // - return { valid: true, finished: false } 279 // - return { valid: true, finished: false }
186 // - otherwise: 280 // - otherwise:
187 // - sendEvent() a tap-cancel event 281 // - sendEvent() a tap-cancel event, with precommit=true
188 // - return { valid: false, finished: false } 282 // - return { valid: false, finished: false }
189 // - otherwise: 283 // - otherwise:
190 // - // this is the move of some bogus secondary press 284 // - // this is the move of some bogus secondary press
191 // // ignore it, but continue listening 285 // // ignore it, but continue listening
192 // - return { valid: true, finished: false } 286 // - return { valid: true, finished: false }
193 // - if the event is pointer-up: 287 // - if the event is pointer-up:
194 // - if it's primary: 288 // - if it's primary:
195 // - sendEvent() a tap event 289 // - sendEvent() a tap event
196 // - this.primaryDown = false 290 // - this.primaryDown = false
197 // - return { valid: true, finished: this.numButtons == 0 } 291 // - return { valid: true, forceCommit: this.numButtons == 0, finished : this.numButtons == 0 }
198 // - otherwise: 292 // - otherwise:
199 // - // this is the 'up' of some bogus secondary press 293 // - // this is the 'up' of some bogus secondary press
200 // // ignore it, but continue listening for our primary up 294 // // ignore it, but continue listening for our primary up
201 // - return { valid: this.primaryDown, finished: this.numButtons == 0 } 295 // - return { valid: this.primaryDown, finished: this.numButtons == 0 }
202 } 296 }
203 297
204 class ScrollGesture : Gesture { 298 class LongPressGesture : Gesture {
205 Boolean processEvent(EventTarget target, Event event); 299 GestureState processEvent(EventTarget target, Event event);
206 // this fires the following events: 300 // long-tap-start: sent when the primary pointer goes down
207 // TODO(ianh): fill this in 301 // long-tap-cancel: sent when cancel(), reset(), or finger goes out of boundin g box
302 // long-tap: sent when the primary pointer is released
208 } 303 }
209 304
305 class DoubleTapGesture : Gesture {
306 GestureState processEvent(EventTarget target, Event event);
307 // double-tap-start: sent when the primary pointer goes down the first time
308 // double-tap-cancel: sent when cancel(), reset(), or finger goes out of bound ing box, or it times out
309 // double-tap: sent when the primary pointer is released the second time
310 }
311
312
313 abstract class ScrollGesture : Gesture {
314 GestureState processEvent(EventTarget target, Event event);
315 // this fires the following events (inertia is a boolean, delta is a float):
316 // scroll-start, with field inertia=false, delta=0; precommit=true
317 // scroll, with fields inertia (is this a simulated scroll from inertia or a real scroll?), delta (number of pixels to scroll); precommit=true
318 // scroll-end, with field inertia (same), delta=0; precommit=true
319 // scroll-start is fired right away
320 // scroll is sent whenever the primary pointer moves while down
321 // scroll is also sent after the pointer goes back up, based on inertia
322 // scroll-end is sent after the pointer goes back up once the scroll reaches d elta=0
323 // scroll-end is also sent when the gesture is canceled or reset
324 // processEvent() returns:
325 // - valid=true pretty much always so long as there's a primary touch (e.g. n ot for a right-click)
326 // - forceCommit=true when you travel a certain distance
327 // - finished=true when the primary pointer goes up
328 }
329
330 class HorizontalScrollGesture : ScrollGesture { }
331 // a ScrollGesture giving x-axis scrolling
332
333 class VerticalScrollGesture : ScrollGesture { }
334 // a ScrollGesture giving y-axis scrolling
335
336
337 class PanGesture : Gesture {
338 // similar to ScrollGesture, but with two axes
339 // pan-start, pan, pan-end
340 // events have inertia (boolean), dx (float), dy (float)
341 }
342
343
344 abstract class ZoomGesture : Gesture {
345 GestureState processEvent(EventTarget target, Event event);
346 // zoom-start: sent when we could start zooming (e.g. for pinch-zoom, when two fingers hit the glass) (precommit)
347 // zoom-end: sent when cancel()ed after zoom-start, or when the fingers are li fted (precommit)
348 // zoom, with a 'scale' attribute, whose value is a multiple of the scale fact or at zoom-start
349 // e.g. if the user zooms to 2x, you'd get a bunch of 'zoom' events like scale =1.0, scale=1.17, ... scale=1.91, scale=2.0
350 }
351
352 class PinchZoomGesture : ZoomGesture {
353 // a ZoomGesture for two-finger-pinch gesture
354 // zoom is precommit
355 }
356
357 class DoubleTapZoomGesture : ZoomGesture {
358 // a ZoomGesture for the double-tap-slide gesture
359 // when the slide starts, forceCommit
360 }
361
362
363 class PanAndZoomGesture : Gesture {
364 GestureState processEvent(EventTarget target, Event event);
365 // manipulate-start (precommit)
366 // manipulate: (precommit)
367 // panX, panY: pixels
368 // scaleX, scaleY: a multiplier of the scale at manipulate-start
369 // rotation: turns
370 // manipulate-end (precommit)
371 }
372
373
374 abstract class FlingGesture : Gesture {
375 GestureState processEvent(EventTarget target, Event event);
376 // fling-start: when the gesture begins (precommit)
377 // fling-move: while the user is directly dragging the element (has delta attr ibute with the distance from fling-start) (precommit)
378 // fling: the user has released the pointer and the decision is it was in fact flung
379 // fling-cancel: cancel(), or the user has released the pointer and the decisi on is it was not flung (precommit)
380 // fling-end: cancel(), reset(), or after fling or fling-cancel (precommit)
381 }
382
383 class FlingLeftGesture : FlingGesture { }
384 class FlingRightGesture : FlingGesture { }
385 class FlingUpGesture : FlingGesture { }
386 class FlingDownGesture : FlingGesture { }
387
210 ``` 388 ```
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698