diff --git a/src/app/GUI/extraactions.cpp b/src/app/GUI/extraactions.cpp index 4c1304b1c..eb7fc15dc 100644 --- a/src/app/GUI/extraactions.cpp +++ b/src/app/GUI/extraactions.cpp @@ -161,7 +161,7 @@ void MainWindow::setupMenuExtras() } // align { - const int alignTotal = 28; + const int alignTotal = 40; const QString alignTextDefault = tr("Align %1 %2 Relative to %3"); const QString alignGeometry = tr("Geometry"); @@ -481,6 +481,126 @@ void MainWindow::setupMenuExtras() alignBoth = false; align = Qt::AlignVCenter; break; + case 28: // Pivot alignment to Bounding Box - VCenter ----------------------------------------------------------------------------------- + alignString = alignVCenter; + pivotString = alignPivot; + relString = alignLast; + pivot = AlignPivot::pivotItself; + rel = AlignRelativeTo::boundingBox; + iconString = alignVCenterIcon; + alignBoth = false; + align = Qt::AlignVCenter; + break; + case 29: // Pivot alignment to Bounding Box - HCenter + alignString = alignVCenter; + pivotString = alignPivot; + relString = alignLast; + pivot = AlignPivot::pivotItself; + rel = AlignRelativeTo::boundingBox; + iconString = alignVCenterIcon; + alignBoth = false; + align = Qt::AlignHCenter; + break; + case 30: // Pivot alignment to Bounding Box - Left + alignString = alignVCenter; + pivotString = alignPivot; + relString = alignLast; + pivot = AlignPivot::pivotItself; + rel = AlignRelativeTo::boundingBox; + iconString = alignVCenterIcon; + alignBoth = false; + align = Qt::AlignLeft; + break; + case 31: // Pivot alignment to Bounding Box - Right + alignString = alignVCenter; + pivotString = alignPivot; + relString = alignLast; + pivot = AlignPivot::pivotItself; + rel = AlignRelativeTo::boundingBox; + iconString = alignVCenterIcon; + alignBoth = false; + align = Qt::AlignRight; + break; + case 32: // Pivot alignment to Bounding Box - Top + alignString = alignVCenter; + pivotString = alignPivot; + relString = alignLast; + pivot = AlignPivot::pivotItself; + rel = AlignRelativeTo::boundingBox; + iconString = alignVCenterIcon; + alignBoth = false; + align = Qt::AlignTop; + break; + case 33: // Pivot alignment to Bounding Box - Bottom + alignString = alignVCenter; + pivotString = alignPivot; + relString = alignLast; + pivot = AlignPivot::pivotItself; + rel = AlignRelativeTo::boundingBox; + iconString = alignVCenterIcon; + alignBoth = false; + align = Qt::AlignBottom; + break; + case 34: // Pivot alignment to Scene - VCenter ----------------------------------------------------------------------------------- + alignString = alignVCenter; + pivotString = alignPivot; + relString = alignLast; + pivot = AlignPivot::pivotItself; + rel = AlignRelativeTo::scene; + iconString = alignVCenterIcon; + alignBoth = false; + align = Qt::AlignVCenter; + break; + case 35: // Pivot alignment to Scene - HCenter + alignString = alignVCenter; + pivotString = alignPivot; + relString = alignLast; + pivot = AlignPivot::pivotItself; + rel = AlignRelativeTo::scene; + iconString = alignVCenterIcon; + alignBoth = false; + align = Qt::AlignHCenter; + break; + case 36: // Pivot alignment to Scene - Left + alignString = alignVCenter; + pivotString = alignPivot; + relString = alignLast; + pivot = AlignPivot::pivotItself; + rel = AlignRelativeTo::scene; + iconString = alignVCenterIcon; + alignBoth = false; + align = Qt::AlignLeft; + break; + case 37: // Pivot alignment to Scene - Right + alignString = alignVCenter; + pivotString = alignPivot; + relString = alignLast; + pivot = AlignPivot::pivotItself; + rel = AlignRelativeTo::scene; + iconString = alignVCenterIcon; + alignBoth = false; + align = Qt::AlignRight; + break; + case 38: // Pivot alignment to Scene - Top + alignString = alignVCenter; + pivotString = alignPivot; + relString = alignLast; + pivot = AlignPivot::pivotItself; + rel = AlignRelativeTo::scene; + iconString = alignVCenterIcon; + alignBoth = false; + align = Qt::AlignTop; + break; + case 39: // Pivot alignment to Scene - Bottom + alignString = alignVCenter; + pivotString = alignPivot; + relString = alignLast; + pivot = AlignPivot::pivotItself; + rel = AlignRelativeTo::scene; + iconString = alignVCenterIcon; + alignBoth = false; + align = Qt::AlignBottom; + break; default: return; } diff --git a/src/app/friction.qss b/src/app/friction.qss index 2e184ce35..834aeabac 100644 --- a/src/app/friction.qss +++ b/src/app/friction.qss @@ -479,7 +479,8 @@ QMenu::separator { background-color: %4; } -QMenu::item:disabled { +QMenu::item:disabled, +AlignWidget QPushButton::disabled { background: transparent; } diff --git a/src/core/Boxes/boundingbox.cpp b/src/core/Boxes/boundingbox.cpp index 010c1fed0..5aa860a9b 100644 --- a/src/core/Boxes/boundingbox.cpp +++ b/src/core/Boxes/boundingbox.cpp @@ -865,6 +865,125 @@ void BoundingBox::alignPivot(const Qt::Alignment align, const QRectF& to) { alignGeometry(QRectF(pivot, pivot), align, to); } +void BoundingBox::alignPivotItself(const Qt::Alignment align, + const QRectF& to, + const AlignRelativeTo relativeTo, + const QPointF lastPivotAbsPos) +{ + QPointF currentPivot = mTransformAnimator->getPivot(); + QPointF currentPivotAbsPos = getPivotAbsPos(); + + QPointF lastSelectedPivotAbsPos = lastPivotAbsPos; + + QPointF center = getRelCenterPosition(); + + switch (relativeTo) { + case AlignRelativeTo::scene: + switch (align) { + case Qt::AlignVCenter: + center.setX(currentPivot.x()); + center.setY(currentPivot.y() - currentPivotAbsPos.y() + to.bottomRight().y()/2); + break; + case Qt::AlignHCenter: + center.setX(currentPivot.x() - currentPivotAbsPos.x() + to.bottomRight().x()/2); + center.setY(currentPivot.y()); + break; + case Qt::AlignLeft: + center.setX(currentPivot.x() - currentPivotAbsPos.x()); + center.setY(currentPivot.y()); + break; + case Qt::AlignRight: + center.setX(currentPivot.x() + (to.topRight().x() - currentPivotAbsPos.x())); + center.setY(currentPivot.y()); + break; + case Qt::AlignTop: + center.setX(currentPivot.x()); + center.setY(currentPivot.y() - currentPivotAbsPos.y()); + break; + case Qt::AlignBottom: + center.setX(currentPivot.x()); + center.setY(currentPivot.y() + (to.bottomRight().y() - currentPivotAbsPos.y())); + break; + } + break; + case AlignRelativeTo::lastSelected: + switch (align) { + case Qt::AlignVCenter: + center.setX(currentPivot.x()); + center.setY(currentPivot.y() - currentPivotAbsPos.y() + to.center().y()); + break; + case Qt::AlignHCenter: + center.setX(currentPivot.x() - currentPivotAbsPos.x() + to.center().x()); + center.setY(currentPivot.y()); + break; + case Qt::AlignLeft: + center.setX(currentPivot.x() - currentPivotAbsPos.x() + to.topLeft().x()); + center.setY(currentPivot.y()); + break; + case Qt::AlignRight: + center.setX(currentPivot.x() + (to.topRight().x() - currentPivotAbsPos.x())); + center.setY(currentPivot.y()); + break; + case Qt::AlignTop: + center.setX(currentPivot.x()); + center.setY(currentPivot.y() - currentPivotAbsPos.y() + to.topLeft().y()); + break; + case Qt::AlignBottom: + center.setX(currentPivot.x()); + center.setY(currentPivot.y() + (to.bottomRight().y() - currentPivotAbsPos.y())); + break; + } + break; + case AlignRelativeTo::lastSelectedPivot: + switch (align) { + case Qt::AlignVCenter: + center.setX(currentPivot.x()); + center.setY(currentPivot.y() - currentPivotAbsPos.y() + lastSelectedPivotAbsPos.y()); + break; + case Qt::AlignHCenter: + center.setX(currentPivot.x() - currentPivotAbsPos.x() + lastSelectedPivotAbsPos.x()); + center.setY(currentPivot.y()); + break; + default: + center.setX(currentPivot.x()); + center.setY(currentPivot.y()); + break; + } + break; + case AlignRelativeTo::boundingBox: + switch (align) { + case Qt::AlignVCenter: + center.setX(currentPivot.x()); + break; + case Qt::AlignHCenter: + center.setY(currentPivot.y()); + break; + case Qt::AlignLeft: + center.setX(mRelRect.topLeft().x()); + center.setY(currentPivot.y()); + break; + case Qt::AlignRight: + center.setX(mRelRect.topRight().x()); + center.setY(currentPivot.y()); + break; + case Qt::AlignTop: + center.setX(currentPivot.x()); + center.setY(mRelRect.topLeft().y()); + break; + case Qt::AlignBottom: + center.setX(currentPivot.x()); + center.setY(mRelRect.bottomLeft().y()); + break; + } + break; + } + + startPivotTransform(); + mTransformAnimator->setPivotFixedTransform(center); + requestGlobalPivotUpdateIfSelected(); + finishPivotTransform(); +} + void BoundingBox::moveByAbs(const QPointF &trans) { mTransformAnimator->moveByAbs(trans); } diff --git a/src/core/Boxes/boundingbox.h b/src/core/Boxes/boundingbox.h index ced978d1f..bbbcbc199 100644 --- a/src/core/Boxes/boundingbox.h +++ b/src/core/Boxes/boundingbox.h @@ -40,6 +40,8 @@ class Canvas; +enum class AlignRelativeTo; + class QrealAction; class MovablePoint; @@ -298,6 +300,10 @@ class CORE_EXPORT BoundingBox : public eBoxOrSound { void alignGeometry(const Qt::Alignment align, const QRectF& to); void alignPivot(const Qt::Alignment align, const QRectF& to); + void alignPivotItself(const Qt::Alignment align, + const QRectF& to, + const AlignRelativeTo relativeTo, + const QPointF lastPivotAbsPos); QMatrix getTotalTransform() const; diff --git a/src/core/canvas.h b/src/core/canvas.h index dfad87691..4724686b5 100644 --- a/src/core/canvas.h +++ b/src/core/canvas.h @@ -69,11 +69,11 @@ class eKeyEvent; enum class CtrlsMode : short; enum class AlignPivot { - geometry, pivot + geometry, pivot, pivotItself }; enum class AlignRelativeTo { - scene, lastSelected + scene, lastSelected, lastSelectedPivot, boundingBox }; class CORE_EXPORT Canvas : public CanvasBase diff --git a/src/core/canvasselectedboxesactions.cpp b/src/core/canvasselectedboxesactions.cpp index efa3099a8..ea73e7ce9 100644 --- a/src/core/canvasselectedboxesactions.cpp +++ b/src/core/canvasselectedboxesactions.cpp @@ -851,24 +851,32 @@ void Canvas::selectedPathsCombine() { void Canvas::alignSelectedBoxes(const Qt::Alignment align, const AlignPivot pivot, - const AlignRelativeTo relativeTo) { - if(mSelectedBoxes.isEmpty()) return; + const AlignRelativeTo relativeTo) +{ + if (mSelectedBoxes.isEmpty()) { return; } QRectF geometry; BoundingBox* skip = nullptr; switch(relativeTo) { case AlignRelativeTo::scene: + case AlignRelativeTo::boundingBox: geometry = QRectF(0., 0., mWidth, mHeight); break; case AlignRelativeTo::lastSelected: - if(!mLastSelectedBox) return; + if (!mLastSelectedBox) { return; } skip = mLastSelectedBox; geometry = mLastSelectedBox->getAbsBoundingRect(); break; + case AlignRelativeTo::lastSelectedPivot: + if (!mLastSelectedBox) { return; } + skip = mLastSelectedBox; + geometry = QRectF(mLastSelectedBox->getPivotAbsPos(), + mLastSelectedBox->getPivotAbsPos()); + break; } - pushUndoRedoName("align"); - for(const auto &box : mSelectedBoxes) { - if(box == skip) continue; + pushUndoRedoName(pivot == AlignPivot::pivotItself ? "pivot align" : "box align"); + for (const auto &box : mSelectedBoxes) { + if (box == skip) { continue; } switch(pivot) { case AlignPivot::pivot: box->alignPivot(align, geometry); @@ -876,6 +884,12 @@ void Canvas::alignSelectedBoxes(const Qt::Alignment align, case AlignPivot::geometry: box->alignGeometry(align, geometry); break; + case AlignPivot::pivotItself: + box->alignPivotItself(align, + geometry, + relativeTo, + mLastSelectedBox->getPivotAbsPos()); + break; } } } diff --git a/src/ui/widgets/alignwidget.cpp b/src/ui/widgets/alignwidget.cpp index 8c00fc64a..92bdba1b0 100644 --- a/src/ui/widgets/alignwidget.cpp +++ b/src/ui/widgets/alignwidget.cpp @@ -28,11 +28,21 @@ #include #include #include +#include #include "GUI/global.h" #include "Private/esettings.h" #include "themesupport.h" +#define INDEX_ALIGN_GEOMETRY 0 +#define INDEX_ALIGN_GEOMETRY_PIVOT 1 +#define INDEX_ALIGN_PIVOT 2 + +#define INDEX_REL_SCENE 0 +#define INDEX_REL_LAST_SELECTED 1 +#define INDEX_REL_LAST_SELECTED_PIVOT 2 +#define INDEX_REL_BOUNDINGBOX 3 + AlignWidget::AlignWidget(QWidget* const parent) : QWidget(parent) , mAlignPivot(nullptr) @@ -45,24 +55,30 @@ AlignWidget::AlignWidget(QWidget* const parent) const auto combosLay = new QHBoxLayout; mainLayout->addLayout(combosLay); - //combosLay->addWidget(new QLabel(tr("Align"))); + combosLay->addWidget(new QLabel(tr("Align"), this)); mAlignPivot = new QComboBox(this); mAlignPivot->setMinimumWidth(20); mAlignPivot->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); mAlignPivot->setFocusPolicy(Qt::NoFocus); - mAlignPivot->addItem(tr("Align Geometry")); - mAlignPivot->addItem(tr("Align Pivot")); + mAlignPivot->addItem(tr("Geometry")); // INDEX_ALIGN_GEOMETRY + mAlignPivot->addItem(tr("Geometry by Pivot")); // INDEX_ALIGN_GEOMETRY_PIVOT + mAlignPivot->addItem(tr("Pivot")); // INDEX_ALIGN_PIVOT combosLay->addWidget(mAlignPivot); - //combosLay->addWidget(new QLabel(tr("Relative to"))); + combosLay->addWidget(new QLabel(tr("To"), this)); mRelativeTo = new QComboBox(this); mRelativeTo->setMinimumWidth(20); mRelativeTo->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); mRelativeTo->setFocusPolicy(Qt::NoFocus); - mRelativeTo->addItem(tr("Relative to Scene")); - mRelativeTo->addItem(tr("Relative to Last Selected")); + mRelativeTo->addItem(tr("Scene")); // INDEX_REL_SCENE + mRelativeTo->addItem(tr("Last Selected")); // INDEX_REL_LAST_SELECTED + mRelativeTo->addItem(tr("Last Selected Pivot")); // INDEX_REL_LAST_SELECTED_PIVOT + mRelativeTo->addItem(tr("Bounding Box")); // INDEX_REL_BOUNDINGBOX combosLay->addWidget(mRelativeTo); + setComboBoxItemState(mRelativeTo, INDEX_REL_LAST_SELECTED_PIVOT, false); + setComboBoxItemState(mRelativeTo, INDEX_REL_BOUNDINGBOX, false); + const auto buttonsLay = new QHBoxLayout; mainLayout->addLayout(buttonsLay); mainLayout->addStretch(); @@ -121,6 +137,31 @@ AlignWidget::AlignWidget(QWidget* const parent) }); buttonsLay->addWidget(bottomButton); + connect(mAlignPivot, + QOverload::of(&QComboBox::currentIndexChanged), + this, [this](int index) { + setComboBoxItemState(mRelativeTo, + INDEX_REL_LAST_SELECTED_PIVOT, + index == INDEX_ALIGN_PIVOT); + setComboBoxItemState(mRelativeTo, + INDEX_REL_BOUNDINGBOX, + index == INDEX_ALIGN_PIVOT); + if (index == INDEX_ALIGN_PIVOT) { mRelativeTo->setCurrentIndex(INDEX_REL_BOUNDINGBOX); } + else { mRelativeTo->setCurrentIndex(INDEX_REL_SCENE); } + }); + + connect(mRelativeTo, + QOverload::of(&QComboBox::currentIndexChanged), + this, [leftButton, + rightButton, + topButton, + bottomButton](int index) { + leftButton->setEnabled(index != INDEX_REL_LAST_SELECTED_PIVOT); + rightButton->setEnabled(index != INDEX_REL_LAST_SELECTED_PIVOT); + topButton->setEnabled(index != INDEX_REL_LAST_SELECTED_PIVOT); + bottomButton->setEnabled(index != INDEX_REL_LAST_SELECTED_PIVOT); + }); + eSizesUI::widget.add(leftButton, [leftButton, hCenterButton, rightButton, @@ -143,3 +184,18 @@ void AlignWidget::triggerAlign(const Qt::Alignment align) const auto relativeTo = static_cast(mRelativeTo->currentIndex()); emit alignTriggered(align, alignPivot, relativeTo); } + +void AlignWidget::setComboBoxItemState(QComboBox *box, + int index, + bool enabled) +{ + auto model = qobject_cast(box->model()); + if (!model) { return; } + + if (index >= box->count() || index < 0) { return; } + + auto item = model->item(index); + if (!item) { return; } + + item->setEnabled(enabled); +} diff --git a/src/ui/widgets/alignwidget.h b/src/ui/widgets/alignwidget.h index 2b74199ad..c670717f0 100644 --- a/src/ui/widgets/alignwidget.h +++ b/src/ui/widgets/alignwidget.h @@ -47,6 +47,9 @@ class UI_EXPORT AlignWidget : public QWidget private: void triggerAlign(const Qt::Alignment align); + void setComboBoxItemState(QComboBox *box, + int index, + bool enabled); QComboBox *mAlignPivot; QComboBox *mRelativeTo;