From b6e7ba2dfcf315aec945e377e858f0badb2cb2b8 Mon Sep 17 00:00:00 2001 From: Hixie Date: Tue, 9 Dec 2014 11:12:06 -0800 Subject: [PATCH] Specs: Make pointer events go up the LayoutManager chain too; improve how you make custom event targets Review URL: https://codereview.chromium.org/787603005 --- specs/apis.md | 20 +++++++++++++++++--- specs/style.md | 19 +++++++++++++++---- specs/ui.md | 40 ++++++++++++++++++++++++---------------- 3 files changed, 56 insertions(+), 23 deletions(-) diff --git a/specs/apis.md b/specs/apis.md index c5556bccf0c..e09ce3fefc1 100644 --- a/specs/apis.md +++ b/specs/apis.md @@ -31,20 +31,31 @@ module 'sky:core' { abstract class EventTarget { any dispatchEvent(Event event); // O(N) in total number of listeners for this type in the chain // sets event.handled to false and event.result to undefined - // makes a record of the event target chain + // makes a record of the event target chain by calling getEventDispatchChain() // invokes all the handlers on the chain in turn // returns event.result + virtual Array getEventDispatchChain(); // O(1) // returns [] void addEventListener(String type, EventListener listener); // O(1) void removeEventListener(String type, EventListener listener); // O(N) in event listeners with that type private Array getRegisteredEventListenerTypes(); // O(N) private Array getRegisteredEventListenersForType(String type); // O(N) } - class CustomEventTarget : EventTarget { + class CustomEventTarget : EventTarget { // implemented in JS constructor (); // O(1) attribute EventTarget parentNode; // getter O(1), setter O(N) in height of tree, throws if this would make a loop + virtual Array getEventDispatchChain(); // O(N) in height of tree // implements EventTarget.getEventDispatchChain() + // let result = []; + // let node = this; + // while (node) { + // result.push(node); + // node = node.parentNode; + // } + // return result; + // you can inherit from this to make your object into an event target + // or you can inherit from EventTarget and implement your own getEventDispatchChain() } @@ -54,13 +65,16 @@ module 'sky:core' { typedef ChildNode (Element or Text); typedef ChildArgument (Element or Text or String); - abstract class Node : EventTarget { + abstract class Node : EventTarget { // implemented in C++ readonly attribute TreeScope? ownerScope; // O(1) readonly attribute ParentNode? parentNode; // O(1) readonly attribute Element? parentElement; // O(1) // if parentNode isn't an element, returns null readonly attribute ChildNode? previousSibling; // O(1) readonly attribute ChildNode? nextSibling; // O(1) + + virtual Array getEventDispatchChain(); // O(N) in number of ancestors across shadow trees // implements EventTarget.getEventDispatchChain() + // returns the event dispatch chain (including handling shadow trees) // the following all throw if parentNode is null void insertBefore(ChildArgument... nodes); // O(N) in number of arguments plus all their descendants diff --git a/specs/style.md b/specs/style.md index 32161cfc7b5..87e95947b66 100644 --- a/specs/style.md +++ b/specs/style.md @@ -281,7 +281,9 @@ class StyleDeclarationList { Array getDeclarations(String? pseudoElement = null); // O(N) in number of declarations } -typedef StyleDeclaration Dictionary; +class StyleDeclaration { + // TODO(ianh): define this +} ``` Rule Matching @@ -347,7 +349,7 @@ partial class Element { callback any ValueResolver (any value, String propertyName, StyleNode node, Float containerWidth, Float containerHeight); -class StyleNode { +class StyleNode { // implemented in C++ with no virtual tables // this is generated before layout readonly attribute String text; readonly attribute Node? parentNode; @@ -434,10 +436,19 @@ sky:core by default registers: Layout managers inherit from the following API: ```javascript -class LayoutManager { +class LayoutManager : EventTarget { readonly attribute StyleNode node; constructor LayoutManager(StyleNode node); + virtual Array getEventDispatchChain(); // O(N) in number of this.node's ancestors // implements EventTarget.getEventDispatchChain() + // let result = []; + // let node = this.node; + // while (node && node.layoutManager) { + // result.push(node.layoutManager); + // node = node.parentNode; + // } + // return result; + void take(StyleNode victim); // sets victim.ownerLayoutManager = this; // assert: victim hasn't been take()n yet during this layout // assert: victim.needsLayout == true @@ -565,7 +576,7 @@ class LayoutManager { // that you don't skip the children that are visible in the new coordinate space but wouldn't be // without the transform - virtual Node hitTest(Float x, Float y); + virtual StyleNode hitTest(Float x, Float y); // default implementation uses the node's children nodes' x, y, // width, and height, skipping any that have width=0 or height=0, or // whose ownerLayoutManager is not |this| diff --git a/specs/ui.md b/specs/ui.md index 6917fcafbf2..a1c456d19d0 100644 --- a/specs/ui.md +++ b/specs/ui.md @@ -67,43 +67,51 @@ that is being clicked, and from "down" back to "up" when this ends. (Note that clicking a button on a stylus doesn't change it from up to down. A stylus can have a button pressed while "up".) -When one switches from "up" to "down", the position of the tap is hit -tested and a 'pointer-down' event is fired at the target element under -the cursor, if any, or the document otherwise. The return value, if -it's a node, is used as the target of future move and up events until -this touch goes up. If there is no return value, the target continues -to be the application's document. +When one switches from "up" to "down", the following algorithm is run: + + 1. Hit test the position of the pointer, let 'node' be the result. + 2. Fire a pointer-down event at the layoutManager for 'node'. + Let 'result' be the returned value. + 3. If 'result' is undefined, then fire a pointer-down event at the + Element for 'node'. + Let 'result' be the returned value. + 4. If 'result' is undefined or is not an EventTarget, let 'result' be + the application document. + 5. Let 'result' capture this pointer. A pointer that is "down" is captured -- all events for that pointer will be routed to the chosen target until the pointer goes up, regardless of whether it's in that target's visible area. -When an element captures a pointer and it has no captured pointers so +When an object captures a pointer and it has no captured pointers so far, if either there are no buttons (touch, stylus) or only the primary button is active (mouse) and this is not an inverted stylus, -then that pointer is marked as "primary" for that element. The pointer +then that pointer is marked as "primary" for that object. The pointer remains the primary pointer until the corresponding pointer-up event (even if the buttons change). When one moves, if it is "up" then a 'pointer-moved' event is fired at the application's document, otherwise if it is "down" then the event -is fired at the element or document that was selected for the -'pointer-down' event (the capturing element). If the return value of a -'pointer-moved' event is 'cancel', then pretend that the pointer moved -to 'up', send 'pointer-up' as described below, and drop all events for -this pointer until such time as it actually changes to be 'up'. +is fired at the object or document that was selected for the +'pointer-down' event (the capturing object). If the return value of a +'pointer-moved' event is 'cancel' then cancel the pointer. When one switches from "down" to "up", a 'pointer-up' event is fired -at the element or document that was selected for the 'pointer-down' +at the object or document that was selected for the 'pointer-down' event (the capturing target). At the time of a pointer-up event, if there is another pointer that is -already down, captured by the same element, and is of the same kind, +already down, captured by the same object, and is of the same kind, and that has either no buttons or only its primary button active, then -that becomes the new primary pointer for that element before the +that becomes the new primary pointer for that object before the pointer-up event is sent. Otherwise, the primary pointer stops being primary just after the pointer-up. +When a pointer is canceled, if it is "down", pretend that the pointer +moved to "up", send 'pointer-up' as described below, and drop all +events for this pointer until such time as it actually changes to be +truly "up". + Nothing special happens when a capturing target moves in the DOM. The x and y position of an -up or -down event always match those of