From 89b9519301451994c5b5f3a1cbe9962a3750cbc5 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 4 Nov 2019 18:58:17 -0800 Subject: [PATCH 01/14] Untested macOS impl --- shell/platform/darwin/macos/BUILD.gn | 2 + .../Source/FlutterMouseCursorPlugin.h | 26 +++ .../Source/FlutterMouseCursorPlugin.mm | 220 ++++++++++++++++++ .../framework/Source/FlutterViewController.mm | 6 + 4 files changed, 254 insertions(+) create mode 100644 shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.h create mode 100644 shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm diff --git a/shell/platform/darwin/macos/BUILD.gn b/shell/platform/darwin/macos/BUILD.gn index ff598fb8071c5..aef1cd7a6cd32 100644 --- a/shell/platform/darwin/macos/BUILD.gn +++ b/shell/platform/darwin/macos/BUILD.gn @@ -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", diff --git a/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.h b/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.h new file mode 100644 index 0000000000000..80512651f14b8 --- /dev/null +++ b/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.h @@ -0,0 +1,26 @@ +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import + +#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. + * + * This is not an FlutterPlugin since it needs access to FlutterViewController + * internals, so needs to be managed differently. + */ +@interface FlutterMouseCursorPlugin : NSObject + +/** + * Initializes a mouse cursor plugin that coordinates key event handling with + * |viewController|. + */ +- (nonnull instancetype)initWithViewController:(FlutterViewController*)viewController; + +@end diff --git a/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm b/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm new file mode 100644 index 0000000000000..4d4b67b24c979 --- /dev/null +++ b/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm @@ -0,0 +1,220 @@ +// 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 "flutter/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.h" + +#import + +#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterCodecs.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h" + +// System shape are constant integers used by the framework to represent a shape +// of system cursors. +// +// They are intentionally designed to be meaningless integers. +// +// They must be kept in sync with Flutter framework's mouse_cursor.dart +static int const kSystemShapeNone = 0x334c4a4c; +static int const kSystemShapeBasic = 0xf17aaabc; +static int const kSystemShapeClick = 0xa8affc08; +static int const kSystemShapeText = 0x1cb251ec; +static int const kSystemShapeForbidden = 0x7fa3b767; +static int const kSystemShapeGrab = 0x28b91f80; +static int const kSystemShapeGrabbing = 0x6631ce3e; + +static int const kDefaultSystemShape = kSystemShapeBasic; + +static NSString* const kMouseCursorChannel = @"flutter/mousecursor"; + +static NSString* const kActivateShapeMethod = @"activateShape"; +static NSString* const kShapeKey = @"shape"; +static NSString* const kDeviceKey = @"device"; + +@interface FlutterMouseCursorDeviceState : NSObject + +- (nonnull instancetype)initWithDevice:(int)device + shape:(nonnull NSCursor*)shape; + +- (void)dispose; + +- (BOOL)activateShape: (nonnull NSCursor *)shape; + +@property(readonly) int device; + +@property() NSCursor *shape; + +@end + +@implementation FlutterMouseCursorDeviceState { + BOOL _hidden; +} + +- (nonnull instancetype)initWithDevice:(int)_device + shape:(nonnull NSCursor*)_shape { + device = _device; + shape = _shape; + _hidden = NO; + [shape set]; + return self; +} + +- (void)dispose { + if (_hidden) + [NSCursor unhide]; +} + +- (void)activateShape: (NSCursor *)targetShape { + if (targetShape == shape) + return; + [cursor set]; + if (_hidden) + [NSCursor unhide]; + shape = targetShape; + _hidden = NO; +} + +- (void)hide { + if (!_hidden) + [NSCursor hide]; +} + +#pragma mark - Private + +@end + +@interface FlutterMouseCursorPlugin +/** + * The channel used to communicate with Flutter. + */ +@property() FlutterMethodChannel* _channel; + +/** + * The FlutterViewController to manage input for. + */ +@property(nonatomic, weak) FlutterViewController* _flutterViewController; + +@property() NSDictionary *_shapeObjects; + +@property() FlutterMouseCursorDeviceState *_deviceState; + +@end + +@implementation FlutterMouseCursorPlugin + +- (instancetype)initWithViewController:(FlutterViewController*)viewController { + self = [super init]; + if (self != nil) { + _flutterViewController = viewController; + _channel = [FlutterMethodChannel methodChannelWithName:kMouseCursorChannel + binaryMessenger:viewController.engine.binaryMessenger + codec:[FlutterStandardMethodCodec sharedInstance]]; + __weak FlutterMouseCursorPlugin* weakSelf = self; + [_channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { + [weakSelf handleMethodCall:call result:result]; + }]; + _shapeObjects = @{}; + } + return self; +} + +#pragma mark - Private + +- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { + BOOL handled = YES; + NSString* method = call.method; + if ([method isEqualToString:kActivateShapeMethod]) { + result([self activateShape:call.arguments]); + } else { + handled = NO; + NSLog(@"Unhandled mouse cursor method '%@'", method); + result(FlutterMethodNotImplemented); + } +} + +- (id)activateShape:(NSDictionary*)arguments { + if (!arguments) { + return [FlutterError + errorWithCode:@"error" + message:@"Missing arguments" + details:@"Missing arguments while trying to activate shape"]; + } + NSNumber* shapeArg = arguments[kShapeKey]; + if (!shapeArg) { + return [FlutterError + errorWithCode:@"error" + message:@"Missing argument" + details:@"Missing argument shape while trying to activate shape"]; + } + NSNumber* deviceArg = arguments[kDeviceKey]; + if (!deviceArg) { + return [FlutterError + errorWithCode:@"error" + message:@"Missing argument" + details:@"Missing argument device while trying to activate shape"]; + } + int shape = [shapeArg intValue]; + int noNoneShape = shape == kSystemShapeNone ? kSystemShapeBasic : shape; + NSCursor* shapeObject = [self resolveShape: noNoneShape]; + if (shapeObject == nil) { + // Unregistered shape. Return false to request fallback. + return [false]; + } + [self ensureDevice: [deviceArg intValue] withShapeObject: shapeObject]; + if (shape == kSystemShapeNone) { + [_deviceState hide]; + } else { + [_deviceState activateShape: shapeObject]; + } + return [true]; +} + +- (nullable NSCursor*)resolveShape:(int)shape { + NSCursor *cachedObject = _shapeObjects[shape]; + if (cachedObject) + return cachedObject; + NSCursor *systemObject = [FlutterMouseCursorPlugin resolveSystemShape: shape]; + if (!systemObject) + return nil; + _shapeObjects[shape] = systemObject; + return systemObject; +} + +- (void)ensureDevice:(int)device + withShapeObject:(nonnull NSCursor*)shapeObject { + // MacOS only supports one device. Destroy the old when the new one is different. + if (_deviceState.device != device) { + [_deviceState dispose]; + _deviceState = nil; + } + if (!_deviceState) { + _deviceState = [[FlutterMouseCursorDeviceState alloc] initWithDevice: device + shape: shapeObject]; + } +} + +#pragma mark - Static + +// Does not handle kSystemShapeNone. +// Returns null for default. ++ (nullable NSCursor*)resolveSystemShape:(int)platformConstant { + switch (platformConstant) { + case kSystemShapeBasic: + return [NSCursor arrowCursor]; + case kSystemShapeClick: + return [NSCursor pointingHandCursor]; + case kSystemShapeText: + return [NSCursor IBeamCursor]; + case kSystemShapeForbidden: + return [NSCursor operationNotAllowedCursor]; + case kSystemShapeGrab: + return [NSCursor openHandCursor]; + case kSystemShapeGrabbing: + return [NSCursor closedHandCursor]; + default: + break; + } + return nil; +} + +@end diff --git a/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm index 2ea2070a1f737..870f7c79edad0 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm @@ -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" @@ -168,6 +169,10 @@ @implementation FlutterViewController { // The project to run in this controller's engine. FlutterDartProject* _project; + // The plugin used to handle mouse cursor. This is not an FlutterPlugin, so must be owned + // separately. + FlutterMouseCursorPlugin* _mouseCursorPlugin; + // The plugin used to handle text input. This is not an FlutterPlugin, so must be owned // separately. FlutterTextInputPlugin* _textInputPlugin; @@ -317,6 +322,7 @@ - (void)configureTrackingArea { } - (void)addInternalPlugins { + _mouseCursorPlugin = [[FlutterMouseCursorPlugin alloc] initWithViewController:self]; _textInputPlugin = [[FlutterTextInputPlugin alloc] initWithViewController:self]; _keyEventChannel = [FlutterBasicMessageChannel messageChannelWithName:@"flutter/keyevent" From 35521522ea1570f2c141b508f4ca4bf088e3a161 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 5 Nov 2019 11:23:56 -0800 Subject: [PATCH 02/14] Workable mac --- .../Source/FlutterMouseCursorPlugin.h | 2 +- .../Source/FlutterMouseCursorPlugin.mm | 80 ++++++++++--------- 2 files changed, 42 insertions(+), 40 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.h b/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.h index 80512651f14b8..c211a23c3a017 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.h @@ -21,6 +21,6 @@ * Initializes a mouse cursor plugin that coordinates key event handling with * |viewController|. */ -- (nonnull instancetype)initWithViewController:(FlutterViewController*)viewController; +- (nonnull instancetype)initWithViewController:(nonnull FlutterViewController*)viewController; @end diff --git a/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm b/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm index 4d4b67b24c979..c13532d9a0ee4 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm @@ -23,8 +23,6 @@ static int const kSystemShapeGrab = 0x28b91f80; static int const kSystemShapeGrabbing = 0x6631ce3e; -static int const kDefaultSystemShape = kSystemShapeBasic; - static NSString* const kMouseCursorChannel = @"flutter/mousecursor"; static NSString* const kActivateShapeMethod = @"activateShape"; @@ -38,7 +36,7 @@ - (nonnull instancetype)initWithDevice:(int)device - (void)dispose; -- (BOOL)activateShape: (nonnull NSCursor *)shape; +- (void)activateShape: (nonnull NSCursor *)targetShape; @property(readonly) int device; @@ -50,12 +48,12 @@ @implementation FlutterMouseCursorDeviceState { BOOL _hidden; } -- (nonnull instancetype)initWithDevice:(int)_device - shape:(nonnull NSCursor*)_shape { - device = _device; - shape = _shape; +- (nonnull instancetype)initWithDevice:(int)device + shape:(nonnull NSCursor*)shape { + _device = device; + _shape = shape; _hidden = NO; - [shape set]; + [_shape set]; return self; } @@ -64,45 +62,48 @@ - (void)dispose { [NSCursor unhide]; } -- (void)activateShape: (NSCursor *)targetShape { - if (targetShape == shape) +- (void)activateShape: (nonnull NSCursor *)targetShape { + if (targetShape == _shape && !_hidden) return; - [cursor set]; - if (_hidden) + [targetShape set]; + if (_hidden) { [NSCursor unhide]; - shape = targetShape; + } + _shape = targetShape; _hidden = NO; } - (void)hide { - if (!_hidden) + if (!_hidden) { [NSCursor hide]; + } + _hidden = YES; } #pragma mark - Private @end -@interface FlutterMouseCursorPlugin +@interface FlutterMouseCursorPlugin () /** * The channel used to communicate with Flutter. */ -@property() FlutterMethodChannel* _channel; +@property() FlutterMethodChannel* channel; /** * The FlutterViewController to manage input for. */ -@property(nonatomic, weak) FlutterViewController* _flutterViewController; +@property(nonatomic, weak) FlutterViewController* flutterViewController; -@property() NSDictionary *_shapeObjects; +@property() NSMutableDictionary *shapeObjects; -@property() FlutterMouseCursorDeviceState *_deviceState; +@property() FlutterMouseCursorDeviceState *deviceState; @end @implementation FlutterMouseCursorPlugin -- (instancetype)initWithViewController:(FlutterViewController*)viewController { +- (instancetype)initWithViewController:(nonnull FlutterViewController*)viewController { self = [super init]; if (self != nil) { _flutterViewController = viewController; @@ -113,14 +114,14 @@ - (instancetype)initWithViewController:(FlutterViewController*)viewController { [_channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { [weakSelf handleMethodCall:call result:result]; }]; - _shapeObjects = @{}; + _shapeObjects = [[NSMutableDictionary alloc] init]; } return self; } #pragma mark - Private -- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { +- (void)handleMethodCall:(nonnull FlutterMethodCall*)call result:(FlutterResult)result { BOOL handled = YES; NSString* method = call.method; if ([method isEqualToString:kActivateShapeMethod]) { @@ -132,7 +133,7 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { } } -- (id)activateShape:(NSDictionary*)arguments { +- (id)activateShape:(nonnull NSDictionary*)arguments { if (!arguments) { return [FlutterError errorWithCode:@"error" @@ -153,43 +154,44 @@ - (id)activateShape:(NSDictionary*)arguments { message:@"Missing argument" details:@"Missing argument device while trying to activate shape"]; } - int shape = [shapeArg intValue]; - int noNoneShape = shape == kSystemShapeNone ? kSystemShapeBasic : shape; - NSCursor* shapeObject = [self resolveShape: noNoneShape]; + NSNumber *noNoneShape = [shapeArg intValue] == kSystemShapeNone ? + [NSNumber numberWithInt:kSystemShapeBasic] : + shapeArg; + NSCursor* shapeObject = [self resolveShape:noNoneShape]; if (shapeObject == nil) { // Unregistered shape. Return false to request fallback. - return [false]; + return @(NO); } - [self ensureDevice: [deviceArg intValue] withShapeObject: shapeObject]; - if (shape == kSystemShapeNone) { + [self ensureDevice:deviceArg withShapeObject:shapeObject]; + if ([shapeArg intValue] == kSystemShapeNone) { [_deviceState hide]; } else { - [_deviceState activateShape: shapeObject]; + [_deviceState activateShape:shapeObject]; } - return [true]; + return @(YES); } -- (nullable NSCursor*)resolveShape:(int)shape { +- (nullable NSCursor*)resolveShape:(NSNumber*)shape { NSCursor *cachedObject = _shapeObjects[shape]; if (cachedObject) return cachedObject; - NSCursor *systemObject = [FlutterMouseCursorPlugin resolveSystemShape: shape]; + NSCursor *systemObject = [FlutterMouseCursorPlugin resolveSystemShape:shape]; if (!systemObject) return nil; _shapeObjects[shape] = systemObject; return systemObject; } -- (void)ensureDevice:(int)device +- (void)ensureDevice:(NSNumber*)device withShapeObject:(nonnull NSCursor*)shapeObject { // MacOS only supports one device. Destroy the old when the new one is different. - if (_deviceState.device != device) { + if (_deviceState.device != [device intValue]) { [_deviceState dispose]; _deviceState = nil; } if (!_deviceState) { - _deviceState = [[FlutterMouseCursorDeviceState alloc] initWithDevice: device - shape: shapeObject]; + _deviceState = [[FlutterMouseCursorDeviceState alloc] initWithDevice:[device intValue] + shape:shapeObject]; } } @@ -197,8 +199,8 @@ - (void)ensureDevice:(int)device // Does not handle kSystemShapeNone. // Returns null for default. -+ (nullable NSCursor*)resolveSystemShape:(int)platformConstant { - switch (platformConstant) { ++ (nullable NSCursor*)resolveSystemShape:(NSNumber*)platformConstant { + switch ([platformConstant intValue]) { case kSystemShapeBasic: return [NSCursor arrowCursor]; case kSystemShapeClick: From 38aedbb262d3f8197913be313234f59902ef7c1a Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 4 May 2020 14:41:56 -0700 Subject: [PATCH 03/14] Modernize --- .../Source/FlutterMouseCursorPlugin.mm | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm b/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm index c13532d9a0ee4..0d192805aabed 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm @@ -15,18 +15,18 @@ // They are intentionally designed to be meaningless integers. // // They must be kept in sync with Flutter framework's mouse_cursor.dart -static int const kSystemShapeNone = 0x334c4a4c; -static int const kSystemShapeBasic = 0xf17aaabc; -static int const kSystemShapeClick = 0xa8affc08; -static int const kSystemShapeText = 0x1cb251ec; -static int const kSystemShapeForbidden = 0x7fa3b767; -static int const kSystemShapeGrab = 0x28b91f80; -static int const kSystemShapeGrabbing = 0x6631ce3e; +static int const kSystemShapeNone = 0x334c4a; +static int const kSystemShapeBasic = 0xf17aaa; +static int const kSystemShapeClick = 0xa8affc; +static int const kSystemShapeText = 0x1cb251; +static int const kSystemShapeForbidden = 0x350f9d; +static int const kSystemShapeGrab = 0x28b91f; +static int const kSystemShapeGrabbing = 0x6631ce; static NSString* const kMouseCursorChannel = @"flutter/mousecursor"; -static NSString* const kActivateShapeMethod = @"activateShape"; -static NSString* const kShapeKey = @"shape"; +static NSString* const kActivateSystemCursorMethod = @"activateSystemCursor"; +static NSString* const kShapeCodeKey = @"shapeCode"; static NSString* const kDeviceKey = @"device"; @interface FlutterMouseCursorDeviceState : NSObject @@ -124,8 +124,8 @@ - (instancetype)initWithViewController:(nonnull FlutterViewController*)viewContr - (void)handleMethodCall:(nonnull FlutterMethodCall*)call result:(FlutterResult)result { BOOL handled = YES; NSString* method = call.method; - if ([method isEqualToString:kActivateShapeMethod]) { - result([self activateShape:call.arguments]); + if ([method isEqualToString:kActivateSystemCursorMethod]) { + result([self activateSystemCursor:call.arguments]); } else { handled = NO; NSLog(@"Unhandled mouse cursor method '%@'", method); @@ -133,37 +133,37 @@ - (void)handleMethodCall:(nonnull FlutterMethodCall*)call result:(FlutterResult) } } -- (id)activateShape:(nonnull NSDictionary*)arguments { +- (id)activateSystemCursor:(nonnull NSDictionary*)arguments { if (!arguments) { return [FlutterError errorWithCode:@"error" message:@"Missing arguments" - details:@"Missing arguments while trying to activate shape"]; + details:@"Missing arguments while trying to activate system cursor"]; } - NSNumber* shapeArg = arguments[kShapeKey]; - if (!shapeArg) { + NSNumber* shapeCodeArg = arguments[kShapeCodeKey]; + if (!shapeCodeArg) { return [FlutterError errorWithCode:@"error" message:@"Missing argument" - details:@"Missing argument shape while trying to activate shape"]; + details:@"Missing argument shapeCode while trying to activate system cursor"]; } NSNumber* deviceArg = arguments[kDeviceKey]; if (!deviceArg) { return [FlutterError errorWithCode:@"error" message:@"Missing argument" - details:@"Missing argument device while trying to activate shape"]; + details:@"Missing argument device while trying to activate system cursor"]; } - NSNumber *noNoneShape = [shapeArg intValue] == kSystemShapeNone ? + NSNumber *noNoneShape = [shapeCodeArg intValue] == kSystemShapeNone ? [NSNumber numberWithInt:kSystemShapeBasic] : - shapeArg; - NSCursor* shapeObject = [self resolveShape:noNoneShape]; + shapeCodeArg; + NSCursor* shapeObject = [self resolveShapeCode:noNoneShape]; if (shapeObject == nil) { // Unregistered shape. Return false to request fallback. return @(NO); } [self ensureDevice:deviceArg withShapeObject:shapeObject]; - if ([shapeArg intValue] == kSystemShapeNone) { + if ([shapeCodeArg intValue] == kSystemShapeNone) { [_deviceState hide]; } else { [_deviceState activateShape:shapeObject]; @@ -171,7 +171,7 @@ - (id)activateShape:(nonnull NSDictionary*)arguments { return @(YES); } -- (nullable NSCursor*)resolveShape:(NSNumber*)shape { +- (nullable NSCursor*)resolveShapeCode:(NSNumber*)shape { NSCursor *cachedObject = _shapeObjects[shape]; if (cachedObject) return cachedObject; From 6272fc1e14b39599bb9be1034737455126d5b142 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Mon, 4 May 2020 15:09:41 -0700 Subject: [PATCH 04/14] Simplify --- .../Source/FlutterMouseCursorPlugin.mm | 99 +++++-------------- 1 file changed, 24 insertions(+), 75 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm b/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm index 0d192805aabed..e3929d39e0719 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm @@ -29,60 +29,6 @@ static NSString* const kShapeCodeKey = @"shapeCode"; static NSString* const kDeviceKey = @"device"; -@interface FlutterMouseCursorDeviceState : NSObject - -- (nonnull instancetype)initWithDevice:(int)device - shape:(nonnull NSCursor*)shape; - -- (void)dispose; - -- (void)activateShape: (nonnull NSCursor *)targetShape; - -@property(readonly) int device; - -@property() NSCursor *shape; - -@end - -@implementation FlutterMouseCursorDeviceState { - BOOL _hidden; -} - -- (nonnull instancetype)initWithDevice:(int)device - shape:(nonnull NSCursor*)shape { - _device = device; - _shape = shape; - _hidden = NO; - [_shape set]; - return self; -} - -- (void)dispose { - if (_hidden) - [NSCursor unhide]; -} - -- (void)activateShape: (nonnull NSCursor *)targetShape { - if (targetShape == _shape && !_hidden) - return; - [targetShape set]; - if (_hidden) { - [NSCursor unhide]; - } - _shape = targetShape; - _hidden = NO; -} - -- (void)hide { - if (!_hidden) { - [NSCursor hide]; - } - _hidden = YES; -} - -#pragma mark - Private - -@end @interface FlutterMouseCursorPlugin () /** @@ -97,12 +43,14 @@ @interface FlutterMouseCursorPlugin () @property() NSMutableDictionary *shapeObjects; -@property() FlutterMouseCursorDeviceState *deviceState; +@property() NSCursor *shape; @end @implementation FlutterMouseCursorPlugin +BOOL _hidden; + - (instancetype)initWithViewController:(nonnull FlutterViewController*)viewController { self = [super init]; if (self != nil) { @@ -154,21 +102,15 @@ - (id)activateSystemCursor:(nonnull NSDictionary*)arguments { message:@"Missing argument" details:@"Missing argument device while trying to activate system cursor"]; } - NSNumber *noNoneShape = [shapeCodeArg intValue] == kSystemShapeNone ? - [NSNumber numberWithInt:kSystemShapeBasic] : - shapeCodeArg; - NSCursor* shapeObject = [self resolveShapeCode:noNoneShape]; + if ([shapeCodeArg intValue] == kSystemShapeNone) { + return [self hide]; + } + NSCursor* shapeObject = [self resolveShapeCode:shapeCodeArg]; if (shapeObject == nil) { // Unregistered shape. Return false to request fallback. return @(NO); } - [self ensureDevice:deviceArg withShapeObject:shapeObject]; - if ([shapeCodeArg intValue] == kSystemShapeNone) { - [_deviceState hide]; - } else { - [_deviceState activateShape:shapeObject]; - } - return @(YES); + return [self activateShapeObject:shapeObject]; } - (nullable NSCursor*)resolveShapeCode:(NSNumber*)shape { @@ -182,17 +124,24 @@ - (nullable NSCursor*)resolveShapeCode:(NSNumber*)shape { return systemObject; } -- (void)ensureDevice:(NSNumber*)device - withShapeObject:(nonnull NSCursor*)shapeObject { - // MacOS only supports one device. Destroy the old when the new one is different. - if (_deviceState.device != [device intValue]) { - [_deviceState dispose]; - _deviceState = nil; +- (id)activateShapeObject: (nonnull NSCursor *)targetShape { + if (targetShape == _shape && !_hidden) + return @(YES); + [targetShape set]; + if (_hidden) { + [NSCursor unhide]; } - if (!_deviceState) { - _deviceState = [[FlutterMouseCursorDeviceState alloc] initWithDevice:[device intValue] - shape:shapeObject]; + _shape = targetShape; + _hidden = NO; + return @(YES); +} + +- (id)hide { + if (!_hidden) { + [NSCursor hide]; } + _hidden = YES; + return @(YES); } #pragma mark - Static From 8ca0900f29eab5b60a8e308826aae2650de48a85 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Thu, 7 May 2020 19:58:33 -0700 Subject: [PATCH 05/14] Address comments --- .../Source/FlutterMouseCursorPlugin.mm | 74 +++++++++---------- 1 file changed, 33 insertions(+), 41 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm b/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm index e3929d39e0719..0e94af9740d1d 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm @@ -7,7 +7,6 @@ #import #import "flutter/shell/platform/darwin/common/framework/Headers/FlutterCodecs.h" -#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h" // System shape are constant integers used by the framework to represent a shape // of system cursors. @@ -15,13 +14,13 @@ // They are intentionally designed to be meaningless integers. // // They must be kept in sync with Flutter framework's mouse_cursor.dart -static int const kSystemShapeNone = 0x334c4a; -static int const kSystemShapeBasic = 0xf17aaa; -static int const kSystemShapeClick = 0xa8affc; -static int const kSystemShapeText = 0x1cb251; -static int const kSystemShapeForbidden = 0x350f9d; -static int const kSystemShapeGrab = 0x28b91f; -static int const kSystemShapeGrabbing = 0x6631ce; +static const int kSystemShapeNone = 0x334c4a; +static const int kSystemShapeBasic = 0xf17aaa; +static const int kSystemShapeClick = 0xa8affc; +static const int kSystemShapeText = 0x1cb251; +static const int kSystemShapeForbidden = 0x350f9d; +static const int kSystemShapeGrab = 0x28b91f; +static const int kSystemShapeGrabbing = 0x6631ce; static NSString* const kMouseCursorChannel = @"flutter/mousecursor"; @@ -29,28 +28,48 @@ static NSString* const kShapeCodeKey = @"shapeCode"; static NSString* const kDeviceKey = @"device"; +// Maps a Flutter's constant to a platform's cursor object. +// +// Returns nil for unknown constants, including kSystemShapeNone. +static NSCursor* resolveSystemShape(NSNumber* platformConstant) { + switch ([platformConstant intValue]) { + case kSystemShapeBasic: + return [NSCursor arrowCursor]; + case kSystemShapeClick: + return [NSCursor pointingHandCursor]; + case kSystemShapeText: + return [NSCursor IBeamCursor]; + case kSystemShapeForbidden: + return [NSCursor operationNotAllowedCursor]; + case kSystemShapeGrab: + return [NSCursor openHandCursor]; + case kSystemShapeGrabbing: + return [NSCursor closedHandCursor]; + } + return nil; +} @interface FlutterMouseCursorPlugin () /** * The channel used to communicate with Flutter. */ -@property() FlutterMethodChannel* channel; +@property(nonatomic) FlutterMethodChannel* channel; /** * The FlutterViewController to manage input for. */ @property(nonatomic, weak) FlutterViewController* flutterViewController; -@property() NSMutableDictionary *shapeObjects; +@property(nonatomic) NSMutableDictionary *shapeObjects; -@property() NSCursor *shape; +@property(nonatomic) NSCursor *shape; + +@property(nonatomic) BOOL hidden; @end @implementation FlutterMouseCursorPlugin -BOOL _hidden; - - (instancetype)initWithViewController:(nonnull FlutterViewController*)viewController { self = [super init]; if (self != nil) { @@ -70,13 +89,10 @@ - (instancetype)initWithViewController:(nonnull FlutterViewController*)viewContr #pragma mark - Private - (void)handleMethodCall:(nonnull FlutterMethodCall*)call result:(FlutterResult)result { - BOOL handled = YES; NSString* method = call.method; if ([method isEqualToString:kActivateSystemCursorMethod]) { result([self activateSystemCursor:call.arguments]); } else { - handled = NO; - NSLog(@"Unhandled mouse cursor method '%@'", method); result(FlutterMethodNotImplemented); } } @@ -117,7 +133,7 @@ - (nullable NSCursor*)resolveShapeCode:(NSNumber*)shape { NSCursor *cachedObject = _shapeObjects[shape]; if (cachedObject) return cachedObject; - NSCursor *systemObject = [FlutterMouseCursorPlugin resolveSystemShape:shape]; + NSCursor *systemObject = resolveSystemShape(shape); if (!systemObject) return nil; _shapeObjects[shape] = systemObject; @@ -144,28 +160,4 @@ - (id)hide { return @(YES); } -#pragma mark - Static - -// Does not handle kSystemShapeNone. -// Returns null for default. -+ (nullable NSCursor*)resolveSystemShape:(NSNumber*)platformConstant { - switch ([platformConstant intValue]) { - case kSystemShapeBasic: - return [NSCursor arrowCursor]; - case kSystemShapeClick: - return [NSCursor pointingHandCursor]; - case kSystemShapeText: - return [NSCursor IBeamCursor]; - case kSystemShapeForbidden: - return [NSCursor operationNotAllowedCursor]; - case kSystemShapeGrab: - return [NSCursor openHandCursor]; - case kSystemShapeGrabbing: - return [NSCursor closedHandCursor]; - default: - break; - } - return nil; -} - @end From 55a63a52d4e270da23d664d18e0ea09e9da5e8e7 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Fri, 8 May 2020 16:58:18 -0700 Subject: [PATCH 06/14] Refactor with FlutterPlugin --- .../Source/FlutterMouseCursorPlugin.h | 11 +- .../Source/FlutterMouseCursorPlugin.mm | 100 ++++++------------ .../framework/Source/FlutterViewController.mm | 6 +- 3 files changed, 37 insertions(+), 80 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.h b/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.h index c211a23c3a017..052c9845357b0 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.h @@ -11,16 +11,7 @@ * * Responsible for bridging the native macOS mouse cursor system with the * Flutter framework mouse cursor classes, via system channels. - * - * This is not an FlutterPlugin since it needs access to FlutterViewController - * internals, so needs to be managed differently. - */ -@interface FlutterMouseCursorPlugin : NSObject - -/** - * Initializes a mouse cursor plugin that coordinates key event handling with - * |viewController|. */ -- (nonnull instancetype)initWithViewController:(nonnull FlutterViewController*)viewController; +@interface FlutterMouseCursorPlugin : NSObject @end diff --git a/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm b/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm index 0e94af9740d1d..f38a0ec7c7c77 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm @@ -2,10 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.h" - #import +#import "FlutterMouseCursorPlugin.h" #import "flutter/shell/platform/darwin/common/framework/Headers/FlutterCodecs.h" // System shape are constant integers used by the framework to represent a shape @@ -30,11 +29,11 @@ // Maps a Flutter's constant to a platform's cursor object. // -// Returns nil for unknown constants, including kSystemShapeNone. +// Returns the arrow cursor for unknown constants, including kSystemShapeNone. static NSCursor* resolveSystemShape(NSNumber* platformConstant) { switch ([platformConstant intValue]) { case kSystemShapeBasic: - return [NSCursor arrowCursor]; + break; case kSystemShapeClick: return [NSCursor pointingHandCursor]; case kSystemShapeText: @@ -46,7 +45,7 @@ case kSystemShapeGrabbing: return [NSCursor closedHandCursor]; } - return nil; + return [NSCursor arrowCursor];; } @interface FlutterMouseCursorPlugin () @@ -56,47 +55,24 @@ @interface FlutterMouseCursorPlugin () @property(nonatomic) FlutterMethodChannel* channel; /** - * The FlutterViewController to manage input for. + * Whether the cursor is currently hidden. */ -@property(nonatomic, weak) FlutterViewController* flutterViewController; - -@property(nonatomic) NSMutableDictionary *shapeObjects; - -@property(nonatomic) NSCursor *shape; - @property(nonatomic) BOOL hidden; @end @implementation FlutterMouseCursorPlugin -- (instancetype)initWithViewController:(nonnull FlutterViewController*)viewController { +- (instancetype)initWithChannel:(FlutterMethodChannel *)channel { self = [super init]; - if (self != nil) { - _flutterViewController = viewController; - _channel = [FlutterMethodChannel methodChannelWithName:kMouseCursorChannel - binaryMessenger:viewController.engine.binaryMessenger - codec:[FlutterStandardMethodCodec sharedInstance]]; - __weak FlutterMouseCursorPlugin* weakSelf = self; - [_channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { - [weakSelf handleMethodCall:call result:result]; - }]; - _shapeObjects = [[NSMutableDictionary alloc] init]; + if (self) { + _channel = channel; } return self; } #pragma mark - Private -- (void)handleMethodCall:(nonnull FlutterMethodCall*)call result:(FlutterResult)result { - NSString* method = call.method; - if ([method isEqualToString:kActivateSystemCursorMethod]) { - result([self activateSystemCursor:call.arguments]); - } else { - result(FlutterMethodNotImplemented); - } -} - - (id)activateSystemCursor:(nonnull NSDictionary*)arguments { if (!arguments) { return [FlutterError @@ -105,59 +81,53 @@ - (id)activateSystemCursor:(nonnull NSDictionary*)arguments { details:@"Missing arguments while trying to activate system cursor"]; } NSNumber* shapeCodeArg = arguments[kShapeCodeKey]; - if (!shapeCodeArg) { - return [FlutterError - errorWithCode:@"error" - message:@"Missing argument" - details:@"Missing argument shapeCode while trying to activate system cursor"]; - } NSNumber* deviceArg = arguments[kDeviceKey]; - if (!deviceArg) { + if (!shapeCodeArg || !deviceArg) { return [FlutterError errorWithCode:@"error" message:@"Missing argument" - details:@"Missing argument device while trying to activate system cursor"]; + details:@"Missing argument while trying to activate system cursor"]; } if ([shapeCodeArg intValue] == kSystemShapeNone) { - return [self hide]; - } - NSCursor* shapeObject = [self resolveShapeCode:shapeCodeArg]; - if (shapeObject == nil) { - // Unregistered shape. Return false to request fallback. - return @(NO); - } - return [self activateShapeObject:shapeObject]; -} - -- (nullable NSCursor*)resolveShapeCode:(NSNumber*)shape { - NSCursor *cachedObject = _shapeObjects[shape]; - if (cachedObject) - return cachedObject; - NSCursor *systemObject = resolveSystemShape(shape); - if (!systemObject) + [self hide]; return nil; - _shapeObjects[shape] = systemObject; - return systemObject; + } + NSCursor* shapeObject = resolveSystemShape(shapeCodeArg); + [self activateShapeObject:shapeObject]; + return nil; } -- (id)activateShapeObject: (nonnull NSCursor *)targetShape { - if (targetShape == _shape && !_hidden) - return @(YES); +- (void)activateShapeObject: (nonnull NSCursor *)targetShape { [targetShape set]; if (_hidden) { [NSCursor unhide]; } - _shape = targetShape; _hidden = NO; - return @(YES); } -- (id)hide { +- (void)hide { if (!_hidden) { [NSCursor hide]; } _hidden = YES; - return @(YES); +} + +#pragma mark - FlutterPlugin implementation + ++ (void)registerWithRegistrar:(id)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 diff --git a/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm index 18ad460cbf4a2..34bee32373a74 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm @@ -174,10 +174,6 @@ @implementation FlutterViewController { // The project to run in this controller's engine. FlutterDartProject* _project; - // The plugin used to handle mouse cursor. This is not an FlutterPlugin, so must be owned - // separately. - FlutterMouseCursorPlugin* _mouseCursorPlugin; - // The plugin used to handle text input. This is not an FlutterPlugin, so must be owned // separately. FlutterTextInputPlugin* _textInputPlugin; @@ -355,7 +351,7 @@ - (void)configureTrackingArea { } - (void)addInternalPlugins { - _mouseCursorPlugin = [[FlutterMouseCursorPlugin alloc] initWithViewController:self]; + [FlutterMouseCursorPlugin registerWithRegistrar:[self registrarForPlugin:@"mousecursor"]]; _textInputPlugin = [[FlutterTextInputPlugin alloc] initWithViewController:self]; _keyEventChannel = [FlutterBasicMessageChannel messageChannelWithName:@"flutter/keyevent" From cd7abf118fc11f78df446648f9262da6b150b0f8 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Fri, 8 May 2020 19:37:45 -0700 Subject: [PATCH 07/14] Cached dict --- .../Source/FlutterMouseCursorPlugin.mm | 74 ++++++++++--------- 1 file changed, 39 insertions(+), 35 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm b/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm index f38a0ec7c7c77..42d0664535c46 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm @@ -7,45 +7,36 @@ #import "FlutterMouseCursorPlugin.h" #import "flutter/shell/platform/darwin/common/framework/Headers/FlutterCodecs.h" -// System shape are constant integers used by the framework to represent a shape -// of system cursors. -// -// They are intentionally designed to be meaningless integers. -// -// They must be kept in sync with Flutter framework's mouse_cursor.dart -static const int kSystemShapeNone = 0x334c4a; -static const int kSystemShapeBasic = 0xf17aaa; -static const int kSystemShapeClick = 0xa8affc; -static const int kSystemShapeText = 0x1cb251; -static const int kSystemShapeForbidden = 0x350f9d; -static const int kSystemShapeGrab = 0x28b91f; -static const int kSystemShapeGrabbing = 0x6631ce; - static NSString* const kMouseCursorChannel = @"flutter/mousecursor"; static NSString* const kActivateSystemCursorMethod = @"activateSystemCursor"; -static NSString* const kShapeCodeKey = @"shapeCode"; +static NSString* const kKindKey = @"kind"; static NSString* const kDeviceKey = @"device"; +static NSString* const kKindValueNone = @"none"; + +static NSMutableDictionary* cachedSystemCursors; + // Maps a Flutter's constant to a platform's cursor object. // // Returns the arrow cursor for unknown constants, including kSystemShapeNone. -static NSCursor* resolveSystemShape(NSNumber* platformConstant) { - switch ([platformConstant intValue]) { - case kSystemShapeBasic: - break; - case kSystemShapeClick: - return [NSCursor pointingHandCursor]; - case kSystemShapeText: - return [NSCursor IBeamCursor]; - case kSystemShapeForbidden: - return [NSCursor operationNotAllowedCursor]; - case kSystemShapeGrab: - return [NSCursor openHandCursor]; - case kSystemShapeGrabbing: - return [NSCursor closedHandCursor]; - } - return [NSCursor arrowCursor];; +static NSCursor* mapKindToCursor(NSString* kind) { + // 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 () @@ -80,19 +71,19 @@ - (id)activateSystemCursor:(nonnull NSDictionary*)arguments { message:@"Missing arguments" details:@"Missing arguments while trying to activate system cursor"]; } - NSNumber* shapeCodeArg = arguments[kShapeCodeKey]; + NSString* kindArg = arguments[kKindKey]; NSNumber* deviceArg = arguments[kDeviceKey]; - if (!shapeCodeArg || !deviceArg) { + if (!kindArg || !deviceArg) { return [FlutterError errorWithCode:@"error" message:@"Missing argument" details:@"Missing argument while trying to activate system cursor"]; } - if ([shapeCodeArg intValue] == kSystemShapeNone) { + if ([kindArg isEqualToString:kKindValueNone]) { [self hide]; return nil; } - NSCursor* shapeObject = resolveSystemShape(shapeCodeArg); + NSCursor* shapeObject = [FlutterMouseCursorPlugin resolveKindToCursor:kindArg]; [self activateShapeObject:shapeObject]; return nil; } @@ -112,6 +103,19 @@ - (void)hide { _hidden = YES; } ++ (NSCursor*)resolveKindToCursor:(NSString*)kind { + if (cachedSystemCursors == nil) { + cachedSystemCursors = [NSMutableDictionary dictionary]; + } + + 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)registrar { From ec138bc9c73d4ffde03606769be2d5453b7b43f9 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 19 May 2020 11:34:12 -0700 Subject: [PATCH 08/14] Update per comments --- .../Source/FlutterMouseCursorPlugin.mm | 86 ++++++++++++------- 1 file changed, 54 insertions(+), 32 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm b/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm index 42d0664535c46..5a4bb1378d6fa 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm @@ -15,12 +15,12 @@ static NSString* const kKindValueNone = @"none"; -static NSMutableDictionary* cachedSystemCursors; - -// 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) { +/** + * Maps a Flutter's constant to a platform's cursor object. + * + * Returns the arrow cursor for unknown constants, including kSystemShapeNone. + */ +static NSCursor* GetCursorForKind(NSString* kind) { // The following mapping must be kept in sync with Flutter framework's // mouse_cursor.dart if ([kind isEqualToString:@"basic"]) @@ -41,36 +41,62 @@ @interface FlutterMouseCursorPlugin () /** - * The channel used to communicate with Flutter. + * Whether the cursor is currently hidden. */ -@property(nonatomic) FlutterMethodChannel* channel; +@property(nonatomic) BOOL hidden; /** - * Whether the cursor is currently hidden. + * Handles the method call that activates a system cursor. + * + * Returns a FlutterError if the arguments can not be recognized. Otherwise + * returns nil. */ -@property(nonatomic) BOOL hidden; +- (FlutterError*)activateSystemCursor:(nonnull NSDictionary*)arguments; + +/** + * Displays the specified cursor. + * + * This method unhides the cursor before displaying the cursor, and updates + * internal states. + */ +- (void)displayCursorObject: (nonnull NSCursor *)cursorObject; + +/** + * Hides the cursor. + * + * This method hides the cursor if the cursor is currently unhidden, and + * updates internal states. + */ +- (void)hide; + +/** + * Handle all method calls. + */ +- (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result; @end @implementation FlutterMouseCursorPlugin -- (instancetype)initWithChannel:(FlutterMethodChannel *)channel { +#pragma mark - Private + +NSMutableDictionary* cachedSystemCursors; + +- (instancetype)init { self = [super init]; if (self) { - _channel = channel; + cachedSystemCursors = [NSMutableDictionary dictionary]; } return self; } -#pragma mark - Private - -- (id)activateSystemCursor:(nonnull NSDictionary*)arguments { - if (!arguments) { - return [FlutterError - errorWithCode:@"error" - message:@"Missing arguments" - details:@"Missing arguments while trying to activate system cursor"]; +- (void)dealloc { + if (_hidden) { + [NSCursor unhide]; } +} + +- (FlutterError*)activateSystemCursor:(nonnull NSDictionary*)arguments { NSString* kindArg = arguments[kKindKey]; NSNumber* deviceArg = arguments[kDeviceKey]; if (!kindArg || !deviceArg) { @@ -83,13 +109,13 @@ - (id)activateSystemCursor:(nonnull NSDictionary*)arguments { [self hide]; return nil; } - NSCursor* shapeObject = [FlutterMouseCursorPlugin resolveKindToCursor:kindArg]; - [self activateShapeObject:shapeObject]; + NSCursor* cursorObject = [FlutterMouseCursorPlugin cursorFromKind:kindArg]; + [self displayCursorObject:cursorObject]; return nil; } -- (void)activateShapeObject: (nonnull NSCursor *)targetShape { - [targetShape set]; +- (void)displayCursorObject: (nonnull NSCursor *)cursorObject { + [cursorObject set]; if (_hidden) { [NSCursor unhide]; } @@ -103,14 +129,10 @@ - (void)hide { _hidden = YES; } -+ (NSCursor*)resolveKindToCursor:(NSString*)kind { - if (cachedSystemCursors == nil) { - cachedSystemCursors = [NSMutableDictionary dictionary]; - } - ++ (NSCursor*)cursorFromKind:(NSString*)kind { NSCursor* cachedValue = [cachedSystemCursors objectForKey:kind]; - if (cachedValue == nil) { - cachedValue = mapKindToCursor(kind); + if (!cachedValue) { + cachedValue = GetCursorForKind(kind); [cachedSystemCursors setValue:cachedValue forKey:kind]; } return cachedValue; @@ -121,7 +143,7 @@ + (NSCursor*)resolveKindToCursor:(NSString*)kind { + (void)registerWithRegistrar:(id)registrar { FlutterMethodChannel *channel = [FlutterMethodChannel methodChannelWithName:kMouseCursorChannel binaryMessenger:registrar.messenger]; - FlutterMouseCursorPlugin *instance = [[FlutterMouseCursorPlugin alloc] initWithChannel:channel]; + FlutterMouseCursorPlugin *instance = [[FlutterMouseCursorPlugin alloc] init]; [registrar addMethodCallDelegate:instance channel:channel]; } From bd95f8278c998d0c0c276e4dd7e8af8ea4e03ea1 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 19 May 2020 13:00:54 -0700 Subject: [PATCH 09/14] format --- .../Source/FlutterMouseCursorPlugin.mm | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm b/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm index 5a4bb1378d6fa..07fffd87caea0 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm @@ -59,7 +59,7 @@ - (FlutterError*)activateSystemCursor:(nonnull NSDictionary*)arguments; * This method unhides the cursor before displaying the cursor, and updates * internal states. */ -- (void)displayCursorObject: (nonnull NSCursor *)cursorObject; +- (void)displayCursorObject:(nonnull NSCursor*)cursorObject; /** * Hides the cursor. @@ -72,7 +72,7 @@ - (void)hide; /** * Handle all method calls. */ -- (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result; +- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result; @end @@ -100,10 +100,9 @@ - (FlutterError*)activateSystemCursor:(nonnull NSDictionary*)arguments { 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"]; + return [FlutterError errorWithCode:@"error" + message:@"Missing argument" + details:@"Missing argument while trying to activate system cursor"]; } if ([kindArg isEqualToString:kKindValueNone]) { [self hide]; @@ -114,7 +113,7 @@ - (FlutterError*)activateSystemCursor:(nonnull NSDictionary*)arguments { return nil; } -- (void)displayCursorObject: (nonnull NSCursor *)cursorObject { +- (void)displayCursorObject:(nonnull NSCursor*)cursorObject { [cursorObject set]; if (_hidden) { [NSCursor unhide]; @@ -141,13 +140,13 @@ + (NSCursor*)cursorFromKind:(NSString*)kind { #pragma mark - FlutterPlugin implementation + (void)registerWithRegistrar:(id)registrar { - FlutterMethodChannel *channel = [FlutterMethodChannel methodChannelWithName:kMouseCursorChannel + FlutterMethodChannel* channel = [FlutterMethodChannel methodChannelWithName:kMouseCursorChannel binaryMessenger:registrar.messenger]; - FlutterMouseCursorPlugin *instance = [[FlutterMouseCursorPlugin alloc] init]; + FlutterMouseCursorPlugin* instance = [[FlutterMouseCursorPlugin alloc] init]; [registrar addMethodCallDelegate:instance channel:channel]; } -- (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { +- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { NSString* method = call.method; if ([method isEqualToString:kActivateSystemCursorMethod]) { result([self activateSystemCursor:call.arguments]); From 8da2c72155762882416ed5262dc0f94d24e16c44 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 19 May 2020 13:49:34 -0700 Subject: [PATCH 10/14] Remove device arg --- .../darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm b/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm index 07fffd87caea0..f9d54c08bc7eb 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm @@ -11,7 +11,6 @@ static NSString* const kActivateSystemCursorMethod = @"activateSystemCursor"; static NSString* const kKindKey = @"kind"; -static NSString* const kDeviceKey = @"device"; static NSString* const kKindValueNone = @"none"; @@ -98,8 +97,7 @@ - (void)dealloc { - (FlutterError*)activateSystemCursor:(nonnull NSDictionary*)arguments { NSString* kindArg = arguments[kKindKey]; - NSNumber* deviceArg = arguments[kDeviceKey]; - if (!kindArg || !deviceArg) { + if (!kindArg) { return [FlutterError errorWithCode:@"error" message:@"Missing argument" details:@"Missing argument while trying to activate system cursor"]; From 17bd7d8016b61b7c9a6b07915aced1646b6dd2a4 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 19 May 2020 14:59:40 -0700 Subject: [PATCH 11/14] Format --- .../darwin/macos/framework/Source/FlutterViewController.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm index 34bee32373a74..31708f233ce37 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterViewController.mm @@ -8,8 +8,8 @@ #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/FlutterMouseCursorPlugin.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterView.h" #import "flutter/shell/platform/embedder/embedder.h" From 81fd9dd4785f00c36f04e62ffaec3472dc9c9593 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 19 May 2020 16:34:16 -0700 Subject: [PATCH 12/14] Update license --- .../darwin/macos/framework/Source/FlutterMouseCursorPlugin.h | 1 + 1 file changed, 1 insertion(+) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.h b/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.h index 052c9845357b0..73b1d63e87d92 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.h +++ b/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.h @@ -1,3 +1,4 @@ +// Copyright 2013 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. From 033014f07e2755c0c1c01978609fee0656f58e12 Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Tue, 19 May 2020 16:46:34 -0700 Subject: [PATCH 13/14] Update licences --- ci/licenses_golden/licenses_flutter | 2 ++ .../darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 4aad0ab44b3e8..77cb61b3f608d 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -956,6 +956,8 @@ FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterEngin FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterExternalTextureGL.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterExternalTextureGL.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.h +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputModel.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputModel.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputPlugin.h diff --git a/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm b/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm index f9d54c08bc7eb..931e354b1d592 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm @@ -1,4 +1,4 @@ -// Copyright 2019 The Flutter Authors. All rights reserved. +// Copyright 2013 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. From 021d2c3cc3cbb81a4b65f75bdb4c9d371c75742f Mon Sep 17 00:00:00 2001 From: Tong Mu Date: Wed, 20 May 2020 13:26:18 -0700 Subject: [PATCH 14/14] Doc update --- .../macos/framework/Source/FlutterMouseCursorPlugin.mm | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm b/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm index 931e354b1d592..6bf19720c7194 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.mm @@ -55,21 +55,18 @@ - (FlutterError*)activateSystemCursor:(nonnull NSDictionary*)arguments; /** * Displays the specified cursor. * - * This method unhides the cursor before displaying the cursor, and updates + * Unhides the cursor before displaying the cursor, and updates * internal states. */ - (void)displayCursorObject:(nonnull NSCursor*)cursorObject; /** * Hides the cursor. - * - * This method hides the cursor if the cursor is currently unhidden, and - * updates internal states. */ - (void)hide; /** - * Handle all method calls. + * Handles all method calls from Flutter. */ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result;