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  }
57  return result;
58 }
59 
61 /**
62  * Whether the cursor is currently hidden.
63  */
64 @property(nonatomic) BOOL hidden;
65 
66 /**
67  * Handles the method call that activates a system cursor.
68  *
69  * Returns a FlutterError if the arguments can not be recognized. Otherwise
70  * returns nil.
71  */
72 - (FlutterError*)activateSystemCursor:(nonnull NSDictionary*)arguments;
73 
74 /**
75  * Displays the specified cursor.
76  *
77  * Unhides the cursor before displaying the cursor, and updates
78  * internal states.
79  */
80 - (void)displayCursorObject:(nonnull NSCursor*)cursorObject;
81 
82 /**
83  * Hides the cursor.
84  */
85 - (void)hide;
86 
87 /**
88  * Handles all method calls from Flutter.
89  */
90 - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result;
91 
92 @end
93 
94 @implementation FlutterMouseCursorPlugin
95 
96 #pragma mark - Private
97 
98 NSMutableDictionary* cachedSystemCursors;
99 
100 - (instancetype)init {
101  self = [super init];
102  if (self) {
103  cachedSystemCursors = [NSMutableDictionary dictionary];
104  }
105  return self;
106 }
107 
108 - (void)dealloc {
109  if (_hidden) {
110  [NSCursor unhide];
111  }
112 }
113 
114 - (FlutterError*)activateSystemCursor:(nonnull NSDictionary*)arguments {
115  NSString* kindArg = arguments[kKindKey];
116  if (!kindArg) {
117  return [FlutterError errorWithCode:@"error"
118  message:@"Missing argument"
119  details:@"Missing argument while trying to activate system cursor"];
120  }
121  if ([kindArg isEqualToString:kKindValueNone]) {
122  [self hide];
123  return nil;
124  }
125  NSCursor* cursorObject = [FlutterMouseCursorPlugin cursorFromKind:kindArg];
126  [self displayCursorObject:cursorObject];
127  return nil;
128 }
129 
130 - (void)displayCursorObject:(nonnull NSCursor*)cursorObject {
131  [cursorObject set];
132  if (_hidden) {
133  [NSCursor unhide];
134  }
135  _hidden = NO;
136 }
137 
138 - (void)hide {
139  if (!_hidden) {
140  [NSCursor hide];
141  }
142  _hidden = YES;
143 }
144 
145 + (NSCursor*)cursorFromKind:(NSString*)kind {
146  NSCursor* cachedValue = [cachedSystemCursors objectForKey:kind];
147  if (!cachedValue) {
148  cachedValue = GetCursorForKind(kind);
149  [cachedSystemCursors setValue:cachedValue forKey:kind];
150  }
151  return cachedValue;
152 }
153 
154 #pragma mark - FlutterPlugin implementation
155 
156 + (void)registerWithRegistrar:(id<FlutterPluginRegistrar>)registrar {
157  FlutterMethodChannel* channel = [FlutterMethodChannel methodChannelWithName:kMouseCursorChannel
158  binaryMessenger:registrar.messenger];
159  FlutterMouseCursorPlugin* instance = [[FlutterMouseCursorPlugin alloc] init];
160  [registrar addMethodCallDelegate:instance channel:channel];
161 }
162 
163 - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
164  NSString* method = call.method;
165  if ([method isEqualToString:kActivateSystemCursorMethod]) {
166  result([self activateSystemCursor:call.arguments]);
167  } else {
169  }
170 }
171 
172 @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)
GAsyncResult * result
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)
int BOOL
Definition: windows_types.h:37
static NSString *const kKindKey
FLUTTER_DARWIN_EXPORT NSObject const * FlutterMethodNotImplemented