1717
1818#import " CoreModulesPlugins.h"
1919
20- #if !TARGET_OS_OSX // [macOS]
21-
2220NSString *const RCTAccessibilityManagerDidUpdateMultiplierNotification =
2321 @" RCTAccessibilityManagerDidUpdateMultiplierNotification" ;
2422
23+ static void *AccessibilityVoiceOverChangeContext = &AccessibilityVoiceOverChangeContext; // [macOS]
24+
2525@interface RCTAccessibilityManager () <NativeAccessibilityManagerSpec>
2626
2727@property (nonatomic , copy ) NSString *contentSizeCategory;
@@ -47,6 +47,7 @@ - (instancetype)init
4747 if (self = [super init ]) {
4848 _multiplier = 1.0 ;
4949
50+ #if !TARGET_OS_OSX // [macOS]
5051 // TODO: can this be moved out of the startup path?
5152 [[NSNotificationCenter defaultCenter ] addObserver: self
5253 selector: @selector (didReceiveNewContentSizeCategory: )
@@ -92,7 +93,18 @@ - (instancetype)init
9293 selector: @selector (voiceVoiceOverStatusDidChange: )
9394 name: UIAccessibilityVoiceOverStatusDidChangeNotification
9495 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]
95106
107+ #if !TARGET_OS_OSX // [macOS]
96108 self.contentSizeCategory = RCTSharedApplication ().preferredContentSizeCategory ;
97109 _isBoldTextEnabled = UIAccessibilityIsBoldTextEnabled ();
98110 _isGrayscaleEnabled = UIAccessibilityIsGrayscaleEnabled ();
@@ -101,10 +113,29 @@ - (instancetype)init
101113 _isDarkerSystemColorsEnabled = UIAccessibilityDarkerSystemColorsEnabled ();
102114 _isReduceTransparencyEnabled = UIAccessibilityIsReduceTransparencyEnabled ();
103115 _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]
104125 }
105126 return self;
106127}
107128
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]
108139- (void )didReceiveNewContentSizeCategory : (NSNotification *)note
109140{
110141 self.contentSizeCategory = note.userInfo [UIContentSizeCategoryNewValueKey];
@@ -186,6 +217,11 @@ - (void)darkerSystemColorsDidChange:(__unused NSNotification *)notification
186217#pragma clang diagnostic ignored "-Wdeprecated-declarations"
187218 [[_moduleRegistry moduleForName: " EventDispatcher" ] sendDeviceEventWithName: @" darkerSystemColorsChanged"
188219 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]
189225
190226#pragma clang diagnostic pop
191227 }
@@ -209,6 +245,71 @@ - (void)voiceVoiceOverStatusDidChange:(__unused NSNotification *)notification
209245 BOOL isVoiceOverEnabled = UIAccessibilityIsVoiceOverRunning ();
210246 [self _setIsVoiceOverEnabled: isVoiceOverEnabled];
211247}
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]
212313
213314- (void )_setIsVoiceOverEnabled : (BOOL )isVoiceOverEnabled
214315{
@@ -257,6 +358,7 @@ - (void)setMultipliers:(NSDictionary<NSString *, NSNumber *> *)multipliers
257358
258359- (NSDictionary <NSString *, NSNumber *> *)multipliers
259360{
361+ #if !TARGET_OS_OSX // [macOS]
260362 if (_multipliers == nil ) {
261363 _multipliers = @{
262364 UIContentSizeCategoryExtraSmall : @0.823 ,
@@ -273,6 +375,7 @@ - (void)setMultipliers:(NSDictionary<NSString *, NSNumber *> *)multipliers
273375 UIContentSizeCategoryAccessibilityExtraExtraExtraLarge : @3.571
274376 };
275377 }
378+ #endif // [macOS]
276379 return _multipliers;
277380}
278381
@@ -281,6 +384,7 @@ - (void)setMultipliers:(NSDictionary<NSString *, NSNumber *> *)multipliers
281384 JSMultipliers)
282385{
283386 NSMutableDictionary <NSString *, NSNumber *> *multipliers = [NSMutableDictionary new ];
387+ #if !TARGET_OS_OSX // [macOS]
284388 setMultipliers (multipliers, UIContentSizeCategoryExtraSmall, JSMultipliers.extraSmall ());
285389 setMultipliers (multipliers, UIContentSizeCategorySmall, JSMultipliers.small ());
286390 setMultipliers (multipliers, UIContentSizeCategoryMedium, JSMultipliers.medium ());
@@ -297,6 +401,7 @@ - (void)setMultipliers:(NSDictionary<NSString *, NSNumber *> *)multipliers
297401 multipliers,
298402 UIContentSizeCategoryAccessibilityExtraExtraExtraLarge,
299403 JSMultipliers.accessibilityExtraExtraExtraLarge ());
404+ #endif // [macOS]
300405 self.multipliers = multipliers;
301406}
302407
@@ -313,20 +418,38 @@ static void setMultipliers(
313418RCT_EXPORT_METHOD (setAccessibilityFocus : (double )reactTag)
314419{
315420 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]
317423 UIAccessibilityPostNotification (UIAccessibilityLayoutChangedNotification, view);
424+ #else // [macOS
425+ [[view window ] makeFirstResponder: view];
426+ NSAccessibilityPostNotification (view, NSAccessibilityLayoutChangedNotification );
427+ #endif // macOS]
318428 });
319429}
320430
321431RCT_EXPORT_METHOD (announceForAccessibility : (NSString *)announcement)
322432{
433+ #if !TARGET_OS_OSX // [macOS]
323434 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]
324446}
325447
326448RCT_EXPORT_METHOD (announceForAccessibilityWithOptions
327449 : (NSString *)announcement options
328450 : (JS::NativeAccessibilityManager::SpecAnnounceForAccessibilityWithOptionsOptions &)options)
329451{
452+ #if !TARGET_OS_OSX // [macOS]
330453 NSMutableDictionary <NSString *, NSNumber *> *attrsDictionary = [NSMutableDictionary new ];
331454 if (options.queue ()) {
332455 attrsDictionary[UIAccessibilitySpeechAttributeQueueAnnouncement] = @(*(options.queue ()) ? YES : NO );
@@ -339,6 +462,18 @@ static void setMultipliers(
339462 } else {
340463 UIAccessibilityPostNotification (UIAccessibilityAnnouncementNotification, announcement);
341464 }
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]
342477}
343478
344479RCT_EXPORT_METHOD (getMultiplier : (RCTResponseSenderBlock)callback)
@@ -387,11 +522,15 @@ static void setMultipliers(
387522 : (RCTResponseSenderBlock)onSuccess onError
388523 : (__unused RCTResponseSenderBlock)onError)
389524{
525+ #if !TARGET_OS_OSX // [macOS]
390526 if (@available (iOS 14.0 , *)) {
391527 onSuccess (@[ @(UIAccessibilityPrefersCrossFadeTransitions ()) ]);
392528 } else {
393529 onSuccess (@[ @(false ) ]);
394530 }
531+ #else // [macOS
532+ onSuccess (@[ @(false ) ]); // iOS only
533+ #endif // macOS]
395534}
396535
397536RCT_EXPORT_METHOD (getCurrentReduceTransparencyState
@@ -408,6 +547,16 @@ static void setMultipliers(
408547 onSuccess (@[ @(_isVoiceOverEnabled) ]);
409548}
410549
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+
411560- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule :
412561 (const facebook::react::ObjCTurboModule::InitParams &)params
413562{
@@ -447,4 +596,3 @@ Class RCTAccessibilityManagerCls(void)
447596{
448597 return RCTAccessibilityManager.class ;
449598}
450- #endif // [macOS]
0 commit comments