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:
         {
         }

Reply via email to