From e88b1b49c90d2cb0e0c863702cd3e19662bbdabd Mon Sep 17 00:00:00 2001 From: wandererfan Date: Mon, 26 Aug 2024 10:34:07 -0400 Subject: [PATCH] [TD]fix balloon drag on rotated view (fix #15726) --- src/Mod/TechDraw/App/DrawView.h | 1 + src/Mod/TechDraw/Gui/QGIViewBalloon.cpp | 142 ++++++++++++++++-------- src/Mod/TechDraw/Gui/QGIViewBalloon.h | 12 +- 3 files changed, 108 insertions(+), 47 deletions(-) diff --git a/src/Mod/TechDraw/App/DrawView.h b/src/Mod/TechDraw/App/DrawView.h index 73097254fe..972a5835f3 100644 --- a/src/Mod/TechDraw/App/DrawView.h +++ b/src/Mod/TechDraw/App/DrawView.h @@ -96,6 +96,7 @@ public: virtual bool checkFit() const; virtual bool checkFit(DrawPage*) const; virtual void setPosition(double x, double y, bool force = false); + virtual Base::Vector3d getPosition() const { return Base::Vector3d(X.getValue(), Y.getValue(), 0.0); } virtual bool keepUpdated(void); boost::signals2::signal signalGuiPaint; diff --git a/src/Mod/TechDraw/Gui/QGIViewBalloon.cpp b/src/Mod/TechDraw/Gui/QGIViewBalloon.cpp index 2329b867a1..9d467132bf 100644 --- a/src/Mod/TechDraw/Gui/QGIViewBalloon.cpp +++ b/src/Mod/TechDraw/Gui/QGIViewBalloon.cpp @@ -36,6 +36,7 @@ #include #include +#include #include #include #include @@ -205,6 +206,11 @@ void QGIBalloonLabel::setLabelCenter() posY = y() + m_labelText->boundingRect().height() / 2.; } +Base::Vector3d QGIBalloonLabel::getLabelCenter() const +{ + return Base::Vector3d(posX, posY, 0.0); +} + void QGIBalloonLabel::setFont(QFont font) { m_labelText->setFont(font); } void QGIBalloonLabel::setDimString(QString text) @@ -381,7 +387,6 @@ void QGIViewBalloon::setViewPartFeature(TechDraw::DrawViewBalloon* balloonFeat) void QGIViewBalloon::updateView(bool update) { - // Base::Console().Message("QGIVB::updateView()\n"); Q_UNUSED(update); auto balloon(dynamic_cast(getViewObject())); if (!balloon) { @@ -406,7 +411,6 @@ void QGIViewBalloon::updateView(bool update) //update the bubble contents void QGIViewBalloon::updateBalloon(bool obtuse) { - // Base::Console().Message("QGIVB::updateBalloon()\n"); (void)obtuse; const auto balloon(dynamic_cast(getViewObject())); if (!balloon) { @@ -459,7 +463,9 @@ void QGIViewBalloon::balloonLabelDragged(bool ctrl) if (!m_dragInProgress) {//first drag movement m_dragInProgress = true; if (ctrl) {//moving whole thing, remember Origin offset from Bubble - m_saveOffset = dvb->getOriginOffset(); + m_saveOriginOffset = dvb->getOriginOffset(); + m_saveOrigin = DU::toVector3d(arrow->pos()); + m_savePosition = DU::toVector3d(balloonLabel->pos()); } } @@ -473,6 +479,10 @@ void QGIViewBalloon::balloonLabelDragged(bool ctrl) // redraw the balloon at the new position // note that we don't store the new position to the X/Y properties // since the dragging is not yet finished + // TODO: we don't need to redraw if the graphic is not changing (other than location). + // all of the balloon components (box, text, line, arrow, etc) would have to be placed + // into a QGraphicsItemGroup (or made children of a single QGI) in order to move them in + // sync drawBalloon(true); } } @@ -488,12 +498,14 @@ void QGIViewBalloon::balloonLabelDragFinished() double scale = 1.0; DrawView* balloonParent = getSourceView(); - if (balloonParent) { - scale = balloonParent->getScale(); + if (!balloonParent) { + return; } + scale = balloonParent->getScale(); //set feature position (x, y) from graphic position - double x = Rez::appX(balloonLabel->X() / scale), y = Rez::appX(balloonLabel->Y() / scale); + double x = Rez::appX(balloonLabel->X() / scale); + double y = Rez::appX(balloonLabel->Y() / scale); Gui::Command::openCommand(QT_TRANSLATE_NOOP("Command", "Drag Balloon")); Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.%s.X = %f", dvb->getNameInDocument(), x); @@ -502,18 +514,27 @@ void QGIViewBalloon::balloonLabelDragFinished() // for the case that origin was also dragged, calc new origin and update feature if (m_originDragged) { - Base::Vector3d pos(x, -y, 0.0); - Base::Vector3d newOrg = pos - m_saveOffset; + auto originGui = arrowPosInDrag(); // Qt rotated + auto originApp = originGui / scale; // Qt rotated unscaled + originApp = Rez::appX(DU::invertY(originApp)); // App rotated + + auto originAppUnrotated = originApp; + auto rotationDeg = balloonParent->Rotation.getValue(); + if (rotationDeg != 0) { + originAppUnrotated.RotateZ(Base::toRadians(-rotationDeg)); + } + Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.%s.OriginX = %f", - dvb->getNameInDocument(), newOrg.x); + dvb->getNameInDocument(), originAppUnrotated.x); Gui::Command::doCommand(Gui::Command::Doc, "App.ActiveDocument.%s.OriginY = %f", - dvb->getNameInDocument(), newOrg.y); + dvb->getNameInDocument(), originAppUnrotated.y); } Gui::Command::commitCommand(); m_dragInProgress = false; m_originDragged = false; + drawBalloon(false); } //from QGVP::mouseReleaseEvent - pos = eventPos in scene coords? @@ -579,14 +600,18 @@ void QGIViewBalloon::placeBalloon(QPointF pos) void QGIViewBalloon::draw() { - // Base::Console().Message("QGIVB::draw()\n"); // just redirect drawBalloon(false); } void QGIViewBalloon::drawBalloon(bool dragged) { - // Base::Console().Message("QGIVB::drawBalloon(%d)\n", dragged); + if ((!dragged) && m_dragInProgress) { + // TODO there are 2 drag status variables. m_dragInProgress appears to be the one to use? + // dragged shows false while drag is still in progress. + return; + } + prepareGeometryChange(); TechDraw::DrawViewBalloon* balloon = dynamic_cast(getViewObject()); @@ -595,6 +620,10 @@ void QGIViewBalloon::drawBalloon(bool dragged) //nothing to draw, don't try return; } + if (balloon->isRestoring()) { + // don't try to draw yet + return; + } TechDraw::DrawView* refObj = balloon->getParentView(); auto vp = static_cast(getViewProvider(getViewObject())); @@ -607,32 +636,14 @@ void QGIViewBalloon::drawBalloon(bool dragged) double textWidth = balloonLabel->getDimText()->boundingRect().width(); double textHeight = balloonLabel->getDimText()->boundingRect().height(); - float x, y, arrowTipX, arrowTipY; - Base::Vector3d arrowTip{balloon->OriginX.getValue(), balloon->OriginY.getValue(), 0.0}; - // when not dragging take the X/Y properties otherwise the current label position - if (!dragged) { - x = Rez::guiX(balloon->X.getValue() * refObj->getScale()); - y = Rez::guiX(balloon->Y.getValue() * refObj->getScale()); - arrowTipX = Rez::guiX(balloon->OriginX.getValue() * refObj->getScale()); - arrowTipY = -Rez::guiX(balloon->OriginY.getValue() * refObj->getScale()); - } - else { - x = balloonLabel->X(); - y = -balloonLabel->Y(); - if (m_originDragged) { - double scale = Rez::guiX(refObj->getScale()); - Base::Vector3d pos(x / scale, y / scale, 0.0); - Base::Vector3d newOrg = pos - m_saveOffset; - arrowTip = newOrg; - arrowTipX = newOrg.x * scale; - arrowTipY = -newOrg.y * scale; - } - else { - arrowTipX = Rez::guiX(balloon->OriginX.getValue() * refObj->getScale()); - arrowTipY = -Rez::guiX(balloon->OriginY.getValue() * refObj->getScale()); - } - } - Base::Vector3d lblCenter(x, -y, 0.0); + + float arrowTipX; + Base::Vector3d arrowTipPosInParent; + bool isDragging = dragged || m_dragInProgress; + Base::Vector3d labelPos; + getBalloonPoints(balloon, refObj, isDragging, labelPos, arrowTipPosInParent); + arrowTipX = arrowTipPosInParent.x; + Base::Vector3d lblCenter(labelPos.x, -labelPos.y, 0.0); if (balloon->isLocked()) { balloonLabel->setFlag(QGraphicsItem::ItemIsMovable, false); @@ -782,24 +793,20 @@ void QGIViewBalloon::drawBalloon(bool dragged) } else { arrow->setStyle(endType); - arrow->setSize(balloon->EndTypeScale.getValue() * QGIArrow::getPrefArrowSize()); arrow->draw(); + arrow->setPos(DU::toQPointF(arrowTipPosInParent)); - Base::Vector3d arrowTipPos(arrowTipX, arrowTipY, 0.0); - // arrowTip set above is unscaled, uninverted, unrotated and unRez'd. - arrowTip = DGU::toGuiPoint(refObj, arrowTip); Base::Vector3d dirballoonLinesLine; if (!DrawUtil::fpCompare(kinkLength, 0.0)) { - dirballoonLinesLine = (arrowTip - kinkPoint).Normalize(); + dirballoonLinesLine = (arrowTipPosInParent - kinkPoint).Normalize(); } else { - dirballoonLinesLine = (arrowTip - dLineStart).Normalize(); + dirballoonLinesLine = (arrowTipPosInParent - dLineStart).Normalize(); } float arAngle = atan2(dirballoonLinesLine.y, dirballoonLinesLine.x) * 180 / M_PI; - arrow->setPos(DU::toQPointF(arrowTip)); if ((endType == ArrowType::FILLED_TRIANGLE) && (prefOrthoPyramid())) { if (arAngle < 0.0) { arAngle += 360.0; @@ -826,7 +833,7 @@ void QGIViewBalloon::drawBalloon(bool dragged) arrow->setRotation(arAngle); arrow->show(); } - dLinePath.lineTo(arrowTip.x - xAdj, arrowTip.y - yAdj); + dLinePath.lineTo(arrowTipPosInParent.x - xAdj, arrowTipPosInParent.y - yAdj); balloonLines->setPath(dLinePath); // This overwrites the previously created QPainterPath with empty one, in case it should be hidden. Should be refactored. @@ -967,4 +974,47 @@ DrawView* QGIViewBalloon::getSourceView() const return balloonParent; } +//! Calculate the required position of the arrow tip during drag operations. Uses the current +//! label position and relative positions of the label and tip at the start of the drag. +//! Note this returns the Gui position of the arrow, not the App position. +Base::Vector3d QGIViewBalloon::arrowPosInDrag() +{ + auto offsetGui = m_savePosition - m_saveOrigin; + auto arrowPosGui = DU::toVector3d(balloonLabel->pos()) - offsetGui; + return arrowPosGui; +} + + +//! retrieves the appropriate label position and origin (arrow) point +void QGIViewBalloon::getBalloonPoints(TechDraw::DrawViewBalloon* balloon, DrawView *refObj, + bool isDragging, + Base::Vector3d& labelPos, + Base::Vector3d& arrowPos) +{ + float x, y; + Base::Vector3d originApp{balloon->OriginX.getValue(), balloon->OriginY.getValue(), 0.0}; + Base::Vector3d arrowTipPosInParent; + + // when not dragging take the X/Y properties otherwise the current label position + if (!isDragging) { + x = Rez::guiX(balloon->X.getValue() * refObj->getScale()); + y = Rez::guiX(balloon->Y.getValue() * refObj->getScale()); + arrowTipPosInParent = DGU::toGuiPoint(refObj, originApp); + } + else { + x = balloonLabel->X(); + y = -balloonLabel->Y(); // invert from Qt scene units to R2 mm + if (m_originDragged) { + // moving the whole bubble object. do not adjust origin point. + arrowTipPosInParent = arrowPosInDrag(); + } else { + // this is a bubble drag, so the origin must remain in the same position on the view. + // if the parent view is rotated, the origin scene position must be rotated to match + arrowTipPosInParent = DGU::toGuiPoint(refObj, originApp); + } + } + labelPos = Base::Vector3d(x, y, 0.0); + arrowPos = arrowTipPosInParent; +} + #include diff --git a/src/Mod/TechDraw/Gui/QGIViewBalloon.h b/src/Mod/TechDraw/Gui/QGIViewBalloon.h index 4181e3327e..be657bb804 100644 --- a/src/Mod/TechDraw/Gui/QGIViewBalloon.h +++ b/src/Mod/TechDraw/Gui/QGIViewBalloon.h @@ -78,6 +78,7 @@ public: void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = nullptr) override; void setLabelCenter(); + Base::Vector3d getLabelCenter() const; void setPosFromCenter(const double& xCenter, const double& yCenter); double X() const { @@ -226,6 +227,13 @@ protected: QString getPrecision(); void parentViewMousePressed(QGIView* view, QPointF pos); TechDraw::DrawView* getSourceView() const; + Base::Vector3d arrowPosInDrag(); + void getBalloonPoints(TechDraw::DrawViewBalloon* balloon, + TechDraw::DrawView* refObj, + bool isDragging, + Base::Vector3d& labelPos, + Base::Vector3d& arrowPos); + private: TechDraw::DrawViewBalloon* dvBalloon; @@ -241,7 +249,9 @@ private: bool m_dragInProgress; bool m_originDragged = false; bool m_ctrl; - Base::Vector3d m_saveOffset; + Base::Vector3d m_saveOriginOffset; + Base::Vector3d m_saveOrigin; + Base::Vector3d m_savePosition; }; }// namespace TechDrawGui