vcl/qa/cppunit/a11y/atspi2/atspi2.cxx |   11 +++++++----
 vcl/unx/gtk3/a11y/atklistener.cxx     |    2 +-
 vcl/unx/gtk3/a11y/atkwrapper.cxx      |   12 ++++++++----
 vcl/unx/gtk3/a11y/atkwrapper.hxx      |    2 +-
 4 files changed, 17 insertions(+), 10 deletions(-)

New commits:
commit a30f8ee1121aa2448be047f104a099b3272e6bde
Author:     Michael Weghorn <m.wegh...@posteo.de>
AuthorDate: Mon Nov 13 11:45:06 2023 +0100
Commit:     Michael Weghorn <m.wegh...@posteo.de>
CommitDate: Tue Dec 19 22:33:22 2023 +0100

    tdf#123864 gtk3 a11y: Consider states when mapping BUTTON_DROPDOWN
    
    Take the checkable state into account when mapping the
    `AccessibleRole::BUTTON_DROPDOWN` role.
    
    There is no direct ATK equivalent for
    `AccessibleRole::BUTTON_DROPDOWN`.
    Don't always use ATK_ROLE_PUSH_BUTTON, but
    use ATK_ROLE_TOGGLE_BUTTON when the button is CHECKABLE,
    i.e. it can be toggled.
    
    With this in place, Orca now announces the state of
    the underline button (on/off) when using the gtk3
    VCL plugin.
    
    Related Orca source code that requires the toggle button
    role: [1]
    
    The state is not announced when using the qt6 VCL
    plugin yet, and Qt currently doesn't have a toggle button
    role, so that would have to be added there first to
    do something similar there.
    
    For gtk4, mapping could probably be done similarly,
    but more is missing for Orca to announce things in
    custom widgets (e.g. event handling),
    so leave that for later.
    
    [1] 
https://gitlab.gnome.org/GNOME/orca/-/blob/b80bb951a651f5f12a5ddfb2a5b1c151568d045b/src/orca/scripts/apps/soffice/speech_generator.py#L177
    
    Change-Id: If69e08d2e4939cc709d44e89cc2fd1d01691a70b
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/160904
    Tested-by: Jenkins
    Reviewed-by: Michael Weghorn <m.wegh...@posteo.de>

diff --git a/vcl/qa/cppunit/a11y/atspi2/atspi2.cxx 
b/vcl/qa/cppunit/a11y/atspi2/atspi2.cxx
index e24bf8078613..04d885d623c3 100644
--- a/vcl/qa/cppunit/a11y/atspi2/atspi2.cxx
+++ b/vcl/qa/cppunit/a11y/atspi2/atspi2.cxx
@@ -25,7 +25,7 @@
 using namespace css;
 
 // from gtk3/a11y/atkwrapper.cxx
-static AtspiRole mapToAtspiRole(sal_Int16 nRole)
+static AtspiRole mapToAtspiRole(sal_Int16 nRole, sal_Int64 nStates)
 {
     switch (nRole)
     {
@@ -103,7 +103,6 @@ static AtspiRole mapToAtspiRole(sal_Int16 nRole)
         MAP_DIRECT(TREE);
         MAP(VIEW_PORT, VIEWPORT);
         MAP_DIRECT(WINDOW);
-        MAP(BUTTON_DROPDOWN, PUSH_BUTTON);
 #if ATSPI_ROLE_COUNT > 130 /* ATSPI_ROLE_PUSH_BUTTON_MENU is 129 */
         MAP(BUTTON_MENU, PUSH_BUTTON_MENU);
 #else
@@ -130,7 +129,10 @@ static AtspiRole mapToAtspiRole(sal_Int16 nRole)
 
 #undef MAP_DIRECT
 #undef MAP
-
+        case css::accessibility::AccessibleRole::BUTTON_DROPDOWN:
+            if (nStates & css::accessibility::AccessibleStateType::CHECKABLE)
+                return ATSPI_ROLE_TOGGLE_BUTTON;
+            return ATSPI_ROLE_PUSH_BUTTON;
         default:
             SAL_WARN("vcl.gtk", "Unmapped accessible role: " << nRole);
             return ATSPI_ROLE_UNKNOWN;
@@ -265,7 +267,8 @@ void Atspi2TestTree::compareObjects(const 
uno::Reference<accessibility::XAccessi
      * to ATK, which in turn converts it to ATSPI.  However, ATK and ATSPI are 
roughly equivalent
      * (ATK basically follows ATSPI), but LO's internal might have more 
complex mappings that can't
      * be represented with a round trip. */
-    const auto nLORole = mapToAtspiRole(xLOContext->getAccessibleRole());
+    const AtspiRole nLORole
+        = mapToAtspiRole(xLOContext->getAccessibleRole(), 
xLOContext->getAccessibleStateSet());
     const auto nAtspiRole = pAtspiAccessible.getRole();
     CPPUNIT_ASSERT_EQUAL(nLORole, nAtspiRole);
     /* name (no need to worry about debugging suffixes as 
AccessibilityTools::nameEquals does, as
diff --git a/vcl/unx/gtk3/a11y/atklistener.cxx 
b/vcl/unx/gtk3/a11y/atklistener.cxx
index 3b7777a088c8..f647c52a5c36 100644
--- a/vcl/unx/gtk3/a11y/atklistener.cxx
+++ b/vcl/unx/gtk3/a11y/atklistener.cxx
@@ -630,7 +630,7 @@ void AtkListener::notifyEvent( const 
accessibility::AccessibleEventObject& aEven
         case accessibility::AccessibleEventId::ROLE_CHANGED:
         {
             uno::Reference< accessibility::XAccessibleContext > xContext = 
getAccessibleContextFromSource( aEvent.Source );
-            atk_object_wrapper_set_role( mpWrapper, 
xContext->getAccessibleRole() );
+            atk_object_wrapper_set_role(mpWrapper, 
xContext->getAccessibleRole(), xContext->getAccessibleStateSet());
             break;
         }
 
diff --git a/vcl/unx/gtk3/a11y/atkwrapper.cxx b/vcl/unx/gtk3/a11y/atkwrapper.cxx
index 49b1d371ae12..c946a6e3daaf 100644
--- a/vcl/unx/gtk3/a11y/atkwrapper.cxx
+++ b/vcl/unx/gtk3/a11y/atkwrapper.cxx
@@ -167,7 +167,7 @@ AtkStateType mapAtkState( sal_Int64 nState )
     return type;
 }
 
-static AtkRole mapToAtkRole( sal_Int16 nRole )
+static AtkRole mapToAtkRole(sal_Int16 nRole, sal_Int64 nStates)
 {
     switch (nRole)
     {
@@ -310,7 +310,11 @@ static AtkRole mapToAtkRole( sal_Int16 nRole )
         case accessibility::AccessibleRole::WINDOW:
             return ATK_ROLE_WINDOW;
         case accessibility::AccessibleRole::BUTTON_DROPDOWN:
+        {
+            if (nStates & css::accessibility::AccessibleStateType::CHECKABLE)
+                return ATK_ROLE_TOGGLE_BUTTON;
             return ATK_ROLE_PUSH_BUTTON;
+        }
         case accessibility::AccessibleRole::BUTTON_MENU:
 #if ATK_CHECK_VERSION(2, 46, 0)
             return ATK_ROLE_PUSH_BUTTON_MENU;
@@ -972,7 +976,7 @@ atk_object_wrapper_new( const css::uno::Reference< 
css::accessibility::XAccessib
         pWrap->mpOrig = orig;
 
         AtkObject* atk_obj = ATK_OBJECT(pWrap);
-        atk_obj->role = mapToAtkRole( xContext->getAccessibleRole() );
+        atk_obj->role = mapToAtkRole(xContext->getAccessibleRole(), 
xContext->getAccessibleStateSet());
         atk_obj->accessible_parent = parent;
 
         ooo_wrapper_registry_add( rxAccessible, atk_obj );
@@ -1070,10 +1074,10 @@ void atk_object_wrapper_remove_child(AtkObjectWrapper* 
wrapper, AtkObject *child
 
 /*****************************************************************************/
 
-void atk_object_wrapper_set_role(AtkObjectWrapper* wrapper, sal_Int16 role)
+void atk_object_wrapper_set_role(AtkObjectWrapper* wrapper, sal_Int16 role, 
sal_Int64 nStates)
 {
     AtkObject *atk_obj = ATK_OBJECT( wrapper );
-    atk_object_set_role( atk_obj, mapToAtkRole( role ) );
+    atk_object_set_role(atk_obj, mapToAtkRole(role, nStates));
 }
 
 /*****************************************************************************/
diff --git a/vcl/unx/gtk3/a11y/atkwrapper.hxx b/vcl/unx/gtk3/a11y/atkwrapper.hxx
index 911433fc8a6f..8d1aeb359bca 100644
--- a/vcl/unx/gtk3/a11y/atkwrapper.hxx
+++ b/vcl/unx/gtk3/a11y/atkwrapper.hxx
@@ -93,7 +93,7 @@ AtkObject *            atk_object_wrapper_new(
 
 void                   atk_object_wrapper_add_child(AtkObjectWrapper* wrapper, 
AtkObject *child, gint index);
 void                   atk_object_wrapper_remove_child(AtkObjectWrapper* 
wrapper, AtkObject *child, gint index);
-void                   atk_object_wrapper_set_role(AtkObjectWrapper* wrapper, 
sal_Int16 role);
+void                   atk_object_wrapper_set_role(AtkObjectWrapper* wrapper, 
sal_Int16 role, sal_Int64 nStates);
 
 void                   atk_object_wrapper_dispose(AtkObjectWrapper* wrapper);
 

Reply via email to