Unverified Commit fa06b340 authored by Tong Mu's avatar Tong Mu Committed by GitHub

Refactor: Move mouse cursor classes to "services" package (#77751)

parent 95d3ac70
...@@ -46,8 +46,7 @@ export 'src/rendering/layer.dart'; ...@@ -46,8 +46,7 @@ export 'src/rendering/layer.dart';
export 'src/rendering/layout_helper.dart'; export 'src/rendering/layout_helper.dart';
export 'src/rendering/list_body.dart'; export 'src/rendering/list_body.dart';
export 'src/rendering/list_wheel_viewport.dart'; export 'src/rendering/list_wheel_viewport.dart';
export 'src/rendering/mouse_cursor.dart'; export 'src/rendering/mouse_tracker.dart';
export 'src/rendering/mouse_tracking.dart';
export 'src/rendering/object.dart'; export 'src/rendering/object.dart';
export 'src/rendering/paragraph.dart'; export 'src/rendering/paragraph.dart';
export 'src/rendering/performance_overlay.dart'; export 'src/rendering/performance_overlay.dart';
......
...@@ -22,6 +22,8 @@ export 'src/services/keyboard_key.dart'; ...@@ -22,6 +22,8 @@ export 'src/services/keyboard_key.dart';
export 'src/services/keyboard_maps.dart'; export 'src/services/keyboard_maps.dart';
export 'src/services/message_codec.dart'; export 'src/services/message_codec.dart';
export 'src/services/message_codecs.dart'; export 'src/services/message_codecs.dart';
export 'src/services/mouse_cursor.dart';
export 'src/services/mouse_tracking.dart';
export 'src/services/platform_channel.dart'; export 'src/services/platform_channel.dart';
export 'src/services/platform_views.dart'; export 'src/services/platform_views.dart';
export 'src/services/raw_keyboard.dart'; export 'src/services/raw_keyboard.dart';
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
/// Interactive states that some of the Material widgets can take on when /// Interactive states that some of the Material widgets can take on when
/// receiving input from the user. /// receiving input from the user.
......
...@@ -12,7 +12,7 @@ import 'package:flutter/services.dart'; ...@@ -12,7 +12,7 @@ import 'package:flutter/services.dart';
import 'box.dart'; import 'box.dart';
import 'debug.dart'; import 'debug.dart';
import 'mouse_tracking.dart'; import 'mouse_tracker.dart';
import 'object.dart'; import 'object.dart';
import 'view.dart'; import 'view.dart';
......
...@@ -11,8 +11,7 @@ import 'package:flutter/services.dart'; ...@@ -11,8 +11,7 @@ import 'package:flutter/services.dart';
import 'box.dart'; import 'box.dart';
import 'layer.dart'; import 'layer.dart';
import 'mouse_cursor.dart'; import 'mouse_tracker.dart';
import 'mouse_tracking.dart';
import 'object.dart'; import 'object.dart';
......
...@@ -8,14 +8,14 @@ import 'package:flutter/animation.dart'; ...@@ -8,14 +8,14 @@ import 'package:flutter/animation.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter/semantics.dart'; import 'package:flutter/semantics.dart';
import 'package:flutter/services.dart';
import 'package:vector_math/vector_math_64.dart'; import 'package:vector_math/vector_math_64.dart';
import 'box.dart'; import 'box.dart';
import 'layer.dart'; import 'layer.dart';
import 'layout_helper.dart'; import 'layout_helper.dart';
import 'mouse_cursor.dart'; import 'mouse_tracker.dart';
import 'mouse_tracking.dart';
import 'object.dart'; import 'object.dart';
export 'package:flutter/gestures.dart' show export 'package:flutter/gestures.dart' show
......
...@@ -4,17 +4,28 @@ ...@@ -4,17 +4,28 @@
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter/services.dart';
import 'mouse_tracking.dart'; import 'system_channels.dart';
/// A mixin for [BaseMouseTracker] that sets the mouse pointer's cursors /// Maintains the state of mouse cursors and manages how cursors are searched
/// on device update. /// for.
/// ///
/// See also: /// This is typically created as a global singleton and owned by [MouseTracker].
/// class MouseCursorManager {
/// * [MouseTracker], which uses this mixin. /// Create a [MouseCursorManager] by specifying the fallback cursor.
mixin MouseTrackerCursorMixin on BaseMouseTracker { ///
/// The `fallbackMouseCursor` must not be [MouseCursor.defer] (typically
/// [SystemMouseCursors.basic]).
MouseCursorManager(this.fallbackMouseCursor)
: assert(fallbackMouseCursor != MouseCursor.defer);
/// The mouse cursor to use if all cursor candidates choose to defer.
///
/// See also:
///
/// * [MouseCursor.defer], the mouse cursor object to use to defer.
final MouseCursor fallbackMouseCursor;
/// Returns the active mouse cursor of a device. /// Returns the active mouse cursor of a device.
/// ///
/// The return value is the last [MouseCursor] activated onto this /// The return value is the last [MouseCursor] activated onto this
...@@ -22,7 +33,6 @@ mixin MouseTrackerCursorMixin on BaseMouseTracker { ...@@ -22,7 +33,6 @@ mixin MouseTrackerCursorMixin on BaseMouseTracker {
/// ///
/// Only valid when asserts are enabled. In release builds, always returns /// Only valid when asserts are enabled. In release builds, always returns
/// null. /// null.
@visibleForTesting
MouseCursor? debugDeviceActiveCursor(int device) { MouseCursor? debugDeviceActiveCursor(int device) {
MouseCursor? result; MouseCursor? result;
assert(() { assert(() {
...@@ -32,38 +42,30 @@ mixin MouseTrackerCursorMixin on BaseMouseTracker { ...@@ -32,38 +42,30 @@ mixin MouseTrackerCursorMixin on BaseMouseTracker {
return result; return result;
} }
@protected
@override
void handleDeviceUpdate(MouseTrackerUpdateDetails details) {
super.handleDeviceUpdate(details);
_handleDeviceUpdateMouseCursor(details);
}
final Map<int, MouseCursorSession> _lastSession = <int, MouseCursorSession>{}; final Map<int, MouseCursorSession> _lastSession = <int, MouseCursorSession>{};
// Find the first non-deferred mouse cursor, which fallbacks to /// Handles the changes that cause a pointer device to have a new list of mouse
// [SystemMouseCursors.basic]. /// cursor candidates.
// ///
// The `annotations` is the current annotations that the device is hovering in /// This change can be caused by a pointer event, in which case
// visual order from front the back. /// `triggeringEvent` should not be null, or by other changes, such as when a
// The return value is never null. /// widget has moved under a still mouse, which is detected after the current
MouseCursor _findFirstCursor(Iterable<MouseTrackerAnnotation> annotations) { /// frame is complete. In either case, `cursorCandidates` should be the list of
return _DeferringMouseCursor.firstNonDeferred( /// cursors at the location of the mouse in hit-test order.
annotations.map((MouseTrackerAnnotation annotation) => annotation.cursor), void handleDeviceCursorUpdate(
) ?? SystemMouseCursors.basic; int device,
} PointerEvent? triggeringEvent,
Iterable<MouseCursor> cursorCandidates,
// Handles device update and changes mouse cursors. ) {
void _handleDeviceUpdateMouseCursor(MouseTrackerUpdateDetails details) { if (triggeringEvent is PointerRemovedEvent) {
final int device = details.device;
if (details.triggeringEvent is PointerRemovedEvent) {
_lastSession.remove(device); _lastSession.remove(device);
return; return;
} }
final MouseCursorSession? lastSession = _lastSession[device]; final MouseCursorSession? lastSession = _lastSession[device];
final MouseCursor nextCursor = _findFirstCursor(details.nextAnnotations.keys); final MouseCursor nextCursor = _DeferringMouseCursor.firstNonDeferred(cursorCandidates)
?? fallbackMouseCursor;
assert(nextCursor is! _DeferringMouseCursor);
if (lastSession?.cursor == nextCursor) if (lastSession?.cursor == nextCursor)
return; return;
...@@ -191,9 +193,9 @@ abstract class MouseCursorSession { ...@@ -191,9 +193,9 @@ abstract class MouseCursorSession {
/// a cursor, and defines the states and behaviors of the cursor. Every mouse /// a cursor, and defines the states and behaviors of the cursor. Every mouse
/// cursor class usually has a corresponding [MouseCursorSession] class. /// cursor class usually has a corresponding [MouseCursorSession] class.
/// ///
/// [MouseTrackerCursorMixin] is a mixin that adds the feature of changing /// [MouseCursorManager] is a class that adds the feature of changing
/// cursors to [BaseMouseTracker], which tracks the relationship between mouse /// cursors to [MouseTracker], which tracks the relationship between mouse
/// devices and annotations. [MouseTrackerCursorMixin] is usually used as a part /// devices and annotations. [MouseCursorManager] is usually used as a part
/// of [MouseTracker]. /// of [MouseTracker].
@immutable @immutable
abstract class MouseCursor with Diagnosticable { abstract class MouseCursor with Diagnosticable {
......
// 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 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'mouse_cursor.dart';
/// Signature for listening to [PointerEnterEvent] events.
///
/// Used by [MouseTrackerAnnotation], [MouseRegion] and [RenderMouseRegion].
typedef PointerEnterEventListener = void Function(PointerEnterEvent event);
/// Signature for listening to [PointerExitEvent] events.
///
/// Used by [MouseTrackerAnnotation], [MouseRegion] and [RenderMouseRegion].
typedef PointerExitEventListener = void Function(PointerExitEvent event);
/// Signature for listening to [PointerHoverEvent] events.
///
/// Used by [MouseTrackerAnnotation], [MouseRegion] and [RenderMouseRegion].
typedef PointerHoverEventListener = void Function(PointerHoverEvent event);
/// The annotation object used to annotate regions that are interested in mouse
/// movements.
///
/// To use an annotation, return this object as a [HitTestEntry] in a hit test.
/// Typically this is implemented by making a [RenderBox] implement this class
/// (see [RenderMouseRegion]).
///
/// [MouseTracker] uses this class as a label to filter the hit test results. Hit
/// test entries that are also [MouseTrackerAnnotation]s are considered as valid
/// targets in terms of computing mouse related effects, such as enter events,
/// exit events, and mouse cursor events.
///
/// See also:
///
/// * [MouseTracker], which uses [MouseTrackerAnnotation].
class MouseTrackerAnnotation with Diagnosticable {
/// Creates an immutable [MouseTrackerAnnotation].
///
/// All arguments are optional. The [cursor] must not be null.
const MouseTrackerAnnotation({
this.onEnter,
this.onExit,
this.cursor = MouseCursor.defer,
this.validForMouseTracker = true,
}) : assert(cursor != null);
/// Triggered when a mouse pointer, with or without buttons pressed, has
/// entered the region and [validForMouseTracker] is true.
///
/// This callback is triggered when the pointer has started to be contained by
/// the region, either due to a pointer event, or due to the movement or
/// disappearance of the region. This method is always matched by a later
/// [onExit].
///
/// See also:
///
/// * [onExit], which is triggered when a mouse pointer exits the region.
/// * [MouseRegion.onEnter], which uses this callback.
final PointerEnterEventListener? onEnter;
/// Triggered when a mouse pointer, with or without buttons pressed, has
/// exited the region and [validForMouseTracker] is true.
///
/// This callback is triggered when the pointer has stopped being contained
/// by the region, either due to a pointer event, or due to the movement or
/// disappearance of the region. This method always matches an earlier
/// [onEnter].
///
/// See also:
///
/// * [onEnter], which is triggered when a mouse pointer enters the region.
/// * [MouseRegion.onExit], which uses this callback, but is not triggered in
/// certain cases and does not always match its earlier [MouseRegion.onEnter].
final PointerExitEventListener? onExit;
/// The mouse cursor for mouse pointers that are hovering over the region.
///
/// When a mouse enters the region, its cursor will be changed to the [cursor].
/// When the mouse leaves the region, the cursor will be set by the region
/// found at the new location.
///
/// Defaults to [MouseCursor.defer], deferring the choice of cursor to the next
/// region behind it in hit-test order.
///
/// See also:
///
/// * [MouseRegion.cursor], which provide values to this field.
final MouseCursor cursor;
/// Whether this is included when [MouseTracker] collects the list of
/// annotations.
///
/// If [validForMouseTracker] is false, this object is excluded from the
/// current annotation list even if it's included in the hit test, affecting
/// mouse-related behavior such as enter events, exit events, and mouse
/// cursors. The [validForMouseTracker] does not affect hit testing.
///
/// The [validForMouseTracker] is true for [MouseTrackerAnnotation]s built by
/// the constructor.
final bool validForMouseTracker;
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(FlagsSummary<Function?>(
'callbacks',
<String, Function?> {
'enter': onEnter,
'exit': onExit,
},
ifEmpty: '<none>',
));
properties.add(DiagnosticsProperty<MouseCursor>('cursor', cursor, defaultValue: MouseCursor.defer));
}
}
...@@ -41,6 +41,8 @@ export 'package:flutter/rendering.dart' show ...@@ -41,6 +41,8 @@ export 'package:flutter/rendering.dart' show
LayerLink, LayerLink,
MainAxisAlignment, MainAxisAlignment,
MainAxisSize, MainAxisSize,
MouseCursor,
SystemMouseCursors,
MultiChildLayoutDelegate, MultiChildLayoutDelegate,
Overflow, Overflow,
PaintingContext, PaintingContext,
......
...@@ -1494,7 +1494,7 @@ class FocusManager with DiagnosticableTreeMixin, ChangeNotifier { ...@@ -1494,7 +1494,7 @@ class FocusManager with DiagnosticableTreeMixin, ChangeNotifier {
/// interaction type. /// interaction type.
/// ///
/// The initial value of [highlightMode] depends upon the value of /// The initial value of [highlightMode] depends upon the value of
/// [defaultTargetPlatform] and [BaseMouseTracker.mouseIsConnected] of /// [defaultTargetPlatform] and [MouseTracker.mouseIsConnected] of
/// [RendererBinding.mouseTracker], making a guess about which interaction is /// [RendererBinding.mouseTracker], making a guess about which interaction is
/// most appropriate for the initial interaction mode. /// most appropriate for the initial interaction mode.
/// ///
......
...@@ -10,7 +10,7 @@ import 'package:flutter/gestures.dart'; ...@@ -10,7 +10,7 @@ import 'package:flutter/gestures.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import '../flutter_test_alternative.dart'; import '../flutter_test_alternative.dart';
import './mouse_tracking_test_utils.dart'; import 'mouse_tracker_test_utils.dart';
typedef MethodCallHandler = Future<dynamic> Function(MethodCall call); typedef MethodCallHandler = Future<dynamic> Function(MethodCall call);
typedef SimpleAnnotationFinder = Iterable<HitTestTarget> Function(Offset offset); typedef SimpleAnnotationFinder = Iterable<HitTestTarget> Function(Offset offset);
......
...@@ -5,12 +5,11 @@ ...@@ -5,12 +5,11 @@
import 'dart:ui' as ui; import 'dart:ui' as ui;
import 'dart:ui' show PointerChange; import 'dart:ui' show PointerChange;
import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import '../flutter_test_alternative.dart'; import '../flutter_test_alternative.dart';
import './mouse_tracking_test_utils.dart'; import 'mouse_tracker_test_utils.dart';
MouseTracker get _mouseTracker => RendererBinding.instance!.mouseTracker; MouseTracker get _mouseTracker => RendererBinding.instance!.mouseTracker;
...@@ -76,32 +75,6 @@ void main() { ...@@ -76,32 +75,6 @@ void main() {
final Matrix4 translate10by20 = Matrix4.translationValues(10, 20, 0); final Matrix4 translate10by20 = Matrix4.translationValues(10, 20, 0);
test('MouseTrackerAnnotation has correct toString', () {
final MouseTrackerAnnotation annotation1 = MouseTrackerAnnotation(
onEnter: (_) {},
onExit: (_) {},
);
expect(
annotation1.toString(),
equals('MouseTrackerAnnotation#${shortHash(annotation1)}(callbacks: [enter, exit])'),
);
const MouseTrackerAnnotation annotation2 = MouseTrackerAnnotation();
expect(
annotation2.toString(),
equals('MouseTrackerAnnotation#${shortHash(annotation2)}(callbacks: <none>)'),
);
final MouseTrackerAnnotation annotation3 = MouseTrackerAnnotation(
onEnter: (_) {},
cursor: SystemMouseCursors.grab,
);
expect(
annotation3.toString(),
equals('MouseTrackerAnnotation#${shortHash(annotation3)}(callbacks: [enter], cursor: SystemMouseCursor(grab))'),
);
});
test('should detect enter, hover, and exit from Added, Hover, and Removed events', () { test('should detect enter, hover, and exit from Added, Hover, and Removed events', () {
final List<PointerEvent> events = <PointerEvent>[]; final List<PointerEvent> events = <PointerEvent>[];
_setUpWithOneAnnotation(logEvents: events); _setUpWithOneAnnotation(logEvents: events);
......
// 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 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
test('MouseTrackerAnnotation has correct toString', () {
final MouseTrackerAnnotation annotation1 = MouseTrackerAnnotation(
onEnter: (_) {},
onExit: (_) {},
);
expect(
annotation1.toString(),
equals('MouseTrackerAnnotation#${shortHash(annotation1)}(callbacks: [enter, exit])'),
);
const MouseTrackerAnnotation annotation2 = MouseTrackerAnnotation();
expect(
annotation2.toString(),
equals('MouseTrackerAnnotation#${shortHash(annotation2)}(callbacks: <none>)'),
);
final MouseTrackerAnnotation annotation3 = MouseTrackerAnnotation(
onEnter: (_) {},
cursor: SystemMouseCursors.grab,
);
expect(
annotation3.toString(),
equals('MouseTrackerAnnotation#${shortHash(annotation3)}(callbacks: [enter], cursor: SystemMouseCursor(grab))'),
);
});
}
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