blob: 6bf19720c71945253ba15297ee2fac75e6bda8b4 [file] [log] [blame]
// 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.
#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 kKindValueNone = @"none";
/**
* 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"])
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 ()
/**
* Whether the cursor is currently hidden.
*/
@property(nonatomic) BOOL hidden;
/**
* Handles the method call that activates a system cursor.
*
* Returns a FlutterError if the arguments can not be recognized. Otherwise
* returns nil.
*/
- (FlutterError*)activateSystemCursor:(nonnull NSDictionary*)arguments;
/**
* Displays the specified cursor.
*
* Unhides the cursor before displaying the cursor, and updates
* internal states.
*/
- (void)displayCursorObject:(nonnull NSCursor*)cursorObject;
/**
* Hides the cursor.
*/
- (void)hide;
/**
* Handles all method calls from Flutter.
*/
- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result;
@end
@implementation FlutterMouseCursorPlugin
#pragma mark - Private
NSMutableDictionary* cachedSystemCursors;
- (instancetype)init {
self = [super init];
if (self) {
cachedSystemCursors = [NSMutableDictionary dictionary];
}
return self;
}
- (void)dealloc {
if (_hidden) {
[NSCursor unhide];
}
}
- (FlutterError*)activateSystemCursor:(nonnull NSDictionary*)arguments {
NSString* kindArg = arguments[kKindKey];
if (!kindArg) {
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* cursorObject = [FlutterMouseCursorPlugin cursorFromKind:kindArg];
[self displayCursorObject:cursorObject];
return nil;
}
- (void)displayCursorObject:(nonnull NSCursor*)cursorObject {
[cursorObject set];
if (_hidden) {
[NSCursor unhide];
}
_hidden = NO;
}
- (void)hide {
if (!_hidden) {
[NSCursor hide];
}
_hidden = YES;
}
+ (NSCursor*)cursorFromKind:(NSString*)kind {
NSCursor* cachedValue = [cachedSystemCursors objectForKey:kind];
if (!cachedValue) {
cachedValue = GetCursorForKind(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] init];
[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