include/svx/diagram/IDiagramHelper.hxx               |   19 +
 include/svx/sdr/primitive2d/svx_primitivetypes2d.hxx |    1 
 sd/source/ui/func/fusel.cxx                          |   26 +
 svx/source/diagram/IDiagramHelper.cxx                |  360 ++++++++++++++++---
 4 files changed, 359 insertions(+), 47 deletions(-)

New commits:
commit 391cb44d415e2126f668ecf62387d5e98ffa6f5c
Author:     Armin Le Grand (Allotropia) <armin.le.gr...@me.com>
AuthorDate: Tue May 31 11:48:32 2022 +0200
Commit:     Armin Le Grand <armin.le.gr...@me.com>
CommitDate: Tue May 31 18:39:24 2022 +0200

    Advanced Diagram support: UI visualization & simple interactions
    
    Added visualization to show an imminently recognizable additional
    visualization for DynamicDiagrams that can also be used to
    show/hide the DiagramDialog by the user. It is also used as
    additional drag/move handle for the object.
    
    Change-Id: I56292cebe7c7a6f79be920c17edafdd7e453b6eb
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/135183
    Tested-by: Jenkins
    Reviewed-by: Armin Le Grand <armin.le.gr...@me.com>

diff --git a/include/svx/diagram/IDiagramHelper.hxx 
b/include/svx/diagram/IDiagramHelper.hxx
index c0bf0539050d..e93056f70049 100644
--- a/include/svx/diagram/IDiagramHelper.hxx
+++ b/include/svx/diagram/IDiagramHelper.hxx
@@ -22,6 +22,7 @@
 #include <vector>
 #include <svx/svxdllapi.h>
 #include <rtl/ustring.hxx>
+#include <svx/svdhdl.hxx>
 
 // Forward declarations
 class SdrObjGroup;
@@ -29,6 +30,24 @@ class SdrHdlList;
 
 namespace svx { namespace diagram {
 
+// Helper class to visualize an imminently recognizable
+// additional visualization for DynamicDiagrams that can also
+// be used to show/hide the DiagramDialog by the user
+// Note: is also used as additional drag/move handle
+class SVXCORE_DLLPUBLIC DiagramFrameHdl final : public SdrHdl
+{
+    // object dimensions
+    basegfx::B2DHomMatrix maTransformation;
+
+    // create marker for this kind
+    virtual void CreateB2dIAObject() override;
+
+public:
+    DiagramFrameHdl(const basegfx::B2DHomMatrix& rTransformation);
+
+    static void clicked(const Point& rPnt);
+};
+
 class DiagramDataState;
 
 // Helper class to allow administer advanced Diagram related
diff --git a/include/svx/sdr/primitive2d/svx_primitivetypes2d.hxx 
b/include/svx/sdr/primitive2d/svx_primitivetypes2d.hxx
index d6cb948a6bf6..0af20f04d710 100644
--- a/include/svx/sdr/primitive2d/svx_primitivetypes2d.hxx
+++ b/include/svx/sdr/primitive2d/svx_primitivetypes2d.hxx
@@ -48,6 +48,7 @@
 #define PRIMITIVE2D_ID_SDRAUTOFITTEXTPRIMITIVE2D        
(PRIMITIVE2D_ID_RANGE_SVX| 22)
 #define PRIMITIVE2D_ID_SDRCHAINEDTEXTPRIMITIVE2D        
(PRIMITIVE2D_ID_RANGE_SVX| 23)
 #define PRIMITIVE2D_ID_SDRFRAMEBORDERTPRIMITIVE2D       
(PRIMITIVE2D_ID_RANGE_SVX| 24)
+#define PRIMITIVE2D_ID_OVERLAYDIAGRAMPRIMITIVE2D        
(PRIMITIVE2D_ID_RANGE_SVX| 25)
 
 
 #endif // INCLUDED_SVX_SDR_PRIMITIVE2D_SVX_PRIMITIVETYPES2D_HXX
diff --git a/sd/source/ui/func/fusel.cxx b/sd/source/ui/func/fusel.cxx
index 5b5e37ab5779..bd82a7a97da0 100644
--- a/sd/source/ui/func/fusel.cxx
+++ b/sd/source/ui/func/fusel.cxx
@@ -55,6 +55,7 @@
 #include <svx/svdundo.hxx>
 
 #include <svx/sdrhittesthelper.hxx>
+#include <svx/diagram/IDiagramHelper.hxx>
 
 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
 #include <comphelper/lok.hxx>
@@ -674,7 +675,7 @@ bool FuSelection::MouseButtonUp(const MouseEvent& rMEvt)
             }
 
             mpView->SetDragWithCopy(bDragWithCopy);
-            mpView->EndDragObj( mpView->IsDragWithCopy() );
+            bool bWasDragged(mpView->EndDragObj( mpView->IsDragWithCopy() ));
 
             mpView->ForceMarkedToAnotherPage();
 
@@ -695,9 +696,8 @@ bool FuSelection::MouseButtonUp(const MouseEvent& rMEvt)
                     mpView->MarkObj(pObj,pPV);
                     return true;
                 }
-                /**************************************************************
-                * Toggle between selection and rotation
-                **************************************************************/
+
+                // check for single object selected
                 SdrObject* pSingleObj = nullptr;
 
                 if (mpView->GetMarkedObjectList().GetMarkCount()==1)
@@ -705,6 +705,24 @@ bool FuSelection::MouseButtonUp(const MouseEvent& rMEvt)
                     pSingleObj = 
mpView->GetMarkedObjectList().GetMark(0)->GetMarkedSdrObj();
                 }
 
+                // Check for klick on svx::diagram::DiagramFrameHdl
+                // - if we hit a SdrHdl
+                // - if it was not moved
+                // - if single object is selected
+                //   - and it is a Diagram
+                if(pHdl && !bWasDragged && nullptr != pSingleObj && 
pSingleObj->isDiagram())
+                {
+                    svx::diagram::DiagramFrameHdl* 
pDiagramFrameHdl(dynamic_cast<svx::diagram::DiagramFrameHdl*>(pHdl));
+                    if(nullptr != pDiagramFrameHdl)
+                    {
+                        // let the DiagramFrameHdl decide what to do
+                        svx::diagram::DiagramFrameHdl::clicked(aPnt);
+                    }
+                }
+
+                /**************************************************************
+                * Toggle between selection and rotation
+                **************************************************************/
                 if (nSlotId == SID_OBJECT_SELECT
                     && !comphelper::LibreOfficeKit::isActive()
                     && mpView->IsRotateAllowed()
diff --git a/svx/source/diagram/IDiagramHelper.cxx 
b/svx/source/diagram/IDiagramHelper.cxx
index 80cd2242b411..25acb3719a1c 100644
--- a/svx/source/diagram/IDiagramHelper.cxx
+++ b/svx/source/diagram/IDiagramHelper.cxx
@@ -19,29 +19,335 @@
 
 #include <svx/diagram/IDiagramHelper.hxx>
 #include <svx/svdogrp.hxx>
-
 #include <svx/svdhdl.hxx>
 #include <svx/svdmrkv.hxx>
 #include <svx/svdpagv.hxx>
 #include <svx/sdrpagewindow.hxx>
 #include <svx/sdrpaintwindow.hxx>
-#include <svx/sdr/overlay/overlaypolypolygon.hxx>
 #include <basegfx/polygon/b2dpolygontools.hxx>
+#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/primitivetools2d.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+#include <drawinglayer/attribute/lineattribute.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <comphelper/dispatchcommand.hxx>
+#include <drawinglayer/primitive2d/textlayoutdevice.hxx>
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
 
 namespace {
 
-class DiagramFrameHdl final : public SdrHdl
+// helper to create the geometry for a rounded polygon, maybe
+// containing a Lap positioned inside top-left for some text
+basegfx::B2DPolygon createRoundedPolygon(
+    const basegfx::B2DRange& rRange,
+    double fDistance,
+    bool bCreateLap,
+    double fTextWidth)
 {
-    basegfx::B2DHomMatrix maTransformation;
+    basegfx::B2DPolygon aRetval;
+
+    // TopLeft rounded edge
+    aRetval.append(
+        basegfx::utils::createPolygonFromEllipseSegment(
+            basegfx::B2DPoint(rRange.getMinX(), rRange.getMinY()),
+            fDistance,
+            fDistance,
+            M_PI * 1.0,
+            M_PI * 1.5));
+
+    // create Lap topLeft inside
+    if(bCreateLap)
+    {
+        const double fLapLeft(rRange.getMinX() + fDistance);
+        double fLapRight(rRange.getMinX() + (rRange.getWidth() * 0.5)  - 
fDistance);
+        const double fLapTop(rRange.getMinY() - fDistance);
+        const double fLapBottom(fLapTop + (fDistance * 2.0));
+        const double fExtendedTextWidth(fTextWidth + (fDistance * 3.0));
+
+        if(0.0 != fExtendedTextWidth && fLapLeft + fExtendedTextWidth < 
fLapRight)
+        {
+            fLapRight = fLapLeft + fExtendedTextWidth;
+        }
+
+        aRetval.append(basegfx::B2DPoint(fLapLeft, fLapTop));
+        aRetval.append(basegfx::B2DPoint(fLapLeft + (fDistance * 0.5), 
fLapBottom));
+        aRetval.append(basegfx::B2DPoint(fLapRight - (fDistance * 0.5), 
fLapBottom));
+        aRetval.append(basegfx::B2DPoint(fLapRight, fLapTop));
+    }
+
+    // TopRight rounded edge
+    aRetval.append(
+        basegfx::utils::createPolygonFromEllipseSegment(
+            basegfx::B2DPoint(rRange.getMaxX(), rRange.getMinY()),
+            fDistance,
+            fDistance,
+            M_PI * 1.5,
+            M_PI * 0.0));
+
+    // BottomRight rounded edge
+    aRetval.append(
+        basegfx::utils::createPolygonFromEllipseSegment(
+            basegfx::B2DPoint(rRange.getMaxX(), rRange.getMaxY()),
+            fDistance,
+            fDistance,
+            M_PI * 0.0,
+            M_PI * 0.5));
+
+    // BottomLeft rounded edge
+    aRetval.append(
+        basegfx::utils::createPolygonFromEllipseSegment(
+            basegfx::B2DPoint(rRange.getMinX(), rRange.getMaxY()),
+            fDistance,
+            fDistance,
+            M_PI * 0.5,
+            M_PI * 1.0));
 
-    // create marker for this kind
-    virtual void CreateB2dIAObject() override;
+    aRetval.setClosed(true);
+
+    return aRetval;
+}
+
+// helper primitive to create/show the overlay geometry for a DynamicDiagram
+class OverlayDiagramPrimitive final : public 
drawinglayer::primitive2d::DiscreteMetricDependentPrimitive2D
+{
+private:
+    basegfx::B2DHomMatrix maTransformation;  // object dimensions
+    double mfDiscreteDistance; // distance from object in pixels
+    double mfDiscreteGap; // gap/widh of visualization in pixels
+    Color maColor;  // base color (made lighter/darker as needed, should be 
system selection color)
+
+    virtual void create2DDecomposition(
+        drawinglayer::primitive2d::Primitive2DContainer& rContainer,
+        const drawinglayer::geometry::ViewInformation2D& rViewInformation) 
const override;
 
 public:
-    DiagramFrameHdl(const basegfx::B2DHomMatrix& rTransformation);
-    // virtual ~DiagramFrameHdl() override;
+    OverlayDiagramPrimitive(
+        const basegfx::B2DHomMatrix& rTransformation,
+        double fDiscreteDistance,
+        double fDiscreteGap,
+        Color const & rColor);
+
+    virtual sal_uInt32 getPrimitive2DID() const override;
 };
 
+void OverlayDiagramPrimitive::create2DDecomposition(
+    drawinglayer::primitive2d::Primitive2DContainer& rContainer,
+    const drawinglayer::geometry::ViewInformation2D& /*rViewInformation*/) 
const
+{
+    // get the dimensions. Do *not* take rotation/shear into account,
+    // this is intended to be a pure expanded/frame visualization as
+    // needed in UI for simplified visualization
+    basegfx::B2DRange aRange(0.0, 0.0, 1.0, 1.0);
+    aRange.transform(maTransformation);
+
+    basegfx::B2DPolyPolygon aPolyPolygon;
+    const double fInnerDistance(mfDiscreteDistance * getDiscreteUnit());
+    const double fOuterDistance((mfDiscreteDistance + mfDiscreteGap) * 
getDiscreteUnit());
+    bool bCreateLap(true);
+    basegfx::B2DPolyPolygon aTextAsPolyPolygon;
+    double fTextWidth(0.0);
+
+    // initially try to create lap
+    if(bCreateLap)
+    {
+        // take a ressource text (for now existing one that fits)
+        const OUString aName(SvxResId(RID_STR_DATANAV_EDIT_ELEMENT));
+        drawinglayer::primitive2d::TextLayouterDevice aTextLayouter;
+        basegfx::B2DPolyPolygonVector aTarget;
+        std::vector<double> aDXArray;
+
+        // to simplify things for now, do not create a 
TextSimplePortionPrimitive2D
+        // and needed FontAttribute, just get the TextOutlines as geometry
+        aTextLayouter.getTextOutlines(
+            aTarget,
+            aName,
+            0,
+            aName.getLength(),
+            aDXArray);
+
+        // put into one PolyPolygon (also simplification - overlapping chars
+        // may create XOR gaps, so these exist for a reason, but low 
probability)
+        for (auto const& elem : aTarget)
+        {
+            aTextAsPolyPolygon.append(elem);
+        }
+
+        // get text dimensions & transform to destination
+        const basegfx::B2DRange aTextRange(aTextAsPolyPolygon.getB2DRange());
+        basegfx::B2DHomMatrix aTextTransform;
+
+        aTextTransform.translate(aTextRange.getMinX(), aTextRange.getMinY());
+        const double fTargetTextHeight((mfDiscreteDistance + mfDiscreteGap - 
2.0) * getDiscreteUnit());
+        const double fTextScale(fTargetTextHeight / aTextRange.getHeight());
+        aTextTransform.scale(fTextScale, fTextScale);
+        aTextTransform.translate(
+            aRange.getMinX() + (fInnerDistance * 2.0),
+            aRange.getMinY() + fTargetTextHeight + (fOuterDistance - 
fInnerDistance) - (2.0 * getDiscreteUnit()));
+        aTextAsPolyPolygon.transform(aTextTransform);
+
+        // check text size/position
+        fTextWidth = aTextRange.getWidth() * fTextScale;
+        const double fLapLeft(aRange.getMinX() + fInnerDistance);
+        const double fLapRight(aRange.getMinX() + (aRange.getWidth() * 0.5)  - 
fInnerDistance);
+
+        // if text is too big, do not create a Lap at all
+        // to avoid trouble. It is expected that the user keeps
+        // the object he works with big enough to do useful actions
+        if(fTextWidth + (4.0 * getDiscreteUnit()) > fLapRight - fLapLeft)
+            bCreateLap = false;
+    }
+
+    // create outer polygon
+    aPolyPolygon.append(
+        createRoundedPolygon(
+            aRange,
+            fOuterDistance,
+            false,
+            0.0));
+
+    // create inner polygon, maybe with Lap
+    aPolyPolygon.append(
+        createRoundedPolygon(
+            aRange,
+            fInnerDistance,
+            bCreateLap,
+            fTextWidth));
+
+    Color aFillColor(maColor);
+    Color aLineColor(maColor);
+
+    aFillColor.IncreaseLuminance(10);
+    aLineColor.DecreaseLuminance(30);
+
+    const drawinglayer::attribute::LineAttribute aLineAttribute(
+        aLineColor.getBColor(),
+        1.0 * getDiscreteUnit());
+
+    // filled polygon as BG (may get transparence for better look ?)
+    rContainer.push_back(
+        new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
+            aPolyPolygon,
+            aFillColor.getBColor()));
+
+    // outline polygon for visibility (may be accentuated shaded
+    // top/left, would require alternative creation)
+    rContainer.push_back(
+        new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
+            aPolyPolygon,
+            aLineAttribute));
+
+    // top-left line pattern (as grep-here-sign to signal
+    // that this construct may be also dragged by the user)
+    const double fLapLeft(aRange.getMinX() + fInnerDistance);
+    const double fLapRight(aRange.getMinX() + (aRange.getWidth() * 0.5)  - 
fInnerDistance);
+    const double  fLapUp(aRange.getMinY() - ((mfDiscreteDistance + 
mfDiscreteDistance * 0.666) * getDiscreteUnit()));
+    const double  fLapDown(aRange.getMinY() - ((mfDiscreteDistance + 
mfDiscreteDistance * 0.333) * getDiscreteUnit()));
+    basegfx::B2DPolygon aPolygonLapUp;
+    aPolygonLapUp.append(basegfx::B2DPoint(fLapLeft, fLapUp));
+    aPolygonLapUp.append(basegfx::B2DPoint(fLapRight, fLapUp));
+    basegfx::B2DPolygon aPolygonLapDown;
+    aPolygonLapDown.append(basegfx::B2DPoint(fLapLeft, fLapDown));
+    aPolygonLapDown.append(basegfx::B2DPoint(fLapRight, fLapDown));
+    drawinglayer::attribute::StrokeAttribute aStrokeAttribute({ 2.0 * 
getDiscreteUnit(), 2.0 * getDiscreteUnit() });
+
+    rContainer.push_back(
+        new drawinglayer::primitive2d::PolygonStrokePrimitive2D(
+            aPolygonLapUp,
+            aLineAttribute,
+            aStrokeAttribute));
+
+    rContainer.push_back(
+        new drawinglayer::primitive2d::PolygonStrokePrimitive2D(
+            aPolygonLapDown,
+            aLineAttribute,
+            aStrokeAttribute));
+
+    // add text last. May use darker text color, go for same color
+    // as accentuation line for now
+    if(bCreateLap && 0 != aTextAsPolyPolygon.count())
+    {
+        rContainer.push_back(
+            new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
+                aTextAsPolyPolygon,
+                aLineColor.getBColor()));
+    }
+}
+
+OverlayDiagramPrimitive::OverlayDiagramPrimitive(
+    const basegfx::B2DHomMatrix& rTransformation,
+    double fDiscreteDistance,
+    double fDiscreteGap,
+    Color const & rColor)
+: drawinglayer::primitive2d::DiscreteMetricDependentPrimitive2D()
+, maTransformation(rTransformation)
+, mfDiscreteDistance(fDiscreteDistance)
+, mfDiscreteGap(fDiscreteGap)
+, maColor(rColor)
+{
+}
+
+sal_uInt32 OverlayDiagramPrimitive::getPrimitive2DID() const
+{
+    return PRIMITIVE2D_ID_OVERLAYDIAGRAMPRIMITIVE2D;
+}
+
+// helper object for DiagramOverlay
+class OverlayDiagramFrame final : public sdr::overlay::OverlayObject
+{
+private:
+    basegfx::B2DHomMatrix maTransformation; // object dimensions
+    Color maColor; // base color
+
+    virtual drawinglayer::primitive2d::Primitive2DContainer 
createOverlayObjectPrimitive2DSequence() override;
+
+public:
+    explicit OverlayDiagramFrame(
+        const basegfx::B2DHomMatrix& rTransformation,
+        Color const & rColor);
+};
+
+OverlayDiagramFrame::OverlayDiagramFrame(
+    const basegfx::B2DHomMatrix& rTransformation,
+    const Color& rColor)
+: sdr::overlay::OverlayObject(rColor)
+, maTransformation(rTransformation)
+, maColor(rColor)
+{
+}
+
+drawinglayer::primitive2d::Primitive2DContainer 
OverlayDiagramFrame::createOverlayObjectPrimitive2DSequence()
+{
+    drawinglayer::primitive2d::Primitive2DContainer aReturnContainer;
+
+    if (getOverlayManager())
+    {
+        aReturnContainer = drawinglayer::primitive2d::Primitive2DContainer {
+            new OverlayDiagramPrimitive(
+                maTransformation,
+                8.0, // distance from geometry in pixels
+                8.0, // gap/width of visualization in pixels
+                maColor) };
+    }
+
+    return aReturnContainer;
+}
+
+} // end of anonymous namespace
+
+namespace svx { namespace diagram {
+
+void DiagramFrameHdl::clicked(const Point& /*rPnt*/)
+{
+    // this may check for a direct hit at the text later
+    // and only then take action. That would require
+    // to evaluate & keep that (maybe during creation).
+    // For now, just trigger to open the Dialog
+    comphelper::dispatchCommand(".uno:EditDiagram", {});
+}
+
 void DiagramFrameHdl::CreateB2dIAObject()
 {
     // first throw away old one
@@ -67,36 +373,12 @@ void DiagramFrameHdl::CreateB2dIAObject()
             if (xManager.is())
             {
                 OutputDevice& 
rOutDev(rPageWindow.GetPaintWindow().GetOutputDevice());
-                const basegfx::B2DVector 
aDiscreteInLogic(rOutDev.GetInverseViewTransformation() * 
basegfx::B2DVector(1.0, 0.0));
-                const double fDiscretePixelSize(aDiscreteInLogic.getLength());
-                const double fOuterDistance(7.0);
-                const double fInnerDistance(5.0);
-
-                basegfx::B2DRange aRange(0.0, 0.0, 1.0, 1.0);
-                aRange.transform(maTransformation);
-
-                basegfx::B2DRange aOuterRange(aRange);
-                aOuterRange.grow(fDiscretePixelSize * fOuterDistance);
-
-                basegfx::B2DRange aInnerRange(aRange);
-                aInnerRange.grow(fDiscretePixelSize * fInnerDistance);
-
-                basegfx::B2DPolyPolygon aPolyPolygon;
-                
aPolyPolygon.append(basegfx::utils::createPolygonFromRect(aOuterRange));
-                
aPolyPolygon.append(basegfx::utils::createPolygonFromRect(aInnerRange));
-
                 const StyleSettings& 
rStyles(rOutDev.GetSettings().GetStyleSettings());
                 Color aFillColor(rStyles.GetHighlightColor());
-                Color aLineColor(aFillColor);
-                aFillColor.IncreaseLuminance(10);
-                aLineColor.DecreaseLuminance(30);
-
                 std::unique_ptr<sdr::overlay::OverlayObject> pNewOverlayObject(
-                    new sdr::overlay::OverlayPolyPolygon(
-                            aPolyPolygon,
-                            aLineColor,  // Line
-                            fDiscretePixelSize * 2.0,
-                            aFillColor)); // Fill
+                    new OverlayDiagramFrame(
+                        maTransformation,
+                        aFillColor));
 
                 // OVERLAYMANAGER
                 insertNewlyCreatedOverlayObjectForSdrHdl(
@@ -114,14 +396,6 @@ DiagramFrameHdl::DiagramFrameHdl(const 
basegfx::B2DHomMatrix& rTransformation)
 {
 }
 
-// DiagramFrameHdl::~DiagramFrameHdl()
-// {
-// }
-
-} // end of anonymous namespace
-
-namespace svx { namespace diagram {
-
 IDiagramHelper::IDiagramHelper()
 : mbUseDiagramThemeData(false)
 , mbUseDiagramModelData(true)

Reply via email to