mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
Accessibility: Add AccessibilityHandler::postSystemNotification() function for posting an OS-specific accessible notification
This commit is contained in:
parent
6d10eb536f
commit
269ebbb525
11 changed files with 308 additions and 149 deletions
|
|
@ -35,8 +35,6 @@
|
|||
namespace juce
|
||||
{
|
||||
|
||||
AccessibilityHandler* AccessibilityHandler::currentlyFocusedHandler = nullptr;
|
||||
|
||||
class NativeChildHandler
|
||||
{
|
||||
public:
|
||||
|
|
@ -403,10 +401,24 @@ void AccessibilityHandler::setNativeChildForComponent (Component& component, voi
|
|||
NativeChildHandler::getInstance().setNativeChild (component, nativeChild);
|
||||
}
|
||||
|
||||
#if JUCE_MODULE_AVAILABLE_juce_gui_extra
|
||||
void privatePostSystemNotification (const String&, const String&);
|
||||
#endif
|
||||
|
||||
void AccessibilityHandler::postSystemNotification ([[maybe_unused]] const String& notificationTitle,
|
||||
[[maybe_unused]] const String& notificationBody)
|
||||
{
|
||||
#if JUCE_MODULE_AVAILABLE_juce_gui_extra
|
||||
if (areAnyAccessibilityClientsActive())
|
||||
privatePostSystemNotification (notificationTitle, notificationBody);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if ! JUCE_NATIVE_ACCESSIBILITY_INCLUDED
|
||||
void AccessibilityHandler::notifyAccessibilityEvent (AccessibilityEvent) const {}
|
||||
void AccessibilityHandler::postAnnouncement (const String&, AnnouncementPriority) {}
|
||||
AccessibilityNativeHandle* AccessibilityHandler::getNativeImplementation() const { return nullptr; }
|
||||
bool AccessibilityHandler::areAnyAccessibilityClientsActive() { return false; }
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
|
|
|
|||
|
|
@ -295,6 +295,30 @@ public:
|
|||
*/
|
||||
static void postAnnouncement (const String& announcementString, AnnouncementPriority priority);
|
||||
|
||||
/** Posts a local system notification.
|
||||
|
||||
In order for this to do anything, the following conditions must be met.
|
||||
- At build time:
|
||||
- The juce_gui_extra module must be included in the project.
|
||||
- Push notifications must be enabled by setting the preprocessor definition
|
||||
JUCE_PUSH_NOTIFICATIONS=1
|
||||
- At run time:
|
||||
- An accessibility client (narrator, voiceover etc.) must be active.
|
||||
|
||||
Additionally, on Android, an icon is required for notifications.
|
||||
This must be specified by adding the path to the icon file called
|
||||
"accessibilitynotificationicon" in the "Extra Android Raw Resources" setting
|
||||
in the Projucer.
|
||||
|
||||
This will use the push notification client on macOS, iOS and Android.
|
||||
On Windows this will create a system tray icon to post the notification.
|
||||
|
||||
@param notificationTitle the title of the notification
|
||||
@param notificationBody the main body text of the notification
|
||||
*/
|
||||
static void postSystemNotification (const String& notificationTitle,
|
||||
const String& notificationBody);
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
AccessibilityNativeHandle* getNativeImplementation() const;
|
||||
|
|
@ -329,8 +353,9 @@ private:
|
|||
void grabFocusInternal (bool);
|
||||
void giveAwayFocusInternal() const;
|
||||
void takeFocus();
|
||||
static bool areAnyAccessibilityClientsActive();
|
||||
|
||||
static AccessibilityHandler* currentlyFocusedHandler;
|
||||
static inline AccessibilityHandler* currentlyFocusedHandler = nullptr;
|
||||
|
||||
//==============================================================================
|
||||
Component& component;
|
||||
|
|
|
|||
|
|
@ -1055,4 +1055,9 @@ void AccessibilityHandler::postAnnouncement (const String& announcementString,
|
|||
javaString (announcementString).get());
|
||||
}
|
||||
|
||||
bool AccessibilityHandler::areAnyAccessibilityClientsActive()
|
||||
{
|
||||
return AccessibilityNativeHandle::areAnyAccessibilityClientsActive();
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
|
|
|||
|
|
@ -666,4 +666,9 @@ void AccessibilityHandler::postAnnouncement (const String& announcementString, A
|
|||
sendAccessibilityEvent (UIAccessibilityAnnouncementNotification, juceStringToNS (announcementString));
|
||||
}
|
||||
|
||||
bool AccessibilityHandler::areAnyAccessibilityClientsActive()
|
||||
{
|
||||
return juce::areAnyAccessibilityClientsActive();
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
|
|
|||
|
|
@ -939,4 +939,9 @@ void AccessibilityHandler::postAnnouncement (const String& announcementString, A
|
|||
NSAccessibilityPriorityKey: @(nsPriority) });
|
||||
}
|
||||
|
||||
bool AccessibilityHandler::areAnyAccessibilityClientsActive()
|
||||
{
|
||||
return juce::areAnyAccessibilityClientsActive();
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
|
|
|||
|
|
@ -39,26 +39,83 @@ namespace juce
|
|||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token")
|
||||
|
||||
static bool isStartingUpOrShuttingDown()
|
||||
//==============================================================================
|
||||
struct WindowsAccessibility
|
||||
{
|
||||
if (auto* app = JUCEApplicationBase::getInstance())
|
||||
if (app->isInitialising())
|
||||
WindowsAccessibility() = delete;
|
||||
|
||||
static long getUiaRootObjectId()
|
||||
{
|
||||
return static_cast<long> (UiaRootObjectId);
|
||||
}
|
||||
|
||||
static bool handleWmGetObject (AccessibilityHandler* handler, WPARAM wParam, LPARAM lParam, LRESULT* res)
|
||||
{
|
||||
if (isStartingUpOrShuttingDown() || (handler == nullptr || ! isHandlerValid (*handler)))
|
||||
return false;
|
||||
|
||||
if (auto* uiaWrapper = WindowsUIAWrapper::getInstance())
|
||||
{
|
||||
ComSmartPtr<IRawElementProviderSimple> provider;
|
||||
handler->getNativeImplementation()->QueryInterface (IID_PPV_ARGS (provider.resetAndGetPointerAddress()));
|
||||
|
||||
if (! uiaWrapper->isProviderDisconnecting (provider))
|
||||
*res = uiaWrapper->returnRawElementProvider ((HWND) handler->getComponent().getWindowHandle(), wParam, lParam, provider);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (auto* mm = MessageManager::getInstanceWithoutCreating())
|
||||
if (mm->hasStopMessageBeenSent())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
static void revokeUIAMapEntriesForWindow (HWND hwnd)
|
||||
{
|
||||
if (auto* uiaWrapper = WindowsUIAWrapper::getInstanceWithoutCreating())
|
||||
uiaWrapper->returnRawElementProvider (hwnd, 0, 0, nullptr);
|
||||
}
|
||||
|
||||
static bool isHandlerValid (const AccessibilityHandler& handler)
|
||||
{
|
||||
if (auto* provider = handler.getNativeImplementation())
|
||||
return provider->isElementValid();
|
||||
static bool isStartingUpOrShuttingDown()
|
||||
{
|
||||
if (auto* app = JUCEApplicationBase::getInstance())
|
||||
if (app->isInitialising())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
if (auto* mm = MessageManager::getInstanceWithoutCreating())
|
||||
if (mm->hasStopMessageBeenSent())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool isHandlerValid (const AccessibilityHandler& handler)
|
||||
{
|
||||
if (auto* provider = handler.getNativeImplementation())
|
||||
return provider->isElementValid();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static 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();
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class AccessibilityHandler::AccessibilityNativeImpl
|
||||
|
|
@ -103,31 +160,12 @@ AccessibilityNativeHandle* AccessibilityHandler::getNativeImplementation() const
|
|||
return nativeImpl->accessibilityElement;
|
||||
}
|
||||
|
||||
static 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 (! areAnyAccessibilityClientsActive() || isStartingUpOrShuttingDown() || ! isHandlerValid (handler))
|
||||
if (! WindowsAccessibility::areAnyAccessibilityClientsActive()
|
||||
|| WindowsAccessibility::isStartingUpOrShuttingDown()
|
||||
|| ! WindowsAccessibility::isHandlerValid (handler))
|
||||
return;
|
||||
|
||||
if (auto* uiaWrapper = WindowsUIAWrapper::getInstanceWithoutCreating())
|
||||
|
|
@ -292,41 +330,11 @@ void AccessibilityHandler::postAnnouncement (const String& announcementString, A
|
|||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
namespace WindowsAccessibility
|
||||
bool AccessibilityHandler::areAnyAccessibilityClientsActive()
|
||||
{
|
||||
static long getUiaRootObjectId()
|
||||
{
|
||||
return static_cast<long> (UiaRootObjectId);
|
||||
}
|
||||
|
||||
static bool handleWmGetObject (AccessibilityHandler* handler, WPARAM wParam, LPARAM lParam, LRESULT* res)
|
||||
{
|
||||
if (isStartingUpOrShuttingDown() || (handler == nullptr || ! isHandlerValid (*handler)))
|
||||
return false;
|
||||
|
||||
if (auto* uiaWrapper = WindowsUIAWrapper::getInstance())
|
||||
{
|
||||
ComSmartPtr<IRawElementProviderSimple> provider;
|
||||
handler->getNativeImplementation()->QueryInterface (IID_PPV_ARGS (provider.resetAndGetPointerAddress()));
|
||||
|
||||
if (! uiaWrapper->isProviderDisconnecting (provider))
|
||||
*res = uiaWrapper->returnRawElementProvider ((HWND) handler->getComponent().getWindowHandle(), wParam, lParam, provider);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void revokeUIAMapEntriesForWindow (HWND hwnd)
|
||||
{
|
||||
if (auto* uiaWrapper = WindowsUIAWrapper::getInstanceWithoutCreating())
|
||||
uiaWrapper->returnRawElementProvider (hwnd, 0, 0, nullptr);
|
||||
}
|
||||
return WindowsAccessibility::areAnyAccessibilityClientsActive();
|
||||
}
|
||||
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
} // namespace juce
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue