not-react-native-macos 0.87.1-rc.7 → 0.87.1-rc.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -48,6 +48,13 @@ const textViewConfig = {
|
|
|
48
48
|
dataDetectorType: true,
|
|
49
49
|
android_hyphenationFrequency: true,
|
|
50
50
|
lineBreakStrategyIOS: true,
|
|
51
|
+
// [macOS] Text is a view too: ParagraphProps derives from ViewProps, which
|
|
52
|
+
// on macOS already carries these. Only the view config stood in the way --
|
|
53
|
+
// a prop missing from validAttributes never reaches C++ at all.
|
|
54
|
+
tooltip: true,
|
|
55
|
+
focusable: true,
|
|
56
|
+
enableFocusRing: true,
|
|
57
|
+
// macOS]
|
|
51
58
|
},
|
|
52
59
|
directEventTypes: {
|
|
53
60
|
topTextLayout: {
|
|
@@ -234,9 +234,22 @@ type TextBaseProps = Readonly<{
|
|
|
234
234
|
*
|
|
235
235
|
* @build-types emit-as-interface Uniwind compatibility
|
|
236
236
|
*/
|
|
237
|
+
// [macOS] Text carries the same three view props react-native-macos gives it.
|
|
238
|
+
// They work because ParagraphProps derives from ViewProps, which on macOS is
|
|
239
|
+
// HostPlatformViewProps.
|
|
240
|
+
export type TextPropsMacOS = Readonly<{
|
|
241
|
+
/** @platform macos */
|
|
242
|
+
tooltip?: ?string,
|
|
243
|
+
/** @platform macos */
|
|
244
|
+
focusable?: ?boolean,
|
|
245
|
+
/** @platform macos */
|
|
246
|
+
enableFocusRing?: ?boolean,
|
|
247
|
+
}>;
|
|
248
|
+
|
|
237
249
|
export type TextProps = Readonly<{
|
|
238
250
|
...TextPointerEventProps,
|
|
239
251
|
...TextPropsIOS,
|
|
252
|
+
...TextPropsMacOS, // [macOS]
|
|
240
253
|
...TextPropsAndroid,
|
|
241
254
|
...TextBaseProps,
|
|
242
255
|
...AccessibilityProps,
|
|
@@ -142,9 +142,19 @@ typedef NS_ENUM(NSInteger, UIKeyboardAppearance) {
|
|
|
142
142
|
- (void)textViewDidBeginEditing:(id)textView;
|
|
143
143
|
- (void)textViewDidChange:(id)textView;
|
|
144
144
|
- (void)textViewDidEndEditing:(id)textView;
|
|
145
|
+
- (void)textViewDidChangeSelection:(id)textView;
|
|
145
146
|
@end
|
|
146
147
|
|
|
147
|
-
|
|
148
|
+
// The runtime name is deliberately not `UITextField`.
|
|
149
|
+
//
|
|
150
|
+
// The compile-time name is what this shim is for, and @compatibility_alias
|
|
151
|
+
// gives that without registering the name with the ObjC runtime. That matters:
|
|
152
|
+
// several Apple frameworks decide whether a process is Catalyst by asking
|
|
153
|
+
// NSClassFromString for a UIKit class. macOS's one-time-code AutoFill does it
|
|
154
|
+
// for the field that holds focus, and answering yes sends it into
|
|
155
|
+
// UIKitMacHelper, which dlopens a UIKit.framework that does not exist on this
|
|
156
|
+
// platform and takes the process down with it.
|
|
157
|
+
@interface RCTUIKitCompatTextField : NSTextField <UITextInput, UITextInputTraits>
|
|
148
158
|
@property (nonatomic, assign) UIKeyboardType keyboardType;
|
|
149
159
|
@property (nonatomic, assign) UIReturnKeyType returnKeyType;
|
|
150
160
|
@property (nonatomic, assign) UITextAutocapitalizationType autocapitalizationType;
|
|
@@ -213,6 +223,7 @@ typedef NS_ENUM(NSInteger, UIKeyboardAppearance) {
|
|
|
213
223
|
- (void)removeTarget:(nullable id)target action:(nullable SEL)action forControlEvents:(UIControlEvents)controlEvents;
|
|
214
224
|
@property (nonatomic, weak, nullable) id<UITextDropDelegate> textDropDelegate;
|
|
215
225
|
@end
|
|
226
|
+
@compatibility_alias UITextField RCTUIKitCompatTextField;
|
|
216
227
|
|
|
217
228
|
// UIKit's per-rect selection geometry. NSTextView exposes selection as ranges,
|
|
218
229
|
// so this is a value object the text layer fills in.
|
|
@@ -78,8 +78,11 @@
|
|
|
78
78
|
|
|
79
79
|
@end
|
|
80
80
|
|
|
81
|
-
@implementation
|
|
81
|
+
@implementation RCTUIKitCompatTextField {
|
|
82
82
|
NSDictionary<NSAttributedStringKey, id> *_typingAttributes;
|
|
83
|
+
NSMutableArray<NSArray *> *_controlEventTargets;
|
|
84
|
+
NSString *_placeholder;
|
|
85
|
+
BOOL _reportedEndEditing;
|
|
83
86
|
}
|
|
84
87
|
|
|
85
88
|
UIKIT_COMPAT_TEXT_INPUT_GEOMETRY
|
|
@@ -143,6 +146,73 @@ UIKIT_COMPAT_TEXT_INPUT_GEOMETRY
|
|
|
143
146
|
}
|
|
144
147
|
}
|
|
145
148
|
|
|
149
|
+
/**
|
|
150
|
+
* NSTextField arrives configured as a form control: bezelled, opaque, and with
|
|
151
|
+
* its own focus ring. React Native draws all of that itself on the view that
|
|
152
|
+
* owns this one, so the field has to be stripped back to just the text.
|
|
153
|
+
*
|
|
154
|
+
* `selectable` is the one that matters for behaviour rather than looks. AppKit
|
|
155
|
+
* will not begin editing a field it cannot select -- `acceptsFirstResponder`
|
|
156
|
+
* returns NO, the click does nothing, and no field editor is ever installed.
|
|
157
|
+
*/
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* A click on text input must never drag the window.
|
|
161
|
+
*
|
|
162
|
+
* React Native's view defaults `mouseDownCanMoveWindow` to YES, matching
|
|
163
|
+
* AppKit, and AppKit asks the view under the cursor before delivering the
|
|
164
|
+
* event at all -- so a text view that inherits YES swallows its own clicks and
|
|
165
|
+
* starts a zero-pixel window drag instead. There is no mouseDown to debug,
|
|
166
|
+
* which is what makes it worth a comment.
|
|
167
|
+
*/
|
|
168
|
+
- (BOOL)mouseDownCanMoveWindow
|
|
169
|
+
{
|
|
170
|
+
return NO;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
- (void)uikitCompat_configureForTextInput
|
|
174
|
+
{
|
|
175
|
+
[super setEditable:YES];
|
|
176
|
+
self.selectable = YES;
|
|
177
|
+
self.bezeled = NO;
|
|
178
|
+
self.bordered = NO;
|
|
179
|
+
self.drawsBackground = NO;
|
|
180
|
+
self.focusRingType = NSFocusRingTypeNone;
|
|
181
|
+
self.usesSingleLineMode = YES;
|
|
182
|
+
self.cell.scrollable = YES;
|
|
183
|
+
self.cell.wraps = NO;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
- (instancetype)initWithFrame:(NSRect)frame
|
|
187
|
+
{
|
|
188
|
+
if (self = [super initWithFrame:frame]) {
|
|
189
|
+
[self uikitCompat_configureForTextInput];
|
|
190
|
+
}
|
|
191
|
+
return self;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
- (instancetype)initWithCoder:(NSCoder *)coder
|
|
195
|
+
{
|
|
196
|
+
if (self = [super initWithCoder:coder]) {
|
|
197
|
+
[self uikitCompat_configureForTextInput];
|
|
198
|
+
}
|
|
199
|
+
return self;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* React Native has no `editable` on UITextField, so RCTUITextField maps the
|
|
204
|
+
* prop onto `enabled` -- and overrides `isEditable` to answer from it. That
|
|
205
|
+
* leaves AppKit's own editable and selectable flags untouched, which is what
|
|
206
|
+
* actually decides whether a click starts editing. Keep them in step here,
|
|
207
|
+
* where the mapping is visible, rather than asking upstream to know about it.
|
|
208
|
+
*/
|
|
209
|
+
- (void)setEnabled:(BOOL)enabled
|
|
210
|
+
{
|
|
211
|
+
[super setEnabled:enabled];
|
|
212
|
+
[super setEditable:enabled];
|
|
213
|
+
self.selectable = enabled;
|
|
214
|
+
}
|
|
215
|
+
|
|
146
216
|
- (NSAttributedString *)attributedText
|
|
147
217
|
{
|
|
148
218
|
return self.attributedStringValue;
|
|
@@ -265,13 +335,23 @@ UIKIT_COMPAT_TEXT_INPUT_GEOMETRY
|
|
|
265
335
|
return nil;
|
|
266
336
|
}
|
|
267
337
|
|
|
338
|
+
/**
|
|
339
|
+
* The plain and attributed placeholders are one value on NSTextField: setting
|
|
340
|
+
* `placeholderAttributedString` clears `placeholderString`, and the reverse.
|
|
341
|
+
* UIKit keeps both, and React Native relies on that -- it sets `placeholder`,
|
|
342
|
+
* builds `attributedPlaceholder` from it, and rebuilds that again whenever the
|
|
343
|
+
* text attributes change. Reading the plain one back through AppKit returns nil
|
|
344
|
+
* the second time round, so the placeholder is rebuilt as empty and silently
|
|
345
|
+
* disappears. Hold the string here instead.
|
|
346
|
+
*/
|
|
268
347
|
- (NSString *)placeholder
|
|
269
348
|
{
|
|
270
|
-
return self.placeholderString;
|
|
349
|
+
return _placeholder ?: self.placeholderString;
|
|
271
350
|
}
|
|
272
351
|
|
|
273
352
|
- (void)setPlaceholder:(NSString *)placeholder
|
|
274
353
|
{
|
|
354
|
+
_placeholder = [placeholder copy];
|
|
275
355
|
self.placeholderString = placeholder;
|
|
276
356
|
}
|
|
277
357
|
|
|
@@ -280,16 +360,154 @@ UIKIT_COMPAT_TEXT_INPUT_GEOMETRY
|
|
|
280
360
|
return [self respondsToSelector:action];
|
|
281
361
|
}
|
|
282
362
|
|
|
283
|
-
|
|
363
|
+
/**
|
|
364
|
+
* UIKit registers a target/action pair per control event; NSControl carries
|
|
365
|
+
* exactly one pair, fired when editing *ends*. Collapsing the two loses the
|
|
366
|
+
* distinction that matters most here -- RCTBackedTextFieldDelegateAdapter
|
|
367
|
+
* registers for EditingChanged and EditingDidEndOnExit, and with one slot the
|
|
368
|
+
* second registration silently replaces the first. The result is an input that
|
|
369
|
+
* accepts typing and never reports it, which is how `onChangeText` came to
|
|
370
|
+
* never fire.
|
|
371
|
+
*
|
|
372
|
+
* So the pairs are kept per event here, and driven from the AppKit
|
|
373
|
+
* notifications that actually correspond to them.
|
|
374
|
+
*/
|
|
375
|
+
- (void)addTarget:(id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents
|
|
376
|
+
{
|
|
377
|
+
if (target == nil || action == NULL) {
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
if (_controlEventTargets == nil) {
|
|
381
|
+
_controlEventTargets = [NSMutableArray new];
|
|
382
|
+
}
|
|
383
|
+
[_controlEventTargets addObject:@[
|
|
384
|
+
[NSValue valueWithNonretainedObject:target],
|
|
385
|
+
[NSValue valueWithPointer:action],
|
|
386
|
+
@(controlEvents),
|
|
387
|
+
]];
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
- (void)removeTarget:(id)target action:(SEL)action forControlEvents:(UIControlEvents)events
|
|
391
|
+
{
|
|
392
|
+
NSMutableArray *kept = [NSMutableArray new];
|
|
393
|
+
for (NSArray *entry in _controlEventTargets) {
|
|
394
|
+
id entryTarget = [entry[0] nonretainedObjectValue];
|
|
395
|
+
SEL entryAction = (SEL)[entry[1] pointerValue];
|
|
396
|
+
UIControlEvents entryEvents = (UIControlEvents)[entry[2] unsignedIntegerValue];
|
|
397
|
+
BOOL matches = (target == nil || entryTarget == target) && (action == NULL || entryAction == action) &&
|
|
398
|
+
(entryEvents & events) != 0;
|
|
399
|
+
if (!matches) {
|
|
400
|
+
[kept addObject:entry];
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
_controlEventTargets = kept;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
- (void)uikitCompat_sendActionsForControlEvents:(UIControlEvents)controlEvents
|
|
407
|
+
{
|
|
408
|
+
// Copied first: an action is free to add or remove targets while running.
|
|
409
|
+
for (NSArray *entry in [_controlEventTargets copy]) {
|
|
410
|
+
if (((UIControlEvents)[entry[2] unsignedIntegerValue] & controlEvents) == 0) {
|
|
411
|
+
continue;
|
|
412
|
+
}
|
|
413
|
+
id target = [entry[0] nonretainedObjectValue];
|
|
414
|
+
SEL action = (SEL)[entry[1] pointerValue];
|
|
415
|
+
if ([target respondsToSelector:action]) {
|
|
416
|
+
#pragma clang diagnostic push
|
|
417
|
+
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
|
|
418
|
+
[target performSelector:action withObject:self];
|
|
419
|
+
#pragma clang diagnostic pop
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* The delegate, as UIKit's protocol rather than AppKit's.
|
|
426
|
+
*
|
|
427
|
+
* NSTextField calls `controlTextDidBeginEditing:` and friends; the adapter on
|
|
428
|
+
* the React Native side implements `textFieldDidBeginEditing:` and friends.
|
|
429
|
+
* Same events, different selectors, so nothing was ever called -- which is why
|
|
430
|
+
* `onFocus`, `onBlur` and `onSubmitEditing` stayed silent while typing itself
|
|
431
|
+
* worked.
|
|
432
|
+
*/
|
|
433
|
+
- (id<UITextFieldDelegate>)uikitCompat_uiDelegate
|
|
284
434
|
{
|
|
285
|
-
|
|
286
|
-
|
|
435
|
+
id delegate = self.delegate;
|
|
436
|
+
return [delegate conformsToProtocol:@protocol(UITextFieldDelegate)] ? delegate : nil;
|
|
287
437
|
}
|
|
288
438
|
|
|
289
|
-
|
|
439
|
+
// NSControl's own hooks, called by the field editor. Preferred over the
|
|
440
|
+
// matching notifications: the notifications are posted onward to the control's
|
|
441
|
+
// delegate, and observing them from the control itself turned out not to see
|
|
442
|
+
// begin and end at all.
|
|
443
|
+
- (void)textDidBeginEditing:(NSNotification *)notification
|
|
290
444
|
{
|
|
291
|
-
|
|
292
|
-
|
|
445
|
+
[super textDidBeginEditing:notification];
|
|
446
|
+
_reportedEndEditing = NO;
|
|
447
|
+
[self uikitCompat_sendActionsForControlEvents:UIControlEventEditingDidBegin];
|
|
448
|
+
|
|
449
|
+
id<UITextFieldDelegate> delegate = [self uikitCompat_uiDelegate];
|
|
450
|
+
if ([delegate respondsToSelector:@selector(textFieldDidBeginEditing:)]) {
|
|
451
|
+
[delegate textFieldDidBeginEditing:self];
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
- (void)textDidChange:(NSNotification *)notification
|
|
456
|
+
{
|
|
457
|
+
[super textDidChange:notification];
|
|
458
|
+
[self uikitCompat_sendActionsForControlEvents:UIControlEventEditingChanged];
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* AppKit reports *why* editing ended in the notification's text movement.
|
|
463
|
+
* Return is what UIKit calls EditingDidEndOnExit -- the submit -- while
|
|
464
|
+
* clicking away or tabbing out is a plain EditingDidEnd.
|
|
465
|
+
*/
|
|
466
|
+
- (void)textDidEndEditing:(NSNotification *)notification
|
|
467
|
+
{
|
|
468
|
+
[super textDidEndEditing:notification];
|
|
469
|
+
|
|
470
|
+
NSNumber *movement = notification.userInfo[@"NSTextMovement"];
|
|
471
|
+
BOOL submitted = movement.integerValue == NSReturnTextMovement;
|
|
472
|
+
id<UITextFieldDelegate> delegate = [self uikitCompat_uiDelegate];
|
|
473
|
+
|
|
474
|
+
// AppKit ends editing on Return and then re-establishes the field editor with
|
|
475
|
+
// the text selected, so this runs again on the next click -- a second blur
|
|
476
|
+
// with no focus in between. Report the end once per editing session.
|
|
477
|
+
if (_reportedEndEditing) {
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
_reportedEndEditing = YES;
|
|
481
|
+
|
|
482
|
+
// Return is a submit before it is an end-of-editing, and the order matters:
|
|
483
|
+
// onSubmitEditing should carry the text, which onBlur may go on to clear.
|
|
484
|
+
// The answer says whether the field should also give up focus: that is
|
|
485
|
+
// `submitBehavior`, which distinguishes `submit` from `blurAndSubmit`.
|
|
486
|
+
BOOL shouldBlurOnSubmit = NO;
|
|
487
|
+
if (submitted && [delegate respondsToSelector:@selector(textFieldShouldReturn:)]) {
|
|
488
|
+
shouldBlurOnSubmit = [delegate textFieldShouldReturn:self];
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
UIControlEvents events = UIControlEventEditingDidEnd;
|
|
492
|
+
if (submitted) {
|
|
493
|
+
events |= UIControlEventEditingDidEndOnExit;
|
|
494
|
+
}
|
|
495
|
+
[self uikitCompat_sendActionsForControlEvents:events];
|
|
496
|
+
|
|
497
|
+
if ([delegate respondsToSelector:@selector(textFieldDidEndEditing:)]) {
|
|
498
|
+
[delegate textFieldDidEndEditing:self];
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
if (submitted && shouldBlurOnSubmit) {
|
|
502
|
+
// Asynchronously: AppKit is still unwinding this edit, and taking the
|
|
503
|
+
// responder away underneath it leaves a caret drawn in a field that no
|
|
504
|
+
// longer has focus.
|
|
505
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
506
|
+
if (self.currentEditor != nil) {
|
|
507
|
+
[self.window makeFirstResponder:nil];
|
|
508
|
+
}
|
|
509
|
+
});
|
|
510
|
+
}
|
|
293
511
|
}
|
|
294
512
|
|
|
295
513
|
- (id<UITextDropDelegate>)textDropDelegate
|
|
@@ -354,6 +572,123 @@ UIKIT_COMPAT_TEXT_INPUT_GEOMETRY
|
|
|
354
572
|
|
|
355
573
|
@implementation UITextView
|
|
356
574
|
|
|
575
|
+
/**
|
|
576
|
+
* The delegate, as UIKit's protocol rather than AppKit's -- the same mismatch
|
|
577
|
+
* UITextField has. NSTextView tells its delegate through `textDidChange:` and
|
|
578
|
+
* `textViewDidChangeSelection:`; the React Native adapter listens for
|
|
579
|
+
* `textViewDidChange:` and the rest of the UIKit set. Without the bridge a
|
|
580
|
+
* multiline input accepts typing and reports none of it.
|
|
581
|
+
*/
|
|
582
|
+
|
|
583
|
+
/**
|
|
584
|
+
* A click on text input must never drag the window.
|
|
585
|
+
*
|
|
586
|
+
* React Native's view defaults `mouseDownCanMoveWindow` to YES, matching
|
|
587
|
+
* AppKit, and AppKit asks the view under the cursor before delivering the
|
|
588
|
+
* event at all -- so a text view that inherits YES swallows its own clicks and
|
|
589
|
+
* starts a zero-pixel window drag instead. There is no mouseDown to debug,
|
|
590
|
+
* which is what makes it worth a comment.
|
|
591
|
+
*/
|
|
592
|
+
- (BOOL)mouseDownCanMoveWindow
|
|
593
|
+
{
|
|
594
|
+
return NO;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* NSTextView sizes itself to its text; UITextView fills the frame it is given.
|
|
599
|
+
* Left alone, a multiline input collapses to a single line's height -- it looks
|
|
600
|
+
* right, because React Native's own view draws the background behind it, but
|
|
601
|
+
* only that top strip is hit-testable, so clicking anywhere below the first
|
|
602
|
+
* line does nothing at all.
|
|
603
|
+
*
|
|
604
|
+
* Fixed height with a width-tracking container is what matches UITextView:
|
|
605
|
+
* the text wraps to the width, and the view keeps whatever height layout gave
|
|
606
|
+
* it.
|
|
607
|
+
*/
|
|
608
|
+
- (void)uikitCompat_configureForTextInput
|
|
609
|
+
{
|
|
610
|
+
self.drawsBackground = NO;
|
|
611
|
+
self.richText = NO;
|
|
612
|
+
self.importsGraphics = NO;
|
|
613
|
+
self.allowsUndo = YES;
|
|
614
|
+
self.minSize = NSMakeSize(0, 0);
|
|
615
|
+
self.maxSize = NSMakeSize(CGFLOAT_MAX, CGFLOAT_MAX);
|
|
616
|
+
self.verticallyResizable = NO;
|
|
617
|
+
self.horizontallyResizable = NO;
|
|
618
|
+
self.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
|
|
619
|
+
self.textContainer.widthTracksTextView = YES;
|
|
620
|
+
self.textContainer.heightTracksTextView = YES;
|
|
621
|
+
self.textContainer.lineFragmentPadding = 0;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
- (instancetype)initWithFrame:(NSRect)frame
|
|
625
|
+
{
|
|
626
|
+
if (self = [super initWithFrame:frame]) {
|
|
627
|
+
[self uikitCompat_configureForTextInput];
|
|
628
|
+
}
|
|
629
|
+
return self;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
- (instancetype)initWithFrame:(NSRect)frame textContainer:(NSTextContainer *)container
|
|
633
|
+
{
|
|
634
|
+
if (self = [super initWithFrame:frame textContainer:container]) {
|
|
635
|
+
[self uikitCompat_configureForTextInput];
|
|
636
|
+
}
|
|
637
|
+
return self;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
- (id<UITextViewDelegate>)uikitCompat_uiDelegate
|
|
641
|
+
{
|
|
642
|
+
id delegate = self.delegate;
|
|
643
|
+
return [delegate conformsToProtocol:@protocol(UITextViewDelegate)] ? delegate : nil;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
// NSTextView funnels every edit through here, including paste, drops and
|
|
647
|
+
// undo -- which a keystroke-level hook would miss.
|
|
648
|
+
- (void)didChangeText
|
|
649
|
+
{
|
|
650
|
+
[super didChangeText];
|
|
651
|
+
id<UITextViewDelegate> delegate = [self uikitCompat_uiDelegate];
|
|
652
|
+
if ([delegate respondsToSelector:@selector(textViewDidChange:)]) {
|
|
653
|
+
[delegate textViewDidChange:self];
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
- (void)setSelectedRange:(NSRange)range affinity:(NSSelectionAffinity)affinity stillSelecting:(BOOL)stillSelecting
|
|
658
|
+
{
|
|
659
|
+
[super setSelectedRange:range affinity:affinity stillSelecting:stillSelecting];
|
|
660
|
+
id<UITextViewDelegate> delegate = [self uikitCompat_uiDelegate];
|
|
661
|
+
if ([delegate respondsToSelector:@selector(textViewDidChangeSelection:)]) {
|
|
662
|
+
[delegate textViewDidChangeSelection:self];
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
// NSTextView is its own responder rather than borrowing the window's field
|
|
667
|
+
// editor, so begin and end editing are the responder transitions themselves.
|
|
668
|
+
- (BOOL)becomeFirstResponder
|
|
669
|
+
{
|
|
670
|
+
if (![super becomeFirstResponder]) {
|
|
671
|
+
return NO;
|
|
672
|
+
}
|
|
673
|
+
id<UITextViewDelegate> delegate = [self uikitCompat_uiDelegate];
|
|
674
|
+
if ([delegate respondsToSelector:@selector(textViewDidBeginEditing:)]) {
|
|
675
|
+
[delegate textViewDidBeginEditing:self];
|
|
676
|
+
}
|
|
677
|
+
return YES;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
- (BOOL)resignFirstResponder
|
|
681
|
+
{
|
|
682
|
+
if (![super resignFirstResponder]) {
|
|
683
|
+
return NO;
|
|
684
|
+
}
|
|
685
|
+
id<UITextViewDelegate> delegate = [self uikitCompat_uiDelegate];
|
|
686
|
+
if ([delegate respondsToSelector:@selector(textViewDidEndEditing:)]) {
|
|
687
|
+
[delegate textViewDidEndEditing:self];
|
|
688
|
+
}
|
|
689
|
+
return YES;
|
|
690
|
+
}
|
|
691
|
+
|
|
357
692
|
@synthesize autocapitalizationType = _autocapitalizationType;
|
|
358
693
|
@synthesize autocorrectionType = _autocorrectionType;
|
|
359
694
|
@synthesize spellCheckingType = _spellCheckingType;
|