toolkit/source/awt/vclxaccessiblecomponent.cxx | 32 ++++++++++ winaccessibility/source/service/AccComponentEventListener.cxx | 1 winaccessibility/source/service/AccObjectWinManager.cxx | 5 + 3 files changed, 38 insertions(+)
New commits: commit 995d17697d1e46f66df67ca3132369d76caea29e Author: Michael Weghorn <m.wegh...@posteo.de> AuthorDate: Mon Sep 19 14:45:05 2022 +0200 Commit: Michael Weghorn <m.wegh...@posteo.de> CommitDate: Mon Sep 26 16:15:06 2022 +0200 tdf#117173 wina11y: Send EVENT_SYSTEM_ALERT when notification shows Together with Change-Id Ifcf9304883e2e824ea1b7998d7767e474b87c8b6 ("tdf#119788 tdf#117173 add accessibility NOTIFICATION role") and Change-Id Id62b3942dc17c3a1ed6a08d23438406e5a19c39d ("tdf#117173 a11y: Send SHOWING state change event on Window{Show,Hide}"), this makes NVDA announce the notification in the Search and Replace dialog as an alert, similar to what browsers do e.g. in the alert on empty input for the input validation example at https://www.w3.org/WAI/tutorials/forms/validation/ . Change-Id: I3263df4711f84a6dd9e178aaaaad340b128aa074 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/140091 Tested-by: Jenkins Reviewed-by: Michael Weghorn <m.wegh...@posteo.de> diff --git a/winaccessibility/source/service/AccComponentEventListener.cxx b/winaccessibility/source/service/AccComponentEventListener.cxx index ca0dec0fa311..060fb201136c 100644 --- a/winaccessibility/source/service/AccComponentEventListener.cxx +++ b/winaccessibility/source/service/AccComponentEventListener.cxx @@ -237,6 +237,7 @@ void AccComponentEventListener::FireStatePropertyChange(sal_Int64 state, bool se pAgent->DecreaseState(m_xAccessible.get(), AccessibleStateType::DEFUNC); // UNO !SHOWING == MSAA OFFSCREEN pAgent->IncreaseState(m_xAccessible.get(), AccessibleStateType::SHOWING ); + pAgent->NotifyAccEvent(UnoMSAAEvent::STATE_SHOWING, m_xAccessible.get()); break; case AccessibleStateType::VISIBLE: // UNO !VISIBLE == MSAA INVISIBLE diff --git a/winaccessibility/source/service/AccObjectWinManager.cxx b/winaccessibility/source/service/AccObjectWinManager.cxx index 44dbcb98ac3a..de527270a8e0 100644 --- a/winaccessibility/source/service/AccObjectWinManager.cxx +++ b/winaccessibility/source/service/AccObjectWinManager.cxx @@ -198,6 +198,11 @@ bool AccObjectWinManager::NotifyAccEvent(XAccessible* pXAcc, UnoMSAAEvent eEvent UpdateAccFocus(pXAcc); NotifyWinEvent( EVENT_OBJECT_FOCUS,hAcc, OBJID_CLIENT,dChildID ); break; + case UnoMSAAEvent::STATE_SHOWING: + // send EVENT_SYSTEM_ALERT when notification gets shown + if (pRContext->getAccessibleRole() == AccessibleRole::NOTIFICATION) + NotifyWinEvent(EVENT_SYSTEM_ALERT, hAcc, OBJID_CLIENT, dChildID); + break; case UnoMSAAEvent::MENU_START: NotifyWinEvent( EVENT_SYSTEM_MENUSTART,hAcc, OBJID_CLIENT,dChildID ); break; commit c26d6cc3c4878d356328a538b5bf11e4e6a0e7dc Author: Michael Weghorn <m.wegh...@posteo.de> AuthorDate: Mon Sep 19 14:44:46 2022 +0200 Commit: Michael Weghorn <m.wegh...@posteo.de> CommitDate: Mon Sep 26 16:14:51 2022 +0200 tdf#117173 a11y: Send SHOWING state change event on Window{Show,Hide} When a `vcl::Window` becomes visible, `Window::ImplSetReallyVisible` calls the registered event listeners with a `VclEventId::WindowShow` event. Likewise, a `VclEventId::WindowHide` events is sent in `Window::ImplResetReallyVisible` when the window is no longer visible. Handle that event in `VCLXAccessibleComponent` by sending a state change event for the SHOWING state, so assistive technology gets notified about this. (Similar handling can already be found e.g. in `AccessibleTabBar::ProcessWindowEvent` or `AccessibleTabBarPageList::ProcessWindowEvent`.) While doing so in `VCLXAccessibleComponent::ProcessWindowEvent` for the object itself would generally seem like a more straightforward and conceptually nicer approach, this would have the problem that the event wouldn't get propagated to the platform-specific a11y integration layer (like winaccessibility) for the `VclEventId::WindowShow` case, since the a11y event listeners are registered and unregistered as a response to the CHILD event (at least for winaccessibility and gtk3, qt6 doesn't do that (yet?)), and if the accessible event listener is not (yet) registered, the event is simply ignored. Since the CHILD event is sent in `VCLXAccessibleComponent::ProcessChildWindowEvent` and that gets called on the parent *after* `VCLXAccessibleComponent::ProcessWindowEvent` gets called for the object that became shown/hidden (s. `Window::CallEventListeners`), also send the state change event for the SHOWING state of the child from there, so the proper order can be made sure. The reverse order (first the state change event for the SHOWING state, then the CHILD event which results in removal of the a11y event listeners) is used for the case where the window gets hidden. In combination with Change-Id Ifcf9304883e2e824ea1b7998d7767e474b87c8b6 ("tdf#119788 tdf#117173 add atk notification role") and commit 155e8b1683f10847ff18e75287e2466220242bb1 ("tdf#117173: qt a11y: Forward changes to SHOWING state"), this makes Orca announce the label with notification role in the Search and Replace dialog for the qt6 VCL plugin as well. The gtk3 case already works with just Change-Id Ifcf9304883e2e824ea1b7998d7767e474b87c8b6 ("tdf#119788 tdf#117173 add atk notification role") in place, because that one uses native gtk widgets in the Search and Replace dialog, and the Gtk library then takes care of sending the object:state-changed:showing event when the label with notification role gets shown. Side note: There are also comments that suggest to rework the a11y event handling for the show/hide changes more fundamentally, e.g. this comment in `Window::ImplSetReallyVisible`: // the SHOW/HIDE events serve as indicators to send child creation/destroy events to the access bridge. // For this, the data member of the event must not be NULL. // Previously, we did this in Window::Show, but there some events got lost in certain situations. Now // we're doing it when the visibility really changes if( bBecameReallyVisible && ImplIsAccessibleCandidate() ) CallEventListeners( VclEventId::WindowShow, this ); // TODO. It's kind of a hack that we're re-using the VclEventId::WindowShow. Normally, we should // introduce another event which explicitly triggers the Accessibility implementations. Similar ones can be found in `Window::ImplResetReallyVisible` and `Window::Show`. Change-Id: Id62b3942dc17c3a1ed6a08d23438406e5a19c39d Reviewed-on: https://gerrit.libreoffice.org/c/core/+/139813 Tested-by: Jenkins Reviewed-by: Michael Weghorn <m.wegh...@posteo.de> diff --git a/toolkit/source/awt/vclxaccessiblecomponent.cxx b/toolkit/source/awt/vclxaccessiblecomponent.cxx index b7c44f53bd68..5d3b15653ba3 100644 --- a/toolkit/source/awt/vclxaccessiblecomponent.cxx +++ b/toolkit/source/awt/vclxaccessiblecomponent.cxx @@ -163,6 +163,20 @@ void VCLXAccessibleComponent::ProcessWindowChildEvent( const VclWindowEvent& rVc { aNewValue <<= xAcc; NotifyAccessibleEvent( accessibility::AccessibleEventId::CHILD, aOldValue, aNewValue ); + + // CHILD event above results in a11y event listeners getting registered, + // so send state change event for SHOWING event after that + uno::Reference<XAccessibleContext> xChildContext = xAcc->getAccessibleContext(); + if (xChildContext.is()) + { + VCLXAccessibleComponent* pChildComponent = dynamic_cast<VCLXAccessibleComponent*>(xChildContext.get()); + if (pChildComponent) + { + css::uno::Any aNewStateValue; + aNewStateValue <<= accessibility::AccessibleStateType::SHOWING; + pChildComponent->NotifyAccessibleEvent(accessibility::AccessibleEventId::STATE_CHANGED, css::uno::Any(), aNewStateValue); + } + } } } break; @@ -171,6 +185,20 @@ void VCLXAccessibleComponent::ProcessWindowChildEvent( const VclWindowEvent& rVc xAcc = GetChildAccessible( rVclWindowEvent ); if( xAcc.is() ) { + // send send state change event for SHOWING before sending the CHILD event below, + // since that one results in a11y event listeners getting removed + uno::Reference<XAccessibleContext> xChildContext = xAcc->getAccessibleContext(); + if (xChildContext.is()) + { + VCLXAccessibleComponent* pChildComponent = dynamic_cast<VCLXAccessibleComponent*>(xChildContext.get()); + if (pChildComponent) + { + css::uno::Any aOldStateValue; + aOldStateValue <<= accessibility::AccessibleStateType::SHOWING; + pChildComponent->NotifyAccessibleEvent(accessibility::AccessibleEventId::STATE_CHANGED, aOldStateValue, css::uno::Any()); + } + } + aOldValue <<= xAcc; NotifyAccessibleEvent( accessibility::AccessibleEventId::CHILD, aOldValue, aNewValue ); } @@ -334,6 +362,10 @@ void VCLXAccessibleComponent::ProcessWindowEvent( const VclWindowEvent& rVclWind NotifyAccessibleEvent( accessibility::AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); } break; + case VclEventId::WindowHide: + case VclEventId::WindowShow: + // WindowHide and WindowShow are handled in ProcessWindowChildEvent so the right order + // regarding the CHILD event can be taken into account default: { }