sw/inc/crsrsh.hxx | 3 ++- sw/inc/formatcontentcontrol.hxx | 7 +++++++ sw/inc/viscrs.hxx | 2 ++ sw/qa/core/txtnode/txtnode.cxx | 21 +++++++++++++++++++++ sw/source/core/crsr/contentcontrolbutton.cxx | 4 +++- sw/source/core/crsr/crstrvl.cxx | 8 ++++---- sw/source/core/crsr/viscrs.cxx | 5 +++++ sw/source/core/inc/contentcontrolbutton.hxx | 2 ++ sw/source/core/txtnode/attrcontentcontrol.cxx | 12 ++++++++++++ sw/source/uibase/docvw/edtwin.cxx | 21 +++++++++++++++++++++ 10 files changed, 79 insertions(+), 6 deletions(-)
New commits: commit 70ad0d597df504aeda5fc4de2e84ca537e03c0b4 Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Fri Jul 15 11:37:58 2022 +0200 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Tue Jul 19 14:48:02 2022 +0200 sw content control, dropdown: allow selecting via the keyboard It was not possible to select an entry from a content control dropdown using the keyboard, which breaks accessibility. This had the benefit that the mouse handler code could contain the popup start calls, but Word can do this with alt-down arrow, so make sense to add it on our side as well. Fix the problem by adding SwContentControl::ShouldOpenPopup(), which knows that dropdowns want a popup with alt-down, and then connecting SwEditWin::KeyInput() to it. Date content controls probably will want something similar, but that's not yet done in this commit. Change-Id: I6853d3a1661e4d826b96b1b5cb938909875af2ad Reviewed-on: https://gerrit.libreoffice.org/c/core/+/137102 Reviewed-by: Miklos Vajna <vmik...@collabora.com> Tested-by: Jenkins Signed-off-by: Xisco Fauli <xiscofa...@libreoffice.org> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/137220 diff --git a/sw/inc/crsrsh.hxx b/sw/inc/crsrsh.hxx index 2dd27529810a..fb6e00106691 100644 --- a/sw/inc/crsrsh.hxx +++ b/sw/inc/crsrsh.hxx @@ -59,6 +59,7 @@ class SwBlockCursor; class SwPostItField; class SwTextField; class SwTextFootnote; +class SwTextContentControl; namespace i18nutil { struct SearchOptions2; @@ -717,7 +718,7 @@ public: const bool bIncludeInputFieldAtStart ); SwField* GetCurField( const bool bIncludeInputFieldAtStart = false ) const; bool CursorInsideInputField() const; - bool CursorInsideContentControl() const; + SwTextContentControl* CursorInsideContentControl() const; static bool PosInsideInputField( const SwPosition& rPos ); bool DocPtInsideInputField( const Point& rDocPt ) const; static sal_Int32 StartOfInputFieldAtPos( const SwPosition& rPos ); diff --git a/sw/inc/formatcontentcontrol.hxx b/sw/inc/formatcontentcontrol.hxx index 9078b1c81314..00f981308cfd 100644 --- a/sw/inc/formatcontentcontrol.hxx +++ b/sw/inc/formatcontentcontrol.hxx @@ -29,6 +29,10 @@ #include "calbck.hxx" #include "swdllapi.h" +namespace vcl +{ +class KeyCode; +} class SwContentControl; class SwTextContentControl; class SwTextNode; @@ -275,6 +279,9 @@ public: /// Should this character (during key input) interact with the content control? bool IsInteractingCharacter(sal_Unicode cCh); + /// Given rKeyCode as a keyboard event, should a popup be opened for this content control? + bool ShouldOpenPopup(const vcl::KeyCode& rKeyCode); + virtual void dumpAsXml(xmlTextWriterPtr pWriter) const; void SetDataBindingPrefixMappings(const OUString& rDataBindingPrefixMappings) diff --git a/sw/inc/viscrs.hxx b/sw/inc/viscrs.hxx index c5d97fc023c1..6126f5ffa0c0 100644 --- a/sw/inc/viscrs.hxx +++ b/sw/inc/viscrs.hxx @@ -115,6 +115,8 @@ public: void SetShowContentControlOverlay(const bool bShow) { m_bShowContentControlOverlay = bShow; } + VclPtr<SwContentControlButton> GetContentControlButton() const; + const SwCursorShell* GetShell() const { return m_pCursorShell; } // check current MapMode of the shell and set possibly the static members. // Optional set the parameters pX, pY diff --git a/sw/qa/core/txtnode/txtnode.cxx b/sw/qa/core/txtnode/txtnode.cxx index 55fece606c4c..7791cf82bbc3 100644 --- a/sw/qa/core/txtnode/txtnode.cxx +++ b/sw/qa/core/txtnode/txtnode.cxx @@ -249,6 +249,27 @@ CPPUNIT_TEST_FIXTURE(SwCoreTxtnodeTest, testCheckboxContentControlKeyboard) CPPUNIT_ASSERT(pContentControl->GetChecked()); } +CPPUNIT_TEST_FIXTURE(SwCoreTxtnodeTest, testDropdownContentControlKeyboard) +{ + // Given an already selected dropdown content control: + SwDoc* pDoc = createSwDoc(); + SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); + pWrtShell->InsertContentControl(SwContentControlType::DROP_DOWN_LIST); + + // When checking if alt-down should open a popup: + SwTextContentControl* pTextContentControl = pWrtShell->CursorInsideContentControl(); + auto& rFormatContentControl + = static_cast<SwFormatContentControl&>(pTextContentControl->GetAttr()); + std::shared_ptr<SwContentControl> pContentControl = rFormatContentControl.GetContentControl(); + vcl::KeyCode aKeyCode(KEY_DOWN, KEY_MOD2); + bool bShouldOpen = pContentControl->ShouldOpenPopup(aKeyCode); + + // Then make sure that the answer is yes for dropdowns: + // Without the accompanying fix in place, this test would have failed, the dropdown popup was + // mouse-only. + CPPUNIT_ASSERT(bShouldOpen); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/crsr/contentcontrolbutton.cxx b/sw/source/core/crsr/contentcontrolbutton.cxx index 34cbd38e663a..108a6fe7eade 100644 --- a/sw/source/core/crsr/contentcontrolbutton.cxx +++ b/sw/source/core/crsr/contentcontrolbutton.cxx @@ -79,7 +79,9 @@ void SwContentControlButton::CalcPosAndSize(const SwRect& rPortionPaintArea) } } -void SwContentControlButton::MouseButtonDown(const MouseEvent&) +void SwContentControlButton::MouseButtonDown(const MouseEvent&) { StartPopup(); } + +void SwContentControlButton::StartPopup() { LaunchPopup(); Invalidate(); diff --git a/sw/source/core/crsr/crstrvl.cxx b/sw/source/core/crsr/crstrvl.cxx index ee12399323ac..adf7617428d4 100644 --- a/sw/source/core/crsr/crstrvl.cxx +++ b/sw/source/core/crsr/crstrvl.cxx @@ -1002,7 +1002,7 @@ bool SwCursorShell::CursorInsideInputField() const return false; } -bool SwCursorShell::CursorInsideContentControl() const +SwTextContentControl* SwCursorShell::CursorInsideContentControl() const { for (SwPaM& rCursor : GetCursor()->GetRingContainer()) { @@ -1014,13 +1014,13 @@ bool SwCursorShell::CursorInsideContentControl() const } sal_Int32 nIndex = pStart->nContent.GetIndex(); - if (pTextNode->GetTextAttrAt(nIndex, RES_TXTATR_CONTENTCONTROL, SwTextNode::PARENT)) + if (SwTextAttr* pAttr = pTextNode->GetTextAttrAt(nIndex, RES_TXTATR_CONTENTCONTROL, SwTextNode::PARENT)) { - return true; + return static_txtattr_cast<SwTextContentControl*>(pAttr); } } - return false; + return nullptr; } bool SwCursorShell::PosInsideInputField( const SwPosition& rPos ) diff --git a/sw/source/core/crsr/viscrs.cxx b/sw/source/core/crsr/viscrs.cxx index ba3d49893233..86878ad50c82 100644 --- a/sw/source/core/crsr/viscrs.cxx +++ b/sw/source/core/crsr/viscrs.cxx @@ -798,6 +798,11 @@ void SwSelPaintRects::HighlightContentControl() } } +VclPtr<SwContentControlButton> SwSelPaintRects::GetContentControlButton() const +{ + return m_pContentControlButton; +} + void SwSelPaintRects::Invalidate( const SwRect& rRect ) { size_type nSz = size(); diff --git a/sw/source/core/inc/contentcontrolbutton.hxx b/sw/source/core/inc/contentcontrolbutton.hxx index a921680ed7d4..cd63bddd4e69 100644 --- a/sw/source/core/inc/contentcontrolbutton.hxx +++ b/sw/source/core/inc/contentcontrolbutton.hxx @@ -29,6 +29,8 @@ public: virtual void MouseButtonDown(const MouseEvent& rMEvt) override; DECL_LINK(PopupModeEndHdl, weld::Popover&, void); + /// Shared MouseButtonDown() and KeyInput() code. + void StartPopup(); virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override; virtual WindowHitTest ImplHitTest(const Point& rFramePos) override; diff --git a/sw/source/core/txtnode/attrcontentcontrol.cxx b/sw/source/core/txtnode/attrcontentcontrol.cxx index 1f3d26f6c2b6..b90998bdcc65 100644 --- a/sw/source/core/txtnode/attrcontentcontrol.cxx +++ b/sw/source/core/txtnode/attrcontentcontrol.cxx @@ -25,6 +25,7 @@ #include <comphelper/propertyvalue.hxx> #include <comphelper/sequenceashashmap.hxx> #include <svl/numformat.hxx> +#include <vcl/keycod.hxx> #include <ndtxt.hxx> #include <textcontentcontrol.hxx> @@ -311,6 +312,17 @@ bool SwContentControl::IsInteractingCharacter(sal_Unicode cCh) return false; } +bool SwContentControl::ShouldOpenPopup(const vcl::KeyCode& rKeyCode) +{ + if (HasListItems()) + { + // Alt-down opens the popup. + return rKeyCode.IsMod2() && rKeyCode.GetCode() == KEY_DOWN; + } + + return false; +} + void SwContentControl::dumpAsXml(xmlTextWriterPtr pWriter) const { (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwContentControl")); diff --git a/sw/source/uibase/docvw/edtwin.cxx b/sw/source/uibase/docvw/edtwin.cxx index b81ac632a0e5..3e117955436c 100644 --- a/sw/source/uibase/docvw/edtwin.cxx +++ b/sw/source/uibase/docvw/edtwin.cxx @@ -151,6 +151,7 @@ #include <txtfrm.hxx> #include <strings.hrc> #include <textcontentcontrol.hxx> +#include <contentcontrolbutton.hxx> using namespace sw::mark; using namespace ::com::sun::star; @@ -1523,6 +1524,26 @@ void SwEditWin::KeyInput(const KeyEvent &rKEvt) return; } + if (SwTextContentControl* pTextContentControl = rSh.CursorInsideContentControl()) + { + // Check if this combination of rKeyCode and pTextContentControl should open a popup. + const SwFormatContentControl& rFormatContentControl = pTextContentControl->GetContentControl(); + std::shared_ptr<SwContentControl> pContentControl = rFormatContentControl.GetContentControl(); + if (pContentControl->ShouldOpenPopup(rKeyCode)) + { + SwShellCursor* pCursor = rSh.GetCursor_(); + if (pCursor) + { + VclPtr<SwContentControlButton> pContentControlButton = pCursor->GetContentControlButton(); + if (pContentControlButton) + { + pContentControlButton->StartPopup(); + return; + } + } + } + } + const SwFrameFormat* pFlyFormat = rSh.GetFlyFrameFormat(); if( pFlyFormat ) {