Flutter Engine
FlutterMouseCursorPlugin.mm
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #import <objc/message.h>
6 
8 #import "flutter/shell/platform/darwin/common/framework/Headers/FlutterCodecs.h"
9 
10 static NSString* const kMouseCursorChannel = @"flutter/mousecursor";
11 
12 static NSString* const kActivateSystemCursorMethod = @"activateSystemCursor";
13 static NSString* const kKindKey = @"kind";
14 
15 static NSString* const kKindValueNone = @"none";
16 
17 static NSDictionary* systemCursors;
18 
19 /**
20  * Maps a Flutter's constant to a platform's cursor object.
21  *
22  * Returns the arrow cursor for unknown constants, including kSystemShapeNone.
23  */
24 static NSCursor* GetCursorForKind(NSString* kind) {
25  // The following mapping must be kept in sync with Flutter framework's
26  // mouse_cursor.dart
27 
28  if (systemCursors == nil) {
29  systemCursors = @{
30  @"alias" : [NSCursor dragLinkCursor],
31  @"basic" : [NSCursor arrowCursor],
32  @"click" : [NSCursor pointingHandCursor],
33  @"contextMenu" : [NSCursor contextualMenuCursor],
34  @"copy" : [NSCursor dragCopyCursor],
35  @"disappearing" : [NSCursor disappearingItemCursor],
36  @"forbidden" : [NSCursor operationNotAllowedCursor],
37  @"grab" : [NSCursor openHandCursor],
38  @"grabbing" : [NSCursor closedHandCursor],
39  @"noDrop" : [NSCursor operationNotAllowedCursor],
40  @"precise" : [NSCursor crosshairCursor],
41  @"text" : [NSCursor IBeamCursor],
42  @"resizeColumn" : [NSCursor resizeLeftRightCursor],
43  @"resizeDown" : [NSCursor resizeDownCursor],
44  @"resizeLeft" : [NSCursor resizeLeftCursor],
45  @"resizeLeftRight" : [NSCursor resizeLeftRightCursor],
46  @"resizeRight" : [NSCursor resizeRightCursor],
47  @"resizeRow" : [NSCursor resizeUpDownCursor],
48  @"resizeUp" : [NSCursor resizeUpCursor],
49  @"resizeUpDown" : [NSCursor resizeUpDownCursor],
50  @"verticalText" : [NSCursor IBeamCursorForVerticalLayout],
51  };
52  }
53  NSCursor* result = [systemCursors objectForKey:kind];
54  if (result == nil)
55  return [NSCursor arrowCursor];
56  return result;
57 }
58 
60 /**
61  * Whether the cursor is currently hidden.
62  */
63 @property(nonatomic) BOOL hidden;
64 
65 /**
66  * Handles the method call that activates a system cursor.
67  *
68  * Returns a FlutterError if the arguments can not be recognized. Otherwise
69  * returns nil.
70  */
71 - (FlutterError*)activateSystemCursor:(nonnull NSDictionary*)arguments;
72 
73 /**
74  * Displays the specified cursor.
75  *
76  * Unhides the cursor before displaying the cursor, and updates
77  * internal states.
78  */
79 - (void)displayCursorObject:(nonnull NSCursor*)cursorObject;
80 
81 /**
82  * Hides the cursor.
83  */
84 - (void)hide;
85 
86 /**
87  * Handles all method calls from Flutter.
88  */
89 - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result;
90 
91 @end
92 
93 @implementation FlutterMouseCursorPlugin
94 
95 #pragma mark - Private
96 
97 NSMutableDictionary* cachedSystemCursors;
98 
99 - (instancetype)init {
100  self = [super init];
101  if (self) {
102  cachedSystemCursors = [NSMutableDictionary dictionary];
103  }
104  return self;
105 }
106 
107 - (void)dealloc {
108  if (_hidden) {
109  [NSCursor unhide];
110  }
111 }
112 
113 - (FlutterError*)activateSystemCursor:(nonnull NSDictionary*)arguments {
114  NSString* kindArg = arguments[kKindKey];
115  if (!kindArg) {
116  return [FlutterError errorWithCode:@"error"
117  message:@"Missing argument"
118  details:@"Missing argument while trying to activate system cursor"];
119  }
120  if ([kindArg isEqualToString:kKindValueNone]) {
121  [self hide];
122  return nil;
123  }
124  NSCursor* cursorObject = [FlutterMouseCursorPlugin cursorFromKind:kindArg];
125  [self displayCursorObject:cursorObject];
126  return nil;
127 }
128 
129 - (void)displayCursorObject:(nonnull NSCursor*)cursorObject {
130  [cursorObject set];
131  if (_hidden) {
132  [NSCursor unhide];
133  }
134  _hidden = NO;
135 }
136 
137 - (void)hide {
138  if (!_hidden) {
139  [NSCursor hide];
140  }
141  _hidden = YES;
142 }
143 
144 + (NSCursor*)cursorFromKind:(NSString*)kind {
145  NSCursor* cachedValue = [cachedSystemCursors objectForKey:kind];
146  if (!cachedValue) {
147  cachedValue = GetCursorForKind(kind);
148  [cachedSystemCursors setValue:cachedValue forKey:kind];
149  }
150  return cachedValue;
151 }
152 
153 #pragma mark - FlutterPlugin implementation
154 
155 + (void)registerWithRegistrar:(id<FlutterPluginRegistrar>)registrar {
156  FlutterMethodChannel* channel = [FlutterMethodChannel methodChannelWithName:kMouseCursorChannel
157  binaryMessenger:registrar.messenger];
158  FlutterMouseCursorPlugin* instance = [[FlutterMouseCursorPlugin alloc] init];
159  [registrar addMethodCallDelegate:instance channel:channel];
160 }
161 
162 - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
163  NSString* method = call.method;
164  if ([method isEqualToString:kActivateSystemCursorMethod]) {
165  result([self activateSystemCursor:call.arguments]);
166  } else {
168  }
169 }
170 
171 @end
static NSString *const kActivateSystemCursorMethod
NSMutableDictionary * cachedSystemCursors
instancetype errorWithCode:message:details:(NSString *code, [message] NSString *_Nullable message, [details] id _Nullable details)
instancetype methodChannelWithName:binaryMessenger:(NSString *name, [binaryMessenger] NSObject< FlutterBinaryMessenger > *messenger)
static NSString *const kMouseCursorChannel
static NSCursor * GetCursorForKind(NSString *kind)
static NSString *const kKindValueNone
static NSDictionary * systemCursors
static FlMethodResponse * hide(FlTextInputPlugin *self)
void(^ FlutterResult)(id _Nullable result)
FLUTTER_EXPORT NSObject const * FlutterMethodNotImplemented
static NSString *const kKindKey