diff options
author | Michael Weghorn <m.weghorn@posteo.de> | 2024-04-30 13:06:15 +0200 |
---|---|---|
committer | Michael Weghorn <m.weghorn@posteo.de> | 2024-04-30 17:36:44 +0200 |
commit | 4cec279cf2bf55d33e0eb6c92348aa883d7593ff (patch) | |
tree | 56b89b6cdd7891f0f7ab352be222d56cc5bd3d0f /vcl/unx | |
parent | 63a62abae01e7beb15d1e7cb12c469b42253bf1b (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.cxx | 70 | ||||
-rw-r--r-- | vcl/unx/gtk4/gtkaccessibleregistry.cxx | 16 | ||||
-rw-r--r-- | vcl/unx/gtk4/gtkaccessibleregistry.hxx | 2 |
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); }; |