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';
export 'src/rendering/layout_helper.dart';
export 'src/rendering/list_body.dart';
export 'src/rendering/list_wheel_viewport.dart';
export 'src/rendering/mouse_cursor.dart';
export 'src/rendering/mouse_tracking.dart';
export 'src/rendering/mouse_tracker.dart';
export 'src/rendering/object.dart';
export 'src/rendering/paragraph.dart';
export 'src/rendering/performance_overlay.dart';
......
......@@ -22,6 +22,8 @@ export 'src/services/keyboard_key.dart';
export 'src/services/keyboard_maps.dart';
export 'src/services/message_codec.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_views.dart';
export 'src/services/raw_keyboard.dart';
......
......@@ -4,6 +4,7 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
/// Interactive states that some of the Material widgets can take on when
/// receiving input from the user.
......
......@@ -12,7 +12,7 @@ import 'package:flutter/services.dart';
import 'box.dart';
import 'debug.dart';
import 'mouse_tracking.dart';
import 'mouse_tracker.dart';
import 'object.dart';
import 'view.dart';
......
......@@ -11,8 +11,7 @@ import 'package:flutter/services.dart';
import 'box.dart';
import 'layer.dart';
import 'mouse_cursor.dart';
import 'mouse_tracking.dart';
import 'mouse_tracker.dart';
import 'object.dart';
......
......@@ -8,14 +8,14 @@ import 'package:flutter/animation.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/semantics.dart';
import 'package:flutter/services.dart';
import 'package:vector_math/vector_math_64.dart';
import 'box.dart';
import 'layer.dart';
import 'layout_helper.dart';
import 'mouse_cursor.dart';
import 'mouse_tracking.dart';
import 'mouse_tracker.dart';
import 'object.dart';
export 'package:flutter/gestures.dart' show
......
......@@ -4,17 +4,28 @@
import 'package:flutter/foundation.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
/// on device update.
/// Maintains the state of mouse cursors and manages how cursors are searched
/// for.
///
/// See also:
///
/// * [MouseTracker], which uses this mixin.
mixin MouseTrackerCursorMixin on BaseMouseTracker {
/// This is typically created as a global singleton and owned by [MouseTracker].
class MouseCursorManager {
/// Create a [MouseCursorManager] by specifying the fallback cursor.
///
/// 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.
///
/// The return value is the last [MouseCursor] activated onto this
......@@ -22,7 +33,6 @@ mixin MouseTrackerCursorMixin on BaseMouseTracker {
///
/// Only valid when asserts are enabled. In release builds, always returns
/// null.
@visibleForTesting
MouseCursor? debugDeviceActiveCursor(int device) {
MouseCursor? result;
assert(() {
......@@ -32,38 +42,30 @@ mixin MouseTrackerCursorMixin on BaseMouseTracker {
return result;
}
@protected
@override
void handleDeviceUpdate(MouseTrackerUpdateDetails details) {
super.handleDeviceUpdate(details);
_handleDeviceUpdateMouseCursor(details);
}
final Map<int, MouseCursorSession> _lastSession = <int, MouseCursorSession>{};
// Find the first non-deferred mouse cursor, which fallbacks to
// [SystemMouseCursors.basic].
//
// The `annotations` is the current annotations that the device is hovering in
// visual order from front the back.
// The return value is never null.
MouseCursor _findFirstCursor(Iterable<MouseTrackerAnnotation> annotations) {
return _DeferringMouseCursor.firstNonDeferred(
annotations.map((MouseTrackerAnnotation annotation) => annotation.cursor),
) ?? SystemMouseCursors.basic;
}
// Handles device update and changes mouse cursors.
void _handleDeviceUpdateMouseCursor(MouseTrackerUpdateDetails details) {
final int device = details.device;
if (details.triggeringEvent is PointerRemovedEvent) {
/// Handles the changes that cause a pointer device to have a new list of mouse
/// cursor candidates.
///
/// This change can be caused by a pointer event, in which case
/// `triggeringEvent` should not be null, or by other changes, such as when a
/// widget has moved under a still mouse, which is detected after the current
/// frame is complete. In either case, `cursorCandidates` should be the list of
/// cursors at the location of the mouse in hit-test order.
void handleDeviceCursorUpdate(
int device,
PointerEvent? triggeringEvent,
Iterable<MouseCursor> cursorCandidates,
) {
if (triggeringEvent is PointerRemovedEvent) {
_lastSession.remove(device);
return;
}
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)
return;
......@@ -191,9 +193,9 @@ abstract class MouseCursorSession {
/// a cursor, and defines the states and behaviors of the cursor. Every mouse
/// cursor class usually has a corresponding [MouseCursorSession] class.
///
/// [MouseTrackerCursorMixin] is a mixin that adds the feature of changing
/// cursors to [BaseMouseTracker], which tracks the relationship between mouse
/// devices and annotations. [MouseTrackerCursorMixin] is usually used as a part
/// [MouseCursorManager] is a class that adds the feature of changing
/// cursors to [MouseTracker], which tracks the relationship between mouse
/// devices and annotations. [MouseCursorManager] is usually used as a part
/// of [MouseTracker].
@immutable
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
LayerLink,
MainAxisAlignment,
MainAxisSize,
MouseCursor,
SystemMouseCursors,
MultiChildLayoutDelegate,
Overflow,
PaintingContext,
......
......@@ -1494,7 +1494,7 @@ class FocusManager with DiagnosticableTreeMixin, ChangeNotifier {
/// interaction type.
///
/// 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
/// most appropriate for the initial interaction mode.
///
......
......@@ -10,7 +10,7 @@ import 'package:flutter/gestures.dart';
import 'package:flutter/services.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 SimpleAnnotationFinder = Iterable<HitTestTarget> Function(Offset offset);
......
......@@ -5,12 +5,11 @@
import 'dart:ui' as ui;
import 'dart:ui' show PointerChange;
import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/gestures.dart';
import '../flutter_test_alternative.dart';
import './mouse_tracking_test_utils.dart';
import 'mouse_tracker_test_utils.dart';
MouseTracker get _mouseTracker => RendererBinding.instance!.mouseTracker;
......@@ -76,32 +75,6 @@ void main() {
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', () {
final List<PointerEvent> events = <PointerEvent>[];
_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