Unverified Commit 5792c734 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

Use Device specific gesture configuration for scroll views (#87604)

parent 08932348
......@@ -17,6 +17,7 @@ export 'src/gestures/drag_details.dart';
export 'src/gestures/eager.dart';
export 'src/gestures/events.dart';
export 'src/gestures/force_press.dart';
export 'src/gestures/gesture_settings.dart';
export 'src/gestures/hit_test.dart';
export 'src/gestures/long_press.dart';
export 'src/gestures/lsq_solver.dart';
......
......@@ -9,6 +9,7 @@ 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;
......@@ -2099,7 +2100,7 @@ class PointerCancelEvent extends PointerEvent with _PointerEventDescription, _Co
}
/// Determine the appropriate hit slop pixels based on the [kind] of pointer.
double computeHitSlop(PointerDeviceKind kind) {
double computeHitSlop(PointerDeviceKind kind, DeviceGestureSettings? settings) {
switch (kind) {
case PointerDeviceKind.mouse:
return kPrecisePointerHitSlop;
......@@ -2107,12 +2108,12 @@ double computeHitSlop(PointerDeviceKind kind) {
case PointerDeviceKind.invertedStylus:
case PointerDeviceKind.unknown:
case PointerDeviceKind.touch:
return kTouchSlop;
return settings?.touchSlop ?? kTouchSlop;
}
}
/// Determine the appropriate pan slop pixels based on the [kind] of pointer.
double computePanSlop(PointerDeviceKind kind) {
double computePanSlop(PointerDeviceKind kind, DeviceGestureSettings? settings) {
switch (kind) {
case PointerDeviceKind.mouse:
return kPrecisePointerPanSlop;
......@@ -2120,7 +2121,7 @@ double computePanSlop(PointerDeviceKind kind) {
case PointerDeviceKind.invertedStylus:
case PointerDeviceKind.unknown:
case PointerDeviceKind.touch:
return kPanSlop;
return settings?.panSlop ?? kPanSlop;
}
}
......
......@@ -249,7 +249,7 @@ class ForcePressGestureRecognizer extends OneSequenceGestureRecognizer {
if (pressure > startPressure) {
_state = _ForceState.started;
resolve(GestureDisposition.accepted);
} else if (event.delta.distanceSquared > computeHitSlop(event.kind)) {
} else if (event.delta.distanceSquared > computeHitSlop(event.kind, gestureSettings)) {
resolve(GestureDisposition.rejected);
}
}
......
// 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' as ui;
import 'package:flutter/foundation.dart';
/// The device specific gesture settings scaled into logical pixels.
///
/// This configuration can be retrieved from the window, or more commonly from a
/// [MediaQuery] widget.
///
/// See also:
///
/// * [ui.GestureSettings], the configuration that this is derived from.
@immutable
class DeviceGestureSettings {
/// Create a new [DeviceGestureSettings] with configured settings in logical
/// pixels.
const DeviceGestureSettings({
this.touchSlop,
});
/// Create a new [DeviceGestureSettings] from the provided [window].
factory DeviceGestureSettings.fromWindow(ui.SingletonFlutterWindow window) {
final double? physicalTouchSlop = window.viewConfiguration.gestureSettings.physicalTouchSlop;
return DeviceGestureSettings(
touchSlop: physicalTouchSlop == null ? null : physicalTouchSlop / window.devicePixelRatio
);
}
/// The touch slop value in logical pixels, or `null` if it was not set.
final double? touchSlop;
/// The touch slop value for pan gestures, in logical pixels, or `null` if it
/// was not set.
double? get panSlop => touchSlop != null ? (touchSlop! * 2) : null;
@override
int get hashCode => ui.hashValues(touchSlop, 23);
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType)
return false;
return other is DeviceGestureSettings
&& other.touchSlop == touchSlop;
}
@override
String toString() => 'DeviceGestureSettings(touchSlop: $touchSlop)';
}
......@@ -233,7 +233,7 @@ abstract class DragGestureRecognizer extends OneSequenceGestureRecognizer {
Offset _getDeltaForDetails(Offset delta);
double? _getPrimaryValueFromOffset(Offset value);
bool _hasSufficientGlobalDistanceToAccept(PointerDeviceKind pointerDeviceKind);
bool _hasSufficientGlobalDistanceToAccept(PointerDeviceKind pointerDeviceKind, double? deviceTouchSlop);
final Map<int, VelocityTracker> _velocityTrackers = <int, VelocityTracker>{};
......@@ -313,7 +313,7 @@ abstract class DragGestureRecognizer extends OneSequenceGestureRecognizer {
untransformedDelta: movedLocally,
untransformedEndPosition: event.localPosition,
).distance * (_getPrimaryValueFromOffset(movedLocally) ?? 1).sign;
if (_hasSufficientGlobalDistanceToAccept(event.kind))
if (_hasSufficientGlobalDistanceToAccept(event.kind, gestureSettings?.touchSlop))
resolve(GestureDisposition.accepted);
}
}
......@@ -535,13 +535,13 @@ class VerticalDragGestureRecognizer extends DragGestureRecognizer {
@override
bool isFlingGesture(VelocityEstimate estimate, PointerDeviceKind kind) {
final double minVelocity = minFlingVelocity ?? kMinFlingVelocity;
final double minDistance = minFlingDistance ?? computeHitSlop(kind);
final double minDistance = minFlingDistance ?? computeHitSlop(kind, gestureSettings);
return estimate.pixelsPerSecond.dy.abs() > minVelocity && estimate.offset.dy.abs() > minDistance;
}
@override
bool _hasSufficientGlobalDistanceToAccept(PointerDeviceKind pointerDeviceKind) {
return _globalDistanceMoved.abs() > computeHitSlop(pointerDeviceKind);
bool _hasSufficientGlobalDistanceToAccept(PointerDeviceKind pointerDeviceKind, double? deviceTouchSlop) {
return _globalDistanceMoved.abs() > computeHitSlop(pointerDeviceKind, gestureSettings);
}
@override
......@@ -585,13 +585,13 @@ class HorizontalDragGestureRecognizer extends DragGestureRecognizer {
@override
bool isFlingGesture(VelocityEstimate estimate, PointerDeviceKind kind) {
final double minVelocity = minFlingVelocity ?? kMinFlingVelocity;
final double minDistance = minFlingDistance ?? computeHitSlop(kind);
final double minDistance = minFlingDistance ?? computeHitSlop(kind, gestureSettings);
return estimate.pixelsPerSecond.dx.abs() > minVelocity && estimate.offset.dx.abs() > minDistance;
}
@override
bool _hasSufficientGlobalDistanceToAccept(PointerDeviceKind pointerDeviceKind) {
return _globalDistanceMoved.abs() > computeHitSlop(pointerDeviceKind);
bool _hasSufficientGlobalDistanceToAccept(PointerDeviceKind pointerDeviceKind, double? deviceTouchSlop) {
return _globalDistanceMoved.abs() > computeHitSlop(pointerDeviceKind, gestureSettings);
}
@override
......@@ -620,14 +620,14 @@ class PanGestureRecognizer extends DragGestureRecognizer {
@override
bool isFlingGesture(VelocityEstimate estimate, PointerDeviceKind kind) {
final double minVelocity = minFlingVelocity ?? kMinFlingVelocity;
final double minDistance = minFlingDistance ?? computeHitSlop(kind);
final double minDistance = minFlingDistance ?? computeHitSlop(kind, gestureSettings);
return estimate.pixelsPerSecond.distanceSquared > minVelocity * minVelocity
&& estimate.offset.distanceSquared > minDistance * minDistance;
}
@override
bool _hasSufficientGlobalDistanceToAccept(PointerDeviceKind pointerDeviceKind) {
return _globalDistanceMoved.abs() > computePanSlop(pointerDeviceKind);
bool _hasSufficientGlobalDistanceToAccept(PointerDeviceKind pointerDeviceKind, double? deviceTouchSlop) {
return _globalDistanceMoved.abs() > computePanSlop(pointerDeviceKind, gestureSettings);
}
@override
......
......@@ -13,6 +13,7 @@ import 'constants.dart';
import 'drag.dart';
import 'drag_details.dart';
import 'events.dart';
import 'gesture_settings.dart';
import 'recognizer.dart';
import 'velocity_tracker.dart';
......@@ -27,10 +28,16 @@ abstract class MultiDragPointerState {
/// Creates per-pointer state for a [MultiDragGestureRecognizer].
///
/// The [initialPosition] argument must not be null.
MultiDragPointerState(this.initialPosition, this.kind)
MultiDragPointerState(this.initialPosition, this.kind, this.gestureSettings)
: assert(initialPosition != null),
_velocityTracker = VelocityTracker.withKind(kind);
/// Device specific gesture configuration that should be preferred over
/// framework constants.
///
/// These settings are commonly retrieved from a [MediaQuery].
final DeviceGestureSettings? gestureSettings;
/// The global coordinates of the pointer when the pointer contacted the screen.
final Offset initialPosition;
......@@ -326,12 +333,12 @@ abstract class MultiDragGestureRecognizer extends GestureRecognizer {
}
class _ImmediatePointerState extends MultiDragPointerState {
_ImmediatePointerState(Offset initialPosition, PointerDeviceKind kind) : super(initialPosition, kind);
_ImmediatePointerState(Offset initialPosition, PointerDeviceKind kind, DeviceGestureSettings? deviceGestureSettings) : super(initialPosition, kind, deviceGestureSettings);
@override
void checkForResolutionAfterMove() {
assert(pendingDelta != null);
if (pendingDelta!.distance > computeHitSlop(kind))
if (pendingDelta!.distance > computeHitSlop(kind, gestureSettings))
resolve(GestureDisposition.accepted);
}
......@@ -377,7 +384,7 @@ class ImmediateMultiDragGestureRecognizer extends MultiDragGestureRecognizer {
@override
MultiDragPointerState createNewPointerState(PointerDownEvent event) {
return _ImmediatePointerState(event.position, event.kind);
return _ImmediatePointerState(event.position, event.kind, gestureSettings);
}
@override
......@@ -386,12 +393,12 @@ class ImmediateMultiDragGestureRecognizer extends MultiDragGestureRecognizer {
class _HorizontalPointerState extends MultiDragPointerState {
_HorizontalPointerState(Offset initialPosition, PointerDeviceKind kind) : super(initialPosition, kind);
_HorizontalPointerState(Offset initialPosition, PointerDeviceKind kind, DeviceGestureSettings? deviceGestureSettings): super(initialPosition, kind, deviceGestureSettings);
@override
void checkForResolutionAfterMove() {
assert(pendingDelta != null);
if (pendingDelta!.dx.abs() > computeHitSlop(kind))
if (pendingDelta!.dx.abs() > computeHitSlop(kind, gestureSettings))
resolve(GestureDisposition.accepted);
}
......@@ -437,7 +444,7 @@ class HorizontalMultiDragGestureRecognizer extends MultiDragGestureRecognizer {
@override
MultiDragPointerState createNewPointerState(PointerDownEvent event) {
return _HorizontalPointerState(event.position, event.kind);
return _HorizontalPointerState(event.position, event.kind, gestureSettings);
}
@override
......@@ -446,12 +453,12 @@ class HorizontalMultiDragGestureRecognizer extends MultiDragGestureRecognizer {
class _VerticalPointerState extends MultiDragPointerState {
_VerticalPointerState(Offset initialPosition, PointerDeviceKind kind) : super(initialPosition, kind);
_VerticalPointerState(Offset initialPosition, PointerDeviceKind kind, DeviceGestureSettings? deviceGestureSettings): super(initialPosition, kind, deviceGestureSettings);
@override
void checkForResolutionAfterMove() {
assert(pendingDelta != null);
if (pendingDelta!.dy.abs() > computeHitSlop(kind))
if (pendingDelta!.dy.abs() > computeHitSlop(kind, gestureSettings))
resolve(GestureDisposition.accepted);
}
......@@ -497,7 +504,7 @@ class VerticalMultiDragGestureRecognizer extends MultiDragGestureRecognizer {
@override
MultiDragPointerState createNewPointerState(PointerDownEvent event) {
return _VerticalPointerState(event.position, event.kind);
return _VerticalPointerState(event.position, event.kind, gestureSettings);
}
@override
......@@ -505,9 +512,9 @@ class VerticalMultiDragGestureRecognizer extends MultiDragGestureRecognizer {
}
class _DelayedPointerState extends MultiDragPointerState {
_DelayedPointerState(Offset initialPosition, Duration delay, PointerDeviceKind kind)
_DelayedPointerState(Offset initialPosition, Duration delay, PointerDeviceKind kind, DeviceGestureSettings? deviceGestureSettings)
: assert(delay != null),
super(initialPosition, kind) {
super(initialPosition, kind, deviceGestureSettings) {
_timer = Timer(delay, _delayPassed);
}
......@@ -517,7 +524,7 @@ class _DelayedPointerState extends MultiDragPointerState {
void _delayPassed() {
assert(_timer != null);
assert(pendingDelta != null);
assert(pendingDelta!.distance <= computeHitSlop(kind));
assert(pendingDelta!.distance <= computeHitSlop(kind, gestureSettings));
_timer = null;
if (_starter != null) {
_starter!(initialPosition);
......@@ -554,7 +561,7 @@ class _DelayedPointerState extends MultiDragPointerState {
return;
}
assert(pendingDelta != null);
if (pendingDelta!.distance > computeHitSlop(kind)) {
if (pendingDelta!.distance > computeHitSlop(kind, gestureSettings)) {
resolve(GestureDisposition.rejected);
_ensureTimerStopped();
}
......@@ -616,7 +623,7 @@ class DelayedMultiDragGestureRecognizer extends MultiDragGestureRecognizer {
@override
MultiDragPointerState createNewPointerState(PointerDownEvent event) {
return _DelayedPointerState(event.position, delay, event.kind);
return _DelayedPointerState(event.position, delay, event.kind, gestureSettings);
}
@override
......
......@@ -9,6 +9,7 @@ import 'arena.dart';
import 'binding.dart';
import 'constants.dart';
import 'events.dart';
import 'gesture_settings.dart';
import 'pointer_router.dart';
import 'recognizer.dart';
import 'tap.dart';
......@@ -60,6 +61,7 @@ class _TapTracker {
required PointerDownEvent event,
required this.entry,
required Duration doubleTapMinTime,
required this.gestureSettings,
}) : assert(doubleTapMinTime != null),
assert(event != null),
assert(event.buttons != null),
......@@ -68,6 +70,7 @@ class _TapTracker {
initialButtons = event.buttons,
_doubleTapMinTimeCountdown = _CountdownZoned(duration: doubleTapMinTime);
final DeviceGestureSettings? gestureSettings;
final int pointer;
final GestureArenaEntry entry;
final Offset _initialGlobalPosition;
......@@ -244,6 +247,7 @@ class DoubleTapGestureRecognizer extends GestureRecognizer {
event: event,
entry: GestureBinding.instance!.gestureArena.add(event.pointer, this),
doubleTapMinTime: kDoubleTapMinTime,
gestureSettings: gestureSettings,
);
_trackers[event.pointer] = tracker;
tracker.startTrackingPointer(_handleEvent, event.transform);
......@@ -380,11 +384,13 @@ class _TapGesture extends _TapTracker {
required this.gestureRecognizer,
required PointerEvent event,
required Duration longTapDelay,
required DeviceGestureSettings? gestureSettings,
}) : _lastPosition = OffsetPair.fromEventPosition(event),
super(
event: event as PointerDownEvent,
entry: GestureBinding.instance!.gestureArena.add(event.pointer, gestureRecognizer),
doubleTapMinTime: kDoubleTapMinTime,
gestureSettings: gestureSettings,
) {
startTrackingPointer(handleEvent, event.transform);
if (longTapDelay > Duration.zero) {
......@@ -406,7 +412,7 @@ class _TapGesture extends _TapTracker {
void handleEvent(PointerEvent event) {
assert(event.pointer == pointer);
if (event is PointerMoveEvent) {
if (!isWithinGlobalTolerance(event, computeHitSlop(event.kind)))
if (!isWithinGlobalTolerance(event, computeHitSlop(event.kind, gestureSettings)))
cancel();
else
_lastPosition = OffsetPair.fromEventPosition(event);
......@@ -513,6 +519,7 @@ class MultiTapGestureRecognizer extends GestureRecognizer {
gestureRecognizer: this,
event: event,
longTapDelay: longTapDelay,
gestureSettings: gestureSettings,
);
if (onTapDown != null)
invokeCallback<void>('onTapDown', () {
......@@ -879,6 +886,7 @@ class SerialTapGestureRecognizer extends GestureRecognizer {
invokeCallback<void>('onSerialTapDown', () => onSerialTapDown!(details));
}
final _TapTracker tracker = _TapTracker(
gestureSettings: gestureSettings,
event: event,
entry: GestureBinding.instance!.gestureArena.add(event.pointer, this),
doubleTapMinTime: kDoubleTapMinTime,
......
......@@ -14,6 +14,7 @@ import 'binding.dart';
import 'constants.dart';
import 'debug.dart';
import 'events.dart';
import 'gesture_settings.dart';
import 'pointer_router.dart';
import 'team.dart';
......@@ -81,6 +82,10 @@ abstract class GestureRecognizer extends GestureArenaMember with DiagnosticableT
/// this gesture recognizer was created, to aid in debugging.
final Object? debugOwner;
/// Optional device specific configuration for device gestures that will
/// take precedence over framework defaults.
DeviceGestureSettings? gestureSettings;
/// The kind of devices that are allowed to be recognized as provided by
/// `supportedDevices` in the constructor, or the currently deprecated `kind`.
/// These cannot both be set. If both are null, events from all device kinds will be
......
......@@ -495,7 +495,7 @@ class ScaleGestureRecognizer extends OneSequenceGestureRecognizer {
if (_state == _ScaleState.possible) {
final double spanDelta = (_currentSpan - _initialSpan).abs();
final double focalPointDelta = (_currentFocalPoint - _initialFocalPoint).distance;
if (spanDelta > computeScaleSlop(pointerDeviceKind) || focalPointDelta > computePanSlop(pointerDeviceKind))
if (spanDelta > computeScaleSlop(pointerDeviceKind) || focalPointDelta > computePanSlop(pointerDeviceKind, gestureSettings))
resolve(GestureDisposition.accepted);
} else if (_state.index >= _ScaleState.accepted.index) {
resolve(GestureDisposition.accepted);
......
......@@ -7,6 +7,7 @@ import 'dart:ui' as ui;
import 'dart:ui' show Brightness;
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'basic.dart';
import 'binding.dart';
......@@ -104,6 +105,7 @@ class MediaQueryData {
this.disableAnimations = false,
this.boldText = false,
this.navigationMode = NavigationMode.traditional,
this.gestureSettings = const DeviceGestureSettings(touchSlop: kTouchSlop)
}) : assert(size != null),
assert(devicePixelRatio != null),
assert(textScaleFactor != null),
......@@ -118,7 +120,8 @@ class MediaQueryData {
assert(highContrast != null),
assert(disableAnimations != null),
assert(boldText != null),
assert(navigationMode != null);
assert(navigationMode != null),
assert(gestureSettings != null);
/// Creates data for a media query based on the given window.
///
......@@ -142,7 +145,8 @@ class MediaQueryData {
boldText = window.accessibilityFeatures.boldText,
highContrast = window.accessibilityFeatures.highContrast,
alwaysUse24HourFormat = window.alwaysUse24HourFormat,
navigationMode = NavigationMode.traditional;
navigationMode = NavigationMode.traditional,
gestureSettings = DeviceGestureSettings.fromWindow(window);
/// The size of the media in logical pixels (e.g, the size of the screen).
///
......@@ -365,6 +369,13 @@ class MediaQueryData {
/// a widget subtree for those widgets sensitive to it.
final NavigationMode navigationMode;
/// The gesture settings for the view this media query is derived from.
///
/// This contains platform specific configuration for gesture behavior,
/// such as touch slop. These settings should be favored for configuring
/// gesture behavior over the framework constants.
final DeviceGestureSettings gestureSettings;
/// The orientation of the media (e.g., whether the device is in landscape or
/// portrait mode).
Orientation get orientation {
......@@ -389,6 +400,7 @@ class MediaQueryData {
bool? accessibleNavigation,
bool? boldText,
NavigationMode? navigationMode,
DeviceGestureSettings? gestureSettings,
}) {
return MediaQueryData(
size: size ?? this.size,
......@@ -406,6 +418,7 @@ class MediaQueryData {
accessibleNavigation: accessibleNavigation ?? this.accessibleNavigation,
boldText: boldText ?? this.boldText,
navigationMode: navigationMode ?? this.navigationMode,
gestureSettings: gestureSettings ?? this.gestureSettings,
);
}
......@@ -456,6 +469,7 @@ class MediaQueryData {
invertColors: invertColors,
accessibleNavigation: accessibleNavigation,
boldText: boldText,
gestureSettings: gestureSettings,
);
}
......@@ -504,6 +518,7 @@ class MediaQueryData {
invertColors: invertColors,
accessibleNavigation: accessibleNavigation,
boldText: boldText,
gestureSettings: gestureSettings,
);
}
......@@ -552,6 +567,7 @@ class MediaQueryData {
invertColors: invertColors,
accessibleNavigation: accessibleNavigation,
boldText: boldText,
gestureSettings: gestureSettings,
);
}
......@@ -573,7 +589,8 @@ class MediaQueryData {
&& other.invertColors == invertColors
&& other.accessibleNavigation == accessibleNavigation
&& other.boldText == boldText
&& other.navigationMode == navigationMode;
&& other.navigationMode == navigationMode
&& other.gestureSettings == gestureSettings;
}
@override
......@@ -593,6 +610,7 @@ class MediaQueryData {
accessibleNavigation,
boldText,
navigationMode,
gestureSettings,
);
}
......@@ -613,6 +631,7 @@ class MediaQueryData {
'invertColors: $invertColors',
'boldText: $boldText',
'navigationMode: ${describeEnum(navigationMode)}',
'gestureSettings: $gestureSettings',
];
return '${objectRuntimeType(this, 'MediaQueryData')}(${properties.join(', ')})';
}
......
......@@ -15,6 +15,7 @@ import 'basic.dart';
import 'focus_manager.dart';
import 'framework.dart';
import 'gesture_detector.dart';
import 'media_query.dart';
import 'notification_listener.dart';
import 'primary_scroll_controller.dart';
import 'restoration.dart';
......@@ -399,6 +400,7 @@ class ScrollableState extends State<Scrollable> with TickerProviderStateMixin, R
late ScrollBehavior _configuration;
ScrollPhysics? _physics;
ScrollController? _fallbackScrollController;
MediaQueryData? _mediaQueryData;
ScrollController get _effectiveScrollController => widget.controller ?? _fallbackScrollController!;
......@@ -452,6 +454,7 @@ class ScrollableState extends State<Scrollable> with TickerProviderStateMixin, R
@override
void didChangeDependencies() {
_mediaQueryData = MediaQuery.maybeOf(context);
_updatePosition();
super.didChangeDependencies();
}
......@@ -566,7 +569,8 @@ class ScrollableState extends State<Scrollable> with TickerProviderStateMixin, R
..minFlingVelocity = _physics?.minFlingVelocity
..maxFlingVelocity = _physics?.maxFlingVelocity
..velocityTrackerBuilder = _configuration.velocityTrackerBuilder(context)
..dragStartBehavior = widget.dragStartBehavior;
..dragStartBehavior = widget.dragStartBehavior
..gestureSettings = _mediaQueryData?.gestureSettings;
},
),
};
......@@ -586,7 +590,8 @@ class ScrollableState extends State<Scrollable> with TickerProviderStateMixin, R
..minFlingVelocity = _physics?.minFlingVelocity
..maxFlingVelocity = _physics?.maxFlingVelocity
..velocityTrackerBuilder = _configuration.velocityTrackerBuilder(context)
..dragStartBehavior = widget.dragStartBehavior;
..dragStartBehavior = widget.dragStartBehavior
..gestureSettings = _mediaQueryData?.gestureSettings;
},
),
};
......
......@@ -37,17 +37,17 @@ void main() {
});
test('computed hit slop values are based on pointer device kind', () {
expect(computeHitSlop(PointerDeviceKind.mouse), kPrecisePointerHitSlop);
expect(computeHitSlop(PointerDeviceKind.stylus), kTouchSlop);
expect(computeHitSlop(PointerDeviceKind.invertedStylus), kTouchSlop);
expect(computeHitSlop(PointerDeviceKind.touch), kTouchSlop);
expect(computeHitSlop(PointerDeviceKind.unknown), kTouchSlop);
expect(computePanSlop(PointerDeviceKind.mouse), kPrecisePointerPanSlop);
expect(computePanSlop(PointerDeviceKind.stylus), kPanSlop);
expect(computePanSlop(PointerDeviceKind.invertedStylus), kPanSlop);
expect(computePanSlop(PointerDeviceKind.touch), kPanSlop);
expect(computePanSlop(PointerDeviceKind.unknown), kPanSlop);
expect(computeHitSlop(PointerDeviceKind.mouse, null), kPrecisePointerHitSlop);
expect(computeHitSlop(PointerDeviceKind.stylus, null), kTouchSlop);
expect(computeHitSlop(PointerDeviceKind.invertedStylus, null), kTouchSlop);
expect(computeHitSlop(PointerDeviceKind.touch, null), kTouchSlop);
expect(computeHitSlop(PointerDeviceKind.unknown, null), kTouchSlop);
expect(computePanSlop(PointerDeviceKind.mouse, null), kPrecisePointerPanSlop);
expect(computePanSlop(PointerDeviceKind.stylus, null), kPanSlop);
expect(computePanSlop(PointerDeviceKind.invertedStylus, null), kPanSlop);
expect(computePanSlop(PointerDeviceKind.touch, null), kPanSlop);
expect(computePanSlop(PointerDeviceKind.unknown, null), kPanSlop);
expect(computeScaleSlop(PointerDeviceKind.mouse), kPrecisePointerScaleSlop);
expect(computeScaleSlop(PointerDeviceKind.stylus), kScaleSlop);
......@@ -56,6 +56,23 @@ void main() {
expect(computeScaleSlop(PointerDeviceKind.unknown), kScaleSlop);
});
test('computed hit slop values defer to device value when pointer kind is touch', () {
const DeviceGestureSettings settings = DeviceGestureSettings(touchSlop: 1);
expect(computeHitSlop(PointerDeviceKind.mouse, settings), kPrecisePointerHitSlop);
expect(computeHitSlop(PointerDeviceKind.stylus, settings), 1);
expect(computeHitSlop(PointerDeviceKind.invertedStylus, settings), 1);
expect(computeHitSlop(PointerDeviceKind.touch, settings), 1);
expect(computeHitSlop(PointerDeviceKind.unknown, settings), 1);
expect(computePanSlop(PointerDeviceKind.mouse, settings), kPrecisePointerPanSlop);
// Pan slop is 2x touch slop
expect(computePanSlop(PointerDeviceKind.stylus, settings), 2);
expect(computePanSlop(PointerDeviceKind.invertedStylus, settings), 2);
expect(computePanSlop(PointerDeviceKind.touch, settings), 2);
expect(computePanSlop(PointerDeviceKind.unknown, settings), 2);
});
group('fromMouseEvent', () {
const PointerEvent hover = PointerHoverEvent(
timeStamp: Duration(days: 1),
......
......@@ -4,6 +4,7 @@
import 'dart:ui' show Brightness;
import 'package:flutter/gestures.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
......@@ -110,6 +111,7 @@ void main() {
expect(data.boldText, false);
expect(data.highContrast, false);
expect(data.platformBrightness, Brightness.light);
expect(data.gestureSettings.touchSlop, null);
});
testWidgets('MediaQueryData.copyWith defaults to source', (WidgetTester tester) async {
......@@ -129,6 +131,7 @@ void main() {
expect(copied.boldText, data.boldText);
expect(copied.highContrast, data.highContrast);
expect(copied.platformBrightness, data.platformBrightness);
expect(copied.gestureSettings, data.gestureSettings);
});
testWidgets('MediaQuery.copyWith copies specified values', (WidgetTester tester) async {
......@@ -141,6 +144,7 @@ void main() {
const EdgeInsets customViewPadding = EdgeInsets.all(11.24031);
const EdgeInsets customViewInsets = EdgeInsets.all(1.67262);
const EdgeInsets customSystemGestureInsets = EdgeInsets.all(1.5556);
const DeviceGestureSettings gestureSettings = DeviceGestureSettings(touchSlop: 8.0);
final MediaQueryData data = MediaQueryData.fromWindow(WidgetsBinding.instance!.window);
final MediaQueryData copied = data.copyWith(
......@@ -158,6 +162,7 @@ void main() {
boldText: true,
highContrast: true,
platformBrightness: Brightness.dark,
gestureSettings: gestureSettings,
);
expect(copied.size, customSize);
expect(copied.devicePixelRatio, customDevicePixelRatio);
......@@ -173,6 +178,7 @@ void main() {
expect(copied.boldText, true);
expect(copied.highContrast, true);
expect(copied.platformBrightness, Brightness.dark);
expect(copied.gestureSettings, gestureSettings);
});
testWidgets('MediaQuery.removePadding removes specified padding', (WidgetTester tester) async {
......@@ -654,8 +660,7 @@ void main() {
expect(hasMediaQueryAsParentInside, true);
});
testWidgets('MediaQueryData.fromWindow is created using window values', (WidgetTester tester)
async {
testWidgets('MediaQueryData.fromWindow is created using window values', (WidgetTester tester) async {
final MediaQueryData windowData = MediaQueryData.fromWindow(WidgetsBinding.instance!.window);
late MediaQueryData fromWindowData;
......@@ -672,4 +677,22 @@ void main() {
expect(windowData, equals(fromWindowData));
});
test('DeviceGestureSettings has reasonable hashCode', () {
final DeviceGestureSettings settingsA = DeviceGestureSettings(touchSlop: nonconst(16));
final DeviceGestureSettings settingsB = DeviceGestureSettings(touchSlop: nonconst(8));
final DeviceGestureSettings settingsC = DeviceGestureSettings(touchSlop: nonconst(16));
expect(settingsA.hashCode, settingsC.hashCode);
expect(settingsA.hashCode, isNot(settingsB.hashCode));
});
test('DeviceGestureSettings has reasonable equality', () {
final DeviceGestureSettings settingsA = DeviceGestureSettings(touchSlop: nonconst(16));
final DeviceGestureSettings settingsB = DeviceGestureSettings(touchSlop: nonconst(8));
final DeviceGestureSettings settingsC = DeviceGestureSettings(touchSlop: nonconst(16));
expect(settingsA, equals(settingsC));
expect(settingsA, isNot(settingsB));
});
}
......@@ -353,6 +353,16 @@ class TestWindow implements ui.SingletonFlutterWindow {
onAccessibilityFeaturesChanged?.call();
}
@override
ui.ViewConfiguration get viewConfiguration => _viewConfiguration ?? _window.viewConfiguration;
ui.ViewConfiguration? _viewConfiguration;
/// Hide the real view configuration and report the provided [value] instead.
set viewConfigurationTestValue(ui.ViewConfiguration? value) {
_viewConfiguration = value;
onMetricsChanged?.call();
}
@override
ui.VoidCallback? get onAccessibilityFeaturesChanged => platformDispatcher.onAccessibilityFeaturesChanged;
@override
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment