17
17
18
18
#import " CoreModulesPlugins.h"
19
19
20
- #if !TARGET_OS_OSX // [macOS]
21
-
22
20
NSString *const RCTAccessibilityManagerDidUpdateMultiplierNotification =
23
21
@" RCTAccessibilityManagerDidUpdateMultiplierNotification" ;
24
22
23
+ static void *AccessibilityVoiceOverChangeContext = &AccessibilityVoiceOverChangeContext; // [macOS]
24
+
25
25
@interface RCTAccessibilityManager () <NativeAccessibilityManagerSpec>
26
26
27
27
@property (nonatomic , copy ) NSString *contentSizeCategory;
@@ -47,6 +47,7 @@ - (instancetype)init
47
47
if (self = [super init ]) {
48
48
_multiplier = 1.0 ;
49
49
50
+ #if !TARGET_OS_OSX // [macOS]
50
51
// TODO: can this be moved out of the startup path?
51
52
[[NSNotificationCenter defaultCenter ] addObserver: self
52
53
selector: @selector (didReceiveNewContentSizeCategory: )
@@ -92,7 +93,18 @@ - (instancetype)init
92
93
selector: @selector (voiceVoiceOverStatusDidChange: )
93
94
name: UIAccessibilityVoiceOverStatusDidChangeNotification
94
95
object: nil ];
96
+ #else // [macOS
97
+ [[NSWorkspace sharedWorkspace ] addObserver: self
98
+ forKeyPath: @" voiceOverEnabled"
99
+ options: (NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld)
100
+ context: AccessibilityVoiceOverChangeContext];
101
+ [[[NSWorkspace sharedWorkspace ] notificationCenter ] addObserver: self
102
+ selector: @selector (accessibilityDisplayOptionsChange: )
103
+ name: NSWorkspaceAccessibilityDisplayOptionsDidChangeNotification
104
+ object: nil ];
105
+ #endif // macOS]
95
106
107
+ #if !TARGET_OS_OSX // [macOS]
96
108
self.contentSizeCategory = RCTSharedApplication ().preferredContentSizeCategory ;
97
109
_isBoldTextEnabled = UIAccessibilityIsBoldTextEnabled ();
98
110
_isGrayscaleEnabled = UIAccessibilityIsGrayscaleEnabled ();
@@ -101,10 +113,29 @@ - (instancetype)init
101
113
_isDarkerSystemColorsEnabled = UIAccessibilityDarkerSystemColorsEnabled ();
102
114
_isReduceTransparencyEnabled = UIAccessibilityIsReduceTransparencyEnabled ();
103
115
_isVoiceOverEnabled = UIAccessibilityIsVoiceOverRunning ();
116
+ _isHighContrastEnabled = UIAccessibilityDarkerSystemColorsEnabled (); // [macOS] Implement high Contrast for iOS
117
+ #else // [macOS
118
+ NSWorkspace *sharedWorkspace = [NSWorkspace sharedWorkspace ];
119
+ _isInvertColorsEnabled = [sharedWorkspace accessibilityDisplayShouldInvertColors ];
120
+ _isReduceMotionEnabled = [sharedWorkspace accessibilityDisplayShouldReduceMotion ];
121
+ _isReduceTransparencyEnabled = [sharedWorkspace accessibilityDisplayShouldReduceTransparency ];
122
+ _isVoiceOverEnabled = [sharedWorkspace isVoiceOverEnabled ];
123
+ _isHighContrastEnabled = [sharedWorkspace accessibilityDisplayShouldIncreaseContrast ];
124
+ #endif // macOS]
104
125
}
105
126
return self;
106
127
}
107
128
129
+ #if TARGET_OS_OSX // [macOS
130
+ - (void )dealloc
131
+ {
132
+ [[NSWorkspace sharedWorkspace ] removeObserver: self
133
+ forKeyPath: @" voiceOverEnabled"
134
+ context: AccessibilityVoiceOverChangeContext];
135
+ }
136
+ #endif // macOS]
137
+
138
+ #if !TARGET_OS_OSX // [macOS]
108
139
- (void )didReceiveNewContentSizeCategory : (NSNotification *)note
109
140
{
110
141
self.contentSizeCategory = note.userInfo [UIContentSizeCategoryNewValueKey];
@@ -186,6 +217,11 @@ - (void)darkerSystemColorsDidChange:(__unused NSNotification *)notification
186
217
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
187
218
[[_moduleRegistry moduleForName: " EventDispatcher" ] sendDeviceEventWithName: @" darkerSystemColorsChanged"
188
219
body: @(_isDarkerSystemColorsEnabled)];
220
+ // [macOS Also fire the highContrastChanged event for iOS
221
+ _isHighContrastEnabled = newDarkerSystemColorsEnabled;
222
+ [[_moduleRegistry moduleForName: " EventDispatcher" ] sendDeviceEventWithName: @" highContrastChanged"
223
+ body: @(_isDarkerSystemColorsEnabled)];
224
+ // macOS]
189
225
190
226
#pragma clang diagnostic pop
191
227
}
@@ -209,6 +245,71 @@ - (void)voiceVoiceOverStatusDidChange:(__unused NSNotification *)notification
209
245
BOOL isVoiceOverEnabled = UIAccessibilityIsVoiceOverRunning ();
210
246
[self _setIsVoiceOverEnabled: isVoiceOverEnabled];
211
247
}
248
+ #else // [macOS
249
+ - (void )accessibilityDisplayOptionsChange : (NSNotification *)notification
250
+ {
251
+ BOOL newInvertColorsEnabled = [[NSWorkspace sharedWorkspace ] accessibilityDisplayShouldInvertColors ];
252
+ BOOL newReduceMotionEnabled = [[NSWorkspace sharedWorkspace ] accessibilityDisplayShouldReduceMotion ];
253
+ BOOL newReduceTransparencyEnabled = [[NSWorkspace sharedWorkspace ] accessibilityDisplayShouldReduceTransparency ];
254
+ BOOL newHighContrastEnabled = [[NSWorkspace sharedWorkspace ] accessibilityDisplayShouldIncreaseContrast ];
255
+
256
+
257
+ if (_isHighContrastEnabled != newHighContrastEnabled) {
258
+ _isHighContrastEnabled = newHighContrastEnabled;
259
+ #pragma clang diagnostic push
260
+ #pragma clang diagnostic ignored "-Wdeprecated-declarations"
261
+ [[_moduleRegistry moduleForName: " EventDispatcher" ] sendDeviceEventWithName: @" highContrastChanged"
262
+ body: @(_isHighContrastEnabled)];
263
+ #pragma clang diagnostic pop
264
+ }
265
+ if (_isInvertColorsEnabled != newInvertColorsEnabled) {
266
+ _isInvertColorsEnabled = newInvertColorsEnabled;
267
+ #pragma clang diagnostic push
268
+ #pragma clang diagnostic ignored "-Wdeprecated-declarations"
269
+ [[_moduleRegistry moduleForName: " EventDispatcher" ] sendDeviceEventWithName: @" invertColorsChanged"
270
+ body: @(_isInvertColorsEnabled)];
271
+ #pragma clang diagnostic pop
272
+ }
273
+ if (_isReduceMotionEnabled != newReduceMotionEnabled) {
274
+ _isReduceMotionEnabled = newReduceMotionEnabled;
275
+ #pragma clang diagnostic push
276
+ #pragma clang diagnostic ignored "-Wdeprecated-declarations"
277
+ [[_moduleRegistry moduleForName: " EventDispatcher" ] sendDeviceEventWithName: @" reduceMotionChanged"
278
+ body: @(_isReduceMotionEnabled)];
279
+ #pragma clang diagnostic pop
280
+ }
281
+ if (_isReduceTransparencyEnabled != newReduceTransparencyEnabled) {
282
+ _isReduceTransparencyEnabled = newReduceTransparencyEnabled;
283
+ #pragma clang diagnostic push
284
+ #pragma clang diagnostic ignored "-Wdeprecated-declarations"
285
+ [[_moduleRegistry moduleForName: " EventDispatcher" ] sendDeviceEventWithName: @" reduceTransparencyChanged"
286
+ body: @(_isReduceTransparencyEnabled)];
287
+ #pragma clang diagnostic pop
288
+ }
289
+ }
290
+
291
+ - (void )observeValueForKeyPath : (NSString *)keyPath
292
+ ofObject : (id )object
293
+ change : (NSDictionary *)change
294
+ context : (void *)context {
295
+ if (context == AccessibilityVoiceOverChangeContext) {
296
+ BOOL newIsVoiceOverEnabled = [[NSWorkspace sharedWorkspace ] isVoiceOverEnabled ];
297
+ if (_isVoiceOverEnabled != newIsVoiceOverEnabled) {
298
+ _isVoiceOverEnabled = newIsVoiceOverEnabled;
299
+ #pragma clang diagnostic push
300
+ #pragma clang diagnostic ignored "-Wdeprecated-declarations"
301
+ [[_moduleRegistry moduleForName: " EventDispatcher" ] sendDeviceEventWithName: @" screenReaderChanged"
302
+ body: @(_isVoiceOverEnabled)];
303
+ #pragma clang diagnostic pop
304
+ }
305
+ } else {
306
+ [super observeValueForKeyPath: keyPath
307
+ ofObject: object
308
+ change: change
309
+ context: context];
310
+ }
311
+ }
312
+ #endif // macOS]
212
313
213
314
- (void )_setIsVoiceOverEnabled : (BOOL )isVoiceOverEnabled
214
315
{
@@ -257,6 +358,7 @@ - (void)setMultipliers:(NSDictionary<NSString *, NSNumber *> *)multipliers
257
358
258
359
- (NSDictionary <NSString *, NSNumber *> *)multipliers
259
360
{
361
+ #if !TARGET_OS_OSX // [macOS]
260
362
if (_multipliers == nil ) {
261
363
_multipliers = @{
262
364
UIContentSizeCategoryExtraSmall : @0.823 ,
@@ -273,6 +375,7 @@ - (void)setMultipliers:(NSDictionary<NSString *, NSNumber *> *)multipliers
273
375
UIContentSizeCategoryAccessibilityExtraExtraExtraLarge : @3.571
274
376
};
275
377
}
378
+ #endif // [macOS]
276
379
return _multipliers;
277
380
}
278
381
@@ -281,6 +384,7 @@ - (void)setMultipliers:(NSDictionary<NSString *, NSNumber *> *)multipliers
281
384
JSMultipliers)
282
385
{
283
386
NSMutableDictionary <NSString *, NSNumber *> *multipliers = [NSMutableDictionary new ];
387
+ #if !TARGET_OS_OSX // [macOS]
284
388
setMultipliers (multipliers, UIContentSizeCategoryExtraSmall, JSMultipliers.extraSmall ());
285
389
setMultipliers (multipliers, UIContentSizeCategorySmall, JSMultipliers.small ());
286
390
setMultipliers (multipliers, UIContentSizeCategoryMedium, JSMultipliers.medium ());
@@ -297,6 +401,7 @@ - (void)setMultipliers:(NSDictionary<NSString *, NSNumber *> *)multipliers
297
401
multipliers,
298
402
UIContentSizeCategoryAccessibilityExtraExtraExtraLarge,
299
403
JSMultipliers.accessibilityExtraExtraExtraLarge ());
404
+ #endif // [macOS]
300
405
self.multipliers = multipliers;
301
406
}
302
407
@@ -313,20 +418,38 @@ static void setMultipliers(
313
418
RCT_EXPORT_METHOD (setAccessibilityFocus : (double )reactTag)
314
419
{
315
420
dispatch_async (dispatch_get_main_queue (), ^{
316
- UIView *view = [self .viewRegistry_DEPRECATED viewForReactTag: @(reactTag)];
421
+ RCTPlatformView *view = [self .viewRegistry_DEPRECATED viewForReactTag: @(reactTag)]; // [macOS]
422
+ #if !TARGET_OS_OSX // [macOS]
317
423
UIAccessibilityPostNotification (UIAccessibilityLayoutChangedNotification, view);
424
+ #else // [macOS
425
+ [[view window ] makeFirstResponder: view];
426
+ NSAccessibilityPostNotification (view, NSAccessibilityLayoutChangedNotification );
427
+ #endif // macOS]
318
428
});
319
429
}
320
430
321
431
RCT_EXPORT_METHOD (announceForAccessibility : (NSString *)announcement)
322
432
{
433
+ #if !TARGET_OS_OSX // [macOS]
323
434
UIAccessibilityPostNotification (UIAccessibilityAnnouncementNotification, announcement);
435
+ #else // [macOS
436
+ dispatch_async (dispatch_get_main_queue (), ^{
437
+ NSAccessibilityPostNotificationWithUserInfo (
438
+ NSApp ,
439
+ NSAccessibilityAnnouncementRequestedNotification ,
440
+ @{NSAccessibilityAnnouncementKey : announcement,
441
+ NSAccessibilityPriorityKey : @(NSAccessibilityPriorityHigh)
442
+ }
443
+ );
444
+ });
445
+ #endif // macOS]
324
446
}
325
447
326
448
RCT_EXPORT_METHOD (announceForAccessibilityWithOptions
327
449
: (NSString *)announcement options
328
450
: (JS::NativeAccessibilityManager::SpecAnnounceForAccessibilityWithOptionsOptions &)options)
329
451
{
452
+ #if !TARGET_OS_OSX // [macOS]
330
453
NSMutableDictionary <NSString *, NSNumber *> *attrsDictionary = [NSMutableDictionary new ];
331
454
if (options.queue ()) {
332
455
attrsDictionary[UIAccessibilitySpeechAttributeQueueAnnouncement] = @(*(options.queue ()) ? YES : NO );
@@ -339,6 +462,18 @@ static void setMultipliers(
339
462
} else {
340
463
UIAccessibilityPostNotification (UIAccessibilityAnnouncementNotification, announcement);
341
464
}
465
+ #else // [macOS
466
+ NSAccessibilityPriorityLevel announcementPriority = options.queue () ? NSAccessibilityPriorityLow : NSAccessibilityPriorityHigh;
467
+ dispatch_async (dispatch_get_main_queue (), ^{
468
+ NSAccessibilityPostNotificationWithUserInfo (
469
+ NSApp ,
470
+ NSAccessibilityAnnouncementRequestedNotification ,
471
+ @{NSAccessibilityAnnouncementKey : announcement,
472
+ NSAccessibilityPriorityKey : @(announcementPriority)
473
+ }
474
+ );
475
+ });
476
+ #endif // macOS]
342
477
}
343
478
344
479
RCT_EXPORT_METHOD (getMultiplier : (RCTResponseSenderBlock)callback)
@@ -387,11 +522,15 @@ static void setMultipliers(
387
522
: (RCTResponseSenderBlock)onSuccess onError
388
523
: (__unused RCTResponseSenderBlock)onError)
389
524
{
525
+ #if !TARGET_OS_OSX // [macOS]
390
526
if (@available (iOS 14.0 , *)) {
391
527
onSuccess (@[ @(UIAccessibilityPrefersCrossFadeTransitions ()) ]);
392
528
} else {
393
529
onSuccess (@[ @(false ) ]);
394
530
}
531
+ #else // [macOS
532
+ onSuccess (@[ @(false ) ]); // iOS only
533
+ #endif // macOS]
395
534
}
396
535
397
536
RCT_EXPORT_METHOD (getCurrentReduceTransparencyState
@@ -408,6 +547,16 @@ static void setMultipliers(
408
547
onSuccess (@[ @(_isVoiceOverEnabled) ]);
409
548
}
410
549
550
+ // [macOS Implement for both iOS and macOS
551
+ RCT_EXPORT_METHOD (getCurrentHighContrastState
552
+ : (RCTResponseSenderBlock)onSuccess onError
553
+ : (__unused RCTResponseSenderBlock)onError)
554
+ {
555
+ onSuccess (@[ @(_isHighContrastEnabled) ]);
556
+ }
557
+ // macOS]
558
+
559
+
411
560
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule :
412
561
(const facebook::react::ObjCTurboModule::InitParams &)params
413
562
{
@@ -447,4 +596,3 @@ Class RCTAccessibilityManagerCls(void)
447
596
{
448
597
return RCTAccessibilityManager.class ;
449
598
}
450
- #endif // [macOS]
0 commit comments