winaccessibility/source/UAccCOM/AccTextBase.cxx |   16 ++++-
 winaccessibility/source/UAccCOM/MAccessible.cxx |   76 ++++++++++--------------
 2 files changed, 48 insertions(+), 44 deletions(-)

New commits:
commit d1d07992a89ba503f1d457a8f79926063f4d3f9c
Author:     Michael Weghorn <[email protected]>
AuthorDate: Tue Aug 8 19:43:09 2023 +0100
Commit:     Michael Weghorn <[email protected]>
CommitDate: Wed Aug 9 06:14:52 2023 +0200

    tdf#156679 wina11y: Convert screen to local coords as needed
    
    When `AccTextBase::get_offsetAtPoint` gets called with
    screen coordinates, convert them to local coordinates
    within the text object first, because that is what
    `XAccessibleText::getIndexAtPoint` expects.
    
    Not doing so resulted in NVDA failing to create
    a TextInfo object in the mouse event handler [1]
    when hovering over a Calc cell containing text,
    because the method would always return an offset
    of -1.
    
    With this change in place, NVDA now announces the
    text when hovering over the text and mouse tracking
    is enabled in NVDA (which is the case by default).
    Other than with Microsoft Excel, the text is only
    announced when the mouse is actually over the text,
    not over free space in the cell, which might be
    because Excel uses UIA and the UIA equivalent,
    `ITextProvider::RangeFromPoint` [2] shall also return
    the index of the closest character when the point
    itself is not over the actual bounds of any
    character.
    
    [1] 
https://github.com/nvaccess/nvda/blob/a198c9b5f27e47ff2830f77c833eec584078dfd8/source/NVDAObjects/__init__.py#L1209
    [2] 
https://learn.microsoft.com/en-us/windows/win32/api/uiautomationcore/nf-uiautomationcore-itextprovider-rangefrompoint
    
    Change-Id: I1e4ab2dd3dace5fea1de2eef67a91fe3c31218a9
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/155492
    Tested-by: Jenkins
    Reviewed-by: Michael Weghorn <[email protected]>

diff --git a/winaccessibility/source/UAccCOM/AccTextBase.cxx 
b/winaccessibility/source/UAccCOM/AccTextBase.cxx
index 183ec3467655..b70b13c0a980 100644
--- a/winaccessibility/source/UAccCOM/AccTextBase.cxx
+++ b/winaccessibility/source/UAccCOM/AccTextBase.cxx
@@ -417,7 +417,7 @@ COM_DECLSPEC_NOTHROW STDMETHODIMP 
CAccTextBase::get_nSelections(long * nSelectio
    * @param offset Variant to accept offset.
    * @return Result.
 */
-COM_DECLSPEC_NOTHROW STDMETHODIMP CAccTextBase::get_offsetAtPoint(long x, long 
y, IA2CoordinateType, long * offset)
+COM_DECLSPEC_NOTHROW STDMETHODIMP CAccTextBase::get_offsetAtPoint(long x, long 
y, IA2CoordinateType coordType, long * offset)
 {
     SolarMutexGuard g;
 
@@ -432,6 +432,20 @@ COM_DECLSPEC_NOTHROW STDMETHODIMP 
CAccTextBase::get_offsetAtPoint(long x, long y
     css::awt::Point point;
     point.X = x;
     point.Y = y;
+
+    if (coordType == IA2_COORDTYPE_SCREEN_RELATIVE)
+    {
+        // convert from screen to local coordinates
+        Reference<XAccessibleContext> xContext = 
pUNOInterface->getAccessibleContext();
+        Reference<XAccessibleComponent> xComponent(xContext, UNO_QUERY);
+        if (!xComponent.is())
+            return S_FALSE;
+
+        css::awt::Point aObjectPos = xComponent->getLocationOnScreen();
+        point.X -= aObjectPos.X;
+        point.Y -= aObjectPos.Y;
+    }
+
     *offset = GetXInterface()->getIndexAtPoint(point);
     return S_OK;
 
commit 39302875c27d4cf4246bb7520ed90abcf0af777a
Author:     Michael Weghorn <[email protected]>
AuthorDate: Tue Aug 8 18:27:16 2023 +0100
Commit:     Michael Weghorn <[email protected]>
CommitDate: Wed Aug 9 06:14:44 2023 +0200

    tdf#156679 wina11y: Implement accHitTest via UNO equivalent
    
    Instead of manually iterating over the children
    and checking whether the given position is in the location
    that they're in in `CMAccessible::accHitTest`, use
    `XAccessibleComponent::XAccessibleComponent`, which
    provides exactly the functionality that's needed.
    (This is similar to what the Qt-based VCL plugins
    on Linux do, s. `QtAccessibleWidget::childAt`.)
    
    This also drops the need to limit this to objects
    that have at most a certain amount of children
    for performance reasons (previously 256) and thus
    makes this work e.g. also to identify a Calc cell
    that the mouse pointer is currently over while previously
    the document was returned, as could be seen also in
    NVDA's Python console:
    
    1) start NVDA
    2) hover over a Calc cell
    3) press NVDA+Ctrl+Z to capture snapshot variables
    4) check what NVDA identifies as the mouse object
       via the Python Console:
    
    Before:
    
        >>> mouse
        <NVDAObjects.IAccessible.IAccessible object at 0x00FB9910>
        >>> mouse.name
        'Untitled 1 - LibreOfficeDev Spreadsheets'
        >>> mouse.role
        <Role.DOCUMENT: 52>
    
    With this change in place:
    
        >>> mouse
        
<NVDAObjects.Dynamic_SymphonyIATableCellEditableTextWithAutoSelectDetectionIAccessible
 object at 0x0774DD10>
        >>> mouse.name
        >>> mouse.role
        <Role.TABLECELL: 29>
    
    The cell's text still isn't announced by NVDA even
    with mouse tracking enabled, but that's another issue.
    
    Change-Id: Ib821020cef6303ab786c4c3fc3ccd917398214f1
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/155491
    Tested-by: Jenkins
    Reviewed-by: Michael Weghorn <[email protected]>

diff --git a/winaccessibility/source/UAccCOM/MAccessible.cxx 
b/winaccessibility/source/UAccCOM/MAccessible.cxx
index 7ca1a8eca836..1a5ec8c4efc1 100644
--- a/winaccessibility/source/UAccCOM/MAccessible.cxx
+++ b/winaccessibility/source/UAccCOM/MAccessible.cxx
@@ -974,48 +974,38 @@ COM_DECLSPEC_NOTHROW STDMETHODIMP 
CMAccessible::accHitTest(long xLeft, long yTop
     if (!pvarChild)
         return E_INVALIDARG;
 
-    try {
-        long x, y, w, h;
-        VARIANT varSelf;
-        VariantInit(&varSelf);
-        varSelf.vt = VT_I4;
-        varSelf.lVal = CHILDID_SELF;
-        accLocation(&x,&y,&w,&h,varSelf);
-        if( (x < xLeft && (x + w) >xLeft) && (y < yTop && (y + h) >yTop) )
-        {
-            sal_Int64 i, nCount;
-            pvarChild->vt = VT_EMPTY;
-            Reference< XAccessibleContext > pRContext = 
GetContextByXAcc(m_xAccessible.get());
-            nCount = pRContext->getAccessibleChildCount();
-            if(nCount > 256)
-                return E_FAIL;
-            IMAccessible* child = nullptr;
-            for( i = 0; i<nCount; i++)
-            {
+    try
+    {
+        pvarChild->vt = VT_EMPTY;
 
-                child = GetChildInterface(i + 1);
-                if(child && child->accHitTest(xLeft,yTop,pvarChild) == S_OK)
-                    break;
-            }
+        Reference<XAccessibleContext> xContext = 
GetContextByXAcc(m_xAccessible.get());
+        Reference<XAccessibleComponent> xComponent(xContext, UNO_QUERY);
+        if (!xComponent.is())
+            return S_FALSE;
 
-            if(pvarChild->vt == VT_DISPATCH)
-                return S_OK;
+        // convert from screen to object-local coordinates
+        css::awt::Point aTopLeft = xComponent->getLocationOnScreen();
+        css::awt::Point aPoint(xLeft - aTopLeft.X, yTop - aTopLeft.Y);
 
-            if( i < nCount)
-            {
-                pvarChild->vt = VT_DISPATCH;
-                pvarChild->pdispVal = child;
-                child->AddRef();
-            }
-            else
-            {
-                pvarChild->vt = VT_I4;
-                pvarChild->lVal = CHILDID_SELF;
-            }
-            return S_OK;
+        Reference<XAccessible> xAccAtPoint = 
xComponent->getAccessibleAtPoint(aPoint);
+        if (!xAccAtPoint.is())
+            return S_FALSE;
+
+        IAccessible* pRet = nullptr;
+        bool bHaveIAccessible = 
get_IAccessibleFromXAccessible(xAccAtPoint.get(), &pRet);
+        if (!bHaveIAccessible)
+        {
+            g_pAccObjectManager->InsertAccObj(xAccAtPoint.get(), 
m_xAccessible.get(), m_hwnd);
+            bHaveIAccessible = 
get_IAccessibleFromXAccessible(xAccAtPoint.get(), &pRet);
         }
-        return S_FALSE;
+        if (!bHaveIAccessible)
+            return S_FALSE;
 
+        pvarChild->vt = VT_DISPATCH;
+        pvarChild->pdispVal = pRet;
+        pRet->AddRef();
+
+        return S_OK;
     } catch(...) { return E_FAIL; }
 }
 
commit 0044ec90b3182a25740b53fb8679d83f0dc8f369
Author:     Michael Weghorn <[email protected]>
AuthorDate: Tue Aug 8 13:12:49 2023 +0100
Commit:     Michael Weghorn <[email protected]>
CommitDate: Wed Aug 9 06:14:38 2023 +0200

    wina11y: Move these 2 checks out of try/catch
    
    Change-Id: I4ae7527d9b5047d46aab44a1ab6fa42a99c43a14
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/155490
    Tested-by: Jenkins
    Reviewed-by: Michael Weghorn <[email protected]>

diff --git a/winaccessibility/source/UAccCOM/MAccessible.cxx 
b/winaccessibility/source/UAccCOM/MAccessible.cxx
index 091acfeb780c..7ca1a8eca836 100644
--- a/winaccessibility/source/UAccCOM/MAccessible.cxx
+++ b/winaccessibility/source/UAccCOM/MAccessible.cxx
@@ -968,13 +968,13 @@ COM_DECLSPEC_NOTHROW STDMETHODIMP 
CMAccessible::accHitTest(long xLeft, long yTop
 {
     SolarMutexGuard g;
 
+    if (m_isDestroy)
+        return S_FALSE;
+
+    if (!pvarChild)
+        return E_INVALIDARG;
+
     try {
-        if (m_isDestroy) return S_FALSE;
-        // #CHECK#
-        if(pvarChild == nullptr)
-        {
-            return E_INVALIDARG;
-        }
         long x, y, w, h;
         VARIANT varSelf;
         VariantInit(&varSelf);

Reply via email to