Specs: Make pointer events go up the LayoutManager chain too; improve how you make custom event targets

Review URL: https://codereview.chromium.org/787603005
This commit is contained in:
Hixie 2014-12-09 11:12:06 -08:00
parent 72a73fc07f
commit b6e7ba2dfc
3 changed files with 56 additions and 23 deletions

View File

@ -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<EventTarget> 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<String> getRegisteredEventListenerTypes(); // O(N)
private Array<EventListener> 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<EventTarget> 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<EventTarget> 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

View File

@ -281,7 +281,9 @@ class StyleDeclarationList {
Array<StyleDeclaration> getDeclarations(String? pseudoElement = null); // O(N) in number of declarations
}
typedef StyleDeclaration Dictionary<ParsedValue>;
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<EventTarget> 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|

View File

@ -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