vcl/unx/gtk3/a11y/atkcomponent.cxx |   50 ++++++++++++++++++++++++++++++++++---
 vcl/unx/gtk3/a11y/atktext.cxx      |    4 +-
 2 files changed, 49 insertions(+), 5 deletions(-)

New commits:
commit 7b312771d7eb33f7410167e36efdaeca6f540b1c
Author:     Michael Weghorn <m.wegh...@posteo.de>
AuthorDate: Thu Jul 14 08:35:53 2022 +0200
Commit:     Michael Weghorn <m.wegh...@posteo.de>
CommitDate: Fri Jul 15 13:21:00 2022 +0200

    tdf#149952 gtk3 a11y: Return pos relative to window when requested
    
    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 <m.wegh...@posteo.de>

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 <com/sun/star/accessibility/XAccessibleComponent.hpp>
+#include <sal/log.hxx>
 #include <gtk/gtk.h>
 
 using namespace ::com::sun::star;
@@ -51,15 +52,41 @@ static 
css::uno::Reference<css::accessibility::XAccessibleComponent>
     return css::uno::Reference<css::accessibility::XAccessibleComponent>();
 }
 
+static awt::Point
+lcl_getLocationInWindow(AtkComponent* pAtkComponent,
+                        
css::uno::Reference<accessibility::XAccessibleComponent> 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<accessibility::XAccessibleComponent> const 
& pComponent,
+translatePoint( AtkComponent* pAtkComponent,
+                css::uno::Reference<accessibility::XAccessibleComponent> 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<css::accessibility::XAccessibleComponent> 
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 aee5c5ef5db0..b898538095cf 100644
--- a/vcl/unx/gtk3/a11y/atktext.cxx
+++ b/vcl/unx/gtk3/a11y/atktext.cxx
@@ -648,7 +648,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 ) );
                 gint nWidth = -1;
@@ -699,7 +699,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 );
                 gint nWidth = -1;

Reply via email to