// Copyright 2014 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import 'dart:ui' show Offset, PointerDeviceKind; import 'package:flutter/foundation.dart'; import 'package:vector_math/vector_math_64.dart'; import 'constants.dart'; import 'gesture_settings.dart'; export 'dart:ui' show Offset, PointerDeviceKind; export 'package:flutter/foundation.dart' show DiagnosticPropertiesBuilder; export 'package:vector_math/vector_math_64.dart' show Matrix4; export 'gesture_settings.dart' show DeviceGestureSettings; /// The bit of [PointerEvent.buttons] that corresponds to a cross-device /// behavior of "primary operation". /// /// More specifically, it includes: /// /// * [kTouchContact]: The pointer contacts the touch screen. /// * [kStylusContact]: The stylus contacts the screen. /// * [kPrimaryMouseButton]: The primary mouse button. /// /// See also: /// /// * [kSecondaryButton], which describes a cross-device behavior of /// "secondary operation". /// * [kTertiaryButton], which describes a cross-device behavior of /// "tertiary operation". const int kPrimaryButton = 0x01; /// The bit of [PointerEvent.buttons] that corresponds to a cross-device /// behavior of "secondary operation". /// /// It is equivalent to: /// /// * [kPrimaryStylusButton]: The stylus contacts the screen. /// * [kSecondaryMouseButton]: The secondary mouse button. /// /// See also: /// /// * [kPrimaryButton], which describes a cross-device behavior of /// "primary operation". /// * [kTertiaryButton], which describes a cross-device behavior of /// "tertiary operation". const int kSecondaryButton = 0x02; /// The bit of [PointerEvent.buttons] that corresponds to the primary mouse button. /// /// The primary mouse button is typically the left button on the top of the /// mouse but can be reconfigured to be a different physical button. /// /// See also: /// /// * [kPrimaryButton], which has the same value but describes its cross-device /// concept. const int kPrimaryMouseButton = kPrimaryButton; /// The bit of [PointerEvent.buttons] that corresponds to the secondary mouse button. /// /// The secondary mouse button is typically the right button on the top of the /// mouse but can be reconfigured to be a different physical button. /// /// See also: /// /// * [kSecondaryButton], which has the same value but describes its cross-device /// concept. const int kSecondaryMouseButton = kSecondaryButton; /// The bit of [PointerEvent.buttons] that corresponds to when a stylus /// contacting the screen. /// /// See also: /// /// * [kPrimaryButton], which has the same value but describes its cross-device /// concept. const int kStylusContact = kPrimaryButton; /// The bit of [PointerEvent.buttons] that corresponds to the primary stylus button. /// /// The primary stylus button is typically the top of the stylus and near the /// tip but can be reconfigured to be a different physical button. /// /// See also: /// /// * [kSecondaryButton], which has the same value but describes its cross-device /// concept. const int kPrimaryStylusButton = kSecondaryButton; /// The bit of [PointerEvent.buttons] that corresponds to a cross-device /// behavior of "tertiary operation". /// /// It is equivalent to: /// /// * [kMiddleMouseButton]: The tertiary mouseButton. /// * [kSecondaryStylusButton]: The secondary button on a stylus. This is considered /// a tertiary button as the primary button of a stylus already corresponds to a /// "secondary operation" (where stylus contact is the primary operation). /// /// See also: /// /// * [kPrimaryButton], which describes a cross-device behavior of /// "primary operation". /// * [kSecondaryButton], which describes a cross-device behavior of /// "secondary operation". const int kTertiaryButton = 0x04; /// The bit of [PointerEvent.buttons] that corresponds to the middle mouse button. /// /// The middle mouse button is typically between the left and right buttons on /// the top of the mouse but can be reconfigured to be a different physical /// button. /// /// See also: /// /// * [kTertiaryButton], which has the same value but describes its cross-device /// concept. const int kMiddleMouseButton = kTertiaryButton; /// The bit of [PointerEvent.buttons] that corresponds to the secondary stylus button. /// /// The secondary stylus button is typically on the end of the stylus farthest /// from the tip but can be reconfigured to be a different physical button. /// /// See also: /// /// * [kTertiaryButton], which has the same value but describes its cross-device /// concept. const int kSecondaryStylusButton = kTertiaryButton; /// The bit of [PointerEvent.buttons] that corresponds to the back mouse button. /// /// The back mouse button is typically on the left side of the mouse but can be /// reconfigured to be a different physical button. const int kBackMouseButton = 0x08; /// The bit of [PointerEvent.buttons] that corresponds to the forward mouse button. /// /// The forward mouse button is typically on the right side of the mouse but can /// be reconfigured to be a different physical button. const int kForwardMouseButton = 0x10; /// The bit of [PointerEvent.buttons] that corresponds to the pointer contacting /// a touch screen. /// /// See also: /// /// * [kPrimaryButton], which has the same value but describes its cross-device /// concept. const int kTouchContact = kPrimaryButton; /// The bit of [PointerEvent.buttons] that corresponds to the nth mouse button. /// /// The `number` argument can be at most 62 in Flutter for mobile and desktop, /// and at most 32 on Flutter for web. /// /// See [kPrimaryMouseButton], [kSecondaryMouseButton], [kMiddleMouseButton], /// [kBackMouseButton], and [kForwardMouseButton] for semantic names for some /// mouse buttons. int nthMouseButton(int number) => (kPrimaryMouseButton << (number - 1)) & kMaxUnsignedSMI; /// The bit of [PointerEvent.buttons] that corresponds to the nth stylus button. /// /// The `number` argument can be at most 62 in Flutter for mobile and desktop, /// and at most 32 on Flutter for web. /// /// See [kPrimaryStylusButton] and [kSecondaryStylusButton] for semantic names /// for some stylus buttons. int nthStylusButton(int number) => (kPrimaryStylusButton << (number - 1)) & kMaxUnsignedSMI; /// Returns the button of `buttons` with the smallest integer. /// /// The `buttons` parameter is a bit field where each set bit represents a button. /// This function returns the set bit closest to the least significant bit. /// /// It returns zero when `buttons` is zero. /// /// Example: /// /// ```dart /// assert(smallestButton(0x01) == 0x01); /// assert(smallestButton(0x11) == 0x01); /// assert(smallestButton(0x10) == 0x10); /// assert(smallestButton(0) == 0); /// ``` /// /// See also: /// /// * [isSingleButton], which checks if a `buttons` contains exactly one button. int smallestButton(int buttons) => buttons & (-buttons); /// Returns whether `buttons` contains one and only one button. /// /// The `buttons` parameter is a bit field where each set bit represents a button. /// This function returns whether there is only one set bit in the given integer. /// /// It returns false when `buttons` is zero. /// /// Example: /// /// ```dart /// assert(isSingleButton(0x1)); /// assert(!isSingleButton(0x11)); /// assert(!isSingleButton(0)); /// ``` /// /// See also: /// /// * [smallestButton], which returns the button in a `buttons` bit field with /// the smallest integer button. bool isSingleButton(int buttons) => buttons != 0 && (smallestButton(buttons) == buttons); /// Base class for touch, stylus, or mouse events. /// /// Pointer events operate in the coordinate space of the screen, scaled to /// logical pixels. Logical pixels approximate a grid with about 38 pixels per /// centimeter, or 96 pixels per inch. /// /// This allows gestures to be recognized independent of the precise hardware /// characteristics of the device. In particular, features such as touch slop /// (see [kTouchSlop]) can be defined in terms of roughly physical lengths so /// that the user can shift their finger by the same distance on a high-density /// display as on a low-resolution device. /// /// For similar reasons, pointer events are not affected by any transforms in /// the rendering layer. This means that deltas may need to be scaled before /// being applied to movement within the rendering. For example, if a scrolling /// list is shown scaled by 2x, the pointer deltas will have to be scaled by the /// inverse amount if the list is to appear to scroll with the user's finger. /// /// See also: /// /// * [dart:ui.FlutterView.devicePixelRatio], which defines the device's /// current resolution. /// * [Listener], a widget that calls callbacks in response to common pointer /// events. @immutable abstract class PointerEvent with Diagnosticable { /// Abstract const constructor. This constructor enables subclasses to provide /// const constructors so that they can be used in const expressions. const PointerEvent({ this.embedderId = 0, this.timeStamp = Duration.zero, this.pointer = 0, this.kind = PointerDeviceKind.touch, this.device = 0, this.position = Offset.zero, this.delta = Offset.zero, this.buttons = 0, this.down = false, this.obscured = false, this.pressure = 1.0, this.pressureMin = 1.0, this.pressureMax = 1.0, this.distance = 0.0, this.distanceMax = 0.0, this.size = 0.0, this.radiusMajor = 0.0, this.radiusMinor = 0.0, this.radiusMin = 0.0, this.radiusMax = 0.0, this.orientation = 0.0, this.tilt = 0.0, this.platformData = 0, this.synthesized = false, this.transform, this.original, }); /// Unique identifier that ties the [PointerEvent] to the embedder event that created it. /// /// No two pointer events can have the same [embedderId] on platforms that set it. /// This is different from [pointer] identifier - used for hit-testing, /// whereas [embedderId] is used to identify the platform event. /// /// On Android this is ID of the underlying [MotionEvent](https://developer.android.com/reference/android/view/MotionEvent). final int embedderId; /// Time of event dispatch, relative to an arbitrary timeline. final Duration timeStamp; /// Unique identifier for the pointer, not reused. Changes for each new /// pointer down event. final int pointer; /// The kind of input device for which the event was generated. final PointerDeviceKind kind; /// Unique identifier for the pointing device, reused across interactions. final int device; /// Coordinate of the position of the pointer, in logical pixels in the global /// coordinate space. /// /// See also: /// /// * [localPosition], which is the [position] transformed into the local /// coordinate system of the event receiver. final Offset position; /// The [position] transformed into the event receiver's local coordinate /// system according to [transform]. /// /// If this event has not been transformed, [position] is returned as-is. /// See also: /// /// * [position], which is the position in the global coordinate system of /// the screen. Offset get localPosition => position; /// Distance in logical pixels that the pointer moved since the last /// [PointerMoveEvent] or [PointerHoverEvent]. /// /// This value is always 0.0 for down, up, and cancel events. /// /// See also: /// /// * [localDelta], which is the [delta] transformed into the local /// coordinate space of the event receiver. final Offset delta; /// The [delta] transformed into the event receiver's local coordinate /// system according to [transform]. /// /// If this event has not been transformed, [delta] is returned as-is. /// /// See also: /// /// * [delta], which is the distance the pointer moved in the global /// coordinate system of the screen. Offset get localDelta => delta; /// Bit field using the *Button constants such as [kPrimaryMouseButton], /// [kSecondaryStylusButton], etc. /// /// For example, if this has the value 6 and the /// [kind] is [PointerDeviceKind.invertedStylus], then this indicates an /// upside-down stylus with both its primary and secondary buttons pressed. final int buttons; /// Set if the pointer is currently down. /// /// For touch and stylus pointers, this means the object (finger, pen) is in /// contact with the input surface. For mice, it means a button is pressed. final bool down; /// Set if an application from a different security domain is in any way /// obscuring this application's window. /// /// This is not currently implemented. final bool obscured; /// The pressure of the touch. /// /// This value is 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. final double pressure; /// The minimum value that [pressure] can return for this pointer. /// /// For devices that do not detect pressure (e.g. mice), returns 1.0. /// This will always be a number less than or equal to 1.0. final double pressureMin; /// The maximum value that [pressure] can return for this pointer. /// /// For devices that do not detect pressure (e.g. mice), returns 1.0. /// This will always be a greater than or equal to 1.0. final double pressureMax; /// The distance of the detected object from the input surface. /// /// For instance, this value could be the distance of a stylus or finger /// from a touch screen, in arbitrary units on an arbitrary (not necessarily /// linear) scale. If the pointer is down, this is 0.0 by definition. final double distance; /// The minimum value that [distance] can return for this pointer. /// /// This value is always 0.0. double get distanceMin => 0.0; /// The maximum value that [distance] can return for this pointer. /// /// If this input device cannot detect "hover touch" input events, /// then this will be 0.0. final double distanceMax; /// The area of the screen being pressed. /// /// This value is scaled to a range between 0 and 1. It can be used to /// determine fat touch events. This value is only set on Android and is /// a device specific approximation within the range of detectable values. /// So, for example, the value of 0.1 could mean a touch with the tip of /// the finger, 0.2 a touch with full finger, and 0.3 the full palm. /// /// Because this value uses device-specific range and is uncalibrated, /// it is of limited use and is primarily retained in order to be able /// to reconstruct original pointer events for [AndroidView]. final double size; /// The radius of the contact ellipse along the major axis, in logical pixels. final double radiusMajor; /// The radius of the contact ellipse along the minor axis, in logical pixels. final double radiusMinor; /// The minimum value that could be reported for [radiusMajor] and [radiusMinor] /// for this pointer, in logical pixels. final double radiusMin; /// The maximum value that could be reported for [radiusMajor] and [radiusMinor] /// for this pointer, in logical pixels. final double radiusMax; /// The orientation angle of the detected object, in radians. /// /// For [PointerDeviceKind.touch] events: /// /// 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). /// /// For [PointerDeviceKind.stylus] and [PointerDeviceKind.invertedStylus] events: /// /// 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 input /// surface, relative to the positive y-axis of that surface (thus 0.0 /// indicates the stylus, if projected onto that surface, 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). final double orientation; /// The tilt angle of the detected object, in radians. /// /// For [PointerDeviceKind.stylus] and [PointerDeviceKind.invertedStylus] events: /// /// 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 input surface (thus 0.0 indicates the stylus is /// orthogonal to the plane of the input surface, while pi/2 indicates that /// the stylus is flat on that surface). final double tilt; /// Opaque platform-specific data associated with the event. final int platformData; /// Set if the event was synthesized by Flutter. /// /// We occasionally synthesize PointerEvents that aren't exact translations /// of [PointerData] from the engine to cover small cross-OS discrepancies /// in pointer behaviors. /// /// For instance, on end events, Android always drops any location changes /// that happened between its reporting intervals when emitting the end events. /// /// On iOS, minor incorrect location changes from the previous move events /// can be reported on end events. We synthesize a [PointerEvent] to cover /// the difference between the 2 events in that case. final bool synthesized; /// The transformation used to transform this event from the global coordinate /// space into the coordinate space of the event receiver. /// /// This value affects what is returned by [localPosition] and [localDelta]. /// If this value is null, it is treated as the identity transformation. /// /// Unlike a paint transform, this transform usually does not contain any /// "perspective" components, meaning that the third row and the third column /// of the matrix should be equal to "0, 0, 1, 0". This ensures that /// [localPosition] describes the point in the local coordinate system of the /// event receiver at which the user is actually touching the screen. /// /// See also: /// /// * [transformed], which transforms this event into a different coordinate /// space. final Matrix4? transform; /// The original un-transformed [PointerEvent] before any [transform]s were /// applied. /// /// If [transform] is null or the identity transformation this may be null. /// /// When multiple event receivers in different coordinate spaces receive an /// event, they all receive the event transformed to their local coordinate /// space. The [original] property can be used to determine if all those /// transformed events actually originated from the same pointer interaction. final PointerEvent? original; /// Transforms the event from the global coordinate space into the coordinate /// space of an event receiver. /// /// The coordinate space of the event receiver is described by `transform`. A /// null value for `transform` is treated as the identity transformation. /// /// The resulting event will store the base event as [original], delegates /// most properties to [original], except for [localPosition] and [localDelta], /// which are calculated based on [transform] on first use and cached. /// /// The method may return the same object instance if for example the /// transformation has no effect on the event. Otherwise, the resulting event /// will be a subclass of, but not exactly, the original event class (e.g. /// [PointerDownEvent.transformed] may return a subclass of [PointerDownEvent]). /// /// Transforms are not commutative, and are based on [original] events. /// If this method is called on a transformed event, the provided `transform` /// will override (instead of multiplied onto) the existing [transform] and /// used to calculate the new [localPosition] and [localDelta]. PointerEvent transformed(Matrix4? transform); /// Creates a copy of event with the specified properties replaced. /// /// Calling this method on a transformed event will return a new transformed /// event based on the current [transform] and the provided properties. PointerEvent copyWith({ Duration? timeStamp, int? pointer, PointerDeviceKind? kind, int? device, Offset? position, Offset? delta, int? buttons, bool? obscured, double? pressure, double? pressureMin, double? pressureMax, double? distance, double? distanceMax, double? size, double? radiusMajor, double? radiusMinor, double? radiusMin, double? radiusMax, double? orientation, double? tilt, bool? synthesized, int? embedderId, }); /// Returns the transformation of `position` into the coordinate system /// described by `transform`. /// /// The z-value of `position` is assumed to be 0.0. If `transform` is null, /// `position` is returned as-is. static Offset transformPosition(Matrix4? transform, Offset position) { if (transform == null) { return position; } final Vector3 position3 = Vector3(position.dx, position.dy, 0.0); final Vector3 transformed3 = transform.perspectiveTransform(position3); return Offset(transformed3.x, transformed3.y); } /// Transforms `untransformedDelta` into the coordinate system described by /// `transform`. /// /// It uses the provided `untransformedEndPosition` and /// `transformedEndPosition` of the provided delta to increase accuracy. /// /// If `transform` is null, `untransformedDelta` is returned. static Offset transformDeltaViaPositions({ required Offset untransformedEndPosition, Offset? transformedEndPosition, required Offset untransformedDelta, required Matrix4? transform, }) { if (transform == null) { return untransformedDelta; } // We could transform the delta directly with the transformation matrix. // While that is mathematically equivalent, in practice we are seeing a // greater precision error with that approach. Instead, we are transforming // start and end point of the delta separately and calculate the delta in // the new space for greater accuracy. transformedEndPosition ??= transformPosition(transform, untransformedEndPosition); final Offset transformedStartPosition = transformPosition(transform, untransformedEndPosition - untransformedDelta); return transformedEndPosition - transformedStartPosition; } /// Removes the "perspective" component from `transform`. /// /// When applying the resulting transform matrix to a point with a /// z-coordinate of zero (which is generally assumed for all points /// represented by an [Offset]), the other coordinates will get transformed as /// before, but the new z-coordinate is going to be zero again. This is /// achieved by setting the third column and third row of the matrix to /// "0, 0, 1, 0". static Matrix4 removePerspectiveTransform(Matrix4 transform) { final Vector4 vector = Vector4(0, 0, 1, 0); return transform.clone() ..setColumn(2, vector) ..setRow(2, vector); } } // A mixin that adds implementation for [debugFillProperties] and [toStringFull] // to [PointerEvent]. mixin _PointerEventDescription on PointerEvent { @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties.add(DiagnosticsProperty<Offset>('position', position)); properties.add(DiagnosticsProperty<Offset>('localPosition', localPosition, defaultValue: position, level: DiagnosticLevel.debug)); properties.add(DiagnosticsProperty<Offset>('delta', delta, defaultValue: Offset.zero, level: DiagnosticLevel.debug)); properties.add(DiagnosticsProperty<Offset>('localDelta', localDelta, defaultValue: delta, level: DiagnosticLevel.debug)); properties.add(DiagnosticsProperty<Duration>('timeStamp', timeStamp, defaultValue: Duration.zero, level: DiagnosticLevel.debug)); properties.add(IntProperty('pointer', pointer, level: DiagnosticLevel.debug)); properties.add(EnumProperty<PointerDeviceKind>('kind', kind, level: DiagnosticLevel.debug)); properties.add(IntProperty('device', device, defaultValue: 0, level: DiagnosticLevel.debug)); properties.add(IntProperty('buttons', buttons, defaultValue: 0, level: DiagnosticLevel.debug)); properties.add(DiagnosticsProperty<bool>('down', down, level: DiagnosticLevel.debug)); properties.add(DoubleProperty('pressure', pressure, defaultValue: 1.0, level: DiagnosticLevel.debug)); properties.add(DoubleProperty('pressureMin', pressureMin, defaultValue: 1.0, level: DiagnosticLevel.debug)); properties.add(DoubleProperty('pressureMax', pressureMax, defaultValue: 1.0, level: DiagnosticLevel.debug)); properties.add(DoubleProperty('distance', distance, defaultValue: 0.0, level: DiagnosticLevel.debug)); properties.add(DoubleProperty('distanceMin', distanceMin, defaultValue: 0.0, level: DiagnosticLevel.debug)); properties.add(DoubleProperty('distanceMax', distanceMax, defaultValue: 0.0, level: DiagnosticLevel.debug)); properties.add(DoubleProperty('size', size, defaultValue: 0.0, level: DiagnosticLevel.debug)); properties.add(DoubleProperty('radiusMajor', radiusMajor, defaultValue: 0.0, level: DiagnosticLevel.debug)); properties.add(DoubleProperty('radiusMinor', radiusMinor, defaultValue: 0.0, level: DiagnosticLevel.debug)); properties.add(DoubleProperty('radiusMin', radiusMin, defaultValue: 0.0, level: DiagnosticLevel.debug)); properties.add(DoubleProperty('radiusMax', radiusMax, defaultValue: 0.0, level: DiagnosticLevel.debug)); properties.add(DoubleProperty('orientation', orientation, defaultValue: 0.0, level: DiagnosticLevel.debug)); properties.add(DoubleProperty('tilt', tilt, defaultValue: 0.0, level: DiagnosticLevel.debug)); properties.add(IntProperty('platformData', platformData, defaultValue: 0, level: DiagnosticLevel.debug)); properties.add(FlagProperty('obscured', value: obscured, ifTrue: 'obscured', level: DiagnosticLevel.debug)); properties.add(FlagProperty('synthesized', value: synthesized, ifTrue: 'synthesized', level: DiagnosticLevel.debug)); properties.add(IntProperty('embedderId', embedderId, defaultValue: 0, level: DiagnosticLevel.debug)); } /// Returns a complete textual description of this event. String toStringFull() { return toString(minLevel: DiagnosticLevel.fine); } } abstract class _AbstractPointerEvent implements PointerEvent { } // The base class for transformed pointer event classes. // // A _TransformedPointerEvent stores an [original] event and the [transform] // matrix. It defers all field getters to the original event, except for // [localPosition] and [localDelta], which are calculated when first used. abstract class _TransformedPointerEvent extends _AbstractPointerEvent with Diagnosticable, _PointerEventDescription { @override PointerEvent get original; @override Matrix4 get transform; @override int get embedderId => original.embedderId; @override Duration get timeStamp => original.timeStamp; @override int get pointer => original.pointer; @override PointerDeviceKind get kind => original.kind; @override int get device => original.device; @override Offset get position => original.position; @override Offset get delta => original.delta; @override int get buttons => original.buttons; @override bool get down => original.down; @override bool get obscured => original.obscured; @override double get pressure => original.pressure; @override double get pressureMin => original.pressureMin; @override double get pressureMax => original.pressureMax; @override double get distance => original.distance; @override double get distanceMin => 0.0; @override double get distanceMax => original.distanceMax; @override double get size => original.size; @override double get radiusMajor => original.radiusMajor; @override double get radiusMinor => original.radiusMinor; @override double get radiusMin => original.radiusMin; @override double get radiusMax => original.radiusMax; @override double get orientation => original.orientation; @override double get tilt => original.tilt; @override int get platformData => original.platformData; @override bool get synthesized => original.synthesized; @override late final Offset localPosition = PointerEvent.transformPosition(transform, position); @override late final Offset localDelta = PointerEvent.transformDeltaViaPositions( transform: transform, untransformedDelta: delta, untransformedEndPosition: position, transformedEndPosition: localPosition, ); } mixin _CopyPointerAddedEvent on PointerEvent { @override PointerAddedEvent copyWith({ Duration? timeStamp, int? pointer, PointerDeviceKind? kind, int? device, Offset? position, Offset? delta, int? buttons, bool? obscured, double? pressure, double? pressureMin, double? pressureMax, double? distance, double? distanceMax, double? size, double? radiusMajor, double? radiusMinor, double? radiusMin, double? radiusMax, double? orientation, double? tilt, bool? synthesized, int? embedderId, }) { return PointerAddedEvent( timeStamp: timeStamp ?? this.timeStamp, kind: kind ?? this.kind, device: device ?? this.device, position: position ?? this.position, obscured: obscured ?? this.obscured, pressureMin: pressureMin ?? this.pressureMin, pressureMax: pressureMax ?? this.pressureMax, distance: distance ?? this.distance, distanceMax: distanceMax ?? this.distanceMax, radiusMin: radiusMin ?? this.radiusMin, radiusMax: radiusMax ?? this.radiusMax, orientation: orientation ?? this.orientation, tilt: tilt ?? this.tilt, embedderId: embedderId ?? this.embedderId, ).transformed(transform); } } /// The device has started tracking the pointer. /// /// For example, the pointer might be hovering above the device, having not yet /// made contact with the surface of the device. class PointerAddedEvent extends PointerEvent with _PointerEventDescription, _CopyPointerAddedEvent { /// Creates a pointer added event. /// /// All of the arguments must be non-null. const PointerAddedEvent({ super.timeStamp, super.pointer, super.kind, super.device, super.position, super.obscured, super.pressureMin, super.pressureMax, super.distance, super.distanceMax, super.radiusMin, super.radiusMax, super.orientation, super.tilt, super.embedderId, }) : super( pressure: 0.0, ); @override PointerAddedEvent transformed(Matrix4? transform) { if (transform == null || transform == this.transform) { return this; } return _TransformedPointerAddedEvent(original as PointerAddedEvent? ?? this, transform); } } class _TransformedPointerAddedEvent extends _TransformedPointerEvent with _CopyPointerAddedEvent implements PointerAddedEvent { _TransformedPointerAddedEvent(this.original, this.transform); @override final PointerAddedEvent original; @override final Matrix4 transform; @override PointerAddedEvent transformed(Matrix4? transform) => original.transformed(transform); } mixin _CopyPointerRemovedEvent on PointerEvent { @override PointerRemovedEvent copyWith({ Duration? timeStamp, int? pointer, PointerDeviceKind? kind, int? device, Offset? position, Offset? delta, int? buttons, bool? obscured, double? pressure, double? pressureMin, double? pressureMax, double? distance, double? distanceMax, double? size, double? radiusMajor, double? radiusMinor, double? radiusMin, double? radiusMax, double? orientation, double? tilt, bool? synthesized, int? embedderId, }) { return PointerRemovedEvent( timeStamp: timeStamp ?? this.timeStamp, kind: kind ?? this.kind, device: device ?? this.device, position: position ?? this.position, obscured: obscured ?? this.obscured, pressureMin: pressureMin ?? this.pressureMin, pressureMax: pressureMax ?? this.pressureMax, distanceMax: distanceMax ?? this.distanceMax, radiusMin: radiusMin ?? this.radiusMin, radiusMax: radiusMax ?? this.radiusMax, embedderId: embedderId ?? this.embedderId, ).transformed(transform); } } /// The device is no longer tracking the pointer. /// /// For example, the pointer might have drifted out of the device's hover /// detection range or might have been disconnected from the system entirely. class PointerRemovedEvent extends PointerEvent with _PointerEventDescription, _CopyPointerRemovedEvent { /// Creates a pointer removed event. /// /// All of the arguments must be non-null. const PointerRemovedEvent({ super.timeStamp, super.pointer, super.kind, super.device, super.position, super.obscured, super.pressureMin, super.pressureMax, super.distanceMax, super.radiusMin, super.radiusMax, PointerRemovedEvent? super.original, super.embedderId, }) : super( pressure: 0.0, ); @override PointerRemovedEvent transformed(Matrix4? transform) { if (transform == null || transform == this.transform) { return this; } return _TransformedPointerRemovedEvent(original as PointerRemovedEvent? ?? this, transform); } } class _TransformedPointerRemovedEvent extends _TransformedPointerEvent with _CopyPointerRemovedEvent implements PointerRemovedEvent { _TransformedPointerRemovedEvent(this.original, this.transform); @override final PointerRemovedEvent original; @override final Matrix4 transform; @override PointerRemovedEvent transformed(Matrix4? transform) => original.transformed(transform); } mixin _CopyPointerHoverEvent on PointerEvent { @override PointerHoverEvent copyWith({ Duration? timeStamp, int? pointer, PointerDeviceKind? kind, int? device, Offset? position, Offset? delta, int? buttons, bool? obscured, double? pressure, double? pressureMin, double? pressureMax, double? distance, double? distanceMax, double? size, double? radiusMajor, double? radiusMinor, double? radiusMin, double? radiusMax, double? orientation, double? tilt, bool? synthesized, int? embedderId, }) { return PointerHoverEvent( timeStamp: timeStamp ?? this.timeStamp, kind: kind ?? this.kind, device: device ?? this.device, position: position ?? this.position, delta: delta ?? this.delta, buttons: buttons ?? this.buttons, obscured: obscured ?? this.obscured, pressureMin: pressureMin ?? this.pressureMin, pressureMax: pressureMax ?? this.pressureMax, distance: distance ?? this.distance, distanceMax: distanceMax ?? this.distanceMax, size: size ?? this.size, radiusMajor: radiusMajor ?? this.radiusMajor, radiusMinor: radiusMinor ?? this.radiusMinor, radiusMin: radiusMin ?? this.radiusMin, radiusMax: radiusMax ?? this.radiusMax, orientation: orientation ?? this.orientation, tilt: tilt ?? this.tilt, synthesized: synthesized ?? this.synthesized, embedderId: embedderId ?? this.embedderId, ).transformed(transform); } } /// The pointer has moved with respect to the device while the pointer is not /// in contact with the device. /// /// See also: /// /// * [PointerEnterEvent], which reports when the pointer has entered an /// object. /// * [PointerExitEvent], which reports when the pointer has left an object. /// * [PointerMoveEvent], which reports movement while the pointer is in /// contact with the device. /// * [Listener.onPointerHover], which allows callers to be notified of these /// events in a widget tree. class PointerHoverEvent extends PointerEvent with _PointerEventDescription, _CopyPointerHoverEvent { /// Creates a pointer hover event. /// /// All of the arguments must be non-null. const PointerHoverEvent({ super.timeStamp, super.kind, super.pointer, super.device, super.position, super.delta, super.buttons, super.obscured, super.pressureMin, super.pressureMax, super.distance, super.distanceMax, super.size, super.radiusMajor, super.radiusMinor, super.radiusMin, super.radiusMax, super.orientation, super.tilt, super.synthesized, super.embedderId, }) : super( down: false, pressure: 0.0, ); @override PointerHoverEvent transformed(Matrix4? transform) { if (transform == null || transform == this.transform) { return this; } return _TransformedPointerHoverEvent(original as PointerHoverEvent? ?? this, transform); } } class _TransformedPointerHoverEvent extends _TransformedPointerEvent with _CopyPointerHoverEvent implements PointerHoverEvent { _TransformedPointerHoverEvent(this.original, this.transform); @override final PointerHoverEvent original; @override final Matrix4 transform; @override PointerHoverEvent transformed(Matrix4? transform) => original.transformed(transform); } mixin _CopyPointerEnterEvent on PointerEvent { @override PointerEnterEvent copyWith({ Duration? timeStamp, int? pointer, PointerDeviceKind? kind, int? device, Offset? position, Offset? delta, int? buttons, bool? obscured, double? pressure, double? pressureMin, double? pressureMax, double? distance, double? distanceMax, double? size, double? radiusMajor, double? radiusMinor, double? radiusMin, double? radiusMax, double? orientation, double? tilt, bool? synthesized, int? embedderId, }) { return PointerEnterEvent( timeStamp: timeStamp ?? this.timeStamp, kind: kind ?? this.kind, device: device ?? this.device, position: position ?? this.position, delta: delta ?? this.delta, buttons: buttons ?? this.buttons, obscured: obscured ?? this.obscured, pressureMin: pressureMin ?? this.pressureMin, pressureMax: pressureMax ?? this.pressureMax, distance: distance ?? this.distance, distanceMax: distanceMax ?? this.distanceMax, size: size ?? this.size, radiusMajor: radiusMajor ?? this.radiusMajor, radiusMinor: radiusMinor ?? this.radiusMinor, radiusMin: radiusMin ?? this.radiusMin, radiusMax: radiusMax ?? this.radiusMax, orientation: orientation ?? this.orientation, tilt: tilt ?? this.tilt, synthesized: synthesized ?? this.synthesized, embedderId: embedderId ?? this.embedderId, ).transformed(transform); } } /// The pointer has moved with respect to the device while the pointer is or is /// not in contact with the device, and it has entered a target object. /// /// See also: /// /// * [PointerHoverEvent], which reports when the pointer has moved while /// within an object. /// * [PointerExitEvent], which reports when the pointer has left an object. /// * [PointerMoveEvent], which reports movement while the pointer is in /// contact with the device. /// * [MouseRegion.onEnter], which allows callers to be notified of these /// events in a widget tree. class PointerEnterEvent extends PointerEvent with _PointerEventDescription, _CopyPointerEnterEvent { /// Creates a pointer enter event. /// /// All of the arguments must be non-null. const PointerEnterEvent({ super.timeStamp, super.pointer, super.kind, super.device, super.position, super.delta, super.buttons, super.obscured, super.pressureMin, super.pressureMax, super.distance, super.distanceMax, super.size, super.radiusMajor, super.radiusMinor, super.radiusMin, super.radiusMax, super.orientation, super.tilt, super.down, super.synthesized, super.embedderId, }) : // Dart doesn't support comparing enums with == in const contexts yet. // https://github.com/dart-lang/language/issues/1811 assert(!identical(kind, PointerDeviceKind.trackpad)), super( pressure: 0.0, ); /// Creates an enter event from a [PointerEvent]. /// /// This is used by the [MouseTracker] to synthesize enter events. factory PointerEnterEvent.fromMouseEvent(PointerEvent event) => PointerEnterEvent( timeStamp: event.timeStamp, pointer: event.pointer, kind: event.kind, device: event.device, position: event.position, delta: event.delta, buttons: event.buttons, obscured: event.obscured, pressureMin: event.pressureMin, pressureMax: event.pressureMax, distance: event.distance, distanceMax: event.distanceMax, size: event.size, radiusMajor: event.radiusMajor, radiusMinor: event.radiusMinor, radiusMin: event.radiusMin, radiusMax: event.radiusMax, orientation: event.orientation, tilt: event.tilt, down: event.down, synthesized: event.synthesized, ).transformed(event.transform); @override PointerEnterEvent transformed(Matrix4? transform) { if (transform == null || transform == this.transform) { return this; } return _TransformedPointerEnterEvent(original as PointerEnterEvent? ?? this, transform); } } class _TransformedPointerEnterEvent extends _TransformedPointerEvent with _CopyPointerEnterEvent implements PointerEnterEvent { _TransformedPointerEnterEvent(this.original, this.transform); @override final PointerEnterEvent original; @override final Matrix4 transform; @override PointerEnterEvent transformed(Matrix4? transform) => original.transformed(transform); } mixin _CopyPointerExitEvent on PointerEvent { @override PointerExitEvent copyWith({ Duration? timeStamp, int? pointer, PointerDeviceKind? kind, int? device, Offset? position, Offset? delta, int? buttons, bool? obscured, double? pressure, double? pressureMin, double? pressureMax, double? distance, double? distanceMax, double? size, double? radiusMajor, double? radiusMinor, double? radiusMin, double? radiusMax, double? orientation, double? tilt, bool? synthesized, int? embedderId, }) { return PointerExitEvent( timeStamp: timeStamp ?? this.timeStamp, kind: kind ?? this.kind, device: device ?? this.device, position: position ?? this.position, delta: delta ?? this.delta, buttons: buttons ?? this.buttons, obscured: obscured ?? this.obscured, pressureMin: pressureMin ?? this.pressureMin, pressureMax: pressureMax ?? this.pressureMax, distance: distance ?? this.distance, distanceMax: distanceMax ?? this.distanceMax, size: size ?? this.size, radiusMajor: radiusMajor ?? this.radiusMajor, radiusMinor: radiusMinor ?? this.radiusMinor, radiusMin: radiusMin ?? this.radiusMin, radiusMax: radiusMax ?? this.radiusMax, orientation: orientation ?? this.orientation, tilt: tilt ?? this.tilt, synthesized: synthesized ?? this.synthesized, embedderId: embedderId ?? this.embedderId, ).transformed(transform); } } /// The pointer has moved with respect to the device while the pointer is or is /// not in contact with the device, and exited a target object. /// /// See also: /// /// * [PointerHoverEvent], which reports when the pointer has moved while /// within an object. /// * [PointerEnterEvent], which reports when the pointer has entered an object. /// * [PointerMoveEvent], which reports movement while the pointer is in /// contact with the device. /// * [MouseRegion.onExit], which allows callers to be notified of these /// events in a widget tree. class PointerExitEvent extends PointerEvent with _PointerEventDescription, _CopyPointerExitEvent { /// Creates a pointer exit event. /// /// All of the arguments must be non-null. const PointerExitEvent({ super.timeStamp, super.kind, super.pointer, super.device, super.position, super.delta, super.buttons, super.obscured, super.pressureMin, super.pressureMax, super.distance, super.distanceMax, super.size, super.radiusMajor, super.radiusMinor, super.radiusMin, super.radiusMax, super.orientation, super.tilt, super.down, super.synthesized, super.embedderId, }) : assert(!identical(kind, PointerDeviceKind.trackpad)), super( pressure: 0.0, ); /// Creates an exit event from a [PointerEvent]. /// /// This is used by the [MouseTracker] to synthesize exit events. factory PointerExitEvent.fromMouseEvent(PointerEvent event) => PointerExitEvent( timeStamp: event.timeStamp, pointer: event.pointer, kind: event.kind, device: event.device, position: event.position, delta: event.delta, buttons: event.buttons, obscured: event.obscured, pressureMin: event.pressureMin, pressureMax: event.pressureMax, distance: event.distance, distanceMax: event.distanceMax, size: event.size, radiusMajor: event.radiusMajor, radiusMinor: event.radiusMinor, radiusMin: event.radiusMin, radiusMax: event.radiusMax, orientation: event.orientation, tilt: event.tilt, down: event.down, synthesized: event.synthesized, ).transformed(event.transform); @override PointerExitEvent transformed(Matrix4? transform) { if (transform == null || transform == this.transform) { return this; } return _TransformedPointerExitEvent(original as PointerExitEvent? ?? this, transform); } } class _TransformedPointerExitEvent extends _TransformedPointerEvent with _CopyPointerExitEvent implements PointerExitEvent { _TransformedPointerExitEvent(this.original, this.transform); @override final PointerExitEvent original; @override final Matrix4 transform; @override PointerExitEvent transformed(Matrix4? transform) => original.transformed(transform); } mixin _CopyPointerDownEvent on PointerEvent { @override PointerDownEvent copyWith({ Duration? timeStamp, int? pointer, PointerDeviceKind? kind, int? device, Offset? position, Offset? delta, int? buttons, bool? obscured, double? pressure, double? pressureMin, double? pressureMax, double? distance, double? distanceMax, double? size, double? radiusMajor, double? radiusMinor, double? radiusMin, double? radiusMax, double? orientation, double? tilt, bool? synthesized, int? embedderId, }) { return PointerDownEvent( timeStamp: timeStamp ?? this.timeStamp, pointer: pointer ?? this.pointer, kind: kind ?? this.kind, device: device ?? this.device, position: position ?? this.position, buttons: buttons ?? this.buttons, obscured: obscured ?? this.obscured, pressure: pressure ?? this.pressure, pressureMin: pressureMin ?? this.pressureMin, pressureMax: pressureMax ?? this.pressureMax, distanceMax: distanceMax ?? this.distanceMax, size: size ?? this.size, radiusMajor: radiusMajor ?? this.radiusMajor, radiusMinor: radiusMinor ?? this.radiusMinor, radiusMin: radiusMin ?? this.radiusMin, radiusMax: radiusMax ?? this.radiusMax, orientation: orientation ?? this.orientation, tilt: tilt ?? this.tilt, embedderId: embedderId ?? this.embedderId, ).transformed(transform); } } /// The pointer has made contact with the device. /// /// See also: /// /// * [Listener.onPointerDown], which allows callers to be notified of these /// events in a widget tree. class PointerDownEvent extends PointerEvent with _PointerEventDescription, _CopyPointerDownEvent { /// Creates a pointer down event. /// /// All of the arguments must be non-null. const PointerDownEvent({ super.timeStamp, super.pointer, super.kind, super.device, super.position, super.buttons = kPrimaryButton, super.obscured, super.pressure, super.pressureMin, super.pressureMax, super.distanceMax, super.size, super.radiusMajor, super.radiusMinor, super.radiusMin, super.radiusMax, super.orientation, super.tilt, super.embedderId, }) : assert(!identical(kind, PointerDeviceKind.trackpad)), super( down: true, distance: 0.0, ); @override PointerDownEvent transformed(Matrix4? transform) { if (transform == null || transform == this.transform) { return this; } return _TransformedPointerDownEvent(original as PointerDownEvent? ?? this, transform); } } class _TransformedPointerDownEvent extends _TransformedPointerEvent with _CopyPointerDownEvent implements PointerDownEvent { _TransformedPointerDownEvent(this.original, this.transform); @override final PointerDownEvent original; @override final Matrix4 transform; @override PointerDownEvent transformed(Matrix4? transform) => original.transformed(transform); } mixin _CopyPointerMoveEvent on PointerEvent { @override PointerMoveEvent copyWith({ Duration? timeStamp, int? pointer, PointerDeviceKind? kind, int? device, Offset? position, Offset? delta, int? buttons, bool? obscured, double? pressure, double? pressureMin, double? pressureMax, double? distance, double? distanceMax, double? size, double? radiusMajor, double? radiusMinor, double? radiusMin, double? radiusMax, double? orientation, double? tilt, bool? synthesized, int? embedderId, }) { return PointerMoveEvent( timeStamp: timeStamp ?? this.timeStamp, pointer: pointer ?? this.pointer, kind: kind ?? this.kind, device: device ?? this.device, position: position ?? this.position, delta: delta ?? this.delta, buttons: buttons ?? this.buttons, obscured: obscured ?? this.obscured, pressure: pressure ?? this.pressure, pressureMin: pressureMin ?? this.pressureMin, pressureMax: pressureMax ?? this.pressureMax, distanceMax: distanceMax ?? this.distanceMax, size: size ?? this.size, radiusMajor: radiusMajor ?? this.radiusMajor, radiusMinor: radiusMinor ?? this.radiusMinor, radiusMin: radiusMin ?? this.radiusMin, radiusMax: radiusMax ?? this.radiusMax, orientation: orientation ?? this.orientation, tilt: tilt ?? this.tilt, synthesized: synthesized ?? this.synthesized, embedderId: embedderId ?? this.embedderId, ).transformed(transform); } } /// The pointer has moved with respect to the device while the pointer is in /// contact with the device. /// /// See also: /// /// * [PointerHoverEvent], which reports movement while the pointer is not in /// contact with the device. /// * [Listener.onPointerMove], which allows callers to be notified of these /// events in a widget tree. class PointerMoveEvent extends PointerEvent with _PointerEventDescription, _CopyPointerMoveEvent { /// Creates a pointer move event. /// /// All of the arguments must be non-null. const PointerMoveEvent({ super.timeStamp, super.pointer, super.kind, super.device, super.position, super.delta, super.buttons = kPrimaryButton, super.obscured, super.pressure, super.pressureMin, super.pressureMax, super.distanceMax, super.size, super.radiusMajor, super.radiusMinor, super.radiusMin, super.radiusMax, super.orientation, super.tilt, super.platformData, super.synthesized, super.embedderId, }) : assert(!identical(kind, PointerDeviceKind.trackpad)), super( down: true, distance: 0.0, ); @override PointerMoveEvent transformed(Matrix4? transform) { if (transform == null || transform == this.transform) { return this; } return _TransformedPointerMoveEvent(original as PointerMoveEvent? ?? this, transform); } } class _TransformedPointerMoveEvent extends _TransformedPointerEvent with _CopyPointerMoveEvent implements PointerMoveEvent { _TransformedPointerMoveEvent(this.original, this.transform); @override final PointerMoveEvent original; @override final Matrix4 transform; @override PointerMoveEvent transformed(Matrix4? transform) => original.transformed(transform); } mixin _CopyPointerUpEvent on PointerEvent { @override PointerUpEvent copyWith({ Duration? timeStamp, int? pointer, PointerDeviceKind? kind, int? device, Offset? position, Offset? localPosition, Offset? delta, int? buttons, bool? obscured, double? pressure, double? pressureMin, double? pressureMax, double? distance, double? distanceMax, double? size, double? radiusMajor, double? radiusMinor, double? radiusMin, double? radiusMax, double? orientation, double? tilt, bool? synthesized, int? embedderId, }) { return PointerUpEvent( timeStamp: timeStamp ?? this.timeStamp, pointer: pointer ?? this.pointer, kind: kind ?? this.kind, device: device ?? this.device, position: position ?? this.position, buttons: buttons ?? this.buttons, obscured: obscured ?? this.obscured, pressure: pressure ?? this.pressure, pressureMin: pressureMin ?? this.pressureMin, pressureMax: pressureMax ?? this.pressureMax, distance: distance ?? this.distance, distanceMax: distanceMax ?? this.distanceMax, size: size ?? this.size, radiusMajor: radiusMajor ?? this.radiusMajor, radiusMinor: radiusMinor ?? this.radiusMinor, radiusMin: radiusMin ?? this.radiusMin, radiusMax: radiusMax ?? this.radiusMax, orientation: orientation ?? this.orientation, tilt: tilt ?? this.tilt, embedderId: embedderId ?? this.embedderId, ).transformed(transform); } } /// The pointer has stopped making contact with the device. /// /// See also: /// /// * [Listener.onPointerUp], which allows callers to be notified of these /// events in a widget tree. class PointerUpEvent extends PointerEvent with _PointerEventDescription, _CopyPointerUpEvent { /// Creates a pointer up event. /// /// All of the arguments must be non-null. const PointerUpEvent({ super.timeStamp, super.pointer, super.kind, super.device, super.position, super.buttons, super.obscured, // Allow pressure customization here because PointerUpEvent can contain // non-zero pressure. See https://github.com/flutter/flutter/issues/31340 super.pressure = 0.0, super.pressureMin, super.pressureMax, super.distance, super.distanceMax, super.size, super.radiusMajor, super.radiusMinor, super.radiusMin, super.radiusMax, super.orientation, super.tilt, super.embedderId, }) : assert(!identical(kind, PointerDeviceKind.trackpad)), super( down: false, ); @override PointerUpEvent transformed(Matrix4? transform) { if (transform == null || transform == this.transform) { return this; } return _TransformedPointerUpEvent(original as PointerUpEvent? ?? this, transform); } } class _TransformedPointerUpEvent extends _TransformedPointerEvent with _CopyPointerUpEvent implements PointerUpEvent { _TransformedPointerUpEvent(this.original, this.transform); @override final PointerUpEvent original; @override final Matrix4 transform; @override PointerUpEvent transformed(Matrix4? transform) => original.transformed(transform); } /// An event that corresponds to a discrete pointer signal. /// /// Pointer signals are events that originate from the pointer but don't change /// the state of the pointer itself, and are discrete rather than needing to be /// interpreted in the context of a series of events. /// /// See also: /// /// * [Listener.onPointerSignal], which allows callers to be notified of these /// events in a widget tree. /// * [PointerSignalResolver], which provides an opt-in mechanism whereby /// participating agents may disambiguate an event's target. abstract class PointerSignalEvent extends PointerEvent { /// Abstract const constructor. This constructor enables subclasses to provide /// const constructors so that they can be used in const expressions. const PointerSignalEvent({ super.timeStamp, super.pointer, super.kind = PointerDeviceKind.mouse, super.device, super.position, super.embedderId, }); } mixin _CopyPointerScrollEvent on PointerEvent { /// The amount to scroll, in logical pixels. Offset get scrollDelta; @override PointerScrollEvent copyWith({ Duration? timeStamp, int? pointer, PointerDeviceKind? kind, int? device, Offset? position, Offset? delta, int? buttons, bool? obscured, double? pressure, double? pressureMin, double? pressureMax, double? distance, double? distanceMax, double? size, double? radiusMajor, double? radiusMinor, double? radiusMin, double? radiusMax, double? orientation, double? tilt, bool? synthesized, int? embedderId, }) { return PointerScrollEvent( timeStamp: timeStamp ?? this.timeStamp, kind: kind ?? this.kind, device: device ?? this.device, position: position ?? this.position, scrollDelta: scrollDelta, embedderId: embedderId ?? this.embedderId, ).transformed(transform); } } /// The pointer issued a scroll event. /// /// Scrolling the scroll wheel on a mouse is an example of an event that /// would create a [PointerScrollEvent]. /// /// See also: /// /// * [Listener.onPointerSignal], which allows callers to be notified of these /// events in a widget tree. /// * [PointerSignalResolver], which provides an opt-in mechanism whereby /// participating agents may disambiguate an event's target. class PointerScrollEvent extends PointerSignalEvent with _PointerEventDescription, _CopyPointerScrollEvent { /// Creates a pointer scroll event. /// /// All of the arguments must be non-null. const PointerScrollEvent({ super.timeStamp, super.kind, super.device, super.position, this.scrollDelta = Offset.zero, super.embedderId, }); @override final Offset scrollDelta; @override PointerScrollEvent transformed(Matrix4? transform) { if (transform == null || transform == this.transform) { return this; } return _TransformedPointerScrollEvent(original as PointerScrollEvent? ?? this, transform); } @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties.add(DiagnosticsProperty<Offset>('scrollDelta', scrollDelta)); } } class _TransformedPointerScrollEvent extends _TransformedPointerEvent with _CopyPointerScrollEvent implements PointerScrollEvent { _TransformedPointerScrollEvent(this.original, this.transform); @override final PointerScrollEvent original; @override final Matrix4 transform; @override Offset get scrollDelta => original.scrollDelta; @override PointerScrollEvent transformed(Matrix4? transform) => original.transformed(transform); @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties.add(DiagnosticsProperty<Offset>('scrollDelta', scrollDelta)); } } mixin _CopyPointerScrollInertiaCancelEvent on PointerEvent { @override PointerScrollInertiaCancelEvent copyWith({ Duration? timeStamp, int? pointer, PointerDeviceKind? kind, int? device, Offset? position, Offset? delta, int? buttons, bool? obscured, double? pressure, double? pressureMin, double? pressureMax, double? distance, double? distanceMax, double? size, double? radiusMajor, double? radiusMinor, double? radiusMin, double? radiusMax, double? orientation, double? tilt, bool? synthesized, int? embedderId, }) { return PointerScrollInertiaCancelEvent( timeStamp: timeStamp ?? this.timeStamp, kind: kind ?? this.kind, device: device ?? this.device, position: position ?? this.position, embedderId: embedderId ?? this.embedderId, ).transformed(transform); } } /// The pointer issued a scroll-inertia cancel event. /// /// Touching the trackpad immediately after a scroll is an example of an event /// that would create a [PointerScrollInertiaCancelEvent]. /// /// See also: /// /// * [Listener.onPointerSignal], which allows callers to be notified of these /// events in a widget tree. /// * [PointerSignalResolver], which provides an opt-in mechanism whereby /// participating agents may disambiguate an event's target. class PointerScrollInertiaCancelEvent extends PointerSignalEvent with _PointerEventDescription, _CopyPointerScrollInertiaCancelEvent { /// Creates a pointer scroll-inertia cancel event. /// /// All of the arguments must be non-null. const PointerScrollInertiaCancelEvent({ super.timeStamp, super.kind, super.device, super.position, super.embedderId, }); @override PointerScrollInertiaCancelEvent transformed(Matrix4? transform) { if (transform == null || transform == this.transform) { return this; } return _TransformedPointerScrollInertiaCancelEvent(original as PointerScrollInertiaCancelEvent? ?? this, transform); } } class _TransformedPointerScrollInertiaCancelEvent extends _TransformedPointerEvent with _CopyPointerScrollInertiaCancelEvent implements PointerScrollInertiaCancelEvent { _TransformedPointerScrollInertiaCancelEvent(this.original, this.transform); @override final PointerScrollInertiaCancelEvent original; @override final Matrix4 transform; @override PointerScrollInertiaCancelEvent transformed(Matrix4? transform) => original.transformed(transform); } mixin _CopyPointerScaleEvent on PointerEvent { /// The scale (zoom factor) of the event. double get scale; @override PointerScaleEvent copyWith({ Duration? timeStamp, int? pointer, PointerDeviceKind? kind, int? device, Offset? position, Offset? delta, int? buttons, bool? obscured, double? pressure, double? pressureMin, double? pressureMax, double? distance, double? distanceMax, double? size, double? radiusMajor, double? radiusMinor, double? radiusMin, double? radiusMax, double? orientation, double? tilt, bool? synthesized, int? embedderId, double? scale, }) { return PointerScaleEvent( timeStamp: timeStamp ?? this.timeStamp, kind: kind ?? this.kind, device: device ?? this.device, position: position ?? this.position, embedderId: embedderId ?? this.embedderId, scale: scale ?? this.scale, ).transformed(transform); } } /// The pointer issued a scale event. /// /// Pinching-to-zoom in the browser is an example of an event /// that would create a [PointerScaleEvent]. /// /// See also: /// /// * [Listener.onPointerSignal], which allows callers to be notified of these /// events in a widget tree. /// * [PointerSignalResolver], which provides an opt-in mechanism whereby /// participating agents may disambiguate an event's target. class PointerScaleEvent extends PointerSignalEvent with _PointerEventDescription, _CopyPointerScaleEvent { /// Creates a pointer scale event. /// /// All of the arguments must be non-null. const PointerScaleEvent({ super.timeStamp, super.kind, super.device, super.position, super.embedderId, this.scale = 1.0, }); @override final double scale; @override PointerScaleEvent transformed(Matrix4? transform) { if (transform == null || transform == this.transform) { return this; } return _TransformedPointerScaleEvent(original as PointerScaleEvent? ?? this, transform); } } class _TransformedPointerScaleEvent extends _TransformedPointerEvent with _CopyPointerScaleEvent implements PointerScaleEvent { _TransformedPointerScaleEvent(this.original, this.transform); @override final PointerScaleEvent original; @override final Matrix4 transform; @override double get scale => original.scale; @override PointerScaleEvent transformed(Matrix4? transform) => original.transformed(transform); } mixin _CopyPointerPanZoomStartEvent on PointerEvent { @override PointerPanZoomStartEvent copyWith({ Duration? timeStamp, int? pointer, PointerDeviceKind? kind, int? device, Offset? position, Offset? delta, int? buttons, bool? obscured, double? pressure, double? pressureMin, double? pressureMax, double? distance, double? distanceMax, double? size, double? radiusMajor, double? radiusMinor, double? radiusMin, double? radiusMax, double? orientation, double? tilt, bool? synthesized, int? embedderId, }) { assert(kind == null || identical(kind, PointerDeviceKind.trackpad)); return PointerPanZoomStartEvent( timeStamp: timeStamp ?? this.timeStamp, device: device ?? this.device, position: position ?? this.position, embedderId: embedderId ?? this.embedderId, ).transformed(transform); } } /// A pan/zoom has begun on this pointer. /// /// See also: /// /// * [Listener.onPointerPanZoomStart], which allows callers to be notified of these /// events in a widget tree. class PointerPanZoomStartEvent extends PointerEvent with _PointerEventDescription, _CopyPointerPanZoomStartEvent { /// Creates a pointer pan/zoom start event. /// /// All of the arguments must be non-null. const PointerPanZoomStartEvent({ super.timeStamp, super.device, super.pointer, super.position, super.embedderId, super.synthesized, }) : super(kind: PointerDeviceKind.trackpad); @override PointerPanZoomStartEvent transformed(Matrix4? transform) { if (transform == null || transform == this.transform) { return this; } return _TransformedPointerPanZoomStartEvent(original as PointerPanZoomStartEvent? ?? this, transform); } } class _TransformedPointerPanZoomStartEvent extends _TransformedPointerEvent with _CopyPointerPanZoomStartEvent implements PointerPanZoomStartEvent { _TransformedPointerPanZoomStartEvent(this.original, this.transform); @override final PointerPanZoomStartEvent original; @override final Matrix4 transform; @override PointerPanZoomStartEvent transformed(Matrix4? transform) => original.transformed(transform); } mixin _CopyPointerPanZoomUpdateEvent on PointerEvent { /// The total pan offset of the pan/zoom. Offset get pan; /// The total pan offset of the pan/zoom, transformed into local coordinates. Offset get localPan; /// The amount the pan offset changed since the last event. Offset get panDelta; /// The amount the pan offset changed since the last event, transformed into local coordinates. Offset get localPanDelta; /// The scale (zoom factor) of the pan/zoom. double get scale; /// The amount the pan/zoom has rotated in radians so far. double get rotation; @override PointerPanZoomUpdateEvent copyWith({ Duration? timeStamp, int? pointer, PointerDeviceKind? kind, int? device, Offset? position, Offset? delta, int? buttons, bool? obscured, double? pressure, double? pressureMin, double? pressureMax, double? distance, double? distanceMax, double? size, double? radiusMajor, double? radiusMinor, double? radiusMin, double? radiusMax, double? orientation, double? tilt, bool? synthesized, int? embedderId, Offset? pan, Offset? localPan, Offset? panDelta, Offset? localPanDelta, double? scale, double? rotation, }) { assert(kind == null || identical(kind, PointerDeviceKind.trackpad)); return PointerPanZoomUpdateEvent( timeStamp: timeStamp ?? this.timeStamp, device: device ?? this.device, position: position ?? this.position, embedderId: embedderId ?? this.embedderId, pan: pan ?? this.pan, panDelta: panDelta ?? this.panDelta, scale: scale ?? this.scale, rotation: rotation ?? this.rotation, ).transformed(transform); } } /// The active pan/zoom on this pointer has updated. /// /// See also: /// /// * [Listener.onPointerPanZoomUpdate], which allows callers to be notified of these /// events in a widget tree. class PointerPanZoomUpdateEvent extends PointerEvent with _PointerEventDescription, _CopyPointerPanZoomUpdateEvent { /// Creates a pointer pan/zoom update event. /// /// All of the arguments must be non-null. const PointerPanZoomUpdateEvent({ super.timeStamp, super.device, super.pointer, super.position, super.embedderId, this.pan = Offset.zero, this.panDelta = Offset.zero, this.scale = 1.0, this.rotation = 0.0, super.synthesized, }) : super(kind: PointerDeviceKind.trackpad); @override final Offset pan; @override Offset get localPan => pan; @override final Offset panDelta; @override Offset get localPanDelta => panDelta; @override final double scale; @override final double rotation; @override PointerPanZoomUpdateEvent transformed(Matrix4? transform) { if (transform == null || transform == this.transform) { return this; } return _TransformedPointerPanZoomUpdateEvent(original as PointerPanZoomUpdateEvent? ?? this, transform); } } class _TransformedPointerPanZoomUpdateEvent extends _TransformedPointerEvent with _CopyPointerPanZoomUpdateEvent implements PointerPanZoomUpdateEvent { _TransformedPointerPanZoomUpdateEvent(this.original, this.transform); @override Offset get pan => original.pan; @override late final Offset localPan = PointerEvent.transformPosition(transform, pan); @override Offset get panDelta => original.panDelta; @override late final Offset localPanDelta = PointerEvent.transformDeltaViaPositions( transform: transform, untransformedDelta: panDelta, untransformedEndPosition: pan, transformedEndPosition: localPan, ); @override double get scale => original.scale; @override double get rotation => original.rotation; @override final PointerPanZoomUpdateEvent original; @override final Matrix4 transform; @override PointerPanZoomUpdateEvent transformed(Matrix4? transform) => original.transformed(transform); } mixin _CopyPointerPanZoomEndEvent on PointerEvent { @override PointerPanZoomEndEvent copyWith({ Duration? timeStamp, int? pointer, PointerDeviceKind? kind, int? device, Offset? position, Offset? delta, int? buttons, bool? obscured, double? pressure, double? pressureMin, double? pressureMax, double? distance, double? distanceMax, double? size, double? radiusMajor, double? radiusMinor, double? radiusMin, double? radiusMax, double? orientation, double? tilt, bool? synthesized, int? embedderId, }) { assert(kind == null || identical(kind, PointerDeviceKind.trackpad)); return PointerPanZoomEndEvent( timeStamp: timeStamp ?? this.timeStamp, device: device ?? this.device, position: position ?? this.position, embedderId: embedderId ?? this.embedderId, ).transformed(transform); } } /// The pan/zoom on this pointer has ended. /// /// See also: /// /// * [Listener.onPointerPanZoomEnd], which allows callers to be notified of these /// events in a widget tree. class PointerPanZoomEndEvent extends PointerEvent with _PointerEventDescription, _CopyPointerPanZoomEndEvent { /// Creates a pointer pan/zoom end event. /// /// All of the arguments must be non-null. const PointerPanZoomEndEvent({ super.timeStamp, super.device, super.pointer, super.position, super.embedderId, super.synthesized, }) : super(kind: PointerDeviceKind.trackpad); @override PointerPanZoomEndEvent transformed(Matrix4? transform) { if (transform == null || transform == this.transform) { return this; } return _TransformedPointerPanZoomEndEvent(original as PointerPanZoomEndEvent? ?? this, transform); } } class _TransformedPointerPanZoomEndEvent extends _TransformedPointerEvent with _CopyPointerPanZoomEndEvent implements PointerPanZoomEndEvent { _TransformedPointerPanZoomEndEvent(this.original, this.transform); @override final PointerPanZoomEndEvent original; @override final Matrix4 transform; @override PointerPanZoomEndEvent transformed(Matrix4? transform) => original.transformed(transform); } mixin _CopyPointerCancelEvent on PointerEvent { @override PointerCancelEvent copyWith({ Duration? timeStamp, int? pointer, PointerDeviceKind? kind, int? device, Offset? position, Offset? delta, int? buttons, bool? obscured, double? pressure, double? pressureMin, double? pressureMax, double? distance, double? distanceMax, double? size, double? radiusMajor, double? radiusMinor, double? radiusMin, double? radiusMax, double? orientation, double? tilt, bool? synthesized, int? embedderId, }) { return PointerCancelEvent( timeStamp: timeStamp ?? this.timeStamp, pointer: pointer ?? this.pointer, kind: kind ?? this.kind, device: device ?? this.device, position: position ?? this.position, buttons: buttons ?? this.buttons, obscured: obscured ?? this.obscured, pressureMin: pressureMin ?? this.pressureMin, pressureMax: pressureMax ?? this.pressureMax, distance: distance ?? this.distance, distanceMax: distanceMax ?? this.distanceMax, size: size ?? this.size, radiusMajor: radiusMajor ?? this.radiusMajor, radiusMinor: radiusMinor ?? this.radiusMinor, radiusMin: radiusMin ?? this.radiusMin, radiusMax: radiusMax ?? this.radiusMax, orientation: orientation ?? this.orientation, tilt: tilt ?? this.tilt, embedderId: embedderId ?? this.embedderId, ).transformed(transform); } } /// The input from the pointer is no longer directed towards this receiver. /// /// See also: /// /// * [Listener.onPointerCancel], which allows callers to be notified of these /// events in a widget tree. class PointerCancelEvent extends PointerEvent with _PointerEventDescription, _CopyPointerCancelEvent { /// Creates a pointer cancel event. /// /// All of the arguments must be non-null. const PointerCancelEvent({ super.timeStamp, super.pointer, super.kind, super.device, super.position, super.buttons, super.obscured, super.pressureMin, super.pressureMax, super.distance, super.distanceMax, super.size, super.radiusMajor, super.radiusMinor, super.radiusMin, super.radiusMax, super.orientation, super.tilt, super.embedderId, }) : assert(!identical(kind, PointerDeviceKind.trackpad)), super( down: false, pressure: 0.0, ); @override PointerCancelEvent transformed(Matrix4? transform) { if (transform == null || transform == this.transform) { return this; } return _TransformedPointerCancelEvent(original as PointerCancelEvent? ?? this, transform); } } /// Determine the appropriate hit slop pixels based on the [kind] of pointer. double computeHitSlop(PointerDeviceKind kind, DeviceGestureSettings? settings) { switch (kind) { case PointerDeviceKind.mouse: return kPrecisePointerHitSlop; case PointerDeviceKind.stylus: case PointerDeviceKind.invertedStylus: case PointerDeviceKind.unknown: case PointerDeviceKind.touch: case PointerDeviceKind.trackpad: return settings?.touchSlop ?? kTouchSlop; } } /// Determine the appropriate pan slop pixels based on the [kind] of pointer. double computePanSlop(PointerDeviceKind kind, DeviceGestureSettings? settings) { switch (kind) { case PointerDeviceKind.mouse: return kPrecisePointerPanSlop; case PointerDeviceKind.stylus: case PointerDeviceKind.invertedStylus: case PointerDeviceKind.unknown: case PointerDeviceKind.touch: case PointerDeviceKind.trackpad: return settings?.panSlop ?? kPanSlop; } } /// Determine the appropriate scale slop pixels based on the [kind] of pointer. double computeScaleSlop(PointerDeviceKind kind) { switch (kind) { case PointerDeviceKind.mouse: return kPrecisePointerScaleSlop; case PointerDeviceKind.stylus: case PointerDeviceKind.invertedStylus: case PointerDeviceKind.unknown: case PointerDeviceKind.touch: case PointerDeviceKind.trackpad: return kScaleSlop; } } class _TransformedPointerCancelEvent extends _TransformedPointerEvent with _CopyPointerCancelEvent implements PointerCancelEvent { _TransformedPointerCancelEvent(this.original, this.transform); @override final PointerCancelEvent original; @override final Matrix4 transform; @override PointerCancelEvent transformed(Matrix4? transform) => original.transformed(transform); }