1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-11 23:54:18 +00:00

Accessibility: Check if any accessibility clients are active before posting notifications and announcements

This commit is contained in:
ed 2021-06-16 17:22:00 +01:00
parent 399f8d5bf6
commit cdf3b619d8
4 changed files with 77 additions and 32 deletions

View file

@ -28,6 +28,8 @@ namespace juce
AccessibilityHandler* AccessibilityHandler::currentlyFocusedHandler = nullptr;
bool areAnyAccessibilityClientsActive();
enum class InternalAccessibilityEvent
{
elementCreated,
@ -37,7 +39,7 @@ enum class InternalAccessibilityEvent
windowClosed
};
void notifyAccessibilityEventInternal (const AccessibilityHandler& handler, InternalAccessibilityEvent event);
void notifyAccessibilityEventInternal (const AccessibilityHandler&, InternalAccessibilityEvent);
inline String getAccessibleApplicationOrPluginName()
{

View file

@ -347,6 +347,7 @@ namespace juce
AccessibilityNativeHandle* AccessibilityHandler::getNativeImplementation() const { return nullptr; }
AccessibilityHandler::AccessibilityNativeImpl* AccessibilityHandler::createNativeImpl (AccessibilityHandler&) { return nullptr; }
void AccessibilityHandler::DestroyNativeImpl::operator() (AccessibilityHandler::AccessibilityNativeImpl*) const noexcept {}
bool areAnyAccessibilityClientsActive() { return false; }
void notifyAccessibilityEventInternal (const AccessibilityHandler&, InternalAccessibilityEvent) {}
}
#endif

View file

