Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

[WIP] Mouse cursor (engine)#13152

Closed
dkwingsmt wants to merge 15 commits intoflutter:masterfrom
dkwingsmt:mouse-cursor
Closed

[WIP] Mouse cursor (engine)#13152
dkwingsmt wants to merge 15 commits intoflutter:masterfrom
dkwingsmt:mouse-cursor

Conversation

@dkwingsmt
Copy link
Contributor

@dkwingsmt dkwingsmt commented Oct 15, 2019

Description

This is a WIP about adding mouse cursor control.

The engine section of the mouse cursor system is basically a bridge between the framework section, which it talks to via a method channel, and the system calls. Although all platforms use the same channel, each platform has their own interfaces depending on how features are provided by the platform. That being said, all platform interfaces are designed with the following principles:

  1. The argument is a Map<String, dynamic>.
  2. States are maintained by the framework if possible.
    For example, macOS requires hide and unhide calls to be balanced. This is counted and enforced by the framework, while the engine blindly follows commands.
    Exception: When we implement custom cursors in the future, the cursor objects have to be cached and maintained by the engine.
  3. Argument values should be system constants whenever possible.
    For example, Flutter has MouseCursorSystemShape which are platform-independent constants that represent system cursors. The mapping between them to Android/GLFW platform constants are done by the framework, so that the engine can directly use the platform constants it receives.
    Exception: On macOS the system mouse cursors are not represented by integer constants but by objects, therefore we have to define our own system constants.

Related Issues

@mklim mklim self-requested a review October 15, 2019 22:01
@mklim mklim self-assigned this Oct 15, 2019
Copy link
Contributor

@mklim mklim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I gave this PR a high-level review and didn't closely vet for readability/style nits since this is still a WIP.

One thing I noticed is that in the engine there's a v1 and a v2 embedding. These changes need to be added to both places, because we're currently supporting both.

Depending on whether context needs to be tied to an activity, and assuming it doesn't, I think it makes more sense for the channel to be initialized and live in FlutterEngine in v2 of the embedding instead of FlutterView. If it really needs a context then it should probably be in the v2 FlutterView, shell/platform/android/io/flutter/embedding/android/FlutterView.java.

Currently this PR is just adding it to the v1 "FlutterView," shell/platform/android/io/flutter/view/FlutterView.java, which I think does make sense as the right place for the v1 embedding.

You can look up LifecycleChannel for an example of how something is used in both places.

Other than that the basic approach doesn't seem to have any issues to me.

/cc @matthew-carroll, I think it would be good for him to verify this too.

private MouseCursorMethodHandler mouseCursorMethodHandler;

public MouseCursorChannel(@NonNull DartExecutor dartExecutor) {
channel = new MethodChannel(dartExecutor, CHANNEL_NAME, JSONMethodCodec.INSTANCE);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason why JSON instead of the Standard method codec here?

Edit: I looked at the other channels and they seem to vary. I'm not sure which is preferred but plugins always uses Standard. /cc @matthew-carroll in case he knows.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, I don't know. I added structure around the channels, but didn't change any of them.

// Argument list is [device, cursor]. Since Android only supports one pointer device,
// we can ignore argument 0.
cursor = argumentList.getInt(1);
} catch (Exception e) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

readability nit: It's generally not great to catch Exception as a top level type. This ends up potentially masking completely unrelated issues to actually parsing the message. I would change this into the specific parsing exceptions that are a concern.

}

@NonNull
@VisibleForTesting
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

readability nit: I wouldn't make this @VisibleForTesting protected unless you really need to expose this for a test and just for a test for some reason. In the state this PR is now it would be better to make this private.

* The given {@code dartExecutor} is used to construct the method channel with which the
* platform communicates with the framework.
*
* The given {@code context} is used to create system cursor object. Usually it's the current
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Usually it's the current activity.

Does it need to be linked to an activity, or is it okay to be just a system context? I'm asking because with the v2 add-to-app embedding changes, it's possible for engine code like this to be executing even when there is no visible UI or activity to speak of.

@NonNull
private View view;

// The target application context.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"application context" would mean that this is never an activity, so this is a little confusing. (Android is confusing in general about this. See the #getApplicationContext docs.)


/* Displays no cursor at the pointer.
*/
public final static int none = 0x334c4a4c;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is hard to read and understand. Would Strings make sense here? What's the significance of these specific int values?

// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package io.flutter.plugin.mouse;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this expected to be used in plugins? I think this would maybe make more sense in io.flutter.embedding.engine.systemchannels.

@mklim mklim requested a review from matthew-carroll October 16, 2019 21:09
private MouseCursorMethodHandler mouseCursorMethodHandler;

public MouseCursorChannel(@NonNull DartExecutor dartExecutor) {
channel = new MethodChannel(dartExecutor, CHANNEL_NAME, JSONMethodCodec.INSTANCE);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, I don't know. I added structure around the channels, but didn't change any of them.

import io.flutter.plugin.mouse.MouseCursors;

/**
* Manages mouse cursor of the mouse device.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please include <p> tags in all javadoc comments, as shown in other files.

mImm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
PlatformViewsController platformViewsController = mNativeView.getPluginRegistry().getPlatformViewsController();
mTextInputPlugin = new TextInputPlugin(this, dartExecutor, platformViewsController);
mMouseCursorController = new MouseCursorController(this, dartExecutor, context);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are changes needed in the new FlutterView, too?

) {
channel = new MouseCursorChannel(dartExecutor);
channel.setMethodHandler(channelHandler);
this.view = view;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than manipulate a View within this class, I would recommend defining a delegate interface and then implement that interface within the old and new FlutterViews. That way, there is no magic or mystery when reading FlutterView as to what it's doing. When we move control of something like a View to other classes, sooner or later we get surprised by conflicts or unexpected behaviors.

@dkwingsmt
Copy link
Contributor Author

Close it in favor of separate PRs for each platform. Also I have some major changes in progress.

@dkwingsmt dkwingsmt closed this Nov 7, 2019
@dkwingsmt dkwingsmt deleted the mouse-cursor branch January 6, 2022 01:46
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants