sw/inc/viscrs.hxx | 5 ++ sw/qa/core/unocore/unocore.cxx | 12 +++++ sw/source/core/crsr/crsrsh.cxx | 2 sw/source/core/crsr/viscrs.cxx | 78 ++++++++++++++++++++++++++++++++++++++ sw/source/core/inc/swfont.hxx | 3 + sw/source/core/inc/txttypes.hxx | 1 sw/source/core/text/atrhndl.hxx | 2 sw/source/core/text/atrstck.cxx | 15 +++++++ sw/source/core/text/inftxt.cxx | 1 sw/source/core/text/itratr.cxx | 2 sw/source/core/text/itrform2.cxx | 20 +++++++++ sw/source/core/text/xmldump.cxx | 2 sw/source/core/txtnode/ndtxt.cxx | 1 sw/source/core/txtnode/swfont.cxx | 3 + 14 files changed, 146 insertions(+), 1 deletion(-)
New commits: commit 8fdb2baf5400f7225aa78d950fd53071950877e8 Author: Miklos Vajna <[email protected]> AuthorDate: Wed Apr 6 08:40:31 2022 +0200 Commit: Miklos Vajna <[email protected]> CommitDate: Fri Apr 22 08:31:31 2022 +0200 sw content controls: add overlay to render a border around the text portions - introduce SwSelPaintRects::HighlightContentControl() to create the overlay - use GetTextAttrAt() to enable this when we're inside a content control (not only when the cursor is exactly at the dummy char of the content control, which is at the start) - tested that this works correctly even if the portions inside the content control don't have the same height (similar to the selection overlay) (cherry picked from commit c2999f9f3e0efa04f89055399f2d176c9c95e92d) Change-Id: I1db51cdb1ddc87c45740cd8677de645dc8906697 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133244 Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Miklos Vajna <[email protected]> diff --git a/sw/inc/viscrs.hxx b/sw/inc/viscrs.hxx index 6755b548e8a0..2eb2f787fbc7 100644 --- a/sw/inc/viscrs.hxx +++ b/sw/inc/viscrs.hxx @@ -84,8 +84,11 @@ class SwSelPaintRects : public SwRects bool m_bShowTextInputFieldOverlay; std::unique_ptr<sw::overlay::OverlayRangesOutline> m_pTextInputFieldOverlay; + bool m_bShowContentControlOverlay; + std::unique_ptr<sw::overlay::OverlayRangesOutline> m_pContentControlOverlay; void HighlightInputField(); + void HighlightContentControl(); public: SwSelPaintRects( const SwCursorShell& rCSh ); @@ -108,6 +111,8 @@ public: m_bShowTextInputFieldOverlay = bShow; } + void SetShowContentControlOverlay(const bool bShow) { m_bShowContentControlOverlay = bShow; } + 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/source/core/crsr/crsrsh.cxx b/sw/source/core/crsr/crsrsh.cxx index ae7b2c81133d..4e9f54aab2e8 100644 --- a/sw/source/core/crsr/crsrsh.cxx +++ b/sw/source/core/crsr/crsrsh.cxx @@ -2393,6 +2393,7 @@ void SwCursorShell::ShowCursor() m_bSVCursorVis = true; m_pCurrentCursor->SetShowTextInputFieldOverlay( true ); + m_pCurrentCursor->SetShowContentControlOverlay(true); if (comphelper::LibreOfficeKit::isActive()) { @@ -2413,6 +2414,7 @@ void SwCursorShell::HideCursor() // possibly reverse selected areas!! CurrShell aCurr( this ); m_pCurrentCursor->SetShowTextInputFieldOverlay( false ); + m_pCurrentCursor->SetShowContentControlOverlay(false); m_pVisibleCursor->Hide(); if (comphelper::LibreOfficeKit::isActive()) diff --git a/sw/source/core/crsr/viscrs.cxx b/sw/source/core/crsr/viscrs.cxx index 0501162a2a16..d72289bdbb36 100644 --- a/sw/source/core/crsr/viscrs.cxx +++ b/sw/source/core/crsr/viscrs.cxx @@ -59,6 +59,7 @@ #include <svtools/optionsdrawinglayer.hxx> #include <cellfrm.hxx> #include <wrtsh.hxx> +#include <textcontentcontrol.hxx> // Here static members are defined. They will get changed on alteration of the // MapMode. This is done so that on ShowCursor the same size does not have to be @@ -350,6 +351,7 @@ SwSelPaintRects::SwSelPaintRects( const SwCursorShell& rCSh ) : m_pCursorShell( &rCSh ) #if HAVE_FEATURE_DESKTOP , m_bShowTextInputFieldOverlay(true) + , m_bShowContentControlOverlay(true) #endif { } @@ -368,6 +370,8 @@ void SwSelPaintRects::swapContent(SwSelPaintRects& rSwap) std::swap(m_pCursorOverlay, rSwap.m_pCursorOverlay); std::swap(m_bShowTextInputFieldOverlay, rSwap.m_bShowTextInputFieldOverlay); std::swap(m_pTextInputFieldOverlay, rSwap.m_pTextInputFieldOverlay); + std::swap(m_bShowContentControlOverlay, rSwap.m_bShowContentControlOverlay); + std::swap(m_pContentControlOverlay, rSwap.m_pContentControlOverlay); #endif } @@ -376,6 +380,7 @@ void SwSelPaintRects::Hide() #if HAVE_FEATURE_DESKTOP m_pCursorOverlay.reset(); m_pTextInputFieldOverlay.reset(); + m_pContentControlOverlay.reset(); #endif SwRects::clear(); @@ -466,6 +471,7 @@ void SwSelPaintRects::Show(std::vector<OString>* pSelectionRectangles) } HighlightInputField(); + HighlightContentControl(); #endif // Tiled editing does not expose the draw and writer cursor, it just @@ -625,6 +631,78 @@ void SwSelPaintRects::HighlightInputField() } } +void SwSelPaintRects::HighlightContentControl() +{ + std::vector<basegfx::B2DRange> aContentControlRanges; + + if (m_bShowContentControlOverlay) + { + const SwPosition* pStart = GetShell()->GetCursor()->Start(); + SwTextNode* pTextNode = pStart->nNode.GetNode().GetTextNode(); + SwTextContentControl* pCurContentControlAtCursor = nullptr; + if (pTextNode) + { + // SwTextNode::PARENT because this way we highlight when the cursor is on the right side + // of the dummy character: ideally the end of the range would have the same behavior. + SwTextAttr* pAttr = pTextNode->GetTextAttrAt( + pStart->nContent.GetIndex(), RES_TXTATR_CONTENTCONTROL, SwTextNode::PARENT); + if (pAttr) + { + pCurContentControlAtCursor = static_txtattr_cast<SwTextContentControl*>(pAttr); + } + } + if (pCurContentControlAtCursor) + { + auto pCursorForContentControl = std::make_unique<SwShellCursor>( + *GetShell(), SwPosition(*pTextNode, pCurContentControlAtCursor->GetStart())); + pCursorForContentControl->SetMark(); + pCursorForContentControl->GetMark()->nNode = *pTextNode; + pCursorForContentControl->GetMark()->nContent.Assign( + pTextNode, *(pCurContentControlAtCursor->End())); + + pCursorForContentControl->FillRects(); + SwRects* pRects = pCursorForContentControl.get(); + for (const auto& rRect : *pRects) + { + tools::Rectangle aRect(rRect.SVRect()); + + aContentControlRanges.emplace_back(aRect.Left(), aRect.Top(), aRect.Right() + 1, + aRect.Bottom() + 1); + } + } + } + + if (!aContentControlRanges.empty()) + { + if (m_pContentControlOverlay) + { + m_pContentControlOverlay->setRanges(std::move(aContentControlRanges)); + } + else + { + SdrView* pView = const_cast<SdrView*>(GetShell()->GetDrawView()); + SdrPaintWindow* pCandidate = pView->GetPaintWindow(0); + const rtl::Reference<sdr::overlay::OverlayManager>& xTargetOverlay + = pCandidate->GetOverlayManager(); + + if (xTargetOverlay.is()) + { + // Use the system's highlight color with decreased luminance as highlight color. + Color aHighlight(SvtOptionsDrawinglayer::getHilightColor()); + aHighlight.DecreaseLuminance(128); + + m_pContentControlOverlay.reset(new sw::overlay::OverlayRangesOutline( + aHighlight, std::move(aContentControlRanges))); + xTargetOverlay->add(*m_pContentControlOverlay); + } + } + } + else + { + m_pContentControlOverlay.reset(); + } +} + void SwSelPaintRects::Invalidate( const SwRect& rRect ) { size_type nSz = size(); diff --git a/sw/source/core/txtnode/ndtxt.cxx b/sw/source/core/txtnode/ndtxt.cxx index 4d109a8add1c..e5040c50a1a7 100644 --- a/sw/source/core/txtnode/ndtxt.cxx +++ b/sw/source/core/txtnode/ndtxt.cxx @@ -1715,6 +1715,7 @@ SwTextNode::GetTextAttrAt(sal_Int32 const nIndex, sal_uInt16 const nWhich, || (nWhich == RES_TXTATR_INETFMT) || (nWhich == RES_TXTATR_CJK_RUBY) || (nWhich == RES_TXTATR_UNKNOWN_CONTAINER) + || (nWhich == RES_TXTATR_CONTENTCONTROL) || (nWhich == RES_TXTATR_INPUTFIELD ) ); // "GetTextAttrAt() will give wrong result for this hint!") commit b6f3fc87699c16db2ccc73f992bbcccfb60695bf Author: Miklos Vajna <[email protected]> AuthorDate: Tue Apr 5 10:53:22 2022 +0200 Commit: Miklos Vajna <[email protected]> CommitDate: Fri Apr 22 08:31:17 2022 +0200 sw content controls: add initial layout support - portions inside content controls are not text portions but content control portions - teach SwTextPaintInfo::DrawViewOpt() to paint field shadings for content control portions - teach the attribute stack code about RES_TXTATR_CONTENTCONTROL, so if the whole document is just a content control, then adding text before/after the content control is properly text portions, not content control portions (cherry picked from commit 8c632d8a837cc722c6e7b3b400f6d97edf9f9800) Change-Id: Ia9f955a5f7c7a4fd633899fafa8fc723e7c0d050 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/133243 Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Miklos Vajna <[email protected]> diff --git a/sw/qa/core/unocore/unocore.cxx b/sw/qa/core/unocore/unocore.cxx index 4f9479a673d1..8b2f3eb5f6ec 100644 --- a/sw/qa/core/unocore/unocore.cxx +++ b/sw/qa/core/unocore/unocore.cxx @@ -351,6 +351,18 @@ CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testContentControlTextPortionEnum) uno::Reference<container::XEnumeration> xContentEnum = xContentEnumAccess->createEnumeration(); uno::Reference<text::XTextRange> xContent(xContentEnum->nextElement(), uno::UNO_QUERY); CPPUNIT_ASSERT_EQUAL(OUString("test"), xContent->getString()); + + // Also test the generated layout: + xmlDocUniquePtr pXmlDoc = parseLayoutDump(); + assertXPath(pXmlDoc, "//SwParaPortion/SwLineLayout/SwLinePortion[1]", "type", + "PortionType::Field"); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: PortionType::ContentControl + // - Actual : PortionType::Text + // i.e. SwContentControl generated a plain text portion, not a dedicated content control + // portion. + assertXPath(pXmlDoc, "//SwParaPortion/SwLineLayout/SwLinePortion[2]", "type", + "PortionType::ContentControl"); } CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sw/source/core/inc/swfont.hxx b/sw/source/core/inc/swfont.hxx index a2eedaca5f64..40a4c2e8d66c 100644 --- a/sw/source/core/inc/swfont.hxx +++ b/sw/source/core/inc/swfont.hxx @@ -158,6 +158,7 @@ class SwFont sal_uInt8 m_nToxCount; // counts the nesting depth of the Tox sal_uInt8 m_nRefCount; // counts the nesting depth of the Refs sal_uInt8 m_nMetaCount; // count META/METAFIELD + sal_uInt8 m_nContentControlCount; // count CONTENTCONTROL sal_uInt8 m_nInputFieldCount; // count INPUTFIELD SwFontScript m_nActual; // actual font (Latin, CJK or CTL) @@ -250,6 +251,8 @@ public: bool IsRef() const { return ( 0 != m_nRefCount ); } sal_uInt8 &GetMeta() { return m_nMetaCount; } bool IsMeta() const { return (0 != m_nMetaCount); } + sal_uInt8& GetContentControl() { return m_nContentControlCount; } + bool IsContentControl() const { return m_nContentControlCount != 0; } sal_uInt8 &GetInputField() { return m_nInputFieldCount; } bool IsInputField() const { return (0 != m_nInputFieldCount); } inline void SetGreyWave( const bool bNew ); diff --git a/sw/source/core/inc/txttypes.hxx b/sw/source/core/inc/txttypes.hxx index e65ed26dc128..d876b0f8e037 100644 --- a/sw/source/core/inc/txttypes.hxx +++ b/sw/source/core/inc/txttypes.hxx @@ -49,6 +49,7 @@ enum class PortionType Ref = 0x808b, IsoRef = 0x808c, Meta = 0x808d, + ContentControl = 0x808e, Expand = 0xc080, Blank = 0xc081, diff --git a/sw/source/core/text/atrhndl.hxx b/sw/source/core/text/atrhndl.hxx index 6c719bf2cbaf..e2d5ed6ea523 100644 --- a/sw/source/core/text/atrhndl.hxx +++ b/sw/source/core/text/atrhndl.hxx @@ -18,7 +18,7 @@ */ #pragma once -#define NUM_ATTRIBUTE_STACKS 44 +#define NUM_ATTRIBUTE_STACKS 45 #include <memory> #include <vector> diff --git a/sw/source/core/text/atrstck.cxx b/sw/source/core/text/atrstck.cxx index 30a88c30110d..1d62f1cc1e5f 100644 --- a/sw/source/core/text/atrstck.cxx +++ b/sw/source/core/text/atrstck.cxx @@ -123,6 +123,7 @@ const sal_uInt8 StackPos[ RES_TXTATR_WITHEND_END - RES_CHRATR_BEGIN + 1 ] = 42, // RES_TXTATR_CJK_RUBY, // 53 0, // RES_TXTATR_UNKNOWN_CONTAINER, // 54 43, // RES_TXTATR_INPUTFIELD // 55 + 44, // RES_TXTATR_CONTENTCONTROL // 56 }; namespace CharFormat @@ -517,6 +518,10 @@ void SwAttrHandler::ActivateTop( SwFont& rFnt, const sal_uInt16 nAttr ) { rFnt.GetMeta()--; } + else if (nAttr == RES_TXTATR_CONTENTCONTROL) + { + rFnt.GetContentControl()--; + } else if ( RES_TXTATR_CJK_RUBY == nAttr ) { // ruby stack has no more attributes @@ -811,6 +816,16 @@ void SwAttrHandler::FontChg(const SfxPoolItem& rItem, SwFont& rFnt, bool bPush ) else rFnt.GetMeta()--; break; + case RES_TXTATR_CONTENTCONTROL: + if (bPush) + { + rFnt.GetContentControl()++; + } + else + { + rFnt.GetContentControl()--; + } + break; case RES_TXTATR_INPUTFIELD : if ( bPush ) rFnt.GetInputField()++; diff --git a/sw/source/core/text/inftxt.cxx b/sw/source/core/text/inftxt.cxx index 422187624cd9..cd248307597f 100644 --- a/sw/source/core/text/inftxt.cxx +++ b/sw/source/core/text/inftxt.cxx @@ -1319,6 +1319,7 @@ void SwTextPaintInfo::DrawViewOpt( const SwLinePortion &rPor, case PortionType::Tox: case PortionType::Ref: case PortionType::Meta: + case PortionType::ContentControl: case PortionType::ControlChar: if ( !GetOpt().IsPagePreview() && !GetOpt().IsReadonly() diff --git a/sw/source/core/text/itratr.cxx b/sw/source/core/text/itratr.cxx index 95f7929a0e14..4f4a840a564f 100644 --- a/sw/source/core/text/itratr.cxx +++ b/sw/source/core/text/itratr.cxx @@ -510,6 +510,7 @@ static bool CanSkipOverRedline( case RES_TXTATR_INETFMT: case RES_TXTATR_CJK_RUBY: case RES_TXTATR_INPUTFIELD: + case RES_TXTATR_CONTENTCONTROL: { if (!isTheAnswerYes) return false; // always break } @@ -600,6 +601,7 @@ static bool CanSkipOverRedline( case RES_TXTATR_INETFMT: case RES_TXTATR_CJK_RUBY: case RES_TXTATR_INPUTFIELD: + case RES_TXTATR_CONTENTCONTROL: { if (!isTheAnswerYes) return false; } diff --git a/sw/source/core/text/itrform2.cxx b/sw/source/core/text/itrform2.cxx index 891678087eee..50fdf3f6acf5 100644 --- a/sw/source/core/text/itrform2.cxx +++ b/sw/source/core/text/itrform2.cxx @@ -863,6 +863,13 @@ public: void SetShadowColor(const Color& rCol ) { m_aShadowColor = rCol; } }; +/// A content control portion is a text portion that is inside RES_TXTATR_CONTENTCONTROL. +class SwContentControlPortion : public SwTextPortion +{ +public: + SwContentControlPortion() { SetWhichPor(PortionType::ContentControl); } + virtual void Paint(const SwTextPaintInfo& rInf) const override; +}; } void SwMetaPortion::Paint( const SwTextPaintInfo &rInf ) const @@ -879,6 +886,15 @@ void SwMetaPortion::Paint( const SwTextPaintInfo &rInf ) const } } +void SwContentControlPortion::Paint(const SwTextPaintInfo& rInf) const +{ + if (Width()) + { + rInf.DrawViewOpt(*this, PortionType::ContentControl); + SwTextPortion::Paint(rInf); + } +} + namespace sw::mark { OUString ExpandFieldmark(IFieldmark* pBM) { @@ -995,6 +1011,10 @@ SwTextPortion *SwTextFormatter::WhichTextPor( SwTextFormatInfo &rInf ) const } pPor = pMetaPor; } + else if (GetFnt()->IsContentControl()) + { + pPor = new SwContentControlPortion; + } else { // Only at the End! diff --git a/sw/source/core/text/xmldump.cxx b/sw/source/core/text/xmldump.cxx index 1e52e8fdbb68..5e1ac0eb002c 100644 --- a/sw/source/core/text/xmldump.cxx +++ b/sw/source/core/text/xmldump.cxx @@ -79,6 +79,8 @@ const char* sw::PortionTypeToString(PortionType nType) return "PortionType::IsoRef"; case PortionType::Meta: return "PortionType::Meta"; + case PortionType::ContentControl: + return "PortionType::ContentControl"; case PortionType::FieldMark: return "PortionType::FieldMark"; case PortionType::FieldFormCheckbox: diff --git a/sw/source/core/txtnode/swfont.cxx b/sw/source/core/txtnode/swfont.cxx index e0b64c24ef52..8553328e2da9 100644 --- a/sw/source/core/txtnode/swfont.cxx +++ b/sw/source/core/txtnode/swfont.cxx @@ -707,6 +707,7 @@ SwFont::SwFont( const SwFont &rFont ) m_nToxCount = 0; m_nRefCount = 0; m_nMetaCount = 0; + m_nContentControlCount = 0; m_nInputFieldCount = 0; m_bFontChg = rFont.m_bFontChg; m_bOrgChg = rFont.m_bOrgChg; @@ -722,6 +723,7 @@ SwFont::SwFont( const SwAttrSet* pAttrSet, m_nToxCount = 0; m_nRefCount = 0; m_nMetaCount = 0; + m_nContentControlCount = 0; m_nInputFieldCount = 0; m_bPaintBlank = false; m_bGreyWave = false; @@ -901,6 +903,7 @@ SwFont& SwFont::operator=( const SwFont &rFont ) m_nToxCount = 0; m_nRefCount = 0; m_nMetaCount = 0; + m_nContentControlCount = 0; m_nInputFieldCount = 0; m_bFontChg = rFont.m_bFontChg; m_bOrgChg = rFont.m_bOrgChg;