@ -1025,6 +1025,20 @@ AccessibilityNativeHandle* AccessibilityHandler::getNativeImplementation() const
return (AccessibilityNativeHandle*) nativeImpl->getAccessibilityElement();
}
bool areAnyAccessibilityClientsActive()
{
const String voiceOverKeyString ("voiceOverOnOffKey");
const String applicationIDString ("com.apple.universalaccess");
CFUniquePtr<CFPropertyListRef> value (CFPreferencesCopyAppValue (voiceOverKeyString.toCFString(),
applicationIDString.toCFString()));
if (value != nullptr)
return CFBooleanGetValue ((CFBooleanRef) value.get());
return false;
}
static void sendAccessibilityEvent (id accessibilityElement,
NSAccessibilityNotificationName notification,
NSDictionary* userInfo)
@ -1034,6 +1048,21 @@ static void sendAccessibilityEvent (id accessibilityElement,
NSAccessibilityPostNotificationWithUserInfo (accessibilityElement, notification, userInfo);
}
static void sendHandlerNotification (const AccessibilityHandler& handler,
NSAccessibilityNotificationName notification)
{
if (! areAnyAccessibilityClientsActive() || notification == NSAccessibilityNotificationName{})
return;
if (id accessibilityElement = (id) handler.getNativeImplementation())
{
sendAccessibilityEvent (accessibilityElement, notification,
(notification == NSAccessibilityLayoutChangedNotification
? @{ NSAccessibilityUIElementsKey: @[ accessibilityElement ] }
: nil));
}
}
void notifyAccessibilityEventInternal (const AccessibilityHandler& handler, InternalAccessibilityEvent eventType)
{
auto notification = [eventType]
@ -1050,9 +1079,7 @@ void notifyAccessibilityEventInternal (const AccessibilityHandler& handler, Inte
return NSAccessibilityNotificationName{};
}();
if (notification != NSAccessibilityNotificationName{})
if (id accessibilityElement = (id) handler.getNativeImplementation())
sendAccessibilityEvent (accessibilityElement, notification, nil);
sendHandlerNotification (handler, notification);
}
void AccessibilityHandler::notifyAccessibilityEvent (AccessibilityEvent eventType) const
@ -1073,20 +1100,14 @@ void AccessibilityHandler::notifyAccessibilityEvent (AccessibilityEvent eventTyp
return NSAccessibilityNotificationName{};
}();
if (notification != NSAccessibilityNotificationName{})
{
if (id accessibilityElement = (id) getNativeImplementation())
{
sendAccessibilityEvent (accessibilityElement, notification,
(notification == NSAccessibilityLayoutChangedNotification
? @{ NSAccessibilityUIElementsKey: @[ accessibilityElement ] }
: nil));
}
}
sendHandlerNotification (*this, notification);
}
void AccessibilityHandler::postAnnouncement (const String& announcementString, AnnouncementPriority priority)
{
if (! areAnyAccessibilityClientsActive())
return;
#if JUCE_OBJC_HAS_AVAILABLE_FEATURE
if (@available (macOS 10.10, *))
#endif

View file

@ -66,15 +66,15 @@ public:
accessibilityElement->invalidateElement();
--providerCount;
if (auto* wrapper = WindowsUIAWrapper::getInstanceWithoutCreating())
if (auto* uiaWrapper = WindowsUIAWrapper::getInstanceWithoutCreating())
{
ComSmartPtr<IRawElementProviderSimple> provider;
accessibilityElement->QueryInterface (IID_PPV_ARGS (provider.resetAndGetPointerAddress()));
wrapper->disconnectProvider (provider);
uiaWrapper->disconnectProvider (provider);
if (providerCount == 0)
wrapper->disconnectAllProviders();
uiaWrapper->disconnectAllProviders();
}
}
@ -94,21 +94,39 @@ AccessibilityNativeHandle* AccessibilityHandler::getNativeImplementation() const
return nativeImpl->accessibilityElement;
}
bool areAnyAccessibilityClientsActive()
{
const auto areClientsListening = []
{
if (auto* uiaWrapper = WindowsUIAWrapper::getInstanceWithoutCreating())
return uiaWrapper->clientsAreListening() != 0;
return false;
};
const auto isScreenReaderRunning = []
{
BOOL isRunning = FALSE;
SystemParametersInfo (SPI_GETSCREENREADER, 0, (PVOID) &isRunning, 0);
return isRunning != 0;
};
return areClientsListening() || isScreenReaderRunning();
}
template <typename Callback>
void getProviderWithCheckedWrapper (const AccessibilityHandler& handler, Callback&& callback)
{
if (isStartingUpOrShuttingDown() || ! isHandlerValid (handler))
if (! areAnyAccessibilityClientsActive() || isStartingUpOrShuttingDown() || ! isHandlerValid (handler))
return;
if (auto* wrapper = WindowsUIAWrapper::getInstanceWithoutCreating())
if (auto* uiaWrapper = WindowsUIAWrapper::getInstanceWithoutCreating())
{
if (! wrapper->clientsAreListening())
return;
ComSmartPtr<IRawElementProviderSimple> provider;
handler.getNativeImplementation()->QueryInterface (IID_PPV_ARGS (provider.resetAndGetPointerAddress()));
callback (wrapper, provider);
callback (uiaWrapper, provider);
}
}
@ -116,9 +134,9 @@ void sendAccessibilityAutomationEvent (const AccessibilityHandler& handler, EVEN
{
jassert (event != EVENTID{});
getProviderWithCheckedWrapper (handler, [event] (WindowsUIAWrapper* wrapper, ComSmartPtr<IRawElementProviderSimple>& provider)
getProviderWithCheckedWrapper (handler, [event] (WindowsUIAWrapper* uiaWrapper, ComSmartPtr<IRawElementProviderSimple>& provider)
{
wrapper->raiseAutomationEvent (provider, event);
uiaWrapper->raiseAutomationEvent (provider, event);
});
}
@ -126,12 +144,12 @@ void sendAccessibilityPropertyChangedEvent (const AccessibilityHandler& handler,
{
jassert (property != PROPERTYID{});
getProviderWithCheckedWrapper (handler, [property, newValue] (WindowsUIAWrapper* wrapper, ComSmartPtr<IRawElementProviderSimple>& provider)
getProviderWithCheckedWrapper (handler, [property, newValue] (WindowsUIAWrapper* uiaWrapper, ComSmartPtr<IRawElementProviderSimple>& provider)
{
VARIANT oldValue;
VariantHelpers::clear (&oldValue);
wrapper->raiseAutomationPropertyChangedEvent (provider, property, oldValue, newValue);
uiaWrapper->raiseAutomationPropertyChangedEvent (provider, property, oldValue, newValue);
});
}
@ -218,6 +236,9 @@ JUCE_IMPLEMENT_SINGLETON (SpVoiceWrapper)
void AccessibilityHandler::postAnnouncement (const String& announcementString, AnnouncementPriority priority)
{
if (! areAnyAccessibilityClientsActive())
return;
if (auto* sharedVoice = SpVoiceWrapper::getInstance())
{
auto voicePriority = [priority]
@ -261,13 +282,13 @@ namespace WindowsAccessibility
if (isStartingUpOrShuttingDown() || (handler == nullptr || ! isHandlerValid (*handler)))
return false;
if (auto* wrapper = WindowsUIAWrapper::getInstance())
if (auto* uiaWrapper = WindowsUIAWrapper::getInstance())
{
ComSmartPtr<IRawElementProviderSimple> provider;
handler->getNativeImplementation()->QueryInterface (IID_PPV_ARGS (provider.resetAndGetPointerAddress()));
if (! wrapper->isProviderDisconnecting (provider))
*res = wrapper->returnRawElementProvider ((HWND) handler->getComponent().getWindowHandle(), wParam, lParam, provider);
if (! uiaWrapper->isProviderDisconnecting (provider))
*res = uiaWrapper->returnRawElementProvider ((HWND) handler->getComponent().getWindowHandle(), wParam, lParam, provider);
return true;
}
@ -277,8 +298,8 @@ namespace WindowsAccessibility
void revokeUIAMapEntriesForWindow (HWND hwnd)
{
if (auto* wrapper = WindowsUIAWrapper::getInstanceWithoutCreating())
wrapper->returnRawElementProvider (hwnd, 0, 0, nullptr);
if (auto* uiaWrapper = WindowsUIAWrapper::getInstanceWithoutCreating())
uiaWrapper->returnRawElementProvider (hwnd, 0, 0, nullptr);
}
}