mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
369 lines
15 KiB
Markdown
369 lines
15 KiB
Markdown
Pointer Events
|
|
==============
|
|
|
|
Scope
|
|
-----
|
|
|
|
The following input devices are supported by sky:
|
|
- fingers on multitouch screens
|
|
- mice, including mouse wheels
|
|
- styluses on screens
|
|
- other devices that emulate mice (track pads, track balls)
|
|
- [Keyboard](keyboard.md)
|
|
|
|
The following input devices are not supported natively by sky, but can
|
|
be used by connecting directly to the mojo application servicing the
|
|
relevant device:
|
|
- joysticks
|
|
- track balls that move focus (or raw data from track balls)
|
|
- raw data from track pads (e.g. multitouch gestures)
|
|
- raw data from styluses that have their own absolute pads
|
|
- raw data from mice (e.g. to handle mouse capture in 3D games)
|
|
|
|
The following interactions are intended to be easy to handle:
|
|
- one finger starts panning, another finger is placed on the surface
|
|
(and ignored), the first finger is lifted, and the second finger
|
|
continues panning (without the scroll position jumping when the
|
|
first finger is lifted)
|
|
- right-clicking doesn't trigger buttons by default
|
|
- fingers after the first within a surface don't trigger buttons by
|
|
default
|
|
- if there are two independent surfaces, they capture fingers
|
|
unrelated to each other
|
|
|
|
Frameworks are responsible for converting pointer events described
|
|
below into widget-specific events such as the following:
|
|
- a click/tap/activation, as distinct from a short drag
|
|
- a context menu request (e.g. right-click, long-press)
|
|
- a drag (moving an item)
|
|
- a pan (scroll)
|
|
- a zoom/rotation (whether using two finger gestures, or one finger
|
|
with the double-tap-and-hold gesture)
|
|
- a double-tap autozoom
|
|
|
|
In particular, this means distinguishing whether a finger tap consists
|
|
of a tap, a drag, or a long-press; it also means distinguishing
|
|
whether a drag, once established as such, should be treated as a pan
|
|
or a drag, and deciding whether a secondary touch should begin a
|
|
zoom/rotation or not.
|
|
|
|
This is done using the [gesture recogniser API](gestures.md)
|
|
|
|
|
|
Pointers
|
|
--------
|
|
|
|
Each touch or pointer is tracked individually.
|
|
|
|
New touches and pointers can appear and disappear over time.
|
|
|
|
Each pointer has a list of current targets.
|
|
|
|
When a new one enters the system, a non-bubbling 'pointer-added' event
|
|
is fired at the application's document, and the pointer's current
|
|
targets list is initialised to just that Document object.
|
|
|
|
When it is removed, a non-bubbling 'pointer-removed' event is fired at
|
|
the application's document and at any other objects in the pointer's
|
|
current targets list. Currently, at the time of a pointer-removed, the
|
|
list will always consist of only the document.
|
|
|
|
A pointer can be "up" or "down". Initially all pointers are "up".
|
|
|
|
A pointer switches from "up" to "down" when it is a touch or stylus
|
|
that is in contact with the display surface, or when it is a mouse
|
|
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".) In the case of a
|
|
mouse with multiple buttons, the pointer switches back to "up" only
|
|
when all the buttons have been released.
|
|
|
|
When a pointer 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 bubbling pointer-down event at the layoutManager for
|
|
'node', with an empty array as the default return value. Let
|
|
'result1' be the returned value.
|
|
3. If result1 is not an array of EventTarget objects, set it to the
|
|
empty array and (if this is debug mode) report the issue.
|
|
4. Fire a bubbling pointer-down event at the Element for 'node', with
|
|
an empty array as the default return value. Let 'result2' be the
|
|
returned value.
|
|
5. If result2 is not an array of EventTarget objects, set it to the
|
|
empty array and (if this is debug mode) report the issue.
|
|
6. Let result be the concatenation of result1's contents, result2's
|
|
contents, and the application document.
|
|
7. Let 'result' be this pointer's current targets.
|
|
|
|
When an object is one of the current targets of a pointer and no other
|
|
pointers have that object as a current target so far, and 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
|
|
considered the "primary" pointer for that object. The pointer remains
|
|
the primary pointer for that object until the corresponding pointer-up
|
|
event (even if the buttons change).
|
|
|
|
When a pointer moves, a non-bubbling 'pointer-move' event is fired at
|
|
each of the pointer's current targets in turn (maintaining the order
|
|
they had in the 'pointer-down' event, if there's more than one). If
|
|
the return value of a 'pointer-moved' event is 'cancel', and the
|
|
pointer is currently down, then the pointer is canceled (see below).
|
|
|
|
When a pointer's button state changes but this doesn't impact whether
|
|
it is "up" or "down", e.g. when a mouse with a button down gets a
|
|
second button down, or when a stylus' buttons change state, but the
|
|
pointer doesn't simultaneously move, then a 'pointer-moved' event is
|
|
fired anyway, as described above, but with dx=dy=0.
|
|
|
|
When a pointer switches from "down" to "up", a non-bubbling
|
|
'pointer-up' event is fired at each of the pointer's current targets
|
|
in turn (maintaining the order they had in the 'pointer-down' event,
|
|
if there's more than one), and then the pointer's current target list
|
|
is emptied except for the application's document. The buttons exposed
|
|
on the 'pointer-up' event are those that were down immediately prior
|
|
to the buttons being released.
|
|
|
|
At the time of a 'pointer-up' event, for each object that is a current
|
|
target of the pointer, and for which the pointer is considered the
|
|
"primary" pointer for that object, if there is another pointer that is
|
|
already down, which is of the same kind, which also has that object as
|
|
a current target, and that has either no buttons or only its primary
|
|
button active, then that pointer 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'
|
|
event. (This matters for whether the 'primary' field is set.)
|
|
|
|
When a pointer is canceled, if it is "down", pretend that the pointer
|
|
moved to "up", sending 'pointer-up' as described above, and entirely
|
|
empty its current targets list. AFter the pointer actually switches
|
|
from "down" to "up", replace the current targets list with an object
|
|
that only contains the application's document.
|
|
|
|
Nothing special happens when a pointer's current target moves in the
|
|
DOM.
|
|
|
|
The x and y position of an -up or -down event always match those of
|
|
the previous -moved or -added event, so their dx and dy are always 0.
|
|
|
|
Positions are floating point numbers; they can have subpixel values.
|
|
|
|
For each pointer, only a single pointer-added or pointer-removed event
|
|
is fired per frame. If a pointer would have been added and removed in
|
|
the same frame, the pointer is ignored, and no events are fired for
|
|
that pointer.
|
|
|
|
For each pointer, only a single pointer-down or pointer-up event is
|
|
fired per frame, representing the change in state from the last frame,
|
|
if any. Exactly when the event is fired is up to the implementation
|
|
and may depend on the hardware.
|
|
|
|
For each pointer, at most two pointer-move events are fired per frame,
|
|
one before the pointer-down or pointer-up event, if any, and one
|
|
after. If the pointer didn't change "down" state, then only one
|
|
pointer-move event is fired. All the actual moves that the pointer
|
|
experienced are coallesced into the event.
|
|
|
|
Example:
|
|
If a mouse experiences the following events:
|
|
- move +1, down, move +2, up, move +4, down, move +8
|
|
...the events might be:
|
|
- move +7, down, move +8
|
|
...or:
|
|
- move +1, down, move +14
|
|
|
|
TODO(ianh): expose the unfiltered uncoalesced stream of events for
|
|
programs that want more precision (e.g. drawing apps)
|
|
|
|
|
|
These data of all these events is an object with the following fields:
|
|
|
|
pointer: an integer assigned to this touch or pointer when it
|
|
enters the system, never reused, increasing
|
|
monotonically every time a new value is assigned,
|
|
starting from 1 (if the system gets a new tap every
|
|
microsecond, this will cause a problem after 285
|
|
years)
|
|
|
|
kind: one of 'touch', 'mouse', 'stylus', 'inverted-stylus'
|
|
|
|
x: x-position relative to the top-left corner of the
|
|
surface of the node on which the event was fired
|
|
|
|
y: y-position relative to the top-left corner of the
|
|
surface of the node on which the event was fired
|
|
|
|
dx: difference in x-position since last pointer-moved
|
|
event
|
|
|
|
dy: difference in y-position since last pointer-moved
|
|
event
|
|
|
|
buttons: a bitfield of the buttons pressed, from the following
|
|
list:
|
|
|
|
1: primary mouse button (not available on stylus)
|
|
|
|
2: secondary mouse button, primary stylus button
|
|
|
|
3: middle mouse button, secondary stylus button
|
|
|
|
4: back button
|
|
|
|
5: forward button
|
|
|
|
additional buttons can be represented by numbers
|
|
greater than six:
|
|
|
|
n: (n-2)th mouse button, ignoring any buttons that
|
|
are explicitly back or forward buttons
|
|
|
|
(n-4)th stylus button, again ignoring any
|
|
explictly back or forward buttons
|
|
|
|
note that stylus buttons can be pressed even when the
|
|
pointer is not "down"
|
|
|
|
e.g. if the left mouse button and the right mouse
|
|
button are pressed at the same time, the value will
|
|
be 3 (bits 1 and 2); if the right mouse button and
|
|
the back button are pressed at the same time, the
|
|
value will be 10 (bits 2 and 4)
|
|
|
|
down: true if the pointer is down (in pointer-down event or
|
|
subsequent pointer-move events); false otherwise (in
|
|
pointer-added, pointer-up, and pointer-removed
|
|
events, and in pointer-move events that aren't
|
|
between pointer-down and pointer-up events)
|
|
|
|
primary: true if this is a primary pointer/touch (see above)
|
|
can only be set for pointer-moved and pointer-up
|
|
|
|
obscured: true if the system was rendering another view on top
|
|
of the sky application at the time of the event (this
|
|
is intended to enable click-jacking protections)
|
|
|
|
|
|
When down is true:
|
|
|
|
pressure: the pressure of the touch as a number ranging from
|
|
0.0, indicating a touch with no discernible pressure,
|
|
to 1.0, indicating a touch with "normal" pressure,
|
|
and possibly beyond, indicating a stronger touch; for
|
|
devices that do not detect pressure (e.g. mice),
|
|
returns 1.0
|
|
|
|
pressure-min: the minimum value that pressure can return for this
|
|
pointer
|
|
|
|
pressure-max: the maximum value that pressure can return for this
|
|
pointer
|
|
|
|
|
|
When kind is 'touch', 'stylus', or 'stylus-inverted':
|
|
|
|
distance: distance of detected object from surface (e.g.
|
|
distance of stylus or finger from screen), if
|
|
supported and down is not true, otherwise 0.0.
|
|
|
|
distance-min: the minimum value that distance can return for this
|
|
pointer (always 0.0)
|
|
|
|
distance-max: the maximum value that distance can return for this
|
|
pointer (0.0 if not supported)
|
|
|
|
|
|
When kind is 'touch', 'stylus', or 'stylus-inverted' and down is true:
|
|
|
|
radius-major: the radius of the contact ellipse along the major
|
|
axis, in pixels
|
|
|
|
radius-minor: the radius of the contact ellipse along the major
|
|
axis, in pixels
|
|
|
|
radius-min: the minimum value that could be reported for
|
|
radius-major or radius-minor for this pointer
|
|
|
|
radius-max: the maximum value that could be reported for
|
|
radius-major or radius-minor for this pointer
|
|
|
|
|
|
When kind is 'touch' and down is true:
|
|
|
|
orientation: the angle of the contact ellipse, in radians in the
|
|
range
|
|
|
|
-pi/2 < orientation <= pi/2
|
|
|
|
...giving the angle of the major axis of the ellipse
|
|
with the y-axis (negative angles indicating an
|
|
orientation along the top-left / bottom-right
|
|
diagonal, positive angles indicating an orientation
|
|
along the top-right / bottom-left diagonal, and zero
|
|
indicating an orientation parallel with the y-axis)
|
|
|
|
|
|
When kind is 'stylus' or 'stylus-inverted':
|
|
|
|
orientation: the angle of the stylus, in radians in the range
|
|
|
|
-pi < orientation <= pi
|
|
|
|
...giving the angle of the axis of the stylus
|
|
projected onto the screen, relative to the positive
|
|
y-axis of the screen (thus 0 indicates the stylus, if
|
|
projected onto the screen, would go from the contact
|
|
point vertically up in the positive y-axis direction,
|
|
pi would indicate that the stylus would go down in
|
|
the negative y-axis direction; pi/4 would indicate
|
|
that the stylus goes up and to the right, -pi/2 would
|
|
indicate that the stylus goes to the left, etc)
|
|
|
|
tilt: the angle of the stylus, in radians in the range
|
|
|
|
0 <= tilt <= pi/2
|
|
|
|
...giving the angle of the axis of the stylus,
|
|
relative to the axis perpendicular to the screen
|
|
(thus 0 indicates the stylus is orthogonal to the
|
|
plane of the screen, while pi/2 indicates that the
|
|
stylus is flat on the screen)
|
|
|
|
|
|
TODO(ianh): add an API that exposes the currently existing pointers,
|
|
so that you can determine e.g. if you have a mouse.
|
|
|
|
|
|
|
|
Wheel events
|
|
------------
|
|
|
|
When a wheel input device is turned, a 'wheel' event that bubbles is
|
|
fired at the application's document, with the following fields:
|
|
|
|
wheel: an integer assigned to this wheel by the system. The
|
|
same wheel on the same system must always be given
|
|
the same ID. The primary wheel (e.g. the vertical
|
|
wheel on a mouse) must be given ID 1.
|
|
|
|
delta: an floating point number representing the fraction of
|
|
the wheel that was turned, with positive numbers
|
|
representing a downward movement on vertical wheels,
|
|
rightward movement on horizontal wheels, and a
|
|
clockwise movement on wheels with a user-facing side.
|
|
|
|
Additionally, if the wheel is associated with a pointer (e.g. a mouse
|
|
wheel), the following fields must be present also:
|
|
|
|
pointer: the integer assigned to the pointer in its
|
|
'pointer-add' event (see above).
|
|
|
|
x: x-position relative to the top-left corner of the
|
|
display, in global layout coordinates
|
|
|
|
y: x-position relative to the top-left corner of the
|
|
display, in global layout coordinates
|
|
|
|
Note: The only wheels that are supported are mouse wheels and physical
|
|
dials. Track balls are not reported as mouse wheels.
|