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

Unified Diff: sky/specs/gestures.md

Issue 858353003: Specs: Gestures API Mark II - on the path to supporting gestures that fire events after they're fin… (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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | sky/specs/idl.md » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: sky/specs/gestures.md
diff --git a/sky/specs/gestures.md b/sky/specs/gestures.md
index 673a132938bd62d7cde7dc4889b98e5def1d96ae..037fa247bd2fddff51a4d7d94a5f00d2d97e5636 100644
--- a/sky/specs/gestures.md
+++ b/sky/specs/gestures.md
@@ -5,74 +5,84 @@ Gestures
callback GestureCallback void (Event event);
+dictionary GestureState {
+ Boolean valid = false; // if true, the event was part of the current gesture
+ Boolean finished = true; // if true, we're ready for the next gesture to start
+}
+
abstract class Gesture {
constructor ();
- // Gestures cycle through states:
- // - idle: nothing is going on (active, accepted, and discarding
- // are false).
- // - buffering: the GestureChooser is passing in some events, but
- // hasn't yet committed to using this Gesture, and this Gesture
- // hasn't yet decided that this set of events isn't interesting
- // (active is true, accepted is false, discarding is false).
- // - forwarding: this Gesture is still interesting and the
- // GestureChooser has decided to use this Gesture so events are
- // being sent along (active is true, accepted is true, discarding
- // is false).
- // - discarding: this Gesture got cancelled or didn't match the
- // pattern (active is true, accepted is false, discarding is
- // true).
-
- // TODO(ianh): Need to handle gestures that want to send events
- // beyond the end of the gesture, e.g. inertia in scrolling. In that
- // mode, gestures are sending events but are simultaneously no
- // longer "active"...
-
Boolean processEvent(EventTarget target, Event event);
- // as the events are received, they get examined to see if they fit
- // the pattern for the gesture; if they do, then returns true, else,
- // returns false
- // - returning true after false has been returned is a contract
- // violation unless active became false in between
- // - manipulating the event is a contract violation
- //
- // TODO(ianh): replace processEvent()'s return value with an enum:
- // - acceptable (true and active is true)
- // - discarding (false but active is still true)
- // - finished (false and active is now false)
- // in such a world, the contract would be that you can't return
- // 'acceptable' after returning 'discarding' without first returning
- // 'finished'
+ // - if this.ready=true, clear the sendEvent() buffer and forget
+ // the last accept() callback, if any.
+ // - let returnValue = this.processEventInternal(...)
+ // - if this.discarding:
+ // - assert: returnValue.valid == false
+ // - if !returnValue.valid, then clear the sendEvent() buffer and
+ // forget the last accept() callback, if any
+ // - set this.ready = returnValue.finished
+ // - set this.canceled = !returnValue.valid
+ // - set this.discarding = !returnValue.valid && !returnValue.finished
+ // - set this.active = returnValue.valid && !returnValue.finished
+ // - return returnValue.valid
+
+ readonly attribute Boolean active; // defaults to false
+ // true if the last time processEvent was invoked, valid was true
+ // and finished was false, and we haven't been cancel()ed
+
+ readonly attribute Boolean canceled; // defaults to false
+ // true if either the last time processEvent was invoked, valid was
+ // false, or, we have been cancel()ed
+
+ readonly attribute Boolean discarding; // defaults to false
+ // true if the last time processEvent was invoked, valid was false
+ // and finished was false, and we haven't been cancel()ed
+
+ readonly attribute Boolean ready; // defaults to true
+ // true if the last time processEvent was invoked, the gesture was
+ // over, or, we have been cancel()ed
void accept(GestureCallback callback);
- // set accepted to true, send the buffered gesture events to
- // callback, and use that callback for all future Gesture events
- // until the gesture is complete
+ // assert: this.canceled == false
+ // remember the giver accept callback, and send the buffered gesture
+ // events to that callback
// - call this immediately after getting a positive result from
// processEvent()
+ virtual void cancel();
+ // set active=false, canceled=true, discarding=false, ready=false
+ // clear the sendEvent() buffer and forget the last accept()
+ // callback, if any
+ // descendants may override this if they have more state to drop
+
// internal API:
- // void sendEvent(Event event)
- // - assert: active is true, discarding is false
+
+ virtual GestureState processEventInternal(EventTarget target, Event event);
+ // descendants override this
+ // default implementation returns { } (defaults)
+ // - if this.discarding == false, then:
+ // - optionally, call sendEvent() to fire gesture-specific
+ // events
+ // - as the events are received, they get examined to see if they
+ // fit the pattern for the gesture; if they do, then return an
+ // object with valid=true; if more events for this gesture could
+ // still come in, return finished=false.
+ // - if you returned valid=false finished=false, then the next call
+ // to this must not return valid=true
+ // - doing anything with the event or target other than reading
+ // state is a contract violation
+ // - you are allowed to call sendEvent() at any time during a
+ // processEventInternal() call, or between calls to
+ // processEventInternal() assuming that the last such call
+ // returned either valid=true or finished=true.
+
+ void sendEvent(Event event);
+ // used internally to queue up or send events
+ // - assert: this.discarding == false
// - if accepted is true, then send the event straight to the
// callback
// - otherwise, add it to the buffer
- //
- // void discard()
- // - throw away the buffer, set discarding to true and accepted to
- // false
-
- readonly attribute Boolean active; // not idle (buffering, forwarding, or discarding)
- readonly attribute Boolean accepted; // true if active and accept() has been called (forwarding or discarding)
- readonly attribute Boolean discarding; // true if active and processEvent() has returned false (discarding)
- // 'active' is currently part of the contract between Gesture and GestureChooser (the other two are not)
-
- // active can be true even if processEvent() returned false; this is
- // the discarding state. It means that the Gesture isn't sending any
- // more events, but that the pointers haven't yet reached a state in
- // which a new touch could begin. For example, a Tap gesture where
- // the finger has gone out of the bounding box can't be retriggered
- // until the finger is lifted.
}
@@ -98,63 +108,97 @@ class GestureChooser : EventTarget {
readonly attribute Boolean active; // at least one of the gestures is active (initially false)
readonly attribute Boolean accepted; // we accepted a gesture since the last time active was false (initially false)
+
// any time one of the pointer events is received:
// - if it's pointer-down and it's already captured, ignore the
// event; otherwise:
- // - let /candidates/ be a list of gestures, initially empty
- // - if none of the registered gestures are active, then add all of
- // them to /candidates/ otherwise, add all the active ones to
- // /candidates/
+ // - let /candidates/ be a list of Gestures, initially empty
+ // - if all of the registered Gestures have ready==true, then add
+ // all of them to /candidates/; otherwise, add all the Gestures
+ // with ready==false to /candidates/
// - call processEvent() with the event on all the Gestures in
// /candidates/
- // - if it's pointer-down and at least one Gesture returned true,
- // then capture the event
+ // - if it's pointer-down then:
+ // - if at least one Gesture returned true, then capture the
+ // event
+ // - else send cancel() to all the gestures in /candidates/.
// - if accepted is false, and exactly one of the processEvent()
// methods returned true, then set accepted to true and call that
// Gesture's accept() method, passing it a method that fires the
// provided event on the current target (if not null)
- // - if all the processEvent() methods returned false, and all the
- // Gestures are no longer active, then set active and accepted to
- // false; otherwise, set active to true
+ // - if all the registered Gestures are now ready==true (regardless
+ // of the return values), then set active and accepted to false;
+ // otherwise, set active to true
+
}
class TapGesture : Gesture {
- Boolean processEvent(EventTarget target, Event event);
- // if discarding is true:
- // - if the event is a primary pointer-up, set active, accepted, and
- // discarding to false, and return false
- // - otherwise, just return false
- // if EventTarget isn't an Element:
- // - set active to true, discard(), and return false
- // if the event is pointer-down:
- // - if it's primary:
- // - assert: active is false
- // - sendEvent() a tap-down event
- // - set active to true and return true
- // - otherwise, if we're active:
- // - return true
- // - otherwise:
- // - return false
- // if the event is pointer-move:
- // - if it's primary:
- // - if it hit tests within target's bounding box:
- // - sendEvent() a tap-move event
- // - return true
+
+ // internal state:
+ // Integer numButtons = 0;
+ // Boolean primaryDown = false;
+
+ virtual Boolean internalProcessEvent(EventTarget target, Event event);
+ // - if the event is a pointer-down:
+ // - increment this.numButtons
+ // - otherwise if it is a pointer-up:
+ // - assert: this.numButtons > 0
+ // - decrement this.numButtons
+ // - if this.discarding == true:
+ // return { valid: false, finished: this.numButtons == 0 }
+ // - if EventTarget isn't an Element:
+ // - assert: event is a pointer-down
+ // - assert: this.numButtons > 0
+ // - return { valid: false, finished: false }
+ // - if the event is pointer-down:
+ // - assert: this.numButtons > 0
+ // - if it's primary:
+ // - assert: this.ready==true // this is the first press
+ // - this.primaryDown = true
+ // - sendEvent() a tap-down event
+ // - return { valid: true, finished: false }
+ // - otherwise:
+ // - if this.ready == false:
+ // - // this is a right-click or similar
+ // - return { valid: false, finished: false }
+ // - otherwise, if this.canceled==false:
+ // - assert: this.active==true
+ // - // this is some bogus secondary press that we should ignore
+ // // but it doesn't invalidate the existing primary press
+ // - return { valid true, finished: false }
+ // - otherwise:
+ // - // this is some secondary press but we don't have a first press
+ // // we have to wait til it's done before we can start a
+ // // tap gesture again
+ // - return { valid: false, finished: false }
+ // - otherwise:
+ // - assert: this.active
+ // // if we're ready, forcibly the first event we'll see is a pointer-down,
+ // // so this.ready will never be true here
+ // // if we're cancelled, then we won't get to here
+ // - if the event is pointer-move:
+ // - assert: this.numButtons > 0
+ // // because otherwise we would have lost capture and thus not be getting the events
+ // - if it's primary:
+ // - if it hit tests within target's bounding box:
+ // - sendEvent() a tap-move event
+ // - return { valid: true, finished: false }
+ // - otherwise:
+ // - sendEvent() a tap-cancel event
+ // - return { valid: false, finished: false }
+ // - otherwise:
+ // - // this is the move of some bogus secondary press
+ // // ignore it, but continue listening
+ // - return { valid: true, finished: false }
+ // - if the event is pointer-up:
+ // - if it's primary:
+ // - sendEvent() a tap event
+ // - this.primaryDown = false
+ // - return { valid: true, finished: this.numButtons == 0 }
// - otherwise:
- // - sendEvent() a tap-cancel event
- // - discard() and return false
- // - otherwise, if we're active:
- // - return true
- // - otherwise:
- // - return false
- // if the event is pointer-up:
- // - if it's primary:
- // - sendEvent() a tap event
- // - set accepted and active to false, discard(), and return false
- // - otherwise, if we're active:
- // - return true
- // - otherwise:
- // - return false
+ // - // this is the 'up' of some bogus secondary press
+ // // ignore it, but continue listening for our primary up
+ // - return { valid: this.primaryDown, finished: this.numButtons == 0 }
}
class ScrollGesture : Gesture {
« no previous file with comments | « no previous file | sky/specs/idl.md » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698