From c61ad057896d134bb2e494bb0f643e24edd7df76 Mon Sep 17 00:00:00 2001 From: theo-vt Date: Sun, 24 Aug 2025 02:36:41 -0400 Subject: [PATCH] Techdraw: Undo/redo when dragging views and rework projection group drag (#22875) * TechDraw: create a transaction when finished dragging a view * TechDraw: drag projection group when dragging a subview in AutoDistribute is enabled * TechDraw: avoid creating 'Drag View' transaction if the document is already in a transaction * TechDraw: Apply suggestions from code review --------- Co-authored-by: Benjamin Nauck --- src/Mod/TechDraw/Gui/QGIProjGroup.cpp | 68 +++++++++++-------- src/Mod/TechDraw/Gui/QGIProjGroup.h | 5 +- src/Mod/TechDraw/Gui/QGIView.cpp | 66 +++++++++++++++--- src/Mod/TechDraw/Gui/QGIView.h | 2 + .../Gui/ViewProviderProjGroupItem.cpp | 1 + 5 files changed, 103 insertions(+), 39 deletions(-) diff --git a/src/Mod/TechDraw/Gui/QGIProjGroup.cpp b/src/Mod/TechDraw/Gui/QGIProjGroup.cpp index 5c5259bf9e..af8f59563a 100644 --- a/src/Mod/TechDraw/Gui/QGIProjGroup.cpp +++ b/src/Mod/TechDraw/Gui/QGIProjGroup.cpp @@ -55,6 +55,10 @@ TechDraw::DrawProjGroup * QGIProjGroup::getDrawView() const App::DocumentObject *obj = getViewObject(); return dynamic_cast(obj); } +bool QGIProjGroup::autoDistributeEnabled() const +{ + return getDrawView() && getDrawView()->AutoDistribute.getValue(); +} bool QGIProjGroup::sceneEventFilter(QGraphicsItem* watched, QEvent *event) { @@ -64,27 +68,34 @@ bool QGIProjGroup::sceneEventFilter(QGraphicsItem* watched, QEvent *event) event->type() == QEvent::GraphicsSceneMouseRelease) { QGIView *qAnchor = getAnchorQItem(); - if(qAnchor && watched == qAnchor) { + QGIView* qWatched = dynamic_cast(watched); + // If AutoDistribute is enabled, catch events and move the anchor directly + if(qAnchor && (watched == qAnchor || (autoDistributeEnabled() && qWatched != nullptr))) { auto *mEvent = dynamic_cast(event); - switch(event->type()) { - case QEvent::GraphicsSceneMousePress: - // TODO - Perhaps just pass the mouse event on to the anchor somehow? - if (scene() && !qAnchor->isSelected()) { - scene()->clearSelection(); - qAnchor->setSelected(true); - } - mousePressEvent(mEvent); - break; - case QEvent::GraphicsSceneMouseMove: - mouseMoveEvent(mEvent); - break; - case QEvent::GraphicsSceneMouseRelease: - mouseReleaseEvent(mEvent); - break; - default: - break; + // Disable moves on the view to prevent double drag + bool initCanMove = qWatched->flags() & QGraphicsItem::ItemIsMovable; + qWatched->setFlag(QGraphicsItem::ItemIsMovable, false); + switch (event->type()) { + case QEvent::GraphicsSceneMousePress: + // TODO - Perhaps just pass the mouse event on to the watched item somehow? + if (scene() && !qWatched->isSelected()) { + scene()->clearSelection(); + qWatched->setSelected(true); + } + mousePressEvent(mEvent); + break; + case QEvent::GraphicsSceneMouseMove: + mouseMoveEvent(mEvent); + break; + case QEvent::GraphicsSceneMouseRelease: + mouseReleaseEvent(qWatched, mEvent); + break; + default: + break; } + // Restore flag + qWatched->setFlag(QGraphicsItem::ItemIsMovable, initCanMove); return true; } } @@ -134,7 +145,7 @@ void QGIProjGroup::mousePressEvent(QGraphicsSceneMouseEvent * event) QGIView *qAnchor = getAnchorQItem(); if(qAnchor) { QPointF transPos = qAnchor->mapFromScene(event->scenePos()); - if(qAnchor->shape().contains(transPos)) { + if(qAnchor->shape().contains(transPos) || autoDistributeEnabled()) { mousePos = event->screenPos(); } } @@ -144,28 +155,29 @@ void QGIProjGroup::mousePressEvent(QGraphicsSceneMouseEvent * event) void QGIProjGroup::mouseMoveEvent(QGraphicsSceneMouseEvent * event) { QGIView *qAnchor = getAnchorQItem(); - if(scene() && qAnchor && (qAnchor == scene()->mouseGrabberItem())) { + if(scene() && qAnchor && (qAnchor == scene()->mouseGrabberItem() || autoDistributeEnabled())) { if((mousePos - event->screenPos()).manhattanLength() > 5) { //if the mouse has moved more than 5, process the mouse event QGIViewCollection::mouseMoveEvent(event); } - } event->accept(); } void QGIProjGroup::mouseReleaseEvent(QGraphicsSceneMouseEvent * event) { - if(scene()) { - QGIView *qAnchor = getAnchorQItem(); + mouseReleaseEvent(getAnchorQItem(), event); +} +void QGIProjGroup::mouseReleaseEvent(QGIView* originator, QGraphicsSceneMouseEvent* event) +{ + if(scene()) { if((mousePos - event->screenPos()).manhattanLength() < 5) { - if(qAnchor && qAnchor->shape().contains(event->pos())) { + if(originator && originator->shape().contains(event->pos())) { event->ignore(); - qAnchor->mouseReleaseEvent(event); + originator->mouseReleaseEvent(event); } } - else if(scene() && qAnchor) { - // End of Drag - getViewObject()->setPosition(Rez::appX(x()), Rez::appX(getY())); + else if(scene() && originator) { + dragFinished(); } } QGIViewCollection::mouseReleaseEvent(event); diff --git a/src/Mod/TechDraw/Gui/QGIProjGroup.h b/src/Mod/TechDraw/Gui/QGIProjGroup.h index 88343834ee..4b67f25d2f 100644 --- a/src/Mod/TechDraw/Gui/QGIProjGroup.h +++ b/src/Mod/TechDraw/Gui/QGIProjGroup.h @@ -66,11 +66,14 @@ protected: void mouseMoveEvent(QGraphicsSceneMouseEvent * event ) override; void mousePressEvent(QGraphicsSceneMouseEvent * event) override; void mouseReleaseEvent(QGraphicsSceneMouseEvent * event) override; - QGIView * getAnchorQItem() const; + + void mouseReleaseEvent(QGIView* originator, QGraphicsSceneMouseEvent* event); + QGIView* getAnchorQItem() const; private: /// Convenience function TechDraw::DrawProjGroup* getDrawView() const; + bool autoDistributeEnabled() const; QGraphicsItem* m_origin; QPoint mousePos; diff --git a/src/Mod/TechDraw/Gui/QGIView.cpp b/src/Mod/TechDraw/Gui/QGIView.cpp index 9953254c11..6952d47879 100644 --- a/src/Mod/TechDraw/Gui/QGIView.cpp +++ b/src/Mod/TechDraw/Gui/QGIView.cpp @@ -32,10 +32,12 @@ #endif #include +#include #include #include #include #include +#include #include #include #include @@ -91,6 +93,7 @@ QGIView::QGIView() setCacheMode(QGraphicsItem::NoCache); setHandlesChildEvents(false); setAcceptHoverEvents(true); + setFlag(QGraphicsItem::ItemIsSelectable, true); setFlag(QGraphicsItem::ItemIsMovable, true); setFlag(QGraphicsItem::ItemSendsScenePositionChanges, true); @@ -168,8 +171,7 @@ QVariant QGIView::itemChange(GraphicsItemChange change, const QVariant &value) // Base::Console().message("QGIV::itemChange(%d)\n", change); if(change == ItemPositionChange && scene()) { QPointF newPos = value.toPointF(); //position within parent! - - TechDraw::DrawView *viewObj = getViewObject(); + TechDraw::DrawView* viewObj = getViewObject(); auto* dpgi = dynamic_cast(viewObj); if (dpgi && dpgi->getPGroup()) { // restrict movements of secondary views. @@ -191,14 +193,6 @@ QVariant QGIView::itemChange(GraphicsItemChange change, const QVariant &value) } } - // tell the feature that we have moved - Gui::ViewProvider *vp = getViewProvider(viewObj); - if (vp && !vp->isRestoring()) { - snapping = true; // avoid triggering updateView by the VP updateData - viewObj->setPosition(Rez::appX(newPos.x()), Rez::appX(-newPos.y())); - snapping = false; - } - return newPos; } @@ -217,6 +211,8 @@ QVariant QGIView::itemChange(GraphicsItemChange change, const QVariant &value) m_label->show(); m_lock->setVisible(getViewObject()->isLocked() && getViewObject()->showLock()); } else { + dragFinished(); + if (!m_isHovered) { m_colCurrent = PreferencesGui::getAccessibleQColor(PreferencesGui::normalQColor()); m_border->hide(); @@ -232,6 +228,55 @@ QVariant QGIView::itemChange(GraphicsItemChange change, const QVariant &value) return QGraphicsItemGroup::itemChange(change, value); } +void QGIView::dragFinished() +{ + if (!viewObj) { + return; + } + + double currX = viewObj->X.getValue(); + double currY = viewObj->Y.getValue(); + double candidateX = Rez::appX(pos().x()); + double candidateY = Rez::appX(-pos().y()); + bool setX = false; + bool setY = false; + + const double tolerance = 0.001; // mm + if (!DrawUtil::fpCompare(currX, candidateX, tolerance)) { + setX = true; + } + if (!DrawUtil::fpCompare(currY, candidateY, tolerance)) { + setY = true; + } + + if (!setX && !setY) { + return; + } + + bool ownTransaction = (viewObj->getDocument()->getTransactionID(true) == 0); + + if (ownTransaction) { + Gui::Command::openCommand("Drag view"); + } + // tell the feature that we have moved + Gui::ViewProvider *vp = getViewProvider(viewObj); + if (vp && !vp->isRestoring()) { + snapping = true; // avoid triggering updateView by the VP updateData + if (setX) { + Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.%s.X = %f", + viewObj->getNameInDocument(), candidateX); + } + if (setY) { + Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.%s.Y = %f", + viewObj->getNameInDocument(), candidateY); + } + + snapping = false; + } + if (ownTransaction) { + Gui::Command::commitCommand(); + } +} //! align this view with others. newPosition is in this view's parent's coord //! system. if this view is not in a ProjectionGroup, then this is the scene @@ -463,6 +508,7 @@ void QGIView::mouseReleaseEvent(QGraphicsSceneMouseEvent * event) m_multiselectActivated = false; } + dragFinished(); QGraphicsItemGroup::mouseReleaseEvent(event); event->setModifiers(originalModifiers); diff --git a/src/Mod/TechDraw/Gui/QGIView.h b/src/Mod/TechDraw/Gui/QGIView.h index 473e7c7bff..6fb1ce3a41 100644 --- a/src/Mod/TechDraw/Gui/QGIView.h +++ b/src/Mod/TechDraw/Gui/QGIView.h @@ -176,6 +176,8 @@ protected: QGIView* getQGIVByName(std::string name); QVariant itemChange(GraphicsItemChange change, const QVariant &value) override; + void dragFinished(); + // Preselection events: void hoverEnterEvent(QGraphicsSceneHoverEvent *event) override; void hoverLeaveEvent(QGraphicsSceneHoverEvent *event) override; diff --git a/src/Mod/TechDraw/Gui/ViewProviderProjGroupItem.cpp b/src/Mod/TechDraw/Gui/ViewProviderProjGroupItem.cpp index fdaccfcf0b..e15b734d54 100644 --- a/src/Mod/TechDraw/Gui/ViewProviderProjGroupItem.cpp +++ b/src/Mod/TechDraw/Gui/ViewProviderProjGroupItem.cpp @@ -33,6 +33,7 @@ #include #include +#include "QGIView.h" #include "ViewProviderProjGroupItem.h" using namespace TechDrawGui;