summaryrefslogtreecommitdiff
path: root/vcl/unx
diff options
context:
space:
mode:
authorMichael Weghorn <m.weghorn@posteo.de>2024-04-30 13:06:15 +0200
committerMichael Weghorn <m.weghorn@posteo.de>2024-04-30 17:36:44 +0200
commit4cec279cf2bf55d33e0eb6c92348aa883d7593ff (patch)
tree56b89b6cdd7891f0f7ab352be222d56cc5bd3d0f /vcl/unx
parent63a62abae01e7beb15d1e7cb12c469b42253bf1b (diff)
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.weghorn@posteo.de>
Diffstat (limited to 'vcl/unx')
-rw-r--r--vcl/unx/gtk4/a11y.cxx70
-rw-r--r--vcl/unx/gtk4/gtkaccessibleregistry.cxx16
-rw-r--r--vcl/unx/gtk4/gtkaccessibleregistry.hxx2
3 files changed, 87 insertions, 1 deletions
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);
};