diff options
author | Michael Weghorn <m.weghorn@posteo.de> | 2022-09-19 14:44:46 +0200 |
---|---|---|
committer | Michael Weghorn <m.weghorn@posteo.de> | 2022-09-26 16:14:51 +0200 |
commit | c26d6cc3c4878d356328a538b5bf11e4e6a0e7dc (patch) | |
tree | e69aca14bce5782552ee3cefef2f6a34afb54099 /toolkit | |
parent | d8789d175af75e7768e601662619b116e9780937 (diff) |
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.weghorn@posteo.de>
Diffstat (limited to 'toolkit')
-rw-r--r-- | toolkit/source/awt/vclxaccessiblecomponent.cxx | 32 |
1 files changed, 32 insertions, 0 deletions
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: { } |