Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions shell/platform/darwin/macos/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ source_set("flutter_framework_source") {
"framework/Source/FlutterEngine_Internal.h",
"framework/Source/FlutterExternalTextureGL.h",
"framework/Source/FlutterExternalTextureGL.mm",
"framework/Source/FlutterMouseCursorPlugin.h",
"framework/Source/FlutterMouseCursorPlugin.mm",
"framework/Source/FlutterTextInputModel.h",
"framework/Source/FlutterTextInputModel.mm",
"framework/Source/FlutterTextInputPlugin.h",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import <Cocoa/Cocoa.h>

#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterBinaryMessenger.h"
#import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterViewController.h"

/**
* A plugin to handle mouse cursor.
*
* Responsible for bridging the native macOS mouse cursor system with the
* Flutter framework mouse cursor classes, via system channels.
*/
@interface FlutterMouseCursorPlugin : NSObject <FlutterPlugin>

@end
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// Copyright 2019 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 <objc/message.h>

#import "FlutterMouseCursorPlugin.h"
#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterCodecs.h"

static NSString* const kMouseCursorChannel = @"flutter/mousecursor";

static NSString* const kActivateSystemCursorMethod = @"activateSystemCursor";
static NSString* const kKindKey = @"kind";
static NSString* const kDeviceKey = @"device";

static NSString* const kKindValueNone = @"none";

static NSMutableDictionary* cachedSystemCursors;
Comment thread
dkwingsmt marked this conversation as resolved.
Outdated

// Maps a Flutter's constant to a platform's cursor object.
//
// Returns the arrow cursor for unknown constants, including kSystemShapeNone.
static NSCursor* mapKindToCursor(NSString* kind) {
Comment thread
dkwingsmt marked this conversation as resolved.
Outdated
// The following mapping must be kept in sync with Flutter framework's
// mouse_cursor.dart
if ([kind isEqualToString:@"basic"])
return [NSCursor arrowCursor];
else if ([kind isEqualToString:@"click"])
return [NSCursor pointingHandCursor];
else if ([kind isEqualToString:@"text"])
return [NSCursor IBeamCursor];
else if ([kind isEqualToString:@"forbidden"])
return [NSCursor operationNotAllowedCursor];
else if ([kind isEqualToString:@"grab"])
return [NSCursor openHandCursor];
else if ([kind isEqualToString:@"grabbing"])
return [NSCursor closedHandCursor];
else
return [NSCursor arrowCursor];
}

@interface FlutterMouseCursorPlugin ()
/**
* The channel used to communicate with Flutter.
*/
@property(nonatomic) FlutterMethodChannel* channel;
Comment thread
dkwingsmt marked this conversation as resolved.
Outdated

/**
* Whether the cursor is currently hidden.
*/
@property(nonatomic) BOOL hidden;

@end

@implementation FlutterMouseCursorPlugin

- (instancetype)initWithChannel:(FlutterMethodChannel *)channel {
self = [super init];
if (self) {
_channel = channel;
}
return self;
}

#pragma mark - Private

- (id)activateSystemCursor:(nonnull NSDictionary*)arguments {
Comment thread
dkwingsmt marked this conversation as resolved.
Outdated
Comment thread
dkwingsmt marked this conversation as resolved.
Outdated
if (!arguments) {
Comment thread
dkwingsmt marked this conversation as resolved.
Outdated
return [FlutterError
errorWithCode:@"error"
message:@"Missing arguments"
details:@"Missing arguments while trying to activate system cursor"];
}
NSString* kindArg = arguments[kKindKey];
NSNumber* deviceArg = arguments[kDeviceKey];
if (!kindArg || !deviceArg) {
return [FlutterError
errorWithCode:@"error"
message:@"Missing argument"
details:@"Missing argument while trying to activate system cursor"];
}
if ([kindArg isEqualToString:kKindValueNone]) {
[self hide];
return nil;
}
NSCursor* shapeObject = [FlutterMouseCursorPlugin resolveKindToCursor:kindArg];
[self activateShapeObject:shapeObject];
return nil;
}

- (void)activateShapeObject: (nonnull NSCursor *)targetShape {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This still needs clang-formating

[targetShape set];
if (_hidden) {
[NSCursor unhide];
}
_hidden = NO;
}

- (void)hide {
if (!_hidden) {
[NSCursor hide];
}
_hidden = YES;
Comment thread
dkwingsmt marked this conversation as resolved.
}

+ (NSCursor*)resolveKindToCursor:(NSString*)kind {
Comment thread
dkwingsmt marked this conversation as resolved.
Outdated
if (cachedSystemCursors == nil) {
Comment thread
dkwingsmt marked this conversation as resolved.
Outdated
cachedSystemCursors = [NSMutableDictionary dictionary];
}
Comment thread
dkwingsmt marked this conversation as resolved.
Outdated

NSCursor* cachedValue = [cachedSystemCursors objectForKey:kind];
if (cachedValue == nil) {
cachedValue = mapKindToCursor(kind);
[cachedSystemCursors setValue:cachedValue forKey:kind];
}
return cachedValue;
}

#pragma mark - FlutterPlugin implementation

+ (void)registerWithRegistrar:(id<FlutterPluginRegistrar>)registrar {
FlutterMethodChannel *channel = [FlutterMethodChannel methodChannelWithName:kMouseCursorChannel
binaryMessenger:registrar.messenger];
FlutterMouseCursorPlugin *instance = [[FlutterMouseCursorPlugin alloc] initWithChannel:channel];
[registrar addMethodCallDelegate:instance channel:channel];
}

- (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result {
NSString* method = call.method;
if ([method isEqualToString:kActivateSystemCursorMethod]) {
result([self activateSystemCursor:call.arguments]);
} else {
result(FlutterMethodNotImplemented);
}
}

@end
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h"
#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterCodecs.h"
#import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.h"
#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterView.h"
Expand Down Expand Up @@ -350,6 +351,7 @@ - (void)configureTrackingArea {
}

- (void)addInternalPlugins {
[FlutterMouseCursorPlugin registerWithRegistrar:[self registrarForPlugin:@"mousecursor"]];
_textInputPlugin = [[FlutterTextInputPlugin alloc] initWithViewController:self];
_keyEventChannel =
[FlutterBasicMessageChannel messageChannelWithName:@"flutter/keyevent"
Expand Down