diff options
-rw-r--r-- | editeng/source/editeng/editview.cxx | 33 | ||||
-rw-r--r-- | editeng/source/editeng/impedit.cxx | 82 | ||||
-rw-r--r-- | editeng/source/editeng/impedit.hxx | 21 | ||||
-rw-r--r-- | editeng/source/editeng/impedit3.cxx | 5 | ||||
-rw-r--r-- | include/editeng/editview.hxx | 21 | ||||
-rw-r--r-- | include/svx/svdedxv.hxx | 12 | ||||
-rw-r--r-- | include/svx/svdotext.hxx | 10 | ||||
-rw-r--r-- | include/svx/svdoutl.hxx | 2 | ||||
-rw-r--r-- | sd/source/ui/view/Outliner.cxx | 11 | ||||
-rw-r--r-- | svx/source/svdraw/svdedxv.cxx | 373 | ||||
-rw-r--r-- | svx/source/svdraw/svdotextdecomposition.cxx | 13 | ||||
-rw-r--r-- | svx/source/svdraw/svdotxat.cxx | 20 | ||||
-rw-r--r-- | svx/source/svdraw/svdoutl.cxx | 16 |
13 files changed, 575 insertions, 44 deletions
diff --git a/editeng/source/editeng/editview.cxx b/editeng/source/editeng/editview.cxx index 4bcc257fc0e4..db7ff1951767 100644 --- a/editeng/source/editeng/editview.cxx +++ b/editeng/source/editeng/editview.cxx @@ -153,8 +153,12 @@ LanguageType EditView::CheckLanguage( return nLang; } -// class EditView +// class EditViewCallbacks +EditViewCallbacks::~EditViewCallbacks() +{ +} +// class EditView EditView::EditView( EditEngine* pEng, vcl::Window* pWindow ) { pImpEditView.reset( new ImpEditView( this, pEng, pWindow ) ); @@ -164,6 +168,16 @@ EditView::~EditView() { } +void EditView::setEditViewCallbacks(const EditViewCallbacks* pEditViewCallbacks) +{ + pImpEditView->setEditViewCallbacks(pEditViewCallbacks); +} + +bool EditView::hasEditViewCallbacks() const +{ + return pImpEditView->hasEditViewCallbacks(); +} + ImpEditEngine* EditView::GetImpEditEngine() const { return pImpEditView->pEditEngine->pImpEditEngine.get(); @@ -190,6 +204,23 @@ tools::Rectangle EditView::GetInvalidateRect() const } } +void EditView::InvalidateWindow(const tools::Rectangle& rClipRect) +{ + if (pImpEditView->hasEditViewCallbacks()) + { + // do not invalidate and trigger a global repaint, but forward + // the need for change to the applied EditViewCallback, can e.g. + // be used to visualize the active edit text in an OverlayObject + pImpEditView->mpEditViewCallbacks->EditViewInvalidate(); + } + else + { + // classic mode: invalidate and trigger full repaint + // of the changed area + GetWindow()->Invalidate(rClipRect); + } +} + void EditView::InvalidateOtherViewWindows( const tools::Rectangle& rInvRect ) { if (comphelper::LibreOfficeKit::isActive()) diff --git a/editeng/source/editeng/impedit.cxx b/editeng/source/editeng/impedit.cxx index 22efd056dbee..fc8ee6745a3a 100644 --- a/editeng/source/editeng/impedit.cxx +++ b/editeng/source/editeng/impedit.cxx @@ -83,6 +83,7 @@ ImpEditView::ImpEditView( EditView* pView, EditEngine* pEng, vcl::Window* pWindo bClickedInSelection = false; eSelectionMode = EESelectionMode::TxtOnly; eAnchorMode = EEAnchorMode::TopLeft; + mpEditViewCallbacks = nullptr; nInvMore = 1; nTravelXPos = TRAVEL_X_DONTKNOW; nControl = EVControlBits::AUTOSCROLL | EVControlBits::ENABLEPASTE; @@ -185,6 +186,35 @@ void lcl_translateTwips(vcl::Window const & rParent, vcl::Window& rChild) void ImpEditView::DrawSelection( EditSelection aTmpSel, vcl::Region* pRegion, OutputDevice* pTargetDevice ) { + if (hasEditViewCallbacks() && !pRegion) + { + // when we have an own mechanism for painting EditViews, collect the Selection + // in a basegfx::B2DRange vector and hand over. To do so, call GetSelectionRectangles + // which recursively calls ImpEditView::DrawSelection, but with nullptr != pRegion + std::vector<tools::Rectangle> aLogicRects; + std::vector<basegfx::B2DRange> aLogicRanges; + OutputDevice* pTarget = pTargetDevice ? pTargetDevice : pOutWin; + const Point aPixel(pTarget ? pTarget->LogicToPixel(Point(1, 1)) : Point(1, 1)); + + GetSelectionRectangles(aLogicRects); + + for (const auto& aRect : aLogicRects) + { + // convert from logic Rectangles to logic Ranges, do not forget to add + // one Unit (in this case logical unit, thus calculate first) + aLogicRanges.push_back( + basegfx::B2DRange( + aRect.Left(), aRect.Top(), + aRect.Right() + aPixel.X(), aRect.Bottom() + aPixel.Y())); + } + + // use callback to tell about change in selection visualisation + mpEditViewCallbacks->EditViewSelectionChange(aLogicRanges); + + // we are done, do *not* visualize self + return; + } + if ( eSelectionMode == EESelectionMode::Hidden ) return; @@ -449,16 +479,9 @@ void ImpEditView::DrawSelection( EditSelection aTmpSel, vcl::Region* pRegion, Ou void ImpEditView::GetSelectionRectangles(std::vector<tools::Rectangle>& rLogicRects) { - bool bMm100ToTwip = pOutWin->GetMapMode().GetMapUnit() == MapUnit::Map100thMM; vcl::Region aRegion; DrawSelection(aEditSelection, &aRegion); aRegion.GetRegionRectangles(rLogicRects); - - for (tools::Rectangle& rRectangle : rLogicRects) - { - if (bMm100ToTwip) - rRectangle = OutputDevice::LogicToLogic(rRectangle, MapUnit::Map100thMM, MapUnit::MapTwip); - } } void ImpEditView::ImplDrawHighlightRect( OutputDevice* _pTarget, const Point& rDocPosTopLeft, const Point& rDocPosBottomRight, tools::PolyPolygon* pPolyPoly ) @@ -627,6 +650,23 @@ void ImpEditView::SetOutputArea( const tools::Rectangle& rRect ) SetScrollDiffX( (sal_uInt16)aOutArea.GetWidth() * 2 / 10 ); } +void ImpEditView::InvalidateAtWindow(const tools::Rectangle& rRect) +{ + if (hasEditViewCallbacks()) + { + // do not invalidate and trigger a global repaint, but forward + // the need for change to the applied EditViewCallback, can e.g. + // be used to visualize the active edit text in an OverlayObject + mpEditViewCallbacks->EditViewInvalidate(); + } + else + { + // classic mode: invalidate and trigger full repaint + // of the changed area + GetWindow()->Invalidate(rRect); + } +} + void ImpEditView::ResetOutputArea( const tools::Rectangle& rRect ) { // remember old out area @@ -643,38 +683,46 @@ void ImpEditView::ResetOutputArea( const tools::Rectangle& rRect ) if(aOldArea.Left() > aOutArea.Left()) { - GetWindow()->Invalidate(tools::Rectangle(aOutArea.Left() - nMore, aOldArea.Top() - nMore, aOldArea.Left(), aOldArea.Bottom() + nMore)); + const tools::Rectangle aRect(aOutArea.Left() - nMore, aOldArea.Top() - nMore, aOldArea.Left(), aOldArea.Bottom() + nMore); + InvalidateAtWindow(aRect); } else if(aOldArea.Left() < aOutArea.Left()) { - GetWindow()->Invalidate(tools::Rectangle(aOldArea.Left() - nMore, aOldArea.Top() - nMore, aOutArea.Left(), aOldArea.Bottom() + nMore)); + const tools::Rectangle aRect(aOldArea.Left() - nMore, aOldArea.Top() - nMore, aOutArea.Left(), aOldArea.Bottom() + nMore); + InvalidateAtWindow(aRect); } if(aOldArea.Right() > aOutArea.Right()) { - GetWindow()->Invalidate(tools::Rectangle(aOutArea.Right(), aOldArea.Top() - nMore, aOldArea.Right() + nMore, aOldArea.Bottom() + nMore)); + const tools::Rectangle aRect(aOutArea.Right(), aOldArea.Top() - nMore, aOldArea.Right() + nMore, aOldArea.Bottom() + nMore); + InvalidateAtWindow(aRect); } else if(aOldArea.Right() < aOutArea.Right()) { - GetWindow()->Invalidate(tools::Rectangle(aOldArea.Right(), aOldArea.Top() - nMore, aOutArea.Right() + nMore, aOldArea.Bottom() + nMore)); + const tools::Rectangle aRect(aOldArea.Right(), aOldArea.Top() - nMore, aOutArea.Right() + nMore, aOldArea.Bottom() + nMore); + InvalidateAtWindow(aRect); } if(aOldArea.Top() > aOutArea.Top()) { - GetWindow()->Invalidate(tools::Rectangle(aOldArea.Left() - nMore, aOutArea.Top() - nMore, aOldArea.Right() + nMore, aOldArea.Top())); + const tools::Rectangle aRect(aOldArea.Left() - nMore, aOutArea.Top() - nMore, aOldArea.Right() + nMore, aOldArea.Top()); + InvalidateAtWindow(aRect); } else if(aOldArea.Top() < aOutArea.Top()) { - GetWindow()->Invalidate(tools::Rectangle(aOldArea.Left() - nMore, aOldArea.Top() - nMore, aOldArea.Right() + nMore, aOutArea.Top())); + const tools::Rectangle aRect(aOldArea.Left() - nMore, aOldArea.Top() - nMore, aOldArea.Right() + nMore, aOutArea.Top()); + InvalidateAtWindow(aRect); } if(aOldArea.Bottom() > aOutArea.Bottom()) { - GetWindow()->Invalidate(tools::Rectangle(aOldArea.Left() - nMore, aOutArea.Bottom(), aOldArea.Right() + nMore, aOldArea.Bottom() + nMore)); + const tools::Rectangle aRect(aOldArea.Left() - nMore, aOutArea.Bottom(), aOldArea.Right() + nMore, aOldArea.Bottom() + nMore); + InvalidateAtWindow(aRect); } else if(aOldArea.Bottom() < aOutArea.Bottom()) { - GetWindow()->Invalidate(tools::Rectangle(aOldArea.Left() - nMore, aOldArea.Bottom(), aOldArea.Right() + nMore, aOutArea.Bottom() + nMore)); + const tools::Rectangle aRect(aOldArea.Left() - nMore, aOldArea.Bottom(), aOldArea.Right() + nMore, aOutArea.Bottom() + nMore); + InvalidateAtWindow(aRect); } } } @@ -1629,6 +1677,10 @@ void ImpEditView::DeselectAll() pEditEngine->SetInSelectionMode(false); DrawSelection(); GetEditSelection().Min() = GetEditSelection().Max(); + + // Selection is empty, still need to draw it due to new forward selection + // functionality. When without that, nothing will be drawn (since it's empty) + DrawSelection(); } bool ImpEditView::IsSelectionAtPoint( const Point& rPosPixel ) diff --git a/editeng/source/editeng/impedit.hxx b/editeng/source/editeng/impedit.hxx index 4d7365d49221..89cf8947d7c8 100644 --- a/editeng/source/editeng/impedit.hxx +++ b/editeng/source/editeng/impedit.hxx @@ -257,6 +257,27 @@ private: EditSelection aEditSelection; EEAnchorMode eAnchorMode; + /// mechanism to change from the classic refresh mode that simply + // invalidates the area where text was changed. When set, the invalidate + // and the direct repaint of the Window-plugged EditView will be suppressed. + // Instead, a consumer that has registered using a EditViewCallbacks + // incarnation has to handle that. Used e.g. to represent the edited text + // in Draw/Impres in an OverlayObject which avoids evtl. expensive full + // repaints of the EditView(s) + const EditViewCallbacks* mpEditViewCallbacks; + + bool hasEditViewCallbacks() const + { + return nullptr != mpEditViewCallbacks; + } + + void setEditViewCallbacks(const EditViewCallbacks* pEditViewCallbacks) + { + mpEditViewCallbacks = pEditViewCallbacks; + } + + void InvalidateAtWindow(const tools::Rectangle& rRect); + protected: // DragAndDropClient diff --git a/editeng/source/editeng/impedit3.cxx b/editeng/source/editeng/impedit3.cxx index 372e0993723f..e07312e4aa05 100644 --- a/editeng/source/editeng/impedit3.cxx +++ b/editeng/source/editeng/impedit3.cxx @@ -287,7 +287,10 @@ void ImpEditEngine::UpdateViews( EditView* pCurView ) { // convert to window coordinates .... aClipRect = pView->pImpEditView->GetWindowPos( aClipRect ); - pView->GetWindow()->Invalidate( aClipRect ); + + // moved to one executing method to allow finer control + pView->InvalidateWindow(aClipRect); + pView->InvalidateOtherViewWindows( aClipRect ); } } diff --git a/include/editeng/editview.hxx b/include/editeng/editview.hxx index 07333c75fe93..ac6ffc5433d1 100644 --- a/include/editeng/editview.hxx +++ b/include/editeng/editview.hxx @@ -72,6 +72,7 @@ namespace linguistic2 { class XLanguageGuessing; } }}} +namespace basegfx { class B2DRange; } enum class ScrollRangeCheck { @@ -79,6 +80,21 @@ enum class ScrollRangeCheck PaperWidthTextSize = 2, // VisArea must be within paper width, Text Size }; +// Helper class that allows to set a callback at the EditView. When +// set, Invalidates and repains are suppressed at the EditView, but +// EditViewInvalidate() will be triggered to allow the consumer to +// react itself as needed. +// Also Selection visualization is suppressed and EditViewSelectionChange +// is triggered when Selection changes and needs reaction. +class EDITENG_DLLPUBLIC EditViewCallbacks +{ +public: + EditViewCallbacks() {} + virtual ~EditViewCallbacks(); + + virtual void EditViewInvalidate() const = 0; + virtual void EditViewSelectionChange(const std::vector<basegfx::B2DRange>& rLogicRanges) const = 0; +}; class EDITENG_DLLPUBLIC EditView final { @@ -105,6 +121,10 @@ public: EditView( EditEngine* pEng, vcl::Window* pWindow ); ~EditView(); + // set EditViewCallbacks for external handling of Repaints/Visualization + void setEditViewCallbacks(const EditViewCallbacks* pEditViewCallbacks); + bool hasEditViewCallbacks() const; + void SetEditEngine( EditEngine* pEditEngine ); EditEngine* GetEditEngine() const; @@ -117,6 +137,7 @@ public: void Paint( const tools::Rectangle& rRect, OutputDevice* pTargetDevice = nullptr ); tools::Rectangle GetInvalidateRect() const; + void InvalidateWindow(const tools::Rectangle& rClipRect); void InvalidateOtherViewWindows( const tools::Rectangle& rInvRect ); void Invalidate(); Pair Scroll( long nHorzScroll, long nVertScroll, ScrollRangeCheck nRangeCheck = ScrollRangeCheck::NoNegative ); diff --git a/include/svx/svdedxv.hxx b/include/svx/svdedxv.hxx index 8dd62ff0b3ec..8bdae7fb0ee4 100644 --- a/include/svx/svdedxv.hxx +++ b/include/svx/svdedxv.hxx @@ -25,6 +25,7 @@ #include <svx/svxdllapi.h> #include <svx/svdglev.hxx> #include <svx/selectioncontroller.hxx> +#include <editeng/editview.hxx> #include <memory> class SdrOutliner; @@ -57,11 +58,20 @@ enum class SdrEndTextEditKind // - macromod -class SVX_DLLPUBLIC SdrObjEditView: public SdrGlueEditView +class SVX_DLLPUBLIC SdrObjEditView: public SdrGlueEditView, public EditViewCallbacks { friend class SdrPageView; friend class ImpSdrEditPara; + // Now derived from EditViewCallbacks and overriding these callbacks to + // allow own EditText visualization + virtual void EditViewInvalidate() const override; + virtual void EditViewSelectionChange(const std::vector<basegfx::B2DRange>& rLogicRanges) const override; + + // The OverlayObjects used for visualizing active TextEdit (currently + // using TextEditOverlayObject, but not limitied to it + sdr::overlay::OverlayObjectList maTEOverlayGroup; + protected: // TextEdit SdrObjectWeakRef mxTextEditObj; // current object in TextEdit diff --git a/include/svx/svdotext.hxx b/include/svx/svdotext.hxx index 0269c88e5d39..33fa54a321fa 100644 --- a/include/svx/svdotext.hxx +++ b/include/svx/svdotext.hxx @@ -598,11 +598,19 @@ public: const drawinglayer::geometry::ViewInformation2D& aViewInformation) const; void impHandleChainingEventsDuringDecomposition(SdrOutliner &rOutliner) const; - // timing generators void impGetBlinkTextTiming(drawinglayer::animation::AnimationEntryList& rAnimList) const; void impGetScrollTextTiming(drawinglayer::animation::AnimationEntryList& rAnimList, double fFrameLength, double fTextLength) const; + // Direct decomposer for text visualization when you already have a prepared + // Outliner containing all the needed information + static void impDecomposeBlockTextPrimitiveDirect( + drawinglayer::primitive2d::Primitive2DContainer& rTarget, + SdrOutliner& rOutliner, + const basegfx::B2DHomMatrix& rNewTransformA, + const basegfx::B2DHomMatrix& rNewTransformB, + const basegfx::B2DRange& rClipRange); + /** returns false if the given pointer is NULL or if the given SdrOutliner contains no text. Also checks for one empty paragraph. diff --git a/include/svx/svdoutl.hxx b/include/svx/svdoutl.hxx index a9a885f68fac..5d1f9e842593 100644 --- a/include/svx/svdoutl.hxx +++ b/include/svx/svdoutl.hxx @@ -45,6 +45,8 @@ public: const SdrPage* getVisualizedPage() const { return mpVisualizedPage; } virtual OUString CalcFieldValue(const SvxFieldItem& rField, sal_Int32 nPara, sal_Int32 nPos, Color*& rpTxtColor, Color*& rpFldColor) override; + + bool hasEditViewCallbacks() const; }; #endif // INCLUDED_SVX_SVDOUTL_HXX diff --git a/sd/source/ui/view/Outliner.cxx b/sd/source/ui/view/Outliner.cxx index 9b2619ae4e14..c70bb15cd1f8 100644 --- a/sd/source/ui/view/Outliner.cxx +++ b/sd/source/ui/view/Outliner.cxx @@ -797,6 +797,17 @@ bool SdOutliner::SearchAndReplaceOnce(std::vector<sd::SearchSelection>* pSelecti std::vector<::tools::Rectangle> aLogicRects; pOutlinerView->GetSelectionRectangles(aLogicRects); + // convert to twips if in 100thmm (seems as if LibreOfficeKit is based on twips?). Do this + // here where we have the only place needing this, *not* in ImpEditView::GetSelectionRectangles + // which makes that method unusable for others + if (pOutlinerView->GetWindow() && MapUnit::Map100thMM == pOutlinerView->GetWindow()->GetMapMode().GetMapUnit()) + { + for (tools::Rectangle& rRectangle : aLogicRects) + { + rRectangle = OutputDevice::LogicToLogic(rRectangle, MapUnit::Map100thMM, MapUnit::MapTwip); + } + } + std::vector<OString> aLogicRectStrings; std::transform(aLogicRects.begin(), aLogicRects.end(), std::back_inserter(aLogicRectStrings), [](const ::tools::Rectangle& rRectangle) { return rRectangle.toString(); }); OString sRectangles = comphelper::string::join("; ", aLogicRectStrings); diff --git a/svx/source/svdraw/svdedxv.cxx b/svx/source/svdraw/svdedxv.cxx index 65dede8abcb1..8348988b2ca1 100644 --- a/svx/source/svdraw/svdedxv.cxx +++ b/svx/source/svdraw/svdedxv.cxx @@ -65,6 +65,9 @@ #include <comphelper/lok.hxx> #include <sfx2/viewsh.hxx> #include <svx/svdviter.hxx> +#include <svx/sdr/overlay/overlayselection.hxx> +#include <svx/sdr/overlay/overlaymanager.hxx> +#include <svx/sdrpagewindow.hxx> #include <memory> @@ -103,7 +106,6 @@ SdrObjEditView::~SdrObjEditView() assert(nullptr == mpOldTextEditUndoManager); // should have been reset } - bool SdrObjEditView::IsAction() const { return IsMacroObj() || SdrGlueEditView::IsAction(); @@ -353,39 +355,298 @@ void SdrObjEditView::ModelHasChanged() } } +namespace +{ + /** + Helper class to visualize the content of an active EditView as an + OverlayObject. These objects work with Primitives and are handled + from the OverlayManager(s) in place as needed. + + It allows complete visualization of the content of the active + EditView without the need of Invalidates triggered by the EditView + and thus avoiding potentially expensive repaints by using the + automatically buffered Overlay mechanism. + + It buffers as amuch as possible locally and *only* triggers a real + change (see call to objectChange()) when really needed. + */ + class TextEditOverlayObject : public sdr::overlay::OverlayObject + { + protected: + /// local access to associated sdr::overlay::OverlaySelection + sdr::overlay::OverlaySelection* mpOverlaySelection; + + /// local definition depends on active OutlinerView + OutlinerView& mrOutlinerView; + + /// geometry definitions with buffering + basegfx::B2DRange maLastRange; + basegfx::B2DRange maRange; + + /// text content definitions with buffering + drawinglayer::primitive2d::Primitive2DContainer maTextPrimitives; + drawinglayer::primitive2d::Primitive2DContainer maLastTextPrimitives; + + /// bitfield + bool mbVisualizeSurroundingFrame : 1; + + // geometry creation for OverlayObject, can use local *Last* values + virtual drawinglayer::primitive2d::Primitive2DContainer createOverlayObjectPrimitive2DSequence() override; + + public: + TextEditOverlayObject( + sdr::overlay::OverlaySelection* pOverlaySelection, + const Color& rColor, + OutlinerView& rOutlinerView, + bool bVisualizeSurroundingFrame); + virtual ~TextEditOverlayObject() override; + + // data read access + const sdr::overlay::OverlaySelection* getOverlaySelection() const { return mpOverlaySelection; } + const OutlinerView& getOutlinerView() const { return mrOutlinerView; } + bool getVisualizeSurroundingFrame() const { return mbVisualizeSurroundingFrame; } + + /// override to check conditions for last createOverlayObjectPrimitive2DSequence + virtual drawinglayer::primitive2d::Primitive2DContainer getOverlayObjectPrimitive2DSequence() const override; + + // data write access. In this OverlayObject we only have the + // callback that triggers detecting if something *has* changed + void checkDataChange(const basegfx::B2DRange& rMinTextEditArea); + void checkSelectionChange(const std::vector<basegfx::B2DRange>& rLogicRanges); + }; + + drawinglayer::primitive2d::Primitive2DContainer TextEditOverlayObject::createOverlayObjectPrimitive2DSequence() + { + drawinglayer::primitive2d::Primitive2DContainer aRetval; + + /// outer frame visualization + if (getVisualizeSurroundingFrame()) + { + const SvtOptionsDrawinglayer aSvtOptionsDrawinglayer; + const double fTransparence(aSvtOptionsDrawinglayer.GetTransparentSelectionPercent() * 0.01); + const sal_uInt16 nPixSiz(getOutlinerView().GetInvalidateMore() - 1); + + aRetval.push_back( + new drawinglayer::primitive2d::OverlayRectanglePrimitive( + maRange, + getBaseColor().getBColor(), + fTransparence, + std::max(6, nPixSiz - 2), // grow + 0.0, // shrink + 0.0)); + } + + // add buffered TextPrimitives + aRetval.append(maTextPrimitives); + + return aRetval; + } + + TextEditOverlayObject::TextEditOverlayObject( + sdr::overlay::OverlaySelection* pOverlaySelection, + const Color& rColor, + OutlinerView& rOutlinerView, + bool bVisualizeSurroundingFrame) + : OverlayObject(rColor), + mpOverlaySelection(pOverlaySelection), + mrOutlinerView(rOutlinerView), + maLastRange(), + maRange(), + maTextPrimitives(), + maLastTextPrimitives(), + mbVisualizeSurroundingFrame(bVisualizeSurroundingFrame) + { + // no AA for TextEdit overlay + allowAntiAliase(false); + } + + TextEditOverlayObject::~TextEditOverlayObject() + { + if (getOverlaySelection()) + { + delete mpOverlaySelection; + mpOverlaySelection = nullptr; + } + + if (getOverlayManager()) + { + getOverlayManager()->remove(*this); + } + } + + drawinglayer::primitive2d::Primitive2DContainer TextEditOverlayObject::getOverlayObjectPrimitive2DSequence() const + { + if (!getPrimitive2DSequence().empty()) + { + if (!maRange.equal(maLastRange) || maLastTextPrimitives != maTextPrimitives) + { + // conditions of last local decomposition have changed, delete to force new evaluation + const_cast<TextEditOverlayObject*>(this)->setPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DContainer()); + } + } + + if (getPrimitive2DSequence().empty()) + { + // remember new buffered values + const_cast<TextEditOverlayObject*>(this)->maLastRange = maRange; + const_cast<TextEditOverlayObject*>(this)->maLastTextPrimitives = maTextPrimitives; + } + + // call base implementation + return OverlayObject::getOverlayObjectPrimitive2DSequence(); + } + + void TextEditOverlayObject::checkDataChange(const basegfx::B2DRange& rMinTextEditArea) + { + bool bObjectChange(false); + + // check current range + const tools::Rectangle aOutArea(mrOutlinerView.GetOutputArea()); + basegfx::B2DRange aNewRange(aOutArea.Left(), aOutArea.Top(), aOutArea.Right(), aOutArea.Bottom()); + aNewRange.expand(rMinTextEditArea); + + if (aNewRange != maRange) + { + maRange = aNewRange; + bObjectChange = true; + } + + // check if text primitives did change + SdrOutliner* pSdrOutliner = dynamic_cast<SdrOutliner*>(getOutlinerView().GetOutliner()); + + if (pSdrOutliner) + { + // get TextPrimitives directly from active Outliner + basegfx::B2DHomMatrix aNewTransformA; + basegfx::B2DHomMatrix aNewTransformB; + basegfx::B2DRange aClipRange; + drawinglayer::primitive2d::Primitive2DContainer aNewTextPrimitives; + + // active Outliner is always in unified oriented coordinate system (currently) + // so just translate to TopLeft of visible Range + tools::Rectangle aVisArea(mrOutlinerView.GetVisArea()); + + aNewTransformB.translate( + aOutArea.Left() - aVisArea.Left(), + aOutArea.Top() - aVisArea.Top()); + + // get the current TextPrimitives. This is the most expensive part + // of this mechanism, it *may* be possible to buffer layouted + // primitives per ParaPortion with/in/dependent on the EditEngine + // content if needed. For now, get and compare + SdrTextObj::impDecomposeBlockTextPrimitiveDirect( + aNewTextPrimitives, + *pSdrOutliner, + aNewTransformA, + aNewTransformB, + aClipRange); + + if (aNewTextPrimitives != maTextPrimitives) + { + maTextPrimitives = aNewTextPrimitives; + bObjectChange = true; + } + } + + if (bObjectChange) + { + // if there really *was* a change signal the OverlayManager to + // refresh this object's visualization + objectChange(); + } + } + + void TextEditOverlayObject::checkSelectionChange(const std::vector<basegfx::B2DRange>& rLogicRanges) + { + if (getOverlaySelection()) + { + mpOverlaySelection->setRanges(rLogicRanges); + } + } +} // end of anonymous namespace // TextEdit +// file-local static bool to control old/new behaviour of active TextEdit visualization +static bool bAllowTextEditVisualizatkionOnOverlay(true); -void SdrObjEditView::TextEditDrawing(SdrPaintWindow& rPaintWindow) const +// callback from the active EditView, forward to evtl. existing instances of the +// TextEditOverlayObject(s) +void SdrObjEditView::EditViewInvalidate() const { - // draw old text edit stuff - if(IsTextEdit()) + if (IsTextEdit()) { - const SdrOutliner* pActiveOutliner = GetTextEditOutliner(); + // MinTextRange may have changed. Forward it, too + const basegfx::B2DRange aMinTextRange( + aMinTextEditArea.Left(), aMinTextEditArea.Top(), + aMinTextEditArea.Right(), aMinTextEditArea.Bottom()); + + for (sal_uInt32 a(0); a < maTEOverlayGroup.count(); a++) + { + TextEditOverlayObject* pCandidate = dynamic_cast< TextEditOverlayObject* >(&maTEOverlayGroup.getOverlayObject(a)); + + if (pCandidate) + { + pCandidate->checkDataChange(aMinTextRange); + } + } + } +} - if(pActiveOutliner) +void SdrObjEditView::EditViewSelectionChange(const std::vector<basegfx::B2DRange>& rLogicRanges) const +{ + if (IsTextEdit()) + { + for (sal_uInt32 a(0); a < maTEOverlayGroup.count(); a++) { - const sal_uInt32 nViewCount(pActiveOutliner->GetViewCount()); + TextEditOverlayObject* pCandidate = dynamic_cast< TextEditOverlayObject* >(&maTEOverlayGroup.getOverlayObject(a)); - if(nViewCount) + if (pCandidate) { - const vcl::Region& rRedrawRegion = rPaintWindow.GetRedrawRegion(); - const tools::Rectangle aCheckRect(rRedrawRegion.GetBoundRect()); + pCandidate->checkSelectionChange(rLogicRanges); + } + } + } +} - for(sal_uInt32 i(0); i < nViewCount; i++) +void SdrObjEditView::TextEditDrawing(SdrPaintWindow& rPaintWindow) const +{ + if (bAllowTextEditVisualizatkionOnOverlay) + { + // adapt TextEditOverlayObject(s). Need also to do this here to + // update the current values accordingly + EditViewInvalidate(); + } + else + { + // draw old text edit stuff + if (IsTextEdit()) + { + const SdrOutliner* pActiveOutliner = GetTextEditOutliner(); + + if (pActiveOutliner) + { + const sal_uInt32 nViewCount(pActiveOutliner->GetViewCount()); + + if (nViewCount) { - OutlinerView* pOLV = pActiveOutliner->GetView(i); - - // If rPaintWindow knows that the output device is a render - // context and is aware of the underlying vcl::Window, - // compare against that; that's how double-buffering can - // still find the matching OutlinerView. - OutputDevice* pOutputDevice = rPaintWindow.GetWindow() ? rPaintWindow.GetWindow() : &rPaintWindow.GetOutputDevice(); - if(pOLV->GetWindow() == pOutputDevice || comphelper::LibreOfficeKit::isActive()) + const vcl::Region& rRedrawRegion = rPaintWindow.GetRedrawRegion(); + const tools::Rectangle aCheckRect(rRedrawRegion.GetBoundRect()); + + for (sal_uInt32 i(0); i < nViewCount; i++) { - ImpPaintOutlinerView(*pOLV, aCheckRect, rPaintWindow.GetTargetOutputDevice()); - return; + OutlinerView* pOLV = pActiveOutliner->GetView(i); + + // If rPaintWindow knows that the output device is a render + // context and is aware of the underlying vcl::Window, + // compare against that; that's how double-buffering can + // still find the matching OutlinerView. + OutputDevice* pOutputDevice = rPaintWindow.GetWindow() ? rPaintWindow.GetWindow() : &rPaintWindow.GetOutputDevice(); + if (pOLV->GetWindow() == pOutputDevice || comphelper::LibreOfficeKit::isActive()) + { + ImpPaintOutlinerView(*pOLV, aCheckRect, rPaintWindow.GetTargetOutputDevice()); + return; + } } } } @@ -513,8 +774,16 @@ OutlinerView* SdrObjEditView::ImpMakeOutlinerView(vcl::Window* pWin, OutlinerVie // create OutlinerView OutlinerView* pOutlView=pGivenView; pTextEditOutliner->SetUpdateMode(false); - if (pOutlView==nullptr) pOutlView = new OutlinerView(pTextEditOutliner,pWin); - else pOutlView->SetWindow(pWin); + + if (pOutlView == nullptr) + { + pOutlView = new OutlinerView(pTextEditOutliner, pWin); + } + else + { + pOutlView->SetWindow(pWin); + } + // disallow scrolling EVControlBits nStat=pOutlView->GetControlWord(); nStat&=~EVControlBits::AUTOSCROLL; @@ -859,6 +1128,57 @@ bool SdrObjEditView::SdrBeginTextEdit( pTextEditOutlinerView=ImpMakeOutlinerView(pWin,pGivenOutlinerView); + if (bAllowTextEditVisualizatkionOnOverlay && pTextEditOutlinerView) + { + // activate visualization of EditView on Overlay + pTextEditOutlinerView->GetEditView().setEditViewCallbacks(this); + + const SvtOptionsDrawinglayer aSvtOptionsDrawinglayer; + const Color aHilightColor(aSvtOptionsDrawinglayer.getHilightColor()); + const SdrTextObj* pText = dynamic_cast<SdrTextObj*>(GetTextEditObject()); + const bool bTextFrame(pText && pText->IsTextFrame()); + const bool bFitToSize(pTextEditOutliner->GetControlWord() & EEControlBits::STRETCHING); + const bool bVisualizeSurroundingFrame(bTextFrame && !bFitToSize); + SdrPageView* pPageView = GetSdrPageView(); + + if (pPageView) + { + for (sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++) + { + const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b); + + if (rPageWindow.GetPaintWindow().OutputToWindow()) + { + rtl::Reference< sdr::overlay::OverlayManager > xManager = rPageWindow.GetOverlayManager(); + if (xManager.is()) + { + const basegfx::B2DRange aMinTEArea( + aMinTextEditArea.Left(), aMinTextEditArea.Top(), + aMinTextEditArea.Right(), aMinTextEditArea.Bottom()); + const std::vector< basegfx::B2DRange > aEmptySelection; + + sdr::overlay::OverlaySelection* pNewOverlaySelection = new sdr::overlay::OverlaySelection( + sdr::overlay::OverlayType::Transparent, + aHilightColor, + aEmptySelection, + true); + + sdr::overlay::OverlayObject* pNewTextEditOverlayObject = new TextEditOverlayObject( + pNewOverlaySelection, + aHilightColor, + *pTextEditOutlinerView, + bVisualizeSurroundingFrame); + + xManager->add(*pNewTextEditOverlayObject); + xManager->add(*pNewOverlaySelection); + + maTEOverlayGroup.append(pNewTextEditOverlayObject); + } + } + } + } + } + // check if this view is already inserted sal_uIntPtr i2,nCount = pTextEditOutliner->GetViewCount(); for( i2 = 0; i2 < nCount; i2++ ) @@ -1067,6 +1387,13 @@ SdrEndTextEditKind SdrObjEditView::SdrEndTextEdit(bool bDontDeleteReally) GetModel()->Broadcast(aHint); } + // if new mechanism was used, clean it up + if (bAllowTextEditVisualizatkionOnOverlay && pTextEditOutlinerView) + { + pTextEditOutlinerView->GetEditView().setEditViewCallbacks(nullptr); + maTEOverlayGroup.clear(); + } + mxTextEditObj.reset(nullptr); pTextEditPV=nullptr; pTextEditWin=nullptr; diff --git a/svx/source/svdraw/svdotextdecomposition.cxx b/svx/source/svdraw/svdotextdecomposition.cxx index 83078bb72216..76ff4f398612 100644 --- a/svx/source/svdraw/svdotextdecomposition.cxx +++ b/svx/source/svdraw/svdotextdecomposition.cxx @@ -1639,5 +1639,18 @@ void SdrTextObj::impDecomposeChainedTextPrimitive( rTarget = aConverter.getPrimitive2DSequence(); } +// Direct decomposer for text visualization when you already have a prepared +// Outliner containing all the needed information +void SdrTextObj::impDecomposeBlockTextPrimitiveDirect( + drawinglayer::primitive2d::Primitive2DContainer& rTarget, + SdrOutliner& rOutliner, + const basegfx::B2DHomMatrix& rNewTransformA, + const basegfx::B2DHomMatrix& rNewTransformB, + const basegfx::B2DRange& rClipRange) +{ + impTextBreakupHandler aConverter(rOutliner); + aConverter.decomposeBlockTextPrimitive(rNewTransformA, rNewTransformB, rClipRange); + rTarget.append(aConverter.getPrimitive2DSequence()); +} /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/svdraw/svdotxat.cxx b/svx/source/svdraw/svdotxat.cxx index 20325165a172..b25ead77cc87 100644 --- a/svx/source/svdraw/svdotxat.cxx +++ b/svx/source/svdraw/svdotxat.cxx @@ -287,8 +287,24 @@ bool SdrTextObj::AdjustTextFrameWidthAndHeight() if (dynamic_cast<const SdrCaptionObj *>(this) != nullptr) { // this is a hack static_cast<SdrCaptionObj*>(this)->ImpRecalcTail(); } - SetChanged(); - BroadcastObjectChange(); + + // to not slow down EditView visualization on Overlay (see + // TextEditOverlayObject) it is necessary to suppress the + // Invalidates for the deep repaint when the size of the + // TextFrame changed (AdjustTextFrameWidthAndHeight returned + // true). The ObjectChanges are valid, invalidate will be + // done on EndTextEdit anyways + const bool bSuppressChangeWhenEditOnOverlay( + IsInEditMode() && + GetTextEditOutliner() && + GetTextEditOutliner()->hasEditViewCallbacks()); + + if (!bSuppressChangeWhenEditOnOverlay) + { + SetChanged(); + BroadcastObjectChange(); + } + SendUserCall(SdrUserCallType::Resize,aBoundRect0); } return bRet; diff --git a/svx/source/svdraw/svdoutl.cxx b/svx/source/svdraw/svdoutl.cxx index 1e3ea5e2e150..580dee39b174 100644 --- a/svx/source/svdraw/svdoutl.cxx +++ b/svx/source/svdraw/svdoutl.cxx @@ -24,6 +24,7 @@ #include <svx/svdmodel.hxx> #include <editeng/eeitem.hxx> #include <svl/itempool.hxx> +#include <editeng/editview.hxx> SdrOutliner::SdrOutliner( SfxItemPool* pItemPool, OutlinerMode nMode ) @@ -94,4 +95,19 @@ const SdrTextObj* SdrOutliner::GetTextObj() const return nullptr; } +bool SdrOutliner::hasEditViewCallbacks() const +{ + for (size_t a(0); a < GetViewCount(); a++) + { + OutlinerView* pOutlinerView = GetView(a); + + if (pOutlinerView && pOutlinerView->GetEditView().hasEditViewCallbacks()) + { + return true; + } + } + + return false; +} + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |