sw/source/core/access/accselectionhelper.cxx | 51 +++++++++++++++++++-------- 1 file changed, 36 insertions(+), 15 deletions(-)
New commits: commit 65a5a4772af919e71a1fe7a752e622ec4cdba7ad Author: Michael Weghorn <m.wegh...@posteo.de> AuthorDate: Tue Jan 14 12:11:46 2025 +0100 Commit: Michael Weghorn <m.wegh...@posteo.de> CommitDate: Wed Jan 15 10:10:12 2025 +0100 tdf#163335 sw a11y: Handle cursor selection consistently Fix the issue described in more detail in Change-Id: I874ad4a7a9faf79492967c943517d83df5f47e75 Author: Michael Weghorn <m.wegh...@posteo.de> Date: Tue Jan 14 11:32:25 2025 +0100 tdf#163335 qt a11y: Work around broken sw selection handling : > SwAccessibleSelectionHelper::getSelectedAccessibleChildCount > returns the count of paragraphs that are (partially) selected > by the text cursor if no other objects are selected. > However, SwAccessibleSelectionHelper::getSelectedAccessibleChild > doesn't take these into account and therefore throws the > IndexOutOfBoundsException when called with any index. Sample steps to reproduce with the gtk3 VCL plugin: 1) have a Writer doc with 3 paragraphs 2) in Accerciser, select the document in the a11y tree 3) interactively query information in Accerciser's IPython console: In [5]: sel = acc.get_selection_iface() In [6]: sel.get_n_selected_children() Out[6]: 2 In [7]: sel.get_selected_child(0) In [8]: sel.get_selected_child(1) The last 2 lines trigger ** (soffice:238967): WARNING **: 12:00:10.723: Exception in getSelectedAccessibleChild() in `selection_ref_selection`. Fix this by aligning the logic in SwAccessibleSelectionHelper::getSelectedAccessibleChild with the one in SwAccessibleSelectionHelper::getSelectedAccessibleChildCount and take the paragraphs that have cursor selection into account there as well. (This is also consistent with the fact that they have the SELECTED a11y state set.) With that in place, the selected paragraphs can now be queried in Accerciser: In [10]: sel = acc.get_selection_iface() In [11]: sel.get_n_selected_children() Out[11]: 2 In [12]: sel.get_selected_child(0) Out[12]: <Atspi.Accessible object at 0x7f0405596a40 (AtspiAccessible at 0x402b7380)> In [13]: sel.get_selected_child(1) Out[13]: <Atspi.Accessible object at 0x7f040557a100 (AtspiAccessible at 0x402b86d0)> In [14]: sel.get_selected_child(1).role Out[14]: <enum ATSPI_ROLE_PARAGRAPH of type Atspi.Role> But querying an invalid index still gives a `None` result as expected: In [16]: sel.get_selected_child(2) In [17]: sel.get_selected_child(2) == None Out[17]: True (Behavior with qt6 VCL plugin is basically the same.) Change-Id: Ic8cf462ae19e494b899643db1709f5075460d287 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/180231 Tested-by: Jenkins Reviewed-by: Michael Weghorn <m.wegh...@posteo.de> diff --git a/sw/source/core/access/accselectionhelper.cxx b/sw/source/core/access/accselectionhelper.cxx index 956ec75ccacd..0a77c606970d 100644 --- a/sw/source/core/access/accselectionhelper.cxx +++ b/sw/source/core/access/accselectionhelper.cxx @@ -274,27 +274,48 @@ Reference<XAccessible> SwAccessibleSelectionHelper::getSelectedAccessibleChild( } else { + std::list<SwAccessibleChild> aChildren; + m_rContext.GetChildren(*(m_rContext.GetMap()), aChildren); + const size_t nSelObjs = pFEShell->GetSelectedObjCount(); - if( 0 == nSelObjs || o3tl::make_unsigned(nSelectedChildIndex) >= nSelObjs ) - throwIndexOutOfBoundsException(); + if (nSelObjs > 0) + { + if (o3tl::make_unsigned(nSelectedChildIndex) >= nSelObjs) + throwIndexOutOfBoundsException(); - std::list< SwAccessibleChild > aChildren; - m_rContext.GetChildren( *(m_rContext.GetMap()), aChildren ); + for( const SwAccessibleChild& rChild : aChildren ) + { + if( rChild.GetDrawObject() && !rChild.GetSwFrame() && + SwAccessibleFrame::GetParent(rChild, m_rContext.IsInPagePreview()) == + m_rContext.GetFrame() && + pFEShell->IsObjSelected( *rChild.GetDrawObject() ) ) + { + if( 0 == nSelectedChildIndex ) + aChild = rChild; + else + --nSelectedChildIndex; + } + if (aChild.IsValid()) + break; + } + } - for( const SwAccessibleChild& rChild : aChildren ) + // check children selected by the selection cursor + if (!aChild.IsValid()) { - if( rChild.GetDrawObject() && !rChild.GetSwFrame() && - SwAccessibleFrame::GetParent(rChild, m_rContext.IsInPagePreview()) == - m_rContext.GetFrame() && - pFEShell->IsObjSelected( *rChild.GetDrawObject() ) ) + sal_Int64 nCount = 0; + for (const SwAccessibleChild& rChild : aChildren) { - if( 0 == nSelectedChildIndex ) - aChild = rChild; - else - --nSelectedChildIndex; + if (lcl_getSelectedState(rChild, &m_rContext, m_rContext.GetMap())) + { + if (nCount == nSelectedChildIndex) + { + aChild = rChild; + break; + } + nCount++; + } } - if (aChild.IsValid()) - break; } }