@@ -135,6 +135,20 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result;
135
135
*/
136
136
- (void )setEditingState : (NSDictionary *)state ;
137
137
138
+ /* *
139
+ * Informs the Flutter framework of changes to the text input model's state.
140
+ */
141
+ - (void )updateEditState ;
142
+
143
+ /* *
144
+ * Updates the stringValue and selectedRange that stored in the NSTextView interface
145
+ * that this plugin inherits from.
146
+ *
147
+ * If there is a FlutterTextField uses this plugin as its field editor, this method
148
+ * will update the stringValue and selectedRange through the API of the FlutterTextField.
149
+ */
150
+ - (void )updateTextAndSelection ;
151
+
138
152
@end
139
153
140
154
@implementation FlutterTextInputPlugin {
@@ -163,6 +177,11 @@ - (instancetype)initWithViewController:(FlutterViewController*)viewController {
163
177
binaryMessenger: viewController.engine.binaryMessenger
164
178
codec: [FlutterJSONMethodCodec sharedInstance ]];
165
179
_shown = FALSE ;
180
+ // NSTextView does not support _weak reference, so we have to
181
+ // use __unsafe_unretained and manage the reference ourselves.
182
+ //
183
+ // Since we will nil the handler in dealloc. the weakSelf should
184
+ // be valid if the handler is ever called.
166
185
__unsafe_unretained FlutterTextInputPlugin* weakSelf = self;
167
186
[_channel setMethodCallHandler: ^(FlutterMethodCall* call, FlutterResult result) {
168
187
[weakSelf handleMethodCall: call result: result];
@@ -181,8 +200,10 @@ - (instancetype)initWithViewController:(FlutterViewController*)viewController {
181
200
}
182
201
183
202
- (BOOL )isFirstResponder {
184
- FlutterAppDelegate* appDelegate = (FlutterAppDelegate*)[NSApp delegate ];
185
- return [appDelegate.mainFlutterWindow firstResponder ] == self;
203
+ if (!self.flutterViewController .viewLoaded || !self.flutterViewController .view .window ) {
204
+ return false ;
205
+ }
206
+ return [self .flutterViewController.view.window firstResponder ] == self;
186
207
}
187
208
188
209
- (void )dealloc {
@@ -298,15 +319,10 @@ - (void)setEditingState:(NSDictionary*)state {
298
319
state[kComposingBaseKey ], state[kComposingExtentKey ], _activeModel->composing_range ());
299
320
size_t cursor_offset = selected_range.base () - composing_range.start ();
300
321
_activeModel->SetComposingRange (composing_range, cursor_offset);
301
- if (_client) {
302
- [_client becomeFirstResponder ];
303
- }
322
+ [_client becomeFirstResponder ];
304
323
[self updateTextAndSelection ];
305
324
}
306
325
307
- /* *
308
- * Informs the Flutter framework of changes to the text input model's state.
309
- */
310
326
- (void )updateEditState {
311
327
if (_activeModel == nullptr ) {
312
328
return ;
@@ -328,10 +344,29 @@ - (void)updateEditState {
328
344
kComposingExtentKey : @(composingExtent),
329
345
kTextKey : [NSString stringWithUTF8String: _activeModel->GetText ().c_str ()]
330
346
};
347
+
331
348
[_channel invokeMethod: kUpdateEditStateResponseMethod arguments: @[ self .clientID, state ]];
332
349
[self updateTextAndSelection ];
333
350
}
334
351
352
+ - (void )updateTextAndSelection {
353
+ NSAssert (_activeModel != nullptr , @" Flutter text model must not be null." );
354
+ NSString * text = @(_activeModel->GetText ().data ());
355
+ int start = _activeModel->selection ().base ();
356
+ int extend = _activeModel->selection ().extent ();
357
+ NSRange selection = NSMakeRange (MIN (start, extend ), ABS (start - extend ));
358
+ // There may be a native text field client if VoiceOver is on.
359
+ // In this case, we have to update text and selection through
360
+ // the client in order for VoiceOver to announce the text editing
361
+ // properly.
362
+ if (_client) {
363
+ [_client updateString: text withSelection: selection];
364
+ } else {
365
+ self.string = text;
366
+ [self setSelectedRange: selection];
367
+ }
368
+ }
369
+
335
370
#pragma mark -
336
371
#pragma mark FlutterKeySecondaryResponder
337
372
@@ -357,117 +392,74 @@ - (BOOL)handleKeyEvent:(NSEvent*)event {
357
392
return [_textInputContext handleEvent: event];
358
393
}
359
394
395
+ #pragma mark -
360
396
#pragma mark NSResponder
361
397
362
398
- (void )keyDown : (NSEvent *)event {
363
- if (!self.flutterViewController ) {
364
- return ;
365
- }
366
399
[self .flutterViewController keyDown: event];
367
400
}
368
401
369
402
- (void )keyUp : (NSEvent *)event {
370
- if (!self.flutterViewController ) {
371
- return ;
372
- }
373
403
[self .flutterViewController keyUp: event];
374
404
}
375
405
406
+ - (BOOL )performKeyEquivalent : (NSEvent *)event {
407
+ return [self .flutterViewController performKeyEquivalent: event];
408
+ }
409
+
376
410
- (void )flagsChanged : (NSEvent *)event {
377
- if (!self.flutterViewController ) {
378
- return ;
379
- }
380
411
[self .flutterViewController flagsChanged: event];
381
412
}
382
413
383
414
- (void )mouseEntered : (NSEvent *)event {
384
- if (!self.flutterViewController ) {
385
- return ;
386
- }
387
415
[self .flutterViewController mouseEntered: event];
388
416
}
389
417
390
418
- (void )mouseExited : (NSEvent *)event {
391
- if (!self.flutterViewController ) {
392
- return ;
393
- }
394
419
[self .flutterViewController mouseExited: event];
395
420
}
396
421
397
422
- (void )mouseDown : (NSEvent *)event {
398
- if (!self.flutterViewController ) {
399
- return ;
400
- }
401
423
[self .flutterViewController mouseDown: event];
402
424
}
403
425
404
426
- (void )mouseUp : (NSEvent *)event {
405
- if (!self.flutterViewController ) {
406
- return ;
407
- }
408
427
[self .flutterViewController mouseUp: event];
409
428
}
410
429
411
430
- (void )mouseDragged : (NSEvent *)event {
412
- if (!self.flutterViewController ) {
413
- return ;
414
- }
415
431
[self .flutterViewController mouseDragged: event];
416
432
}
417
433
418
434
- (void )rightMouseDown : (NSEvent *)event {
419
- if (!self.flutterViewController ) {
420
- return ;
421
- }
422
435
[self .flutterViewController rightMouseDown: event];
423
436
}
424
437
425
438
- (void )rightMouseUp : (NSEvent *)event {
426
- if (!self.flutterViewController ) {
427
- return ;
428
- }
429
439
[self .flutterViewController rightMouseUp: event];
430
440
}
431
441
432
442
- (void )rightMouseDragged : (NSEvent *)event {
433
- if (!self.flutterViewController ) {
434
- return ;
435
- }
436
443
[self .flutterViewController rightMouseDragged: event];
437
444
}
438
445
439
446
- (void )otherMouseDown : (NSEvent *)event {
440
- if (!self.flutterViewController ) {
441
- return ;
442
- }
443
447
[self .flutterViewController otherMouseDown: event];
444
448
}
445
449
446
450
- (void )otherMouseUp : (NSEvent *)event {
447
- if (!self.flutterViewController ) {
448
- return ;
449
- }
450
451
[self .flutterViewController otherMouseUp: event];
451
452
}
452
453
453
454
- (void )otherMouseDragged : (NSEvent *)event {
454
- if (!self.flutterViewController ) {
455
- return ;
456
- }
457
455
[self .flutterViewController otherMouseDragged: event];
458
456
}
459
457
460
458
- (void )mouseMoved : (NSEvent *)event {
461
- if (!self.flutterViewController ) {
462
- return ;
463
- }
464
459
[self .flutterViewController mouseMoved: event];
465
460
}
466
461
467
462
- (void )scrollWheel : (NSEvent *)event {
468
- if (!self.flutterViewController ) {
469
- return ;
470
- }
471
463
[self .flutterViewController scrollWheel: event];
472
464
}
473
465
@@ -553,24 +545,6 @@ - (void)unmarkText {
553
545
[self updateEditState ];
554
546
}
555
547
556
- - (void )updateTextAndSelection {
557
- NSAssert (_activeModel != nullptr , @" Flutter text model must not be null." );
558
- NSString * text = @(_activeModel->GetText ().data ());
559
- int start = _activeModel->selection ().base ();
560
- int extend = _activeModel->selection ().extent ();
561
- NSRange selection = NSMakeRange (MIN (start, extend ), ABS (start - extend ));
562
- // There may be a native text field client if voiceover is on.
563
- // In this case, we have to update text and selection through
564
- // the client in order for voiceover to announce the text editing
565
- // properly.
566
- if (_client) {
567
- [_client updateString: text withSelection: selection];
568
- } else {
569
- self.string = text;
570
- [self setSelectedRange: selection];
571
- }
572
- }
573
-
574
548
- (NSRange )markedRange {
575
549
if (_activeModel == nullptr ) {
576
550
return NSMakeRange (NSNotFound , 0 );
@@ -602,6 +576,9 @@ - (NSAttributedString*)attributedSubstringForProposedRange:(NSRange)range
602
576
}
603
577
604
578
- (NSRect )firstRectForCharacterRange : (NSRange )range actualRange : (NSRangePointer )actualRange {
579
+ if (!self.flutterViewController .viewLoaded ) {
580
+ return CGRectZero ;
581
+ }
605
582
// This only determines position of caret instead of any arbitrary range, but it's enough
606
583
// to properly position accent selection popup
607
584
if (CATransform3DIsAffine (_editableTransform) && !CGRectEqualToRect (_caretRect, CGRectNull )) {
0 commit comments