vcl/unx/gtk4/a11y.cxx                  |   70 +++++++++++++++++++++++++++++++++
 vcl/unx/gtk4/gtkaccessibleregistry.cxx |   16 +++++++
 vcl/unx/gtk4/gtkaccessibleregistry.hxx |    2 
 3 files changed, 87 insertions(+), 1 deletion(-)

New commits:
commit 4cec279cf2bf55d33e0eb6c92348aa883d7593ff
Author:     Michael Weghorn <m.wegh...@posteo.de>
AuthorDate: Tue Apr 30 13:06:15 2024 +0200
Commit:     Michael Weghorn <m.wegh...@posteo.de>
CommitDate: Tue Apr 30 17:36:44 2024 +0200

    gtk4 a11y: Handle accessible relations
    
    When creating a new LoAccessible, also consider
    accessible relations.
    These can be retrieved via the XAccessibleRelationSet
    UNO interface.
    
    Bridge those that have an equivalent in the GTK 4 a11y API.
    
    As the parent GtkAccessible of a target XAccessible
    is not known, extend `GtkAccessibleRegistry::getLOAccessible`
    with the functionality to find a parent via the XAccessible
    hierarchy (i.e. by walking up the XAccessible hierarchy
    until a parent is reached that has an entry in registry
    already).
    This could potentially be problematic as an a11y hierarchy
    solely based on the XAccessible hierarchy doesn't necessarily
    match the one with the GtkAccessibles from the native GtkWidget
    hierarchy. In my current understanding, this should presumably
    be OK as long as that mechanism finds one existing
    entry before getting to the root, so assert that this is the case.
    
    With this in place, having a Writer doc with multiple paragraphs,
    there is a flows_to relation shown for the first paragraph to the
    second in Accerciser, etc., and jumping to the target selects
    the next paragraph as expected.
    
    Change-Id: I2ec1fe8c48dd4250d407ae4fb3c8d9c0f6671646
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/166926
    Tested-by: Jenkins
    Reviewed-by: Michael Weghorn <m.wegh...@posteo.de>

diff --git a/vcl/unx/gtk4/a11y.cxx b/vcl/unx/gtk4/a11y.cxx
index 4b9447c19ae4..bf66b058b231 100644
--- a/vcl/unx/gtk4/a11y.cxx
+++ b/vcl/unx/gtk4/a11y.cxx
@@ -7,6 +7,7 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
+#include <com/sun/star/accessibility/AccessibleRelationType.hpp>
 #include <com/sun/star/accessibility/AccessibleRole.hpp>
 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
 #include <com/sun/star/accessibility/XAccessibleComponent.hpp>
@@ -391,6 +392,73 @@ applyObjectAttributes(GtkAccessible* pGtkAccessible,
     } while (nIndex >= 0);
 }
 
+static void applyRelations(LoAccessible* pLoAccessible,
+                           
css::uno::Reference<css::accessibility::XAccessibleContext>& xContext)
+{
+    assert(pLoAccessible);
+
+    if (!xContext)
+        return;
+
+    css::uno::Reference<css::accessibility::XAccessibleRelationSet> 
xRelationSet
+        = xContext->getAccessibleRelationSet();
+    if (!xRelationSet.is())
+        return;
+
+    for (sal_Int32 i = 0; i < xRelationSet->getRelationCount(); i++)
+    {
+        GtkAccessibleRelation eGtkRelation;
+        css::accessibility::AccessibleRelation aRelation = 
xRelationSet->getRelation(i);
+        switch (aRelation.RelationType)
+        {
+            case css::accessibility::AccessibleRelationType::CONTENT_FLOWS_TO:
+                eGtkRelation = GTK_ACCESSIBLE_RELATION_FLOW_TO;
+                break;
+            case css::accessibility::AccessibleRelationType::CONTROLLER_FOR:
+                eGtkRelation = GTK_ACCESSIBLE_RELATION_CONTROLS;
+                break;
+            case css::accessibility::AccessibleRelationType::DESCRIBED_BY:
+                eGtkRelation = GTK_ACCESSIBLE_RELATION_DESCRIBED_BY;
+                break;
+            case css::accessibility::AccessibleRelationType::LABELED_BY:
+                eGtkRelation = GTK_ACCESSIBLE_RELATION_LABELLED_BY;
+                break;
+            case 
css::accessibility::AccessibleRelationType::CONTENT_FLOWS_FROM:
+            case css::accessibility::AccessibleRelationType::CONTROLLED_BY:
+            case css::accessibility::AccessibleRelationType::INVALID:
+            case css::accessibility::AccessibleRelationType::LABEL_FOR:
+            case css::accessibility::AccessibleRelationType::MEMBER_OF:
+            case css::accessibility::AccessibleRelationType::NODE_CHILD_OF:
+            case css::accessibility::AccessibleRelationType::SUB_WINDOW_OF:
+                // GTK has no equivalent for these
+                continue;
+            default:
+                assert(false && "Unhandled relation type");
+        }
+
+        gtk_accessible_reset_relation(GTK_ACCESSIBLE(pLoAccessible),
+                                      GTK_ACCESSIBLE_RELATION_FLOW_TO);
+
+        GList* pTargetObjects = nullptr;
+        for (const css::uno::Reference<css::accessibility::XAccessible>& 
xTargetAcc :
+             aRelation.TargetSet)
+        {
+            LoAccessible* pTargetLOAcc
+                = GtkAccessibleRegistry::getLOAccessible(xTargetAcc, 
pLoAccessible->display);
+            assert(pTargetLOAcc);
+            GObject* pObject = G_OBJECT(pTargetLOAcc);
+            g_object_ref(pObject);
+            pTargetObjects = g_list_append(pTargetObjects, pObject);
+        }
+
+        GValue aValue = G_VALUE_INIT;
+        gtk_accessible_relation_init_value(eGtkRelation, &aValue);
+        g_value_set_pointer(&aValue, pTargetObjects);
+        gtk_accessible_update_relation_value(GTK_ACCESSIBLE(pLoAccessible), 1, 
&eGtkRelation,
+                                             &aValue);
+    }
+}
+
 struct LoAccessibleClass
 {
     GObjectClass parent_class;
@@ -620,6 +688,8 @@ lo_accessible_new(GdkDisplay* pDisplay, GtkAccessible* 
pParent,
 
     applyObjectAttributes(GTK_ACCESSIBLE(ret), xContext);
 
+    applyRelations(ret, xContext);
+
     // set values from XAccessibleValue interface if that's implemented
     css::uno::Reference<css::accessibility::XAccessibleValue> 
xAccessibleValue(xContext,
                                                                                
css::uno::UNO_QUERY);
diff --git a/vcl/unx/gtk4/gtkaccessibleregistry.cxx 
b/vcl/unx/gtk4/gtkaccessibleregistry.cxx
index e29a86bdb648..6288a99f2fed 100644
--- a/vcl/unx/gtk4/gtkaccessibleregistry.cxx
+++ b/vcl/unx/gtk4/gtkaccessibleregistry.cxx
@@ -28,6 +28,22 @@ 
GtkAccessibleRegistry::getLOAccessible(css::uno::Reference<css::accessibility::X
     if (entry != m_aMapping.end())
         return entry->second;
 
+    assert(pDisplay);
+    if (!pParent)
+    {
+        // try to find parent via XAccessible hierarchy; this could be 
problematic
+        // as it could create a separate hierarchy besides the one including 
native
+        // GTK widgets if no object which already has its native parent set 
exists
+        // in the a11y tree path from the root to this object
+        css::uno::Reference<css::accessibility::XAccessibleContext> xContext
+            = xAcc->getAccessibleContext();
+        assert(xContext);
+        css::uno::Reference<css::accessibility::XAccessible> xParent
+            = xContext->getAccessibleParent();
+        pParent = GTK_ACCESSIBLE(getLOAccessible(xParent, pDisplay, nullptr));
+        assert(pParent && "No parent explicitly given and none found via the 
a11y hierarchy");
+    }
+
     // create a new object and remember it in the map
     LoAccessible* pLoAccessible = lo_accessible_new(pDisplay, pParent, xAcc);
     m_aMapping.emplace(xAcc.get(), pLoAccessible);
diff --git a/vcl/unx/gtk4/gtkaccessibleregistry.hxx 
b/vcl/unx/gtk4/gtkaccessibleregistry.hxx
index 390b60c4345d..e2ad1588e8db 100644
--- a/vcl/unx/gtk4/gtkaccessibleregistry.hxx
+++ b/vcl/unx/gtk4/gtkaccessibleregistry.hxx
@@ -24,7 +24,7 @@ private:
 public:
     /** Returns the related LoAccessible* for the XAccessible. Creates a new 
one if none exists yet. */
     static LoAccessible* 
getLOAccessible(css::uno::Reference<css::accessibility::XAccessible> xAcc,
-                                         GdkDisplay* pDisplay, GtkAccessible* 
pParent);
+                                         GdkDisplay* pDisplay, GtkAccessible* 
pParent = nullptr);
     /** Removes the entry for the given XAccessible. */
     static void remove(css::uno::Reference<css::accessibility::XAccessible> 
xAcc);
 };

Reply via email to