From 0fba0df4f62b523e5315aea61393db0b40288e09 Mon Sep 17 00:00:00 2001 From: Michael Weghorn Date: Thu, 14 Jul 2022 08:35:53 +0200 Subject: tdf#149952 gtk3 a11y: Return pos relative to window when requested MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ATK (and AT-SPI) allows to specify 3 different coordinate types [1]: 1) `ATK_XY_SCREEN`: coordinates relative to the screen. 2) `ATK_XY_WINDOW`: coordinates relative to the widget's top-level window. 3) `ATK_XY_PARENT` coordinates relative to the widget's immediate parent. The `XAccessibleComponent` interface provides equivalents for 1) and 3), but not 2), and the gtk3 VCL plugin wasn't really handling 2) so far either. This adds handling for `ATK_XY_WINDOW`. The position in the window is calculated by recursively walking up the a11y hierarchy and summing up the positions relative to the parent, until a window is reached (or there's no parent, or none implementing `AtkComponent`). Also add an explicit check for `ATK_XY_PARENT` if none of the other two constants is used and warn and return if that isn't used either, to cover the case that new constants will be introduced in newer ATK versions. (That check needs to be conditional, because `ATK_XY_PARENT` is only defined from ATK 2.30 on, while our minimum required version is currently 2.28.1.) The coordinates received when testing this with a selected Writer paragraph in Accerciser's IPython console look generally plausible now with this change in place: In [108]: acc.queryComponent().getPosition(pyatspi.component.XY_SCREEN) Out[109]: (1939, 417) In [109]: acc.queryComponent().getPosition(pyatspi.component.XY_WINDOW) Out[110]: (19, 245) In [110]: acc.queryComponent().getPosition(pyatspi.component.XY_PARENT) Out[111]: (19, 113) In [111]: acc.queryText().getCharacterExtents(0, pyatspi.component.XY_SCREEN) Out[112]: (2015, 417, 5, 19) In [112]: acc.queryText().getCharacterExtents(0, pyatspi.component.XY_WINDOW) Out[113]: (95, 245, 5, 19) In [113]: acc.queryText().getCharacterExtents(0, pyatspi.component.XY_PARENT) Out[114]: (76, 0, 5, 19) (Previously, requesting window-relative coordinates gave the same result as coordinates relative to direct parent.) [1] https://docs.gtk.org/atk/enum.CoordType.html Change-Id: Idf8f7d08f9054a8df28d1ee8fccc791a803de045 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/137027 Tested-by: Jenkins Reviewed-by: Michael Weghorn (cherry picked from commit 7b312771d7eb33f7410167e36efdaeca6f540b1c) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/137061 Reviewed-by: Caolán McNamara --- vcl/unx/gtk3/a11y/atkcomponent.cxx | 50 +++++++++++++++++++++++++++++++++++--- vcl/unx/gtk3/a11y/atktext.cxx | 4 +-- 2 files changed, 49 insertions(+), 5 deletions(-) (limited to 'vcl') diff --git a/vcl/unx/gtk3/a11y/atkcomponent.cxx b/vcl/unx/gtk3/a11y/atkcomponent.cxx index 21301d4da6bb..154f0117f1bc 100644 --- a/vcl/unx/gtk3/a11y/atkcomponent.cxx +++ b/vcl/unx/gtk3/a11y/atkcomponent.cxx @@ -19,6 +19,7 @@ #include "atkwrapper.hxx" #include +#include #include using namespace ::com::sun::star; @@ -51,15 +52,41 @@ static css::uno::Reference return css::uno::Reference(); } +static awt::Point +lcl_getLocationInWindow(AtkComponent* pAtkComponent, + css::uno::Reference const& xComponent) +{ + // calculate position in window by adding the component's position in the parent + // to the parent's position in the window (unless parent is a window itself) + awt::Point aPos = xComponent->getLocation(); + AtkObject* pParent = atk_object_get_parent(ATK_OBJECT(pAtkComponent)); + if (ATK_IS_COMPONENT(pParent) && pParent->role != AtkRole::ATK_ROLE_DIALOG + && pParent->role != AtkRole::ATK_ROLE_FILE_CHOOSER + && pParent->role != AtkRole::ATK_ROLE_FRAME + && pParent->role != AtkRole::ATK_ROLE_WINDOW) + { + int nX; + int nY; + atk_component_get_extents(ATK_COMPONENT(pParent), &nX, &nY, nullptr, nullptr, ATK_XY_WINDOW); + aPos.X += nX; + aPos.Y += nY; + } + + return aPos; +} + /*****************************************************************************/ static awt::Point -translatePoint( css::uno::Reference const & pComponent, +translatePoint( AtkComponent* pAtkComponent, + css::uno::Reference const & pComponent, gint x, gint y, AtkCoordType t) { awt::Point aOrigin( 0, 0 ); if( t == ATK_XY_SCREEN ) aOrigin = pComponent->getLocationOnScreen(); + else if (t == ATK_XY_WINDOW) + aOrigin = lcl_getLocationInWindow(pAtkComponent, pComponent); return awt::Point( x - aOrigin.X, y - aOrigin.Y ); } @@ -111,7 +138,8 @@ component_wrapper_contains (AtkComponent *component, css::uno::Reference pComponent = getComponent(obj); if( pComponent.is() ) - return pComponent->containsPoint( translatePoint( pComponent, x, y, coord_type ) ); + return pComponent->containsPoint( + translatePoint(component, pComponent, x, y, coord_type)); } catch( const uno::Exception & ) { @@ -142,7 +170,7 @@ component_wrapper_ref_accessible_at_point (AtkComponent *component, if( pComponent.is() ) { uno::Reference< accessibility::XAccessible > xAccessible = pComponent->getAccessibleAtPoint( - translatePoint( pComponent, x, y, coord_type ) ); + translatePoint(component, pComponent, x, y, coord_type)); return atk_object_wrapper_ref( xAccessible ); } } @@ -182,8 +210,24 @@ component_wrapper_get_position (AtkComponent *component, if( coord_type == ATK_XY_SCREEN ) aPos = pComponent->getLocationOnScreen(); + else if (coord_type == ATK_XY_WINDOW) + aPos = lcl_getLocationInWindow(component, pComponent); +#if ATK_CHECK_VERSION(2, 30, 0) + else if (coord_type == ATK_XY_PARENT) +#else + // ATK_XY_PARENT added in ATK 2.30, so can't use the constant here else +#endif aPos = pComponent->getLocation(); +#if ATK_CHECK_VERSION(2, 30, 0) + else + { + SAL_WARN("vcl.gtk", + "component_wrapper_get_position called with unknown AtkCoordType " + << coord_type); + return; + } +#endif *x = aPos.X; *y = aPos.Y; diff --git a/vcl/unx/gtk3/a11y/atktext.cxx b/vcl/unx/gtk3/a11y/atktext.cxx index 713b03325518..ecbb6cd6ed60 100644 --- a/vcl/unx/gtk3/a11y/atktext.cxx +++ b/vcl/unx/gtk3/a11y/atktext.cxx @@ -666,7 +666,7 @@ text_wrapper_get_character_extents( AtkText *text, gint origin_x = 0; gint origin_y = 0; - if( coords == ATK_XY_SCREEN ) + if (coords == ATK_XY_SCREEN || coords == ATK_XY_WINDOW) { g_return_if_fail( ATK_IS_COMPONENT( text ) ); SAL_WNODEPRECATED_DECLARATIONS_PUSH @@ -717,7 +717,7 @@ text_wrapper_get_offset_at_point (AtkText *text, gint origin_x = 0; gint origin_y = 0; - if( coords == ATK_XY_SCREEN ) + if (coords == ATK_XY_SCREEN || coords == ATK_XY_WINDOW) { g_return_val_if_fail( ATK_IS_COMPONENT( text ), -1 ); SAL_WNODEPRECATED_DECLARATIONS_PUSH -- cgit