[WIP] Mouse cursor (framework)#42797
Conversation
| /// * [MouseCursorPlatformDelegate], which uses this type to define how | ||
| /// system cursors are implemented on platforms. | ||
| enum MouseCursorSystemShape { | ||
| /// The shape that corresponds to [SystemCursors.none]. |
There was a problem hiding this comment.
I couldn't find the class SystemCursors in this PR.
goderbauer
left a comment
There was a problem hiding this comment.
Left some comments from a first pass-through to get familiar with the change.
My main high-level question is: What's the advantage of having the different platform-specific delegates on the framework side? Could the framework just send to the embedder: I want curser xyz and then the embedder translates that into the platform-specific ID for its platform?
| } | ||
|
|
||
| /// Details for [MouseCursorPlatformDelegate.activateSystemCursor], such as the | ||
| /// target device and the system cursor shape. |
There was a problem hiding this comment.
From reading the implementation it sounds as if this class represents a request to change the cursor of device to systemShape? Maybe add that to the documentation?
|
|
||
| @override | ||
| bool operator ==(dynamic other) { | ||
| if (other.runtimeType != MouseCursorPlatformActivateSystemCursorDetails) |
There was a problem hiding this comment.
if (other.runtimeType != runtimeType)
| /// Asks the platform to change the cursor of `device` to the system cursor | ||
| /// specified by `systemShape`. | ||
| /// | ||
| /// It resolves to `true` if the operation is successful, `false` if the |
There was a problem hiding this comment.
true and false don't need to be in `
| /// Create a [MouseCursorPlatformDelegate]. | ||
| const MouseCursorPlatformDelegate(); | ||
|
|
||
| /// Asks the platform to change the cursor of `device` to the system cursor |
There was a problem hiding this comment.
Maybe add that device and systemShape are provided within the details object?
| /// Details for [MouseCursorPlatformDelegate.activateSystemCursor], such as the | ||
| /// target device and the system cursor shape. | ||
| @immutable | ||
| class MouseCursorPlatformActivateSystemCursorDetails { |
There was a problem hiding this comment.
This name is a mouthful. Can we find something a little shorter?
|
|
||
| // The channel interface with the platform. | ||
| // | ||
| // It is separated to a class for the conventience of reference by the shell |
|
|
||
| /// The same constant as Android's `PointerIcon.TYPE_DEFAULT`, | ||
| /// used internally to set system cursor. | ||
| static const int kPlatformConstantDefault = 1000; |
| final _AndroidPlatformActions _platform; | ||
|
|
||
| Future<bool> _activatePlatformConstant(int platformConstant) async { | ||
| await _platform.setAsSystemCursor(platformConstant: platformConstant); |
There was a problem hiding this comment.
Why the indirection via _AndroidPlatformActions? Couldn't this method just implement that method call directly?
| /// | ||
| /// * `MouseCursor.setCursors`: Request to set the cursors of multiple pointer | ||
| /// devices. The argument is a `Map<int, int>` that maps from device to | ||
| /// cursor. See [MouseCursors] for a list of system cursors. |
There was a problem hiding this comment.
MouseCursors doesn't exist?
What does this map to? device id to what?
| // throw errors. | ||
| if (kIsWeb) | ||
| return const MouseCursorUnsupportedPlatformDelegate(); | ||
| if (Platform.isLinux) { |
There was a problem hiding this comment.
What's the advantage of having these different platform-specific delegates on the framework side? Could the framework just send to the embedder: I want curser xyz and then the embedder translates that into the platform-specific ID?
There was a problem hiding this comment.
Yeah I'm not a fan of having this platform-specific code in the framework.
The most significant reason is that, Dart code is easier to maintain and test. We have to write the same code either in the framework or in the engine. By writing most code in Dart, we leave only a one-to-one API translator in the engine. |
dkwingsmt
left a comment
There was a problem hiding this comment.
Answered 2 concerns. I'll work on the new design in the mean time.
| // Change mouse cursor. | ||
| final MouseCursor cursor = _findDeviceCursor(nextAnnotations); | ||
| if (cursor != mouseState.cursor) { | ||
| _cursorManager.setDeviceCursor(device, cursor); |
There was a problem hiding this comment.
If the cursor is not available, the action is considered successful, therefore older shells will not throw exceptions for not implementing a latest feature introduced in the framework. Only actual runtime errors are failures.
| /// material. | ||
| final ValueChanged<bool> onHover; | ||
|
|
||
| /// The cursor for a mouse pointer when it enters or is hovering the region. |
There was a problem hiding this comment.
Mouse cursors are changed based on Enter and Exit (instead of Hover), therefore it does not rely on whether the pointer is down. The short answer is nothing will be affected.
If you want to change the cursor when the mouse is down, you can assign different cursors to MouseRegion.cursor depending on the state.
|
|
||
| import 'package:flutter/foundation.dart'; | ||
|
|
||
| /// Internal identifiers for the system cursors supported by Flutter. |
There was a problem hiding this comment.
should it be private then?
I don't really understand the value of this enum.
| abstract class MouseCursor { | ||
| /// Create a mouse cursor. | ||
| /// | ||
| /// The base constructor does nothing. |
There was a problem hiding this comment.
for no-argument const constructors, please use the boilerplate that we use for all no-argument const constructors.
(this applies to other classes in this PR as well)
| /// A platform-independent short description for this cursor. | ||
| /// | ||
| /// It is usually one or several readable words. | ||
| String describeCursor(); |
| /// Create a [NoopMouseCursor]. | ||
| const NoopMouseCursor(); | ||
|
|
||
| /// Does nothing and immediately returns true. |
There was a problem hiding this comment.
we generally don't want to override the docs of virtual methods, it hides the documentation of the superclass. better to put that information in the class docs.
| } | ||
|
|
||
| @override | ||
| String describeCursor() => 'noop'; |
| /// * [MouseCursorPlatformDelegate], which uses this type to define how | ||
| /// system cursors are implemented on platforms. | ||
| enum MouseCursorSystemShape { | ||
| /// The shape that corresponds to [SystemCursors.none]. |
There was a problem hiding this comment.
is "SystemCursors" supposed to be "SystemMouseCursors"? I couldn't find a definition of SystemCursors.
|
|
||
| // The channel interface with the platform. | ||
| // | ||
| // It is separated to a class for the conventience of reference by the shell |
| ? '<none>' | ||
| : callbacks.join(' '); | ||
| return '${describeIdentity(this)}(callbacks: $describeCallbacks)'; | ||
| // TODO(dkwingsmt): Add property for cursor |
There was a problem hiding this comment.
please do this before landing
|
I had trouble following this PR. I kept looking for a class with detailed dartdocs explaining how everything fit together. I recommend taking whatever class you feel is the center of this API, and adding significant detail to that class's dartdocs, along the lines of the docs for RenderBox, explaining how all the parts fit together, how to create a custom cursor, how to port the API to a new platform, how to add a new system cursor, etc. |
|
I'll close it for now until after some major changes I'm working on. |
Description
This PR is the framework side of adding mouse cursor support. The engine side (see Related Issues) is required for mouse cursor to work, but isn't required to run.
The framework side of the system consists of 3 parts:
MouseCursor, which are stateless, platform-independent definitions of a kind of cursor. They are extended by Flutter and app developers, and used by widgets. "A kind of cursor" here stands for an arrow, a pointing hand, etc.MouseCursorPlatformDelegate, which are interfaces for operations related to mouse cursor on different platforms. They are extended by Flutter, one for each platform that supports mouse cursor, and their implementations are tightly related to their shells.MouseCursorManager, which is a class for the Flutter framework (more specifically,MouseTracker) to dispatch mouse cursor changes.Feature-wise, this PR includes:
SystemCursors.mouseCursortoInkWell,InkResponse,RawMaterialButtonandFloatingActionButton. (It's there for the convenience of testing, up to discussion if we want to keep them)Future work includes:
mouseCursorto more widgets (up to discussion)Related Issues
Tests
I added the following tests:
Replace this with a list of the tests that you added as part of this PR. A change in behaviour with no test covering it
will likely get reverted accidentally sooner or later. PRs must include tests for all changed/updated/fixed behaviors. See Test Coverage.
Checklist
Before you create this PR confirm that it meets all requirements listed below by checking the relevant checkboxes (
[x]). This will ensure a smooth and quick review process.///).flutter analyze --flutter-repo) does not report any problems on my PR.Breaking Change
Does your PR require Flutter developers to manually update their apps to accommodate your change?