not-react-native-macos 0.87.1-rc.6 → 0.87.1-rc.8
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.
- package/Libraries/AppState/AppState.js +5 -1
- package/Libraries/Components/Button.js +2 -1
- package/Libraries/Components/Keyboard/Keyboard.js +5 -1
- package/Libraries/Components/ScrollView/ScrollView.js +5 -1
- package/Libraries/Components/TextInput/TextInput.js +7 -2
- package/Libraries/Components/TextInput/TextInputState.js +9 -3
- package/Libraries/EventEmitter/NativeEventEmitter.js +6 -1
- package/Libraries/Linking/Linking.js +6 -1
- package/Libraries/Modal/Modal.js +5 -1
- package/Libraries/Pressability/HoverState.js +5 -0
- package/Libraries/PushNotificationIOS/PushNotificationIOS.js +5 -1
- package/Libraries/ReactNative/PaperUIManager.js +2 -1
- package/Libraries/Utilities/DevSettings.js +5 -1
- package/Libraries/Utilities/HMRClient.js +4 -2
- package/Libraries/WebSocket/WebSocket.js +5 -1
- package/Libraries/WebSocket/WebSocketInterceptor.js +5 -1
- package/macos/UIKitCompat/UIKit/UIText.h +62 -1
- package/macos/UIKitCompat/UIKit/UIText.m +517 -7
- package/package.json +1 -1
- package/src/private/animated/NativeAnimatedHelper.js +5 -1
- package/src/private/specs_DEPRECATED/modules/NativeExceptionsManager.js +6 -1
|
@@ -88,7 +88,11 @@ class AppStateImpl {
|
|
|
88
88
|
new NativeEventEmitter(
|
|
89
89
|
// T88715063: NativeEventEmitter only used this parameter on iOS. Now it uses it on all platforms, so this code was modified automatically to preserve its behavior
|
|
90
90
|
// If you want to use the native module on other platforms, please remove this condition and test its behavior
|
|
91
|
-
|
|
91
|
+
// [macOS] macOS uses this parameter too: the module is an
|
|
92
|
+
// RCTEventEmitter subclass here, exactly as it is on iOS.
|
|
93
|
+
Platform.OS !== 'ios' && Platform.OS !== 'macos'
|
|
94
|
+
? null
|
|
95
|
+
: NativeAppState,
|
|
92
96
|
);
|
|
93
97
|
this._emitter = emitter;
|
|
94
98
|
|
|
@@ -234,7 +234,8 @@ const Button: component(
|
|
|
234
234
|
const buttonStyles: Array<ViewStyleProp> = [styles.button];
|
|
235
235
|
const textStyles: Array<TextStyleProp> = [styles.text];
|
|
236
236
|
if (color) {
|
|
237
|
-
|
|
237
|
+
// [macOS] Apple platforms tint the label; Android tints the background.
|
|
238
|
+
if (Platform.OS === 'ios' || Platform.OS === 'macos') {
|
|
238
239
|
textStyles.push({color: color});
|
|
239
240
|
} else {
|
|
240
241
|
buttonStyles.push({backgroundColor: color});
|
|
@@ -77,7 +77,11 @@ class KeyboardImpl {
|
|
|
77
77
|
new NativeEventEmitter(
|
|
78
78
|
// T88715063: NativeEventEmitter only used this parameter on iOS. Now it uses it on all platforms, so this code was modified automatically to preserve its behavior
|
|
79
79
|
// If you want to use the native module on other platforms, please remove this condition and test its behavior
|
|
80
|
-
|
|
80
|
+
// [macOS] macOS uses this parameter too: the module is an
|
|
81
|
+
// RCTEventEmitter subclass here, exactly as it is on iOS.
|
|
82
|
+
Platform.OS !== 'ios' && Platform.OS !== 'macos'
|
|
83
|
+
? null
|
|
84
|
+
: NativeKeyboardObserver,
|
|
81
85
|
);
|
|
82
86
|
|
|
83
87
|
constructor() {
|
|
@@ -1101,7 +1101,11 @@ class ScrollView extends React.Component<ScrollViewProps, ScrollViewState> {
|
|
|
1101
1101
|
},
|
|
1102
1102
|
animated?: boolean, // deprecated, put this inside the rect argument instead
|
|
1103
1103
|
) => {
|
|
1104
|
-
|
|
1104
|
+
// [macOS] The AppKit scroll view zooms as well.
|
|
1105
|
+
invariant(
|
|
1106
|
+
Platform.OS === 'ios' || Platform.OS === 'macos',
|
|
1107
|
+
'zoomToRect is not implemented',
|
|
1108
|
+
);
|
|
1105
1109
|
if ('animated' in rect) {
|
|
1106
1110
|
this._animated = rect.animated;
|
|
1107
1111
|
delete rect.animated;
|
|
@@ -75,7 +75,11 @@ if (Platform.OS === 'android') {
|
|
|
75
75
|
AndroidTextInput = require('./AndroidTextInputNativeComponent').default;
|
|
76
76
|
AndroidTextInputCommands =
|
|
77
77
|
require('./AndroidTextInputNativeComponent').Commands;
|
|
78
|
-
|
|
78
|
+
// [macOS] macOS uses the same two Apple-platform components. Without this
|
|
79
|
+
// branch neither module is ever required, both locals stay undefined, and
|
|
80
|
+
// the render below -- also gated on `ios` -- returns nothing at all: a
|
|
81
|
+
// TextInput silently renders as empty space, with no error anywhere.
|
|
82
|
+
} else if (Platform.OS === 'ios' || Platform.OS === 'macos') {
|
|
79
83
|
RCTSinglelineTextInputView =
|
|
80
84
|
require('./RCTSingelineTextInputNativeComponent').default;
|
|
81
85
|
RCTSinglelineTextInputNativeCommands =
|
|
@@ -564,7 +568,8 @@ function InternalTextInput(props: TextInputProps): React.Node {
|
|
|
564
568
|
}
|
|
565
569
|
}
|
|
566
570
|
|
|
567
|
-
|
|
571
|
+
// [macOS] The Apple-platform render path; see the require above.
|
|
572
|
+
if (Platform.OS === 'ios' || Platform.OS === 'macos') {
|
|
568
573
|
const RCTTextInputView =
|
|
569
574
|
props.multiline === true
|
|
570
575
|
? RCTMultilineTextInputView
|
|
@@ -87,7 +87,9 @@ function focusTextInput(textField: ?HostInstance) {
|
|
|
87
87
|
|
|
88
88
|
if (textField != null) {
|
|
89
89
|
const fieldCanBeFocused =
|
|
90
|
-
|
|
90
|
+
// [macOS] On a desktop any view can hold focus, so the currently focused
|
|
91
|
+
// *input* is not a reliable answer to whether this field already has it.
|
|
92
|
+
(Platform.OS === 'macos' || currentlyFocusedInputRef !== textField) &&
|
|
91
93
|
// $FlowFixMe[prop-missing] - `currentProps` is missing in `NativeMethods`
|
|
92
94
|
textField.currentProps?.editable !== false;
|
|
93
95
|
|
|
@@ -95,7 +97,9 @@ function focusTextInput(textField: ?HostInstance) {
|
|
|
95
97
|
return;
|
|
96
98
|
}
|
|
97
99
|
focusInput(textField);
|
|
98
|
-
|
|
100
|
+
// [macOS] The commands are declared by the Apple-platform native
|
|
101
|
+
// components, which macOS shares.
|
|
102
|
+
if (Platform.OS === 'ios' || Platform.OS === 'macos') {
|
|
99
103
|
// This isn't necessarily a single line text input
|
|
100
104
|
// But commands don't actually care as long as the thing being passed in
|
|
101
105
|
// actually has a command with that name. So this should work with single
|
|
@@ -126,7 +130,9 @@ function blurTextInput(textField: ?HostInstance) {
|
|
|
126
130
|
|
|
127
131
|
if (currentlyFocusedInputRef === textField && textField != null) {
|
|
128
132
|
blurInput(textField);
|
|
129
|
-
|
|
133
|
+
// [macOS] The commands are declared by the Apple-platform native
|
|
134
|
+
// components, which macOS shares.
|
|
135
|
+
if (Platform.OS === 'ios' || Platform.OS === 'macos') {
|
|
130
136
|
// This isn't necessarily a single line text input
|
|
131
137
|
// But commands don't actually care as long as the thing being passed in
|
|
132
138
|
// actually has a command with that name. So this should work with single
|
|
@@ -71,7 +71,12 @@ export default class NativeEventEmitter<
|
|
|
71
71
|
* an invariant error if undefined.
|
|
72
72
|
*/
|
|
73
73
|
constructor(nativeModule?: ?NativeModule) {
|
|
74
|
-
|
|
74
|
+
// [macOS] Same requirement on macOS: the module backing the emitter is an
|
|
75
|
+
// RCTEventEmitter subclass here too, so a null one is a bug rather than a
|
|
76
|
+
// platform difference. This only holds because the callers that used to
|
|
77
|
+
// pass `Platform.OS !== 'ios' ? null : NativeX` now pass the module on
|
|
78
|
+
// macOS as well -- widening this without those is an import-time crash.
|
|
79
|
+
if (Platform.OS === 'ios' || Platform.OS === 'macos') {
|
|
75
80
|
invariant(
|
|
76
81
|
nativeModule != null,
|
|
77
82
|
'`new NativeEventEmitter()` requires a non-null argument.',
|
|
@@ -23,7 +23,12 @@ type LinkingEventDefinitions = {
|
|
|
23
23
|
|
|
24
24
|
class LinkingImpl extends NativeEventEmitter<LinkingEventDefinitions> {
|
|
25
25
|
constructor() {
|
|
26
|
-
|
|
26
|
+
// [macOS] RCTLinkingManager is built for macOS too.
|
|
27
|
+
super(
|
|
28
|
+
Platform.OS === 'ios' || Platform.OS === 'macos'
|
|
29
|
+
? nullthrows(NativeLinkingManager)
|
|
30
|
+
: undefined,
|
|
31
|
+
);
|
|
27
32
|
}
|
|
28
33
|
|
|
29
34
|
/**
|
package/Libraries/Modal/Modal.js
CHANGED
|
@@ -43,7 +43,11 @@ const ModalEventEmitter =
|
|
|
43
43
|
? new NativeEventEmitter<ModalEventDefinitions>(
|
|
44
44
|
// T88715063: NativeEventEmitter only used this parameter on iOS. Now it uses it on all platforms, so this code was modified automatically to preserve its behavior
|
|
45
45
|
// If you want to use the native module on other platforms, please remove this condition and test its behavior
|
|
46
|
-
|
|
46
|
+
// [macOS] macOS uses this parameter too: the module is an
|
|
47
|
+
// RCTEventEmitter subclass here, exactly as it is on iOS.
|
|
48
|
+
Platform.OS !== 'ios' && Platform.OS !== 'macos'
|
|
49
|
+
? null
|
|
50
|
+
: NativeModalManager,
|
|
47
51
|
)
|
|
48
52
|
: null;
|
|
49
53
|
|
|
@@ -53,6 +53,11 @@ if (Platform.OS === 'web') {
|
|
|
53
53
|
document.addEventListener('touchstart', disableHover, true);
|
|
54
54
|
document.addEventListener('touchmove', disableHover, true);
|
|
55
55
|
document.addEventListener('mousemove', enableHover, true);
|
|
56
|
+
// [macOS] A Mac always has a pointer, and unlike the web there is no touch
|
|
57
|
+
// input to disable hover for. Without this `onHoverIn` / `onHoverOut` never
|
|
58
|
+
// fire on Pressable, because Pressability checks this flag first.
|
|
59
|
+
} else if (Platform.OS === 'macos') {
|
|
60
|
+
isEnabled = true;
|
|
56
61
|
}
|
|
57
62
|
}
|
|
58
63
|
|
|
@@ -67,7 +67,11 @@ const PushNotificationEmitter =
|
|
|
67
67
|
new NativeEventEmitter<NativePushNotificationIOSEventDefinitions>(
|
|
68
68
|
// T88715063: NativeEventEmitter only used this parameter on iOS. Now it uses it on all platforms, so this code was modified automatically to preserve its behavior
|
|
69
69
|
// If you want to use the native module on other platforms, please remove this condition and test its behavior
|
|
70
|
-
|
|
70
|
+
// [macOS] macOS uses this parameter too: the module is an
|
|
71
|
+
// RCTEventEmitter subclass here, exactly as it is on iOS.
|
|
72
|
+
Platform.OS !== 'ios' && Platform.OS !== 'macos'
|
|
73
|
+
? null
|
|
74
|
+
: NativePushNotificationManagerIOS,
|
|
71
75
|
);
|
|
72
76
|
|
|
73
77
|
const _notifHandlers = new Map<string, void | EventSubscription>();
|
|
@@ -155,7 +155,8 @@ function lazifyViewManagerConfig(viewName: string) {
|
|
|
155
155
|
* only needed for iOS, which puts the constants in the ViewManager
|
|
156
156
|
* namespace instead of UIManager, unlike Android.
|
|
157
157
|
*/
|
|
158
|
-
|
|
158
|
+
// [macOS] Apple platforms namespace view-manager constants the same way.
|
|
159
|
+
if (Platform.OS === 'ios' || Platform.OS === 'macos') {
|
|
159
160
|
Object.keys(getConstants()).forEach(viewName => {
|
|
160
161
|
lazifyViewManagerConfig(viewName);
|
|
161
162
|
});
|
|
@@ -53,7 +53,11 @@ if (__DEV__) {
|
|
|
53
53
|
const emitter = new NativeEventEmitter<DevSettingsEventDefinitions>(
|
|
54
54
|
// T88715063: NativeEventEmitter only used this parameter on iOS. Now it uses it on all platforms, so this code was modified automatically to preserve its behavior
|
|
55
55
|
// If you want to use the native module on other platforms, please remove this condition and test its behavior
|
|
56
|
-
|
|
56
|
+
// [macOS] macOS uses this parameter too: the module is an
|
|
57
|
+
// RCTEventEmitter subclass here, exactly as it is on iOS.
|
|
58
|
+
Platform.OS !== 'ios' && Platform.OS !== 'macos'
|
|
59
|
+
? null
|
|
60
|
+
: NativeDevSettings,
|
|
57
61
|
);
|
|
58
62
|
const subscriptions = new Map<string, EventSubscription>();
|
|
59
63
|
|
|
@@ -192,7 +192,8 @@ const HMRClient: HMRClientNativeInterface = {
|
|
|
192
192
|
Try the following to fix the issue:
|
|
193
193
|
- Ensure that Metro is running and available on the same network`;
|
|
194
194
|
|
|
195
|
-
|
|
195
|
+
// [macOS] The AppDelegate advice is just as true here.
|
|
196
|
+
if (Platform.OS === 'ios' || Platform.OS === 'macos') {
|
|
196
197
|
error += `
|
|
197
198
|
- Ensure that the Metro URL is correctly set in AppDelegate`;
|
|
198
199
|
} else {
|
|
@@ -349,7 +350,8 @@ function flushEarlyLogs(client: MetroHMRClient) {
|
|
|
349
350
|
|
|
350
351
|
function dismissRedbox() {
|
|
351
352
|
if (
|
|
352
|
-
|
|
353
|
+
// [macOS] RedBox is an Apple-platform module; macOS has it too.
|
|
354
|
+
(Platform.OS === 'ios' || Platform.OS === 'macos') &&
|
|
353
355
|
NativeRedBox != null &&
|
|
354
356
|
NativeRedBox.dismiss != null
|
|
355
357
|
) {
|
|
@@ -141,7 +141,11 @@ class WebSocket extends EventTarget {
|
|
|
141
141
|
this._eventEmitter = new NativeEventEmitter(
|
|
142
142
|
// T88715063: NativeEventEmitter only used this parameter on iOS. Now it uses it on all platforms, so this code was modified automatically to preserve its behavior
|
|
143
143
|
// If you want to use the native module on other platforms, please remove this condition and test its behavior
|
|
144
|
-
|
|
144
|
+
// [macOS] macOS uses this parameter too: the module is an
|
|
145
|
+
// RCTEventEmitter subclass here, exactly as it is on iOS.
|
|
146
|
+
Platform.OS !== 'ios' && Platform.OS !== 'macos'
|
|
147
|
+
? null
|
|
148
|
+
: NativeWebSocketModule,
|
|
145
149
|
);
|
|
146
150
|
this._socketId = nextWebSocketId++;
|
|
147
151
|
this._registerEvents();
|
|
@@ -169,7 +169,11 @@ const WebSocketInterceptor = {
|
|
|
169
169
|
eventEmitter = new NativeEventEmitter(
|
|
170
170
|
// T88715063: NativeEventEmitter only used this parameter on iOS. Now it uses it on all platforms, so this code was modified automatically to preserve its behavior
|
|
171
171
|
// If you want to use the native module on other platforms, please remove this condition and test its behavior
|
|
172
|
-
|
|
172
|
+
// [macOS] macOS uses this parameter too: the module is an
|
|
173
|
+
// RCTEventEmitter subclass here, exactly as it is on iOS.
|
|
174
|
+
Platform.OS !== 'ios' && Platform.OS !== 'macos'
|
|
175
|
+
? null
|
|
176
|
+
: NativeWebSocketModule,
|
|
173
177
|
);
|
|
174
178
|
WebSocketInterceptor._registerEvents();
|
|
175
179
|
|
|
@@ -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;
|
|
@@ -158,6 +168,35 @@ typedef NS_ENUM(NSInteger, UIKeyboardAppearance) {
|
|
|
158
168
|
// UIKit's per-field default attributes. NSTextField applies typing attributes
|
|
159
169
|
// through its field editor instead, so these are stored and applied on edit.
|
|
160
170
|
@property (nonatomic, copy, nullable) NSDictionary<NSAttributedStringKey, id> *defaultTextAttributes;
|
|
171
|
+
// The attributes newly typed text takes on. NSTextField has no such property:
|
|
172
|
+
// editing happens in the window's shared field editor, which is an NSTextView
|
|
173
|
+
// and does. Reads and writes are forwarded there while editing, and held here
|
|
174
|
+
// otherwise so the value survives between edits.
|
|
175
|
+
@property (nonatomic, copy, null_resettable) NSDictionary<NSAttributedStringKey, id> *typingAttributes;
|
|
176
|
+
// NSTextField spells the styled value attributedStringValue.
|
|
177
|
+
@property (nonatomic, copy, nullable) NSAttributedString *attributedText;
|
|
178
|
+
// NSControl spells it alignment.
|
|
179
|
+
@property (nonatomic, assign) NSTextAlignment textAlignment;
|
|
180
|
+
// UIKit's clear button. NSTextField has no equivalent, so this is stored and
|
|
181
|
+
// otherwise unused -- a Mac text field does not show one.
|
|
182
|
+
@property (nonatomic, assign) UITextFieldViewMode clearButtonMode;
|
|
183
|
+
// UIKit hangs custom keyboards and toolbars off the responder. AppKit has no
|
|
184
|
+
// software keyboard, so these are stored and never presented.
|
|
185
|
+
@property (nonatomic, strong, nullable) UIView *inputView;
|
|
186
|
+
@property (nonatomic, strong, nullable) UIView *inputAccessoryView;
|
|
187
|
+
// Traits UITextInputTraits declares as @optional. A protocol property creates
|
|
188
|
+
// no storage, so each one an ObjC class is expected to answer has to be
|
|
189
|
+
// synthesized by that class -- omitting them is an unrecognized selector at
|
|
190
|
+
// the first access, not a compile error.
|
|
191
|
+
@property (nonatomic, assign) UITextSmartInsertDeleteType smartInsertDeleteType;
|
|
192
|
+
@property (nonatomic, assign) UITextSmartQuotesType smartQuotesType;
|
|
193
|
+
@property (nonatomic, assign) UITextSmartDashesType smartDashesType;
|
|
194
|
+
@property (nonatomic, copy, nullable) NSString *textContentType;
|
|
195
|
+
@property (nonatomic, strong, nullable) id passwordRules;
|
|
196
|
+
@property (nonatomic, assign) BOOL enablesReturnKeyAutomatically;
|
|
197
|
+
// UIKit tints the caret and selection through the view; AppKit takes it from
|
|
198
|
+
// the field editor's insertion-point colour.
|
|
199
|
+
@property (nonatomic, strong, nullable) UIColor *tintColor;
|
|
161
200
|
// NSTextField spells it placeholderAttributedString.
|
|
162
201
|
@property (nonatomic, copy, nullable) NSAttributedString *attributedPlaceholder;
|
|
163
202
|
// UIResponder's editing-menu hook; NSResponder has -validateUserInterfaceItem:.
|
|
@@ -184,6 +223,7 @@ typedef NS_ENUM(NSInteger, UIKeyboardAppearance) {
|
|
|
184
223
|
- (void)removeTarget:(nullable id)target action:(nullable SEL)action forControlEvents:(UIControlEvents)controlEvents;
|
|
185
224
|
@property (nonatomic, weak, nullable) id<UITextDropDelegate> textDropDelegate;
|
|
186
225
|
@end
|
|
226
|
+
@compatibility_alias UITextField RCTUIKitCompatTextField;
|
|
187
227
|
|
|
188
228
|
// UIKit's per-rect selection geometry. NSTextView exposes selection as ranges,
|
|
189
229
|
// so this is a value object the text layer fills in.
|
|
@@ -219,6 +259,27 @@ typedef NS_ENUM(NSInteger, UIKeyboardAppearance) {
|
|
|
219
259
|
@property (nonatomic, assign) UIReturnKeyType returnKeyType;
|
|
220
260
|
@property (nonatomic, copy, nullable) NSString *text;
|
|
221
261
|
@property (nonatomic, weak, nullable) id<UITextDropDelegate> textDropDelegate;
|
|
262
|
+
// As on UITextField: UITextInputTraits declares these @optional, so a class
|
|
263
|
+
// expected to answer them has to synthesize its own storage.
|
|
264
|
+
@property (nonatomic, assign) UITextAutocapitalizationType autocapitalizationType;
|
|
265
|
+
@property (nonatomic, assign) UITextAutocorrectionType autocorrectionType;
|
|
266
|
+
@property (nonatomic, assign) UITextSpellCheckingType spellCheckingType;
|
|
267
|
+
@property (nonatomic, assign) UIKeyboardAppearance keyboardAppearance;
|
|
268
|
+
@property (nonatomic, assign) UITextSmartInsertDeleteType smartInsertDeleteType;
|
|
269
|
+
@property (nonatomic, assign) UITextSmartQuotesType smartQuotesType;
|
|
270
|
+
@property (nonatomic, assign) UITextSmartDashesType smartDashesType;
|
|
271
|
+
@property (nonatomic, assign, getter=isSecureTextEntry) BOOL secureTextEntry;
|
|
272
|
+
@property (nonatomic, assign) BOOL enablesReturnKeyAutomatically;
|
|
273
|
+
@property (nonatomic, copy, nullable) NSString *textContentType;
|
|
274
|
+
@property (nonatomic, strong, nullable) id passwordRules;
|
|
275
|
+
// UIKit's text view scrolls itself. NSTextView is the document view of an
|
|
276
|
+
// enclosing NSScrollView, so this forwards to that.
|
|
277
|
+
@property (nonatomic, assign, getter=isScrollEnabled) BOOL scrollEnabled;
|
|
278
|
+
@property (nonatomic, assign) CGPoint contentOffset;
|
|
279
|
+
@property (nonatomic, assign) CGFloat zoomScale;
|
|
280
|
+
@property (nonatomic, strong, nullable) UIView *inputView;
|
|
281
|
+
@property (nonatomic, strong, nullable) UIView *inputAccessoryView;
|
|
282
|
+
@property (nonatomic, assign) UIDataDetectorTypes dataDetectorTypes;
|
|
222
283
|
@end
|
|
223
284
|
|
|
224
285
|
// UIKit's editing notifications. AppKit posts NSControlTextDidChange and
|
|
@@ -78,10 +78,209 @@
|
|
|
78
78
|
|
|
79
79
|
@end
|
|
80
80
|
|
|
81
|
-
@implementation
|
|
81
|
+
@implementation RCTUIKitCompatTextField {
|
|
82
|
+
NSDictionary<NSAttributedStringKey, id> *_typingAttributes;
|
|
83
|
+
NSMutableArray<NSArray *> *_controlEventTargets;
|
|
84
|
+
}
|
|
82
85
|
|
|
83
86
|
UIKIT_COMPAT_TEXT_INPUT_GEOMETRY
|
|
84
87
|
|
|
88
|
+
/**
|
|
89
|
+
* The live field editor, when this field is the one being edited.
|
|
90
|
+
*
|
|
91
|
+
* `-fieldEditor:forObject:` with NO does not create one, so this is nil unless
|
|
92
|
+
* editing is actually under way -- which is what makes it safe to ask for on
|
|
93
|
+
* every access.
|
|
94
|
+
*/
|
|
95
|
+
- (NSTextView *)uikitCompat_activeFieldEditor
|
|
96
|
+
{
|
|
97
|
+
NSText *editor = [self.window fieldEditor:NO forObject:self];
|
|
98
|
+
if ([editor isKindOfClass:[NSTextView class]] && self.currentEditor == editor) {
|
|
99
|
+
return (NSTextView *)editor;
|
|
100
|
+
}
|
|
101
|
+
return nil;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
@synthesize smartInsertDeleteType = _smartInsertDeleteType;
|
|
105
|
+
@synthesize smartQuotesType = _smartQuotesType;
|
|
106
|
+
@synthesize smartDashesType = _smartDashesType;
|
|
107
|
+
@synthesize textContentType = _textContentType;
|
|
108
|
+
@synthesize passwordRules = _passwordRules;
|
|
109
|
+
@synthesize enablesReturnKeyAutomatically = _enablesReturnKeyAutomatically;
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Marked text is an in-progress IME composition. AppKit tracks it on the field
|
|
113
|
+
* editor, so there is nothing marked when the field is not being edited.
|
|
114
|
+
*/
|
|
115
|
+
- (UITextRange *)markedTextRange
|
|
116
|
+
{
|
|
117
|
+
NSTextView *editor = [self uikitCompat_activeFieldEditor];
|
|
118
|
+
if (editor == nil || !editor.hasMarkedText) {
|
|
119
|
+
return nil;
|
|
120
|
+
}
|
|
121
|
+
NSRange range = editor.markedRange;
|
|
122
|
+
return [UITextRange rangeWithStart:[UITextPosition positionWithOffset:(NSInteger)range.location]
|
|
123
|
+
end:[UITextPosition positionWithOffset:(NSInteger)(range.location + range.length)]];
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
- (UITextInputMode *)textInputMode
|
|
127
|
+
{
|
|
128
|
+
// UIKit reports the software keyboard's language. AppKit has no software
|
|
129
|
+
// keyboard, and no caller here does more than null-check the result.
|
|
130
|
+
return nil;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
- (UIColor *)tintColor
|
|
134
|
+
{
|
|
135
|
+
NSTextView *editor = [self uikitCompat_activeFieldEditor];
|
|
136
|
+
return editor != nil ? editor.insertionPointColor : nil;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
- (void)setTintColor:(UIColor *)tintColor
|
|
140
|
+
{
|
|
141
|
+
NSTextView *editor = [self uikitCompat_activeFieldEditor];
|
|
142
|
+
if (editor != nil && tintColor != nil) {
|
|
143
|
+
editor.insertionPointColor = tintColor;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* NSTextField arrives configured as a form control: bezelled, opaque, and with
|
|
149
|
+
* its own focus ring. React Native draws all of that itself on the view that
|
|
150
|
+
* owns this one, so the field has to be stripped back to just the text.
|
|
151
|
+
*
|
|
152
|
+
* `selectable` is the one that matters for behaviour rather than looks. AppKit
|
|
153
|
+
* will not begin editing a field it cannot select -- `acceptsFirstResponder`
|
|
154
|
+
* returns NO, the click does nothing, and no field editor is ever installed.
|
|
155
|
+
*/
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* A click on text input must never drag the window.
|
|
159
|
+
*
|
|
160
|
+
* React Native's view defaults `mouseDownCanMoveWindow` to YES, matching
|
|
161
|
+
* AppKit, and AppKit asks the view under the cursor before delivering the
|
|
162
|
+
* event at all -- so a text view that inherits YES swallows its own clicks and
|
|
163
|
+
* starts a zero-pixel window drag instead. There is no mouseDown to debug,
|
|
164
|
+
* which is what makes it worth a comment.
|
|
165
|
+
*/
|
|
166
|
+
- (BOOL)mouseDownCanMoveWindow
|
|
167
|
+
{
|
|
168
|
+
return NO;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
- (void)uikitCompat_configureForTextInput
|
|
172
|
+
{
|
|
173
|
+
[super setEditable:YES];
|
|
174
|
+
self.selectable = YES;
|
|
175
|
+
self.bezeled = NO;
|
|
176
|
+
self.bordered = NO;
|
|
177
|
+
self.drawsBackground = NO;
|
|
178
|
+
self.focusRingType = NSFocusRingTypeNone;
|
|
179
|
+
self.usesSingleLineMode = YES;
|
|
180
|
+
self.cell.scrollable = YES;
|
|
181
|
+
self.cell.wraps = NO;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
- (instancetype)initWithFrame:(NSRect)frame
|
|
185
|
+
{
|
|
186
|
+
if (self = [super initWithFrame:frame]) {
|
|
187
|
+
[self uikitCompat_configureForTextInput];
|
|
188
|
+
}
|
|
189
|
+
return self;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
- (instancetype)initWithCoder:(NSCoder *)coder
|
|
193
|
+
{
|
|
194
|
+
if (self = [super initWithCoder:coder]) {
|
|
195
|
+
[self uikitCompat_configureForTextInput];
|
|
196
|
+
}
|
|
197
|
+
return self;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* React Native has no `editable` on UITextField, so RCTUITextField maps the
|
|
202
|
+
* prop onto `enabled` -- and overrides `isEditable` to answer from it. That
|
|
203
|
+
* leaves AppKit's own editable and selectable flags untouched, which is what
|
|
204
|
+
* actually decides whether a click starts editing. Keep them in step here,
|
|
205
|
+
* where the mapping is visible, rather than asking upstream to know about it.
|
|
206
|
+
*/
|
|
207
|
+
- (void)setEnabled:(BOOL)enabled
|
|
208
|
+
{
|
|
209
|
+
[super setEnabled:enabled];
|
|
210
|
+
[super setEditable:enabled];
|
|
211
|
+
self.selectable = enabled;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
- (NSAttributedString *)attributedText
|
|
215
|
+
{
|
|
216
|
+
return self.attributedStringValue;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
- (void)setAttributedText:(NSAttributedString *)attributedText
|
|
220
|
+
{
|
|
221
|
+
self.attributedStringValue = attributedText ?: [[NSAttributedString alloc] initWithString:@""];
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
- (NSTextAlignment)textAlignment
|
|
225
|
+
{
|
|
226
|
+
return self.alignment;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
- (void)setTextAlignment:(NSTextAlignment)textAlignment
|
|
230
|
+
{
|
|
231
|
+
self.alignment = textAlignment;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* The selection, which on AppKit lives in the field editor rather than the
|
|
236
|
+
* field. With no editor there is nothing selected, and UIKit reports nil for
|
|
237
|
+
* a field that is not being edited too.
|
|
238
|
+
*/
|
|
239
|
+
- (UITextRange *)selectedTextRange
|
|
240
|
+
{
|
|
241
|
+
NSTextView *editor = [self uikitCompat_activeFieldEditor];
|
|
242
|
+
if (editor == nil) {
|
|
243
|
+
return nil;
|
|
244
|
+
}
|
|
245
|
+
NSRange range = editor.selectedRange;
|
|
246
|
+
return [UITextRange rangeWithStart:[UITextPosition positionWithOffset:(NSInteger)range.location]
|
|
247
|
+
end:[UITextPosition positionWithOffset:(NSInteger)(range.location + range.length)]];
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
- (void)setSelectedTextRange:(UITextRange *)selectedTextRange
|
|
251
|
+
{
|
|
252
|
+
NSTextView *editor = [self uikitCompat_activeFieldEditor];
|
|
253
|
+
if (editor == nil || selectedTextRange == nil) {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
NSInteger start = MAX((NSInteger)0, selectedTextRange.start.offset);
|
|
257
|
+
NSInteger end = MAX(start, selectedTextRange.end.offset);
|
|
258
|
+
NSInteger length = (NSInteger)self.stringValue.length;
|
|
259
|
+
start = MIN(start, length);
|
|
260
|
+
end = MIN(end, length);
|
|
261
|
+
editor.selectedRange = NSMakeRange((NSUInteger)start, (NSUInteger)(end - start));
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
- (NSDictionary<NSAttributedStringKey, id> *)typingAttributes
|
|
265
|
+
{
|
|
266
|
+
NSTextView *editor = [self uikitCompat_activeFieldEditor];
|
|
267
|
+
if (editor != nil) {
|
|
268
|
+
return editor.typingAttributes;
|
|
269
|
+
}
|
|
270
|
+
// Between edits there is no field editor to ask. UIKit's own default for a
|
|
271
|
+
// field that was never typed into is its default text attributes.
|
|
272
|
+
return _typingAttributes ?: self.defaultTextAttributes ?: @{};
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
- (void)setTypingAttributes:(NSDictionary<NSAttributedStringKey, id> *)typingAttributes
|
|
276
|
+
{
|
|
277
|
+
_typingAttributes = [typingAttributes copy];
|
|
278
|
+
NSTextView *editor = [self uikitCompat_activeFieldEditor];
|
|
279
|
+
if (editor != nil) {
|
|
280
|
+
editor.typingAttributes = _typingAttributes ?: @{};
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
85
284
|
- (UITextPosition *)endOfDocument
|
|
86
285
|
{
|
|
87
286
|
return [UITextPosition positionWithOffset:(NSInteger)self.stringValue.length];
|
|
@@ -149,16 +348,131 @@ UIKIT_COMPAT_TEXT_INPUT_GEOMETRY
|
|
|
149
348
|
return [self respondsToSelector:action];
|
|
150
349
|
}
|
|
151
350
|
|
|
152
|
-
|
|
351
|
+
/**
|
|
352
|
+
* UIKit registers a target/action pair per control event; NSControl carries
|
|
353
|
+
* exactly one pair, fired when editing *ends*. Collapsing the two loses the
|
|
354
|
+
* distinction that matters most here -- RCTBackedTextFieldDelegateAdapter
|
|
355
|
+
* registers for EditingChanged and EditingDidEndOnExit, and with one slot the
|
|
356
|
+
* second registration silently replaces the first. The result is an input that
|
|
357
|
+
* accepts typing and never reports it, which is how `onChangeText` came to
|
|
358
|
+
* never fire.
|
|
359
|
+
*
|
|
360
|
+
* So the pairs are kept per event here, and driven from the AppKit
|
|
361
|
+
* notifications that actually correspond to them.
|
|
362
|
+
*/
|
|
363
|
+
- (void)addTarget:(id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents
|
|
364
|
+
{
|
|
365
|
+
if (target == nil || action == NULL) {
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
if (_controlEventTargets == nil) {
|
|
369
|
+
_controlEventTargets = [NSMutableArray new];
|
|
370
|
+
}
|
|
371
|
+
[_controlEventTargets addObject:@[
|
|
372
|
+
[NSValue valueWithNonretainedObject:target],
|
|
373
|
+
[NSValue valueWithPointer:action],
|
|
374
|
+
@(controlEvents),
|
|
375
|
+
]];
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
- (void)removeTarget:(id)target action:(SEL)action forControlEvents:(UIControlEvents)events
|
|
379
|
+
{
|
|
380
|
+
NSMutableArray *kept = [NSMutableArray new];
|
|
381
|
+
for (NSArray *entry in _controlEventTargets) {
|
|
382
|
+
id entryTarget = [entry[0] nonretainedObjectValue];
|
|
383
|
+
SEL entryAction = (SEL)[entry[1] pointerValue];
|
|
384
|
+
UIControlEvents entryEvents = (UIControlEvents)[entry[2] unsignedIntegerValue];
|
|
385
|
+
BOOL matches = (target == nil || entryTarget == target) && (action == NULL || entryAction == action) &&
|
|
386
|
+
(entryEvents & events) != 0;
|
|
387
|
+
if (!matches) {
|
|
388
|
+
[kept addObject:entry];
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
_controlEventTargets = kept;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
- (void)uikitCompat_sendActionsForControlEvents:(UIControlEvents)controlEvents
|
|
395
|
+
{
|
|
396
|
+
// Copied first: an action is free to add or remove targets while running.
|
|
397
|
+
for (NSArray *entry in [_controlEventTargets copy]) {
|
|
398
|
+
if (((UIControlEvents)[entry[2] unsignedIntegerValue] & controlEvents) == 0) {
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
401
|
+
id target = [entry[0] nonretainedObjectValue];
|
|
402
|
+
SEL action = (SEL)[entry[1] pointerValue];
|
|
403
|
+
if ([target respondsToSelector:action]) {
|
|
404
|
+
#pragma clang diagnostic push
|
|
405
|
+
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
|
|
406
|
+
[target performSelector:action withObject:self];
|
|
407
|
+
#pragma clang diagnostic pop
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* The delegate, as UIKit's protocol rather than AppKit's.
|
|
414
|
+
*
|
|
415
|
+
* NSTextField calls `controlTextDidBeginEditing:` and friends; the adapter on
|
|
416
|
+
* the React Native side implements `textFieldDidBeginEditing:` and friends.
|
|
417
|
+
* Same events, different selectors, so nothing was ever called -- which is why
|
|
418
|
+
* `onFocus`, `onBlur` and `onSubmitEditing` stayed silent while typing itself
|
|
419
|
+
* worked.
|
|
420
|
+
*/
|
|
421
|
+
- (id<UITextFieldDelegate>)uikitCompat_uiDelegate
|
|
422
|
+
{
|
|
423
|
+
id delegate = self.delegate;
|
|
424
|
+
return [delegate conformsToProtocol:@protocol(UITextFieldDelegate)] ? delegate : nil;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// NSControl's own hooks, called by the field editor. Preferred over the
|
|
428
|
+
// matching notifications: the notifications are posted onward to the control's
|
|
429
|
+
// delegate, and observing them from the control itself turned out not to see
|
|
430
|
+
// begin and end at all.
|
|
431
|
+
- (void)textDidBeginEditing:(NSNotification *)notification
|
|
153
432
|
{
|
|
154
|
-
|
|
155
|
-
self
|
|
433
|
+
[super textDidBeginEditing:notification];
|
|
434
|
+
[self uikitCompat_sendActionsForControlEvents:UIControlEventEditingDidBegin];
|
|
435
|
+
|
|
436
|
+
id<UITextFieldDelegate> delegate = [self uikitCompat_uiDelegate];
|
|
437
|
+
if ([delegate respondsToSelector:@selector(textFieldDidBeginEditing:)]) {
|
|
438
|
+
[delegate textFieldDidBeginEditing:self];
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
- (void)textDidChange:(NSNotification *)notification
|
|
443
|
+
{
|
|
444
|
+
[super textDidChange:notification];
|
|
445
|
+
[self uikitCompat_sendActionsForControlEvents:UIControlEventEditingChanged];
|
|
156
446
|
}
|
|
157
447
|
|
|
158
|
-
|
|
448
|
+
/**
|
|
449
|
+
* AppKit reports *why* editing ended in the notification's text movement.
|
|
450
|
+
* Return is what UIKit calls EditingDidEndOnExit -- the submit -- while
|
|
451
|
+
* clicking away or tabbing out is a plain EditingDidEnd.
|
|
452
|
+
*/
|
|
453
|
+
- (void)textDidEndEditing:(NSNotification *)notification
|
|
159
454
|
{
|
|
160
|
-
|
|
161
|
-
|
|
455
|
+
[super textDidEndEditing:notification];
|
|
456
|
+
|
|
457
|
+
NSNumber *movement = notification.userInfo[@"NSTextMovement"];
|
|
458
|
+
BOOL submitted = movement.integerValue == NSReturnTextMovement;
|
|
459
|
+
id<UITextFieldDelegate> delegate = [self uikitCompat_uiDelegate];
|
|
460
|
+
|
|
461
|
+
// Return is a submit before it is an end-of-editing, and the order matters:
|
|
462
|
+
// onSubmitEditing should carry the text, which onBlur may go on to clear.
|
|
463
|
+
if (submitted && [delegate respondsToSelector:@selector(textFieldShouldReturn:)]) {
|
|
464
|
+
[delegate textFieldShouldReturn:self];
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
UIControlEvents events = UIControlEventEditingDidEnd;
|
|
468
|
+
if (submitted) {
|
|
469
|
+
events |= UIControlEventEditingDidEndOnExit;
|
|
470
|
+
}
|
|
471
|
+
[self uikitCompat_sendActionsForControlEvents:events];
|
|
472
|
+
|
|
473
|
+
if ([delegate respondsToSelector:@selector(textFieldDidEndEditing:)]) {
|
|
474
|
+
[delegate textFieldDidEndEditing:self];
|
|
475
|
+
}
|
|
162
476
|
}
|
|
163
477
|
|
|
164
478
|
- (id<UITextDropDelegate>)textDropDelegate
|
|
@@ -223,6 +537,202 @@ UIKIT_COMPAT_TEXT_INPUT_GEOMETRY
|
|
|
223
537
|
|
|
224
538
|
@implementation UITextView
|
|
225
539
|
|
|
540
|
+
/**
|
|
541
|
+
* The delegate, as UIKit's protocol rather than AppKit's -- the same mismatch
|
|
542
|
+
* UITextField has. NSTextView tells its delegate through `textDidChange:` and
|
|
543
|
+
* `textViewDidChangeSelection:`; the React Native adapter listens for
|
|
544
|
+
* `textViewDidChange:` and the rest of the UIKit set. Without the bridge a
|
|
545
|
+
* multiline input accepts typing and reports none of it.
|
|
546
|
+
*/
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* A click on text input must never drag the window.
|
|
550
|
+
*
|
|
551
|
+
* React Native's view defaults `mouseDownCanMoveWindow` to YES, matching
|
|
552
|
+
* AppKit, and AppKit asks the view under the cursor before delivering the
|
|
553
|
+
* event at all -- so a text view that inherits YES swallows its own clicks and
|
|
554
|
+
* starts a zero-pixel window drag instead. There is no mouseDown to debug,
|
|
555
|
+
* which is what makes it worth a comment.
|
|
556
|
+
*/
|
|
557
|
+
- (BOOL)mouseDownCanMoveWindow
|
|
558
|
+
{
|
|
559
|
+
return NO;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* NSTextView sizes itself to its text; UITextView fills the frame it is given.
|
|
564
|
+
* Left alone, a multiline input collapses to a single line's height -- it looks
|
|
565
|
+
* right, because React Native's own view draws the background behind it, but
|
|
566
|
+
* only that top strip is hit-testable, so clicking anywhere below the first
|
|
567
|
+
* line does nothing at all.
|
|
568
|
+
*
|
|
569
|
+
* Fixed height with a width-tracking container is what matches UITextView:
|
|
570
|
+
* the text wraps to the width, and the view keeps whatever height layout gave
|
|
571
|
+
* it.
|
|
572
|
+
*/
|
|
573
|
+
- (void)uikitCompat_configureForTextInput
|
|
574
|
+
{
|
|
575
|
+
self.drawsBackground = NO;
|
|
576
|
+
self.richText = NO;
|
|
577
|
+
self.importsGraphics = NO;
|
|
578
|
+
self.allowsUndo = YES;
|
|
579
|
+
self.minSize = NSMakeSize(0, 0);
|
|
580
|
+
self.maxSize = NSMakeSize(CGFLOAT_MAX, CGFLOAT_MAX);
|
|
581
|
+
self.verticallyResizable = NO;
|
|
582
|
+
self.horizontallyResizable = NO;
|
|
583
|
+
self.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
|
|
584
|
+
self.textContainer.widthTracksTextView = YES;
|
|
585
|
+
self.textContainer.heightTracksTextView = YES;
|
|
586
|
+
self.textContainer.lineFragmentPadding = 0;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
- (instancetype)initWithFrame:(NSRect)frame
|
|
590
|
+
{
|
|
591
|
+
if (self = [super initWithFrame:frame]) {
|
|
592
|
+
[self uikitCompat_configureForTextInput];
|
|
593
|
+
}
|
|
594
|
+
return self;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
- (instancetype)initWithFrame:(NSRect)frame textContainer:(NSTextContainer *)container
|
|
598
|
+
{
|
|
599
|
+
if (self = [super initWithFrame:frame textContainer:container]) {
|
|
600
|
+
[self uikitCompat_configureForTextInput];
|
|
601
|
+
}
|
|
602
|
+
return self;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
- (id<UITextViewDelegate>)uikitCompat_uiDelegate
|
|
606
|
+
{
|
|
607
|
+
id delegate = self.delegate;
|
|
608
|
+
return [delegate conformsToProtocol:@protocol(UITextViewDelegate)] ? delegate : nil;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
// NSTextView funnels every edit through here, including paste, drops and
|
|
612
|
+
// undo -- which a keystroke-level hook would miss.
|
|
613
|
+
- (void)didChangeText
|
|
614
|
+
{
|
|
615
|
+
[super didChangeText];
|
|
616
|
+
id<UITextViewDelegate> delegate = [self uikitCompat_uiDelegate];
|
|
617
|
+
if ([delegate respondsToSelector:@selector(textViewDidChange:)]) {
|
|
618
|
+
[delegate textViewDidChange:self];
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
- (void)setSelectedRange:(NSRange)range affinity:(NSSelectionAffinity)affinity stillSelecting:(BOOL)stillSelecting
|
|
623
|
+
{
|
|
624
|
+
[super setSelectedRange:range affinity:affinity stillSelecting:stillSelecting];
|
|
625
|
+
id<UITextViewDelegate> delegate = [self uikitCompat_uiDelegate];
|
|
626
|
+
if ([delegate respondsToSelector:@selector(textViewDidChangeSelection:)]) {
|
|
627
|
+
[delegate textViewDidChangeSelection:self];
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
// NSTextView is its own responder rather than borrowing the window's field
|
|
632
|
+
// editor, so begin and end editing are the responder transitions themselves.
|
|
633
|
+
- (BOOL)becomeFirstResponder
|
|
634
|
+
{
|
|
635
|
+
if (![super becomeFirstResponder]) {
|
|
636
|
+
return NO;
|
|
637
|
+
}
|
|
638
|
+
id<UITextViewDelegate> delegate = [self uikitCompat_uiDelegate];
|
|
639
|
+
if ([delegate respondsToSelector:@selector(textViewDidBeginEditing:)]) {
|
|
640
|
+
[delegate textViewDidBeginEditing:self];
|
|
641
|
+
}
|
|
642
|
+
return YES;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
- (BOOL)resignFirstResponder
|
|
646
|
+
{
|
|
647
|
+
if (![super resignFirstResponder]) {
|
|
648
|
+
return NO;
|
|
649
|
+
}
|
|
650
|
+
id<UITextViewDelegate> delegate = [self uikitCompat_uiDelegate];
|
|
651
|
+
if ([delegate respondsToSelector:@selector(textViewDidEndEditing:)]) {
|
|
652
|
+
[delegate textViewDidEndEditing:self];
|
|
653
|
+
}
|
|
654
|
+
return YES;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
@synthesize autocapitalizationType = _autocapitalizationType;
|
|
658
|
+
@synthesize autocorrectionType = _autocorrectionType;
|
|
659
|
+
@synthesize spellCheckingType = _spellCheckingType;
|
|
660
|
+
@synthesize keyboardAppearance = _keyboardAppearance;
|
|
661
|
+
@synthesize smartInsertDeleteType = _smartInsertDeleteType;
|
|
662
|
+
@synthesize smartQuotesType = _smartQuotesType;
|
|
663
|
+
@synthesize smartDashesType = _smartDashesType;
|
|
664
|
+
@synthesize secureTextEntry = _secureTextEntry;
|
|
665
|
+
@synthesize enablesReturnKeyAutomatically = _enablesReturnKeyAutomatically;
|
|
666
|
+
@synthesize textContentType = _textContentType;
|
|
667
|
+
@synthesize passwordRules = _passwordRules;
|
|
668
|
+
@synthesize inputView = _inputView;
|
|
669
|
+
@synthesize inputAccessoryView = _inputAccessoryView;
|
|
670
|
+
@synthesize dataDetectorTypes = _dataDetectorTypes;
|
|
671
|
+
@synthesize zoomScale = _zoomScale;
|
|
672
|
+
|
|
673
|
+
/**
|
|
674
|
+
* UIKit's text view scrolls on its own. NSTextView is the document view inside
|
|
675
|
+
* an NSScrollView, so scrolling is a property of the enclosing view -- and
|
|
676
|
+
* there may not be one, when the text view is used unwrapped.
|
|
677
|
+
*/
|
|
678
|
+
- (BOOL)isScrollEnabled
|
|
679
|
+
{
|
|
680
|
+
NSScrollView *scrollView = self.enclosingScrollView;
|
|
681
|
+
return scrollView != nil ? (scrollView.hasVerticalScroller || scrollView.hasHorizontalScroller) : NO;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
- (void)setScrollEnabled:(BOOL)scrollEnabled
|
|
685
|
+
{
|
|
686
|
+
NSScrollView *scrollView = self.enclosingScrollView;
|
|
687
|
+
scrollView.hasVerticalScroller = scrollEnabled;
|
|
688
|
+
scrollView.hasHorizontalScroller = NO;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
- (CGPoint)contentOffset
|
|
692
|
+
{
|
|
693
|
+
NSScrollView *scrollView = self.enclosingScrollView;
|
|
694
|
+
return scrollView != nil ? scrollView.contentView.bounds.origin : CGPointZero;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
- (void)setContentOffset:(CGPoint)contentOffset
|
|
698
|
+
{
|
|
699
|
+
[self.enclosingScrollView.contentView scrollToPoint:contentOffset];
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
- (UITextInputMode *)textInputMode
|
|
703
|
+
{
|
|
704
|
+
// No software keyboard on macOS; callers only null-check this.
|
|
705
|
+
return nil;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
- (UITextRange *)markedTextRange
|
|
709
|
+
{
|
|
710
|
+
if (!self.hasMarkedText) {
|
|
711
|
+
return nil;
|
|
712
|
+
}
|
|
713
|
+
NSRange range = self.markedRange;
|
|
714
|
+
return [UITextRange rangeWithStart:[UITextPosition positionWithOffset:(NSInteger)range.location]
|
|
715
|
+
end:[UITextPosition positionWithOffset:(NSInteger)(range.location + range.length)]];
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
- (UITextRange *)selectedTextRange
|
|
719
|
+
{
|
|
720
|
+
NSRange range = self.selectedRange;
|
|
721
|
+
return [UITextRange rangeWithStart:[UITextPosition positionWithOffset:(NSInteger)range.location]
|
|
722
|
+
end:[UITextPosition positionWithOffset:(NSInteger)(range.location + range.length)]];
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
- (void)setSelectedTextRange:(UITextRange *)selectedTextRange
|
|
726
|
+
{
|
|
727
|
+
if (selectedTextRange == nil) {
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
NSInteger length = (NSInteger)self.string.length;
|
|
731
|
+
NSInteger start = MIN(MAX((NSInteger)0, selectedTextRange.start.offset), length);
|
|
732
|
+
NSInteger end = MIN(MAX(start, selectedTextRange.end.offset), length);
|
|
733
|
+
self.selectedRange = NSMakeRange((NSUInteger)start, (NSUInteger)(end - start));
|
|
734
|
+
}
|
|
735
|
+
|
|
226
736
|
UIKIT_COMPAT_TEXT_INPUT_GEOMETRY
|
|
227
737
|
|
|
228
738
|
- (UITextPosition *)endOfDocument
|
package/package.json
CHANGED
|
@@ -489,7 +489,11 @@ export default {
|
|
|
489
489
|
nativeEventEmitter = new NativeEventEmitter(
|
|
490
490
|
// T88715063: NativeEventEmitter only used this parameter on iOS. Now it uses it on all platforms, so this code was modified automatically to preserve its behavior
|
|
491
491
|
// If you want to use the native module on other platforms, please remove this condition and test its behavior
|
|
492
|
-
|
|
492
|
+
// [macOS] macOS uses this parameter too: the module is an
|
|
493
|
+
// RCTEventEmitter subclass here, exactly as it is on iOS.
|
|
494
|
+
Platform.OS !== 'ios' && Platform.OS !== 'macos'
|
|
495
|
+
? null
|
|
496
|
+
: NativeAnimatedModule,
|
|
493
497
|
);
|
|
494
498
|
}
|
|
495
499
|
return nativeEventEmitter;
|
|
@@ -70,7 +70,12 @@ const ExceptionsManager = {
|
|
|
70
70
|
NativeModule.reportSoftException(message, stack, exceptionId);
|
|
71
71
|
},
|
|
72
72
|
dismissRedbox(): void {
|
|
73
|
-
|
|
73
|
+
// [macOS] RedBox is dismissed natively on Apple platforms.
|
|
74
|
+
if (
|
|
75
|
+
Platform.OS !== 'ios' &&
|
|
76
|
+
Platform.OS !== 'macos' &&
|
|
77
|
+
NativeModule.dismissRedbox
|
|
78
|
+
) {
|
|
74
79
|
// TODO(T53311281): This is a noop on iOS now. Implement it.
|
|
75
80
|
NativeModule.dismissRedbox();
|
|
76
81
|
}
|