From 129a35a5e1867e8bcffcf0db2d18868cc651c6e7 Mon Sep 17 00:00:00 2001 From: Valentin Vignal <32538273+ValentinVignal@users.noreply.github.com> Date: Tue, 10 Dec 2024 08:25:20 +0800 Subject: [PATCH] Add `mouseCursor` to `Tooltip` (#159922) Part of https://github.com/flutter/flutter/issues/58192 ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md --------- Co-authored-by: Bruno Leroux --- .../flutter/lib/src/material/tooltip.dart | 11 +++++++ .../flutter/test/material/tooltip_test.dart | 32 +++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/packages/flutter/lib/src/material/tooltip.dart b/packages/flutter/lib/src/material/tooltip.dart index 1258e285f3e..778aff0e267 100644 --- a/packages/flutter/lib/src/material/tooltip.dart +++ b/packages/flutter/lib/src/material/tooltip.dart @@ -46,6 +46,7 @@ class _ExclusiveMouseRegion extends MouseRegion { const _ExclusiveMouseRegion({ super.onEnter, super.onExit, + super.cursor, super.child, }); @@ -54,6 +55,7 @@ class _ExclusiveMouseRegion extends MouseRegion { return _RenderExclusiveMouseRegion( onEnter: onEnter, onExit: onExit, + cursor: cursor, ); } } @@ -62,6 +64,7 @@ class _RenderExclusiveMouseRegion extends RenderMouseRegion { _RenderExclusiveMouseRegion({ super.onEnter, super.onExit, + super.cursor, }); static bool isOutermostMouseRegion = true; @@ -195,6 +198,7 @@ class Tooltip extends StatefulWidget { this.triggerMode, this.enableFeedback, this.onTriggered, + this.mouseCursor, this.child, }) : assert((message == null) != (richMessage == null), 'Either `message` or `richMessage` must be specified'), assert( @@ -363,6 +367,12 @@ class Tooltip extends StatefulWidget { /// or after a long press when [triggerMode] is [TooltipTriggerMode.longPress]. final TooltipTriggeredCallback? onTriggered; + /// The cursor for a mouse pointer when it enters or is hovering over the + /// widget. + /// + /// If this property is null, [MouseCursor.defer] will be used. + final MouseCursor? mouseCursor; + static final List _openedTooltips = []; /// Dismiss all of the tooltips that are currently shown on the screen, @@ -839,6 +849,7 @@ class TooltipState extends State with SingleTickerProviderStateMixin { result = _ExclusiveMouseRegion( onEnter: _handleMouseEnter, onExit: _handleMouseExit, + cursor: widget.mouseCursor ?? MouseCursor.defer, child: Listener( onPointerDown: _handlePointerDown, behavior: HitTestBehavior.opaque, diff --git a/packages/flutter/test/material/tooltip_test.dart b/packages/flutter/test/material/tooltip_test.dart index e9046f7f6a1..70f6e9a5b44 100644 --- a/packages/flutter/test/material/tooltip_test.dart +++ b/packages/flutter/test/material/tooltip_test.dart @@ -3091,6 +3091,38 @@ void main() { expect(selectedText, isNot(contains('A'))); }); + + testWidgets('Tooltip mouse cursor behavior', (WidgetTester tester) async { + const SystemMouseCursor customCursor = SystemMouseCursors.grab; + + await tester.pumpWidget( + const MaterialApp( + home: Center( + child: Tooltip( + message: tooltipText, + mouseCursor: customCursor, + child: SizedBox.square(dimension: 50), + ), + ), + ), + ); + + final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse, pointer: 1); + await gesture.addPointer(location: const Offset(10, 10)); + await tester.pump(); + expect( + RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), + SystemMouseCursors.basic, + ); + + final Offset chip = tester.getCenter(find.byType(Tooltip)); + await gesture.moveTo(chip); + await tester.pump(); + expect( + RendererBinding.instance.mouseTracker.debugDeviceActiveCursor(1), + customCursor, + ); + }); } Future setWidgetForTooltipMode(