summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArmin Le Grand <Armin.Le.Grand@cib.de>2017-08-04 18:56:43 +0200
committerArmin Le Grand <Armin.Le.Grand@cib.de>2017-08-16 19:03:08 +0200
commit2662c11294cef950204b798333b4cd2e9c2ddcb3 (patch)
treef936a9f81936cf8471bc4f0f94253f0ef8f55ebc
parente4de923e0e771e40414d6b3b9b77389b457aa562 (diff)
editviewoverlay: Allow EditView to run in Overlay
This is the first basic functionality to get the active EditView with EditEngine work in the Overlay which all Apps support. Reason is that the current EditEngine 'plugs' into the Window and uses Invalidate() calls to repaint deeply everything under a text change. While this is acceptable for simple cases it can get very slow when there are excessive, expensive to paint objects in the background, e.g. MasterPages in Draw/Impress with gradients and other stuff. This was avoided in older versions (LO51) by 'guessing' a good BackgrundColor by the EditEngine, not invalidating but painting actively using that guess (with better or worse results) which someone removed. For the future it is anyways the better way to get the EditEngine functionality to Overlay and using Primitives, this will be a first step. This may enable Text Editing without repainting the Background (fast), using a non-XOR selection paint and more. It will need thorough testing and further experimenting due to EditEngine being used in many places (DrawObjects, Calc Cells, Formular Fields, Controls, ...) Change-Id: Ib9eb0f3999fd61a82ddf7a60ab1ea6ccda3a60da
-rw-r--r--editeng/source/editeng/editview.cxx33
-rw-r--r--editeng/source/editeng/impedit.cxx84
-rw-r--r--editeng/source/editeng/impedit.hxx21
-rw-r--r--editeng/source/editeng/impedit3.cxx4
-rw-r--r--include/editeng/editview.hxx22
-rw-r--r--include/svx/svdedxv.hxx12
-rw-r--r--include/svx/svdotext.hxx10
-rw-r--r--include/svx/svdoutl.hxx2
-rw-r--r--sd/source/ui/view/Outliner.cxx11
-rw-r--r--svx/source/svdraw/svdedxv.cxx376
-rw-r--r--svx/source/svdraw/svdotextdecomposition.cxx13
-rw-r--r--svx/source/svdraw/svdotxat.cxx20
-rw-r--r--svx/source/svdraw/svdoutl.cxx16
13 files changed, 578 insertions, 46 deletions
diff --git a/editeng/source/editeng/editview.cxx b/editeng/source/editeng/editview.cxx
index 609f1d032ae4..d7db92bf6039 100644
--- a/editeng/source/editeng/editview.cxx
+++ b/editeng/source/editeng/editview.cxx
@@ -151,8 +151,12 @@ LanguageType EditView::CheckLanguage(
return nLang;
}
-// class EditView
+// class EditViewCallbacks
+EditViewCallbacks::~EditViewCallbacks()
+{
+}
+// class EditView
EditView::EditView( EditEngine* pEng, vcl::Window* pWindow )
{
pImpEditView = new ImpEditView( this, pEng, pWindow );
@@ -163,6 +167,16 @@ EditView::~EditView()
delete pImpEditView;
}
+void EditView::setEditViewCallbacks(const EditViewCallbacks* pEditViewCallbacks)
+{
+ pImpEditView->setEditViewCallbacks(pEditViewCallbacks);
+}
+
+bool EditView::hasEditViewCallbacks() const
+{
+ return pImpEditView->hasEditViewCallbacks();
+}
+
ImpEditEngine* EditView::GetImpEditEngine() const
{
return pImpEditView->pEditEngine->pImpEditEngine;
@@ -189,6 +203,23 @@ void EditView::Invalidate()
}
}
+void EditView::InvalidateWindow(const 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::SetReadOnly( bool bReadOnly )
{
pImpEditView->bReadOnly = bReadOnly;
diff --git a/editeng/source/editeng/impedit.cxx b/editeng/source/editeng/impedit.cxx
index 13d98c05a010..82bfaeaebfa3 100644
--- a/editeng/source/editeng/impedit.cxx
+++ b/editeng/source/editeng/impedit.cxx
@@ -89,6 +89,7 @@ ImpEditView::ImpEditView( EditView* pView, EditEngine* pEng, vcl::Window* pWindo
bClickedInSelection = false;
eSelectionMode = EE_SELMODE_TXTONLY;
eAnchorMode = ANCHOR_TOP_LEFT;
+ mpEditViewCallbacks = nullptr;
nInvMore = 1;
nTravelXPos = TRAVEL_X_DONTKNOW;
nControl = EVControlBits::AUTOSCROLL | EVControlBits::ENABLEPASTE;
@@ -163,7 +164,36 @@ void ImpEditView::SetEditSelection( const EditSelection& rEditSelection )
void ImpEditView::DrawSelection( EditSelection aTmpSel, vcl::Region* pRegion, OutputDevice* pTargetDevice )
{
- if ( GetSelectionMode() == EE_SELMODE_HIDDEN )
+ 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<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 (GetSelectionMode() == EE_SELMODE_HIDDEN)
return;
// It must be ensured before rendering the selection, that the contents of
@@ -377,16 +407,9 @@ void ImpEditView::DrawSelection( EditSelection aTmpSel, vcl::Region* pRegion, Ou
void ImpEditView::GetSelectionRectangles(std::vector<Rectangle>& rLogicRects)
{
- bool bMm100ToTwip = pOutWin->GetMapMode().GetMapUnit() == MAP_100TH_MM;
vcl::Region aRegion;
DrawSelection(aEditSelection, &aRegion);
aRegion.GetRegionRectangles(rLogicRects);
-
- for (Rectangle& rRectangle : rLogicRects)
- {
- if (bMm100ToTwip)
- rRectangle = OutputDevice::LogicToLogic(rRectangle, MAP_100TH_MM, MAP_TWIP);
- }
}
void ImpEditView::ImplDrawHighlightRect( OutputDevice* _pTarget, const Point& rDocPosTopLeft, const Point& rDocPosBottomRight, tools::PolyPolygon* pPolyPoly )
@@ -534,6 +557,23 @@ void ImpEditView::SetOutputArea( const Rectangle& rRect )
SetScrollDiffX( (sal_uInt16)aOutArea.GetWidth() * 2 / 10 );
}
+void ImpEditView::InvalidateAtWindow(const 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 Rectangle& rRect )
{
// remember old out area
@@ -550,38 +590,46 @@ void ImpEditView::ResetOutputArea( const Rectangle& rRect )
if(aOldArea.Left() > aOutArea.Left())
{
- GetWindow()->Invalidate(Rectangle(aOutArea.Left() - nMore, aOldArea.Top() - nMore, aOldArea.Left(), aOldArea.Bottom() + nMore));
+ const Rectangle aRect(aOutArea.Left() - nMore, aOldArea.Top() - nMore, aOldArea.Left(), aOldArea.Bottom() + nMore);
+ InvalidateAtWindow(aRect);
}
else if(aOldArea.Left() < aOutArea.Left())
{
- GetWindow()->Invalidate(Rectangle(aOldArea.Left() - nMore, aOldArea.Top() - nMore, aOutArea.Left(), aOldArea.Bottom() + nMore));
+ const Rectangle aRect(aOldArea.Left() - nMore, aOldArea.Top() - nMore, aOutArea.Left(), aOldArea.Bottom() + nMore);
+ InvalidateAtWindow(aRect);
}
if(aOldArea.Right() > aOutArea.Right())
{
- GetWindow()->Invalidate(Rectangle(aOutArea.Right(), aOldArea.Top() - nMore, aOldArea.Right() + nMore, aOldArea.Bottom() + nMore));
+ const Rectangle aRect(aOutArea.Right(), aOldArea.Top() - nMore, aOldArea.Right() + nMore, aOldArea.Bottom() + nMore);
+ InvalidateAtWindow(aRect);
}
else if(aOldArea.Right() < aOutArea.Right())
{
- GetWindow()->Invalidate(Rectangle(aOldArea.Right(), aOldArea.Top() - nMore, aOutArea.Right() + nMore, aOldArea.Bottom() + nMore));
+ const Rectangle aRect(aOldArea.Right(), aOldArea.Top() - nMore, aOutArea.Right() + nMore, aOldArea.Bottom() + nMore);
+ InvalidateAtWindow(aRect);
}
if(aOldArea.Top() > aOutArea.Top())
{
- GetWindow()->Invalidate(Rectangle(aOldArea.Left() - nMore, aOutArea.Top() - nMore, aOldArea.Right() + nMore, aOldArea.Top()));
+ const Rectangle aRect(aOldArea.Left() - nMore, aOutArea.Top() - nMore, aOldArea.Right() + nMore, aOldArea.Top());
+ InvalidateAtWindow(aRect);
}
else if(aOldArea.Top() < aOutArea.Top())
{
- GetWindow()->Invalidate(Rectangle(aOldArea.Left() - nMore, aOldArea.Top() - nMore, aOldArea.Right() + nMore, aOutArea.Top()));
+ const Rectangle aRect(aOldArea.Left() - nMore, aOldArea.Top() - nMore, aOldArea.Right() + nMore, aOutArea.Top());
+ InvalidateAtWindow(aRect);
}
if(aOldArea.Bottom() > aOutArea.Bottom())
{
- GetWindow()->Invalidate(Rectangle(aOldArea.Left() - nMore, aOutArea.Bottom(), aOldArea.Right() + nMore, aOldArea.Bottom() + nMore));
+ const Rectangle aRect(aOldArea.Left() - nMore, aOutArea.Bottom(), aOldArea.Right() + nMore, aOldArea.Bottom() + nMore);
+ InvalidateAtWindow(aRect);
}
else if(aOldArea.Bottom() < aOutArea.Bottom())
{
- GetWindow()->Invalidate(Rectangle(aOldArea.Left() - nMore, aOldArea.Bottom(), aOldArea.Right() + nMore, aOutArea.Bottom() + nMore));
+ const Rectangle aRect(aOldArea.Left() - nMore, aOldArea.Bottom(), aOldArea.Right() + nMore, aOutArea.Bottom() + nMore);
+ InvalidateAtWindow(aRect);
}
}
}
@@ -1503,6 +1551,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 0a1ac52911e3..b323ca026e68 100644
--- a/editeng/source/editeng/impedit.hxx
+++ b/editeng/source/editeng/impedit.hxx
@@ -247,6 +247,27 @@ private:
EditSelection aEditSelection;
EVAnchorMode 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 Rectangle& rRect);
+
protected:
// DragAndDropClient
diff --git a/editeng/source/editeng/impedit3.cxx b/editeng/source/editeng/impedit3.cxx
index 0f75baa2ab83..7ba4d98d0ec4 100644
--- a/editeng/source/editeng/impedit3.cxx
+++ b/editeng/source/editeng/impedit3.cxx
@@ -288,7 +288,9 @@ 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);
}
}
diff --git a/include/editeng/editview.hxx b/include/editeng/editview.hxx
index 163ea7c6653c..f3f85a990004 100644
--- a/include/editeng/editview.hxx
+++ b/include/editeng/editview.hxx
@@ -70,6 +70,7 @@ namespace linguistic2 {
class XLanguageGuessing;
}
}}}
+namespace basegfx { class B2DRange; }
enum class ScrollRangeCheck
{
@@ -78,6 +79,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
{
@@ -100,6 +116,10 @@ public:
EditView( EditEngine* pEng, vcl::Window* pWindow );
virtual ~EditView();
+ // set EditViewCallbacks for external handling of Repaints/Visualization
+ void setEditViewCallbacks(const EditViewCallbacks* pEditViewCallbacks);
+ bool hasEditViewCallbacks() const;
+
void SetEditEngine( EditEngine* pEditEngine );
EditEngine* GetEditEngine() const;
@@ -107,6 +127,8 @@ public:
vcl::Window* GetWindow() const;
void Paint( const Rectangle& rRect, OutputDevice* pTargetDevice = nullptr );
+ Rectangle GetInvalidateRect() const;
+ void InvalidateWindow(const Rectangle& rClipRect);
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 519ba6eb5e2e..090f34994f58 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;
@@ -55,11 +56,20 @@ enum SdrEndTextEditKind {SDRENDTEXTEDIT_UNCHANGED, // textobject unchanged
// - 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 2adf9949ed1c..692d3b036979 100644
--- a/include/svx/svdotext.hxx
+++ b/include/svx/svdotext.hxx
@@ -616,11 +616,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 c76fbb9c2345..ea78a466eefc 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 42d4a20cbda7..3a4ba9e87d48 100644
--- a/sd/source/ui/view/Outliner.cxx
+++ b/sd/source/ui/view/Outliner.cxx
@@ -792,6 +792,17 @@ bool Outliner::SearchAndReplaceOnce(std::vector<SearchSelection>* pSelections)
std::vector<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::MAP_100TH_MM == pOutlinerView->GetWindow()->GetMapMode().GetMapUnit())
+ {
+ for (Rectangle& rRectangle : aLogicRects)
+ {
+ rRectangle = OutputDevice::LogicToLogic(rRectangle, MapUnit::MAP_100TH_MM, MapUnit::MAP_TWIP);
+ }
+ }
+
std::vector<OString> aLogicRectStrings;
std::transform(aLogicRects.begin(), aLogicRects.end(), std::back_inserter(aLogicRectStrings), [](const 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 968738c6fde0..78560b5fd9e3 100644
--- a/svx/source/svdraw/svdedxv.cxx
+++ b/svx/source/svdraw/svdedxv.cxx
@@ -63,7 +63,11 @@
#include <svx/sdr/table/tablecontroller.hxx>
#include <drawinglayer/processor2d/processor2dtools.hxx>
#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>
void SdrObjEditView::ImpClearVars()
@@ -102,7 +106,6 @@ SdrObjEditView::~SdrObjEditView()
delete mpOldTextEditUndoManager;
}
-
bool SdrObjEditView::IsAction() const
{
return IsMacroObj() || SdrGlueEditView::IsAction();
@@ -286,39 +289,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 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
+ 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())
+ {
+ // 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);
+ }
+ }
+ }
+}
+
+void SdrObjEditView::EditViewSelectionChange(const std::vector<basegfx::B2DRange>& rLogicRanges) const
+{
+ if (IsTextEdit())
{
- const SdrOutliner* pActiveOutliner = GetTextEditOutliner();
+ for (sal_uInt32 a(0); a < maTEOverlayGroup.count(); a++)
+ {
+ TextEditOverlayObject* pCandidate = dynamic_cast< TextEditOverlayObject* >(&maTEOverlayGroup.getOverlayObject(a));
+
+ if (pCandidate)
+ {
+ pCandidate->checkSelectionChange(rLogicRanges);
+ }
+ }
+ }
+}
- if(pActiveOutliner)
+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 sal_uInt32 nViewCount(pActiveOutliner->GetViewCount());
+ const SdrOutliner* pActiveOutliner = GetTextEditOutliner();
- if(nViewCount)
+ if (pActiveOutliner)
{
- const vcl::Region& rRedrawRegion = rPaintWindow.GetRedrawRegion();
- const Rectangle aCheckRect(rRedrawRegion.GetBoundRect());
+ const sal_uInt32 nViewCount(pActiveOutliner->GetViewCount());
- for(sal_uInt32 i(0); i < nViewCount; i++)
+ 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 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;
+ }
}
}
}
@@ -446,8 +708,16 @@ OutlinerView* SdrObjEditView::ImpMakeOutlinerView(vcl::Window* pWin, bool /*bNoP
// 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;
@@ -785,6 +1055,57 @@ bool SdrObjEditView::SdrBeginTextEdit(
pTextEditOutlinerView=ImpMakeOutlinerView(pWin,!bEmpty,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::OVERLAY_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++ )
@@ -973,6 +1294,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 3de59d7deed0..c571f2a26ba8 100644
--- a/svx/source/svdraw/svdotextdecomposition.cxx
+++ b/svx/source/svdraw/svdotextdecomposition.cxx
@@ -1614,5 +1614,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 f78499f84818..140f6975befe 100644
--- a/svx/source/svdraw/svdotxat.cxx
+++ b/svx/source/svdraw/svdotxat.cxx
@@ -288,8 +288,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(SDRUSERCALL_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: */