From c68a1e18db06dd8955644c20ad3c7f09ef446f54 Mon Sep 17 00:00:00 2001 From: letsfindaway Date: Fri, 27 Sep 2024 12:38:59 +0200 Subject: [PATCH 1/2] feat: draw snap indicator at arbitrary angle - add optional angle parameter - use painter rotation to draw triangle at any angle --- src/board/UBBoardView.cpp | 4 +- src/board/UBBoardView.h | 2 +- src/gui/UBSnapIndicator.cpp | 80 +++++++++++++++++-------------------- src/gui/UBSnapIndicator.h | 4 +- 4 files changed, 41 insertions(+), 49 deletions(-) diff --git a/src/board/UBBoardView.cpp b/src/board/UBBoardView.cpp index e2a9ac4ee..45f14a8ab 100644 --- a/src/board/UBBoardView.cpp +++ b/src/board/UBBoardView.cpp @@ -944,7 +944,7 @@ void UBBoardView::setMultiselection(bool enable) mMultipleSelectionIsEnabled = enable; } -void UBBoardView::updateSnapIndicator(Qt::Corner corner, QPointF snapPoint) +void UBBoardView::updateSnapIndicator(Qt::Corner corner, QPointF snapPoint, double angle) { if (!mSnapIndicator) { @@ -952,7 +952,7 @@ void UBBoardView::updateSnapIndicator(Qt::Corner corner, QPointF snapPoint) mSnapIndicator->resize(120, 120); } - mSnapIndicator->appear(corner, snapPoint); + mSnapIndicator->appear(corner, snapPoint, angle); } void UBBoardView::setBoxing(const QMargins& margins) diff --git a/src/board/UBBoardView.h b/src/board/UBBoardView.h index 7c1daaa19..789b74f97 100644 --- a/src/board/UBBoardView.h +++ b/src/board/UBBoardView.h @@ -67,7 +67,7 @@ class UBBoardView : public QGraphicsView bool isMultipleSelectionEnabled() { return mMultipleSelectionIsEnabled; } void setBoxing(const QMargins& margins); - void updateSnapIndicator(Qt::Corner corner, QPointF snapPoint); + void updateSnapIndicator(Qt::Corner corner, QPointF snapPoint, double angle = 0); // work around for handling tablet events on MAC OS with Qt 4.8.0 and above #if defined(Q_OS_OSX) diff --git a/src/gui/UBSnapIndicator.cpp b/src/gui/UBSnapIndicator.cpp index 1990f2ddb..a87d0d6f4 100644 --- a/src/gui/UBSnapIndicator.cpp +++ b/src/gui/UBSnapIndicator.cpp @@ -27,9 +27,7 @@ #include #include "board/UBBoardController.h" -#include "board/UBBoardPaletteManager.h" #include "board/UBBoardView.h" -#include "core/UBApplication.h" UBSnapIndicator::UBSnapIndicator(QWidget* parent) : QLabel(parent) @@ -42,23 +40,42 @@ UBSnapIndicator::UBSnapIndicator(QWidget* parent) connect(mAnimation, &QPropertyAnimation::finished, this, &QWidget::hide); } -void UBSnapIndicator::appear(Qt::Corner corner, QPointF snapPoint) +void UBSnapIndicator::appear(Qt::Corner corner, QPointF snapPoint, double angle) { - if (corner != mCorner) + mAnimation->stop(); + mAngle = -angle; // painter rotate is clockwise + + switch (corner) { - mAnimation->stop(); - mCorner = corner; - show(); + case Qt::TopLeftCorner: + mAngle += 270; + break; + + case Qt::TopRightCorner: + break; + + case Qt::BottomLeftCorner: + mAngle += 180; + break; - UBBoardView* view = dynamic_cast(parentWidget()); + case Qt::BottomRightCorner: + mAngle += 90; + break; - if (view) - { - QPoint indicationPoint{view->mapFromScene(snapPoint) - QPoint(width() / 2, height() / 2)}; + default: + break; + } + + show(); + + UBBoardView* view = dynamic_cast(parentWidget()); + + if (view) + { + QPoint indicationPoint{view->mapFromScene(snapPoint) - QPoint(width() / 2, height() / 2)}; - move(indicationPoint); - mAnimation->start(); - } + move(indicationPoint); + mAnimation->start(); } } @@ -81,7 +98,9 @@ void UBSnapIndicator::setColor(const QColor& color) void UBSnapIndicator::paintEvent(QPaintEvent* event) { QPainter painter(this); - QRect area = rect(); + + painter.translate(width() / 2, height() / 2); + painter.rotate(mAngle); QPen pen; QColor penColor{mColor}; @@ -91,35 +110,8 @@ void UBSnapIndicator::paintEvent(QPaintEvent* event) painter.setPen(pen); QPoint p1; - QPoint p2(area.center()); - QPoint p3; - int dist = area.width() / 3; - - switch (mCorner) - { - case Qt::TopLeftCorner: - p1 = p2 + QPoint{0, dist}; - p3 = p2 + QPoint(dist, 0); - break; - - case Qt::TopRightCorner: - p1 = p2 + QPoint{0, dist}; - p3 = p2 + QPoint(-dist, 0); - break; - - case Qt::BottomLeftCorner: - p1 = p2 + QPoint{0, -dist}; - p3 = p2 + QPoint(dist, 0); - break; - - case Qt::BottomRightCorner: - p1 = p2 + QPoint{0, -dist}; - p3 = p2 + QPoint(-dist, 0); - break; - - default: - break; - } + QPoint p2{-width() / 2, 0}; + QPoint p3{0, height() / 2}; QPolygon polygon; polygon << p1 << p2 << p3 << p1; diff --git a/src/gui/UBSnapIndicator.h b/src/gui/UBSnapIndicator.h index a8987073c..6e98bfb84 100644 --- a/src/gui/UBSnapIndicator.h +++ b/src/gui/UBSnapIndicator.h @@ -35,7 +35,7 @@ class UBSnapIndicator : public QLabel public: UBSnapIndicator(QWidget* parent); - void appear(Qt::Corner corner, QPointF snapPoint); + void appear(Qt::Corner corner, QPointF snapPoint, double angle = 0); int alpha() const; void setAlpha(int opacity); @@ -46,7 +46,7 @@ class UBSnapIndicator : public QLabel virtual void paintEvent(QPaintEvent* event) override; private: - Qt::Corner mCorner{Qt::TopLeftCorner}; + double mAngle{0}; int mAlpha; QColor mColor{0x62a7e0}; QPropertyAnimation* mAnimation; From 55a2144d4575700a4657547d849743063700ed5c Mon Sep 17 00:00:00 2001 From: letsfindaway Date: Fri, 27 Sep 2024 14:28:13 +0200 Subject: [PATCH 2/2] feat: snaping rotated items - snap rotated item when moving w/o delegate frame - snap rotated item when moving by delegate frame - snap resizing for rotation angles 0 / 90 / 180 / 270 - snap newly drawn rotated line at line end points instead of outline --- src/board/UBBoardView.cpp | 35 +++++++++-- src/board/UBBoardView.h | 3 +- src/domain/UBGraphicsDelegateFrame.cpp | 86 +++++++++++++++++++------- src/domain/UBGraphicsDelegateFrame.h | 2 +- src/domain/UBGraphicsScene.cpp | 6 +- 5 files changed, 98 insertions(+), 34 deletions(-) diff --git a/src/board/UBBoardView.cpp b/src/board/UBBoardView.cpp index 45f14a8ab..aca75976c 100644 --- a/src/board/UBBoardView.cpp +++ b/src/board/UBBoardView.cpp @@ -519,6 +519,14 @@ void UBBoardView::handleItemsSelection(QGraphicsItem *item) { scene()->deselectAllItemsExcept(item); scene()->updateSelectionFrame(); + + // calculate initial corner points + mCornerPoints.clear(); + const auto bounds = UBGraphicsScene::itemRect(item); + mCornerPoints << item->mapToScene(bounds.topLeft()); + mCornerPoints << item->mapToScene(bounds.topRight()); + mCornerPoints << item->mapToScene(bounds.bottomLeft()); + mCornerPoints << item->mapToScene(bounds.bottomRight()); } } } @@ -774,6 +782,7 @@ QGraphicsItem* UBBoardView::determineItemToMove(QGraphicsItem *item) void UBBoardView::handleItemMousePress(QMouseEvent *event) { mLastPressedMousePos = mapToScene(event->pos()); + mFirstPressedMousePos = mLastPressedMousePos; // Determining item who will take mouse press event //all other items will be deselected and if all item will be deselected, then @@ -852,13 +861,29 @@ void UBBoardView::handleItemMouseMove(QMouseEvent *event) // snap to grid if (scene()->isSnapping()) { - QRectF rect = UBGraphicsScene::itemRect(movingItem); - Qt::Corner corner; - auto offset = scene()->snap(rect, &corner); - newPos += offset; + QPointF moved = scenePos - mFirstPressedMousePos; + std::vector corners; + + for (const auto& cornerPoint : mCornerPoints) + { + corners.push_back(cornerPoint + moved); + } + + int snapIndex; + QPointF snapVector = scene()->snap(corners, &snapIndex); + Qt::Corner corner = Qt::Corner(snapIndex); + + if (!snapVector.isNull()) + { + auto* view = UBApplication::boardController->controlView(); + const auto angle = QLineF{corners.at(0), corners.at(1)}.angle(); + view->updateSnapIndicator(corner, corners.at(snapIndex) + snapVector, angle); + } + + newPos += snapVector; movingItem->setPos(newPos); - mLastPressedMousePos = scenePos + offset; + mLastPressedMousePos = scenePos + snapVector; } else { diff --git a/src/board/UBBoardView.h b/src/board/UBBoardView.h index 789b74f97..5b722b9d8 100644 --- a/src/board/UBBoardView.h +++ b/src/board/UBBoardView.h @@ -165,8 +165,9 @@ class UBBoardView : public QGraphicsView bool mOkOnWidget; bool mWidgetMoved; + QPointF mFirstPressedMousePos; QPointF mLastPressedMousePos; - + QList mCornerPoints; /* when an item is moved around, the tracking must stop if the object is deleted */ QGraphicsItem *_movingItem; diff --git a/src/domain/UBGraphicsDelegateFrame.cpp b/src/domain/UBGraphicsDelegateFrame.cpp index 9fe71bbff..ea9f4bf49 100644 --- a/src/domain/UBGraphicsDelegateFrame.cpp +++ b/src/domain/UBGraphicsDelegateFrame.cpp @@ -281,7 +281,6 @@ void UBGraphicsDelegateFrame::mousePressEvent(QGraphicsSceneMouseEvent *event) mDelegate->startUndoStep(); mStartingPoint = event->scenePos(); - mStartingBounds = UBGraphicsScene::itemRect(delegated()); initializeTransform(); @@ -293,6 +292,27 @@ void UBGraphicsDelegateFrame::mousePressEvent(QGraphicsSceneMouseEvent *event) mRotatedAngle = mAngle; mInitialTransform = buildTransform(); + + // calculate initial corner points and respect mirroring + mCornerPoints.clear(); + const auto bounds = UBGraphicsScene::itemRect(delegated()); + mCornerPoints << delegated()->mapToScene(bounds.topLeft()); + mCornerPoints << delegated()->mapToScene(bounds.topRight()); + mCornerPoints << delegated()->mapToScene(bounds.bottomLeft()); + mCornerPoints << delegated()->mapToScene(bounds.bottomRight()); + + if (mMirrorX) + { + std::swap(mCornerPoints[0], mCornerPoints[1]); + std::swap(mCornerPoints[2], mCornerPoints[3]); + } + + if (mMirrorY) + { + std::swap(mCornerPoints[0], mCornerPoints[2]); + std::swap(mCornerPoints[1], mCornerPoints[3]); + } + mOriginalSize = delegated()->boundingRect().size(); mCurrentTool = toolFromPos(event->pos()); @@ -420,8 +440,9 @@ void UBGraphicsDelegateFrame::mouseMoveEvent(QGraphicsSceneMouseEvent *event) return; QLineF move = QLineF(mStartingPoint, event->scenePos()); - qreal moveX = (event->pos() - mStartingPoint).x(); - qreal moveY = (event->pos() - mStartingPoint).y(); + QPointF itemStartingPoint = mapFromScene(mStartingPoint); + qreal moveX = (event->pos() - itemStartingPoint).x(); + qreal moveY = (event->pos() - itemStartingPoint).y(); qreal width = delegated()->boundingRect().width() * mTotalScaleX; qreal height = delegated()->boundingRect().height() * mTotalScaleY; @@ -430,13 +451,20 @@ void UBGraphicsDelegateFrame::mouseMoveEvent(QGraphicsSceneMouseEvent *event) if(!rotating()) { const auto* ubscene = dynamic_cast(scene()); + constexpr double epsilon = 0.0001; - if (ubscene && ubscene->isSnapping()) + if (ubscene && ubscene->isSnapping() && !resizingBottomRight() && std::fmod(mAngle + epsilon, 90.) <= 2. * epsilon) { QPointF snap = snapVector(event->scenePos()); + move.setP2(move.p2() + snap); + + // rotate the snap according to item angle + QLineF snapLine{{}, snap}; + snapLine.setAngle(snapLine.angle() - mAngle); + snap = snapLine.p2(); + moveX += snap.x(); moveY += snap.y(); - move.setP2(move.p2() + snap); } mTranslateX = moveX; @@ -574,10 +602,23 @@ void UBGraphicsDelegateFrame::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { // snap to grid QPointF moved = event->scenePos() - mStartingPoint; - QRectF movedBounds = mStartingBounds.translated(moved); + std::vector corners; + + for (const auto& cornerPoint : mCornerPoints) + { + corners.push_back(cornerPoint + moved); + } + + int snapIndex; + QPointF snapVector = ubscene->snap(corners, &snapIndex); + Qt::Corner corner = Qt::Corner(snapIndex); + + if (!snapVector.isNull()) + { + auto* view = UBApplication::boardController->controlView(); + view->updateSnapIndicator(corner, corners.at(snapIndex) + snapVector, mAngle); + } - Qt::Corner corner; - QPointF snapVector = ubscene->snap(movedBounds, &corner); moveX += snapVector.x(); moveY += snapVector.y(); move.setP2(move.p2() + snapVector); @@ -1091,33 +1132,32 @@ void UBGraphicsDelegateFrame::refreshGeometry() QPointF UBGraphicsDelegateFrame::snapVector(QPointF scenePos) const { - QPointF moved = scenePos - mStartingPoint; - QRectF movedBounds = mStartingBounds.translated(moved); + const auto moved = scenePos - mStartingPoint; std::vector corners; - if (resizingLeft()) + if (resizingLeft() && !mMirrorX || resizingRight() && mMirrorX) { - corners.push_back(movedBounds.topLeft()); - corners.push_back(movedBounds.bottomLeft()); + corners.push_back(mCornerPoints[0] + moved); + corners.push_back(mCornerPoints[2] + moved); } - else if (resizingRight()) + else if (resizingRight() && !mMirrorX || resizingLeft() && mMirrorX) { - corners.push_back(movedBounds.topRight()); - corners.push_back(movedBounds.bottomRight()); + corners.push_back(mCornerPoints[1] + moved); + corners.push_back(mCornerPoints[3] + moved); } - else if (resizingTop()) + else if (resizingTop() && !mMirrorY || resizingBottom() && mMirrorY) { - corners.push_back(movedBounds.topLeft()); - corners.push_back(movedBounds.topRight()); + corners.push_back(mCornerPoints[0] + moved); + corners.push_back(mCornerPoints[1] + moved); } - else if (resizingBottom()) + else if (resizingBottom() && !mMirrorY || resizingTop() && mMirrorY) { - corners.push_back(movedBounds.bottomLeft()); - corners.push_back(movedBounds.bottomRight()); + corners.push_back(mCornerPoints[2] + moved); + corners.push_back(mCornerPoints[3] + moved); } else if (resizingBottomRight()) { - corners.push_back(movedBounds.bottomRight()); + corners.push_back(mCornerPoints[3] + moved); } UBGraphicsScene* ubscene = dynamic_cast(scene()); diff --git a/src/domain/UBGraphicsDelegateFrame.h b/src/domain/UBGraphicsDelegateFrame.h index df7473b26..8c1b54bfb 100644 --- a/src/domain/UBGraphicsDelegateFrame.h +++ b/src/domain/UBGraphicsDelegateFrame.h @@ -119,8 +119,8 @@ class UBGraphicsDelegateFrame: public QGraphicsRectItem, public QObject QRect mAngleRect; QPointF mStartingPoint; - QRectF mStartingBounds; QTransform mInitialTransform; + QList mCornerPoints; QSizeF mOriginalSize; QPointF mFixedPoint; diff --git a/src/domain/UBGraphicsScene.cpp b/src/domain/UBGraphicsScene.cpp index db37a010b..93080f3a6 100644 --- a/src/domain/UBGraphicsScene.cpp +++ b/src/domain/UBGraphicsScene.cpp @@ -2626,7 +2626,7 @@ QPointF UBGraphicsScene::snap(const QRectF& rect, Qt::Corner* corner) const QRectF UBGraphicsScene::itemRect(const QGraphicsItem* item) { - // compute an item's rectangle in scene coordinates + // compute an item's rectangle in item coordinates // taking into account the shape of the item and // the nature of nominal lines QRectF bounds = item->boundingRect(); @@ -2655,9 +2655,7 @@ QRectF UBGraphicsScene::itemRect(const QGraphicsItem* item) } } - QRectF rect = item->mapRectToScene(bounds); - - return rect; + return bounds; } void UBGraphicsScene::addMask(const QPointF ¢er)