diff options
author | Marco Bubke <marco.bubke@digia.com> | 2013-05-14 16:21:29 +0200 |
---|---|---|
committer | Marco Bubke <marco.bubke@digia.com> | 2013-05-14 18:07:15 +0200 |
commit | fc6d51aff103cfad5c64124077bf3e26b987bcd5 (patch) | |
tree | 54c2325464a89778628c101cd5323406a07f4eed /src/plugins/qmldesigner | |
parent | 81cbe76c8502acfa4b17d4376d6202eb7f0ca50f (diff) | |
download | qt-creator-fc6d51aff103cfad5c64124077bf3e26b987bcd5.tar.gz |
QmlDesigner: Support anchoring at snapping
Change-Id: I3ec504e931ee63761538acb4666a3c8ce1a592e5
Reviewed-by: Thomas Hartmann <Thomas.Hartmann@digia.com>
Diffstat (limited to 'src/plugins/qmldesigner')
16 files changed, 386 insertions, 115 deletions
diff --git a/src/plugins/qmldesigner/components/formeditor/abstractformeditortool.cpp b/src/plugins/qmldesigner/components/formeditor/abstractformeditortool.cpp index 7dc1d2a93a..7b67273fb8 100644 --- a/src/plugins/qmldesigner/components/formeditor/abstractformeditortool.cpp +++ b/src/plugins/qmldesigner/components/formeditor/abstractformeditortool.cpp @@ -29,6 +29,7 @@ #include "abstractformeditortool.h" #include "formeditorview.h" +#include "formeditorwidget.h" #include <modelnodecontextmenu.h> @@ -36,6 +37,7 @@ #include <QGraphicsSceneDragDropEvent> #include <QMimeData> #include <nodemetainfo.h> +#include <QAction> namespace QmlDesigner { @@ -219,6 +221,22 @@ void AbstractFormEditorTool::showContextMenu(QGraphicsSceneMouseEvent *event) ModelNodeContextMenu::showContextMenu(view(), event->screenPos(), event->scenePos().toPoint(), true); } +Snapper::Snapping AbstractFormEditorTool::generateUseSnapping(Qt::KeyboardModifiers keyboardModifier) const +{ + bool shouldSnapping = view()->formEditorWidget()->snappingAction()->isChecked(); + bool shouldSnappingAndAnchoring = view()->formEditorWidget()->snappingAndAnchoringAction()->isChecked(); + + Snapper::Snapping useSnapping = Snapper::NoSnapping; + if (keyboardModifier.testFlag(Qt::ControlModifier) != (shouldSnapping || shouldSnappingAndAnchoring)) { + if (shouldSnappingAndAnchoring) + useSnapping = Snapper::UseSnappingAndAnchoring; + else + useSnapping = Snapper::UseSnapping; + } + + return useSnapping; +} + void AbstractFormEditorTool::clear() { m_itemList.clear(); diff --git a/src/plugins/qmldesigner/components/formeditor/abstractformeditortool.h b/src/plugins/qmldesigner/components/formeditor/abstractformeditortool.h index c43c6ad9aa..43c2113709 100644 --- a/src/plugins/qmldesigner/components/formeditor/abstractformeditortool.h +++ b/src/plugins/qmldesigner/components/formeditor/abstractformeditortool.h @@ -34,6 +34,8 @@ #include <qmldesignercorelib_global.h> +#include "snapper.h" + QT_BEGIN_NAMESPACE class QGraphicsItem; QT_END_NAMESPACE @@ -99,6 +101,7 @@ public: protected: virtual void selectedItemsChanged(const QList<FormEditorItem*> &itemList) = 0; virtual void showContextMenu(QGraphicsSceneMouseEvent *event); + Snapper::Snapping generateUseSnapping(Qt::KeyboardModifiers keyboardModifier) const; FormEditorView *view() const; void setView(FormEditorView *view); diff --git a/src/plugins/qmldesigner/components/formeditor/dragtool.cpp b/src/plugins/qmldesigner/components/formeditor/dragtool.cpp index 40469e3977..937abb9e3b 100644 --- a/src/plugins/qmldesigner/components/formeditor/dragtool.cpp +++ b/src/plugins/qmldesigner/components/formeditor/dragtool.cpp @@ -275,8 +275,8 @@ void DragTool::dropEvent(QGraphicsSceneDragDropEvent * event) if (event->mimeData()->hasFormat("application/vnd.bauhaus.itemlibraryinfo") || event->mimeData()->hasFormat("application/vnd.bauhaus.libraryresource")) { event->accept(); - end(event->scenePos()); - //Q_ASSERT(m_token.isValid()); + end(generateUseSnapping(event->modifiers())); + try { m_rewriterTransaction.commit(); } catch (RewritingException &e) { @@ -333,7 +333,9 @@ void DragTool::dragLeaveEvent(QGraphicsSceneDragDropEvent * event) event->accept(); if (m_dragNode.isValid()) m_dragNode.destroy(); - end(event->scenePos()); + + m_moveManipulator.end(); + clear(); try { m_rewriterTransaction.commit(); @@ -367,7 +369,7 @@ void DragTool::dragMoveEvent(QGraphicsSceneDragDropEvent * event) FormEditorItem *parentItem = calculateContainer(event->scenePos() + QPoint(2, 2)); if (!parentItem) { //if there is no parent any more - the use left the scene - end(event->scenePos()); + end(); QmlDesignerItemLibraryDragAndDrop::CustomDragAndDrop::show(); m_dragNode.destroy(); //delete the node then return; @@ -405,9 +407,15 @@ void DragTool::dragMoveEvent(QGraphicsSceneDragDropEvent * event) } } -void DragTool::end(QPointF scenePos) +void DragTool::end() +{ + m_moveManipulator.end(); + clear(); +} + +void DragTool::end(Snapper::Snapping useSnapping) { - m_moveManipulator.end(scenePos); + m_moveManipulator.end(useSnapping); clear(); } @@ -424,7 +432,7 @@ void DragTool::move(QPointF scenePos) } //MoveManipulator::Snapping useSnapping = MoveManipulator::NoSnapping; - MoveManipulator::Snapping useSnapping = MoveManipulator::UseSnapping; + Snapper::Snapping useSnapping = Snapper::UseSnapping; /* if (event->modifiers().testFlag(Qt::ControlModifier) != view()->isSnapButtonChecked()) useSnapping = MoveManipulator::UseSnapping;*/ diff --git a/src/plugins/qmldesigner/components/formeditor/dragtool.h b/src/plugins/qmldesigner/components/formeditor/dragtool.h index 107755bdaa..ed4961a05b 100644 --- a/src/plugins/qmldesigner/components/formeditor/dragtool.h +++ b/src/plugins/qmldesigner/components/formeditor/dragtool.h @@ -116,7 +116,8 @@ private: QList<Import> missingImportList(const ItemLibraryEntry &itemLibraryEntry); void begin(QPointF scenePos); - void end(QPointF scenePos); + void end(); + void end(Snapper::Snapping useSnapping); void move(QPointF scenePos); MoveManipulator m_moveManipulator; diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp index 4c2ae66711..b283e40380 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp @@ -60,17 +60,23 @@ FormEditorWidget::FormEditorWidget(FormEditorView *view) m_toolActionGroup = new QActionGroup(this); - m_transformToolAction = m_toolActionGroup->addAction(tr("Transform Tool (Q).")); - m_transformToolAction->setShortcut(Qt::Key_Q); - m_transformToolAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); - m_transformToolAction->setCheckable(true); - m_transformToolAction->setChecked(true); - m_transformToolAction->setIcon(QPixmap(":/icon/tool/transform.png")); - connect(m_transformToolAction.data(), SIGNAL(triggered(bool)), SLOT(changeTransformTool(bool))); + QActionGroup *layoutActionGroup = new QActionGroup(this); + layoutActionGroup->setExclusive(true); + m_noSnappingAction = layoutActionGroup->addAction(tr("No snapping (T).")); + m_noSnappingAction->setShortcut(Qt::Key_W); + m_noSnappingAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); + m_noSnappingAction->setCheckable(true); + m_noSnappingAction->setChecked(true); + m_noSnappingAction->setIcon(QPixmap(":/icon/layout/no_snapping.png")); + + m_snappingAndAnchoringAction = layoutActionGroup->addAction(tr("Snapping with anchoring (W).")); + m_snappingAndAnchoringAction->setShortcut(Qt::Key_W); + m_snappingAndAnchoringAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); + m_snappingAndAnchoringAction->setCheckable(true); + m_snappingAndAnchoringAction->setChecked(true); + m_snappingAndAnchoringAction->setIcon(QPixmap(":/icon/layout/snapping_and_anchoring.png")); - QActionGroup *layoutActionGroup = new QActionGroup(this); - layoutActionGroup->setExclusive(false); m_snappingAction = layoutActionGroup->addAction(tr("Snap to guides (E).")); m_snappingAction->setShortcut(Qt::Key_E); m_snappingAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); @@ -78,14 +84,6 @@ FormEditorWidget::FormEditorWidget(FormEditorView *view) m_snappingAction->setChecked(true); m_snappingAction->setIcon(QPixmap(":/icon/layout/snapping.png")); - m_snappingAndAnchoringAction = layoutActionGroup->addAction(tr("Toggle snapping and anchoring (R).")); - m_snappingAndAnchoringAction->setShortcut(Qt::Key_R); - m_snappingAndAnchoringAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); - m_snappingAndAnchoringAction->setCheckable(true); - m_snappingAndAnchoringAction->setChecked(false); - m_snappingAndAnchoringAction->setEnabled(false); - m_snappingAndAnchoringAction->setVisible(false); - m_snappingAndAnchoringAction->setIcon(QPixmap(":/icon/layout/snapping_and_anchoring.png")); addActions(layoutActionGroup->actions()); upperActions.append(layoutActionGroup->actions()); @@ -125,8 +123,6 @@ FormEditorWidget::FormEditorWidget(FormEditorView *view) addAction(m_rootHeightAction.data()); upperActions.append(m_rootHeightAction.data()); - m_snappingAndAnchoringAction = layoutActionGroup->addAction(tr("Toggle snapping and anchoring (R).")); - m_toolBox = new ToolBox(this); fillLayout->addWidget(m_toolBox.data()); m_toolBox->setLeftSideActions(upperActions); @@ -246,11 +242,6 @@ ZoomAction *FormEditorWidget::zoomAction() const return m_zoomAction.data(); } -QAction *FormEditorWidget::transformToolAction() const -{ - return m_transformToolAction.data(); -} - QAction *FormEditorWidget::showBoundingRectAction() const { return m_showBoundingRectAction.data(); diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorwidget.h b/src/plugins/qmldesigner/components/formeditor/formeditorwidget.h index 745c8f5acc..2e0f90337d 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorwidget.h +++ b/src/plugins/qmldesigner/components/formeditor/formeditorwidget.h @@ -56,7 +56,6 @@ public: FormEditorWidget(FormEditorView *view); ZoomAction *zoomAction() const; - QAction *transformToolAction() const; QAction *showBoundingRectAction() const; QAction *selectOnlyContentItemsAction() const; QAction *snappingAction() const; diff --git a/src/plugins/qmldesigner/components/formeditor/movemanipulator.cpp b/src/plugins/qmldesigner/components/formeditor/movemanipulator.cpp index a258283856..783eb791ee 100644 --- a/src/plugins/qmldesigner/components/formeditor/movemanipulator.cpp +++ b/src/plugins/qmldesigner/components/formeditor/movemanipulator.cpp @@ -102,7 +102,7 @@ void MoveManipulator::synchronizeParent(const QList<FormEditorItem*> &itemList, } if (!parentNode.metaInfo().isSubclassOf("<cpp>.QDeclarativeBasePositioner", -1, -1)) - update(m_lastPosition, NoSnapping, UseBaseState); + update(m_lastPosition, Snapper::NoSnapping, UseBaseState); } void MoveManipulator::synchronizeInstanceParent(const QList<FormEditorItem*> &itemList) @@ -260,7 +260,7 @@ QHash<FormEditorItem*, QRectF> MoveManipulator::tanslatedBoundingRects(const QHa /* /brief updates the position of the items. */ -void MoveManipulator::update(const QPointF& updatePoint, Snapping useSnapping, State stateToBeManipulated) +void MoveManipulator::update(const QPointF& updatePoint, Snapper::Snapping useSnapping, State stateToBeManipulated) { m_lastPosition = updatePoint; deleteSnapLines(); //Since they position is changed and the item is moved the snapping lines are @@ -275,12 +275,7 @@ void MoveManipulator::update(const QPointF& updatePoint, Snapping useSnapping, S QPointF offsetVector(updatePointInContainerSpace - beginPointInContainerSpace); - if (useSnapping == UseSnappingAndAnchoring) - { - - } - - if (useSnapping == UseSnapping || useSnapping == UseSnappingAndAnchoring) { + if (useSnapping == Snapper::UseSnapping || useSnapping == Snapper::UseSnappingAndAnchoring) { offsetVector -= findSnappingOffset(tanslatedBoundingRects(m_beginItemRectHash, offsetVector)); generateSnappingLines(tanslatedBoundingRects(m_beginItemRectHash, offsetVector)); } @@ -386,15 +381,25 @@ void MoveManipulator::reparentTo(FormEditorItem *newParent) } } - -void MoveManipulator::end(const QPointF &/*endPoint*/) +void MoveManipulator::end() { m_isActive = false; deleteSnapLines(); -// setOpacityForAllElements(1.0); clear(); } + + +void MoveManipulator::end(Snapper::Snapping useSnapping) +{ + if (useSnapping == Snapper::UseSnappingAndAnchoring) { + foreach (FormEditorItem *formEditorItem, m_itemList) + m_snapper.adjustAnchoringOfItem(formEditorItem); + } + + end(); +} + void MoveManipulator::moveBy(double deltaX, double deltaY) { foreach (FormEditorItem* item, m_itemList) { diff --git a/src/plugins/qmldesigner/components/formeditor/movemanipulator.h b/src/plugins/qmldesigner/components/formeditor/movemanipulator.h index 00870b154b..d7ead11c16 100644 --- a/src/plugins/qmldesigner/components/formeditor/movemanipulator.h +++ b/src/plugins/qmldesigner/components/formeditor/movemanipulator.h @@ -49,12 +49,6 @@ class Model; class MoveManipulator { public: - enum Snapping { - UseSnapping, - UseSnappingAndAnchoring, - NoSnapping - }; - enum State { UseActualState, UseBaseState @@ -68,9 +62,10 @@ public: void synchronizeParent(const QList<FormEditorItem*> &itemList, const ModelNode &parentNode); void begin(const QPointF& beginPoint); - void update(const QPointF& updatePoint, Snapping useSnapping, State stateToBeManipulated = UseActualState); + void update(const QPointF& updatePoint, Snapper::Snapping useSnapping, State stateToBeManipulated = UseActualState); void reparentTo(FormEditorItem *newParent); - void end(const QPointF& endPoint); + void end(); + void end(Snapper::Snapping useSnapping); void moveBy(double deltaX, double deltaY); @@ -101,6 +96,8 @@ protected: void setPosition(QmlItemNode itemNode, const QPointF &position); + void adjustAnchoringOfItem(FormEditorItem *item); + private: Snapper m_snapper; QWeakPointer<LayerItem> m_layerItem; diff --git a/src/plugins/qmldesigner/components/formeditor/movetool.cpp b/src/plugins/qmldesigner/components/formeditor/movetool.cpp index 5bfe3498a3..7167b0d3ff 100644 --- a/src/plugins/qmldesigner/components/formeditor/movetool.cpp +++ b/src/plugins/qmldesigner/components/formeditor/movetool.cpp @@ -103,18 +103,7 @@ void MoveTool::mouseMoveEvent(const QList<QGraphicsItem*> &itemList, } } - bool shouldSnapping = view()->formEditorWidget()->snappingAction()->isChecked(); - bool shouldSnappingAndAnchoring = view()->formEditorWidget()->snappingAndAnchoringAction()->isChecked(); - - MoveManipulator::Snapping useSnapping = MoveManipulator::NoSnapping; - if (event->modifiers().testFlag(Qt::ControlModifier) != (shouldSnapping || shouldSnappingAndAnchoring)) { - if (shouldSnappingAndAnchoring) - useSnapping = MoveManipulator::UseSnappingAndAnchoring; - else - useSnapping = MoveManipulator::UseSnapping; - } - - m_moveManipulator.update(event->scenePos(), useSnapping); + m_moveManipulator.update(event->scenePos(), generateUseSnapping(event->modifiers())); } } @@ -214,25 +203,11 @@ void MoveTool::mouseReleaseEvent(const QList<QGraphicsItem*> &itemList, if (m_movingItems.isEmpty()) return; - QLineF moveVector(event->scenePos(), m_moveManipulator.beginPoint()); - if (moveVector.length() < QApplication::startDragDistance()) - { - QPointF beginPoint(m_moveManipulator.beginPoint()); - - m_moveManipulator.end(beginPoint); - - // m_selectionIndicator.show(); - m_resizeIndicator.show(); - m_movingItems.clear(); + m_moveManipulator.end(generateUseSnapping(event->modifiers())); - view()->changeToSelectionTool(event); - } else { - m_moveManipulator.end(event->scenePos()); - - m_selectionIndicator.show(); - m_resizeIndicator.show(); - m_movingItems.clear(); - } + m_selectionIndicator.show(); + m_resizeIndicator.show(); + m_movingItems.clear(); } AbstractFormEditorTool::mouseReleaseEvent(itemList, event); diff --git a/src/plugins/qmldesigner/components/formeditor/resizemanipulator.cpp b/src/plugins/qmldesigner/components/formeditor/resizemanipulator.cpp index f4462eb829..5d92f0f4aa 100644 --- a/src/plugins/qmldesigner/components/formeditor/resizemanipulator.cpp +++ b/src/plugins/qmldesigner/components/formeditor/resizemanipulator.cpp @@ -99,14 +99,14 @@ void ResizeManipulator::begin(const QPointF &/*beginPoint*/) // return QSizeF(sizeAsPoint.x(), sizeAsPoint.y()); //} -void ResizeManipulator::update(const QPointF& updatePoint, Snapping useSnapping) +void ResizeManipulator::update(const QPointF& updatePoint, Snapper::Snapping useSnapping) { const double minimumWidth = 0.0; const double minimumHeight = 0.0; deleteSnapLines(); - bool snap = useSnapping == UseSnapping || useSnapping == UseSnappingAndAnchoring; + bool snap = useSnapping == Snapper::UseSnapping || useSnapping == Snapper::UseSnappingAndAnchoring; if (m_resizeController.isValid()) { @@ -376,8 +376,15 @@ void ResizeManipulator::update(const QPointF& updatePoint, Snapping useSnapping) } } -void ResizeManipulator::end() +void ResizeManipulator::end(Snapper::Snapping useSnapping) { + if (useSnapping == Snapper::UseSnappingAndAnchoring) { + deleteSnapLines(); + m_snapper.setTransformtionSpaceFormEditorItem(m_snapper.containerFormEditorItem()); + m_snapper.updateSnappingLines(m_resizeController.formEditorItem()); + m_snapper.adjustAnchoringOfItem(m_resizeController.formEditorItem()); + } + m_isActive = false; m_rewriterTransaction.commit(); clear(); diff --git a/src/plugins/qmldesigner/components/formeditor/resizemanipulator.h b/src/plugins/qmldesigner/components/formeditor/resizemanipulator.h index 117343c1dd..61a8e23044 100644 --- a/src/plugins/qmldesigner/components/formeditor/resizemanipulator.h +++ b/src/plugins/qmldesigner/components/formeditor/resizemanipulator.h @@ -44,12 +44,6 @@ class Model; class ResizeManipulator { public: - enum Snapping { - UseSnapping, - UseSnappingAndAnchoring, - NoSnapping - }; - ResizeManipulator(LayerItem *layerItem, FormEditorView *view); ~ResizeManipulator(); @@ -57,8 +51,8 @@ public: void removeHandle(); void begin(const QPointF& beginPoint); - void update(const QPointF& updatePoint, Snapping useSnapping); - void end(); + void update(const QPointF& updatePoint, Snapper::Snapping useSnapping); + void end(Snapper::Snapping useSnapping); void moveBy(double deltaX, double deltaY); diff --git a/src/plugins/qmldesigner/components/formeditor/resizetool.cpp b/src/plugins/qmldesigner/components/formeditor/resizetool.cpp index 3f4fc48085..78d8f094cd 100644 --- a/src/plugins/qmldesigner/components/formeditor/resizetool.cpp +++ b/src/plugins/qmldesigner/components/formeditor/resizetool.cpp @@ -75,20 +75,8 @@ void ResizeTool::mousePressEvent(const QList<QGraphicsItem*> &itemList, void ResizeTool::mouseMoveEvent(const QList<QGraphicsItem*> &, QGraphicsSceneMouseEvent *event) { - if (m_resizeManipulator.isActive()) { - bool shouldSnapping = view()->formEditorWidget()->snappingAction()->isChecked(); - bool shouldSnappingAndAnchoring = view()->formEditorWidget()->snappingAndAnchoringAction()->isChecked(); - - ResizeManipulator::Snapping useSnapping = ResizeManipulator::NoSnapping; - if (event->modifiers().testFlag(Qt::ControlModifier) != (shouldSnapping || shouldSnappingAndAnchoring)) { - if (shouldSnappingAndAnchoring) - useSnapping = ResizeManipulator::UseSnappingAndAnchoring; - else - useSnapping = ResizeManipulator::UseSnapping; - } - - m_resizeManipulator.update(event->scenePos(), useSnapping); - } + if (m_resizeManipulator.isActive()) + m_resizeManipulator.update(event->scenePos(), generateUseSnapping(event->modifiers())); } void ResizeTool::hoverMoveEvent(const QList<QGraphicsItem*> &itemList, @@ -128,7 +116,7 @@ void ResizeTool::mouseReleaseEvent(const QList<QGraphicsItem*> &itemList, m_selectionIndicator.show(); m_resizeIndicator.show(); - m_resizeManipulator.end(); + m_resizeManipulator.end(generateUseSnapping(event->modifiers())); } AbstractFormEditorTool::mouseReleaseEvent(itemList, event); diff --git a/src/plugins/qmldesigner/components/formeditor/snapper.cpp b/src/plugins/qmldesigner/components/formeditor/snapper.cpp index ee12bb9e72..fda60d362a 100644 --- a/src/plugins/qmldesigner/components/formeditor/snapper.cpp +++ b/src/plugins/qmldesigner/components/formeditor/snapper.cpp @@ -35,6 +35,7 @@ #include <QLineF> #include <QPen> #include <QApplication> +#include <qmlanchors.h> namespace QmlDesigner { @@ -525,11 +526,6 @@ static QList<QLineF> mergedHorizontalLines(const QList<QLineF> &lineList) return mergedLineList; } - - - - - static QList<QLineF> mergedVerticalLines(const QList<QLineF> &lineList) { QList<QLineF> mergedLineList; @@ -567,6 +563,155 @@ QList<QGraphicsItem*> Snapper::generateSnappingLines(const QRectF &boundingRect, return generateSnappingLines(boundingRectList, layerItem, transform); } +static QmlItemNode findItemOnSnappingLine(const QmlItemNode &sourceQmlItemNode, const SnapLineMap &snappingLines, double anchorLine, AnchorLine::Type anchorLineType) +{ + QmlItemNode targetQmlItemNode; + double targetAnchorLine = 0.0; + + targetAnchorLine = std::numeric_limits<double>::max(); + + AnchorLine::Type compareAnchorLineType; + + if (anchorLineType == AnchorLine::Left + || anchorLineType == AnchorLine::Right) + compareAnchorLineType = AnchorLine::Top; + else + compareAnchorLineType = AnchorLine::Left; + + SnapLineMapIterator snapLineIterator(snappingLines); + while (snapLineIterator.hasNext()) { + snapLineIterator.next(); + double snapLine = snapLineIterator.key(); + + if (qAbs(snapLine - anchorLine ) < 1.0) { + QmlItemNode possibleAchorItemNode = snapLineIterator.value().second->qmlItemNode(); + + double currentToAnchorLine = possibleAchorItemNode.anchors().instanceAnchorLine(compareAnchorLineType); + + if (possibleAchorItemNode != sourceQmlItemNode) { + if (sourceQmlItemNode.instanceParent() == possibleAchorItemNode) { + targetQmlItemNode = possibleAchorItemNode; + targetAnchorLine = currentToAnchorLine; + break; + } else if (currentToAnchorLine < targetAnchorLine) { + targetQmlItemNode = possibleAchorItemNode; + targetAnchorLine = currentToAnchorLine; + } + } + } + } + + return targetQmlItemNode; +} + +static void adjustAnchorLine(const QmlItemNode &sourceQmlItemNode, + const QmlItemNode &containerQmlItemNode, + const SnapLineMap &snappingLines, + const SnapLineMap &snappingOffsets, + AnchorLine::Type lineAnchorLineType, + AnchorLine::Type offsetAnchorLineType) +{ + QmlAnchors qmlAnchors = sourceQmlItemNode.anchors(); + + double fromAnchorLine = sourceQmlItemNode.anchors().instanceAnchorLine(lineAnchorLineType); + QmlItemNode targetQmlItemNode = findItemOnSnappingLine(sourceQmlItemNode, snappingLines, fromAnchorLine, lineAnchorLineType); + + if (targetQmlItemNode.isValid() && !targetQmlItemNode.anchors().checkForCycle(lineAnchorLineType, sourceQmlItemNode)) { + double margin = 0.0; + + if (targetQmlItemNode == containerQmlItemNode) { + if (lineAnchorLineType == AnchorLine::Left + || lineAnchorLineType == AnchorLine::Top) + margin = fromAnchorLine; + else if (lineAnchorLineType == AnchorLine::Right) + margin = targetQmlItemNode.instanceSize().width() - fromAnchorLine; + else if (lineAnchorLineType == AnchorLine::Bottom) + margin = targetQmlItemNode.instanceSize().height() - fromAnchorLine; + + } + + if (!qFuzzyIsNull(margin) || !qFuzzyIsNull(qmlAnchors.instanceMargin(lineAnchorLineType))) + qmlAnchors.setMargin(lineAnchorLineType, margin); + + qmlAnchors.setAnchor(lineAnchorLineType, targetQmlItemNode, lineAnchorLineType); + } else if (!snappingOffsets.isEmpty()) { + targetQmlItemNode = findItemOnSnappingLine(sourceQmlItemNode, snappingOffsets, fromAnchorLine, lineAnchorLineType); + if (targetQmlItemNode.isValid() && !targetQmlItemNode.anchors().checkForCycle(lineAnchorLineType, sourceQmlItemNode)) { + double margin = fromAnchorLine - targetQmlItemNode.anchors().instanceAnchorLine(offsetAnchorLineType); + + if (lineAnchorLineType == AnchorLine::Right + || lineAnchorLineType == AnchorLine::Bottom) + margin *= -1.; + + + if (!qFuzzyIsNull(margin) || !qFuzzyIsNull(qmlAnchors.instanceMargin(lineAnchorLineType))) + qmlAnchors.setMargin(lineAnchorLineType, margin); + + qmlAnchors.setAnchor(lineAnchorLineType, targetQmlItemNode, offsetAnchorLineType); + } + } +} + +void Snapper::adjustAnchoringOfItem(FormEditorItem *formEditorItem) +{ + QmlItemNode qmlItemNode = formEditorItem->qmlItemNode(); + QmlAnchors qmlAnchors = qmlItemNode.anchors(); + + if (!qmlAnchors.instanceHasAnchor(AnchorLine::HorizontalCenter)) { + adjustAnchorLine(qmlItemNode, + containerFormEditorItem()->qmlItemNode(), + containerFormEditorItem()->leftSnappingLines(), + containerFormEditorItem()->rightSnappingOffsets(), + AnchorLine::Left, + AnchorLine::Right); + } + + if (!qmlAnchors.instanceHasAnchor(AnchorLine::VerticalCenter)) { + adjustAnchorLine(qmlItemNode, + containerFormEditorItem()->qmlItemNode(), + containerFormEditorItem()->topSnappingLines(), + containerFormEditorItem()->bottomSnappingOffsets(), + AnchorLine::Top, + AnchorLine::Bottom); + } + + if (!qmlAnchors.instanceHasAnchor(AnchorLine::VerticalCenter)) { + adjustAnchorLine(qmlItemNode, + containerFormEditorItem()->qmlItemNode(), + containerFormEditorItem()->bottomSnappingLines(), + containerFormEditorItem()->topSnappingOffsets(), + AnchorLine::Bottom, + AnchorLine::Top); + } + + if (!qmlAnchors.instanceHasAnchor(AnchorLine::HorizontalCenter)) { + adjustAnchorLine(qmlItemNode, + containerFormEditorItem()->qmlItemNode(), + containerFormEditorItem()->rightSnappingLines(), + containerFormEditorItem()->leftSnappingOffsets(), + AnchorLine::Right, + AnchorLine::Left); + } + + if (!qmlAnchors.instanceHasAnchor(AnchorLine::Left) && !qmlAnchors.instanceHasAnchor(AnchorLine::Right)) { + adjustAnchorLine(qmlItemNode, + containerFormEditorItem()->qmlItemNode(), + containerFormEditorItem()->verticalCenterSnappingLines(), + SnapLineMap(), + AnchorLine::HorizontalCenter, + AnchorLine::HorizontalCenter); + } + + if (!qmlAnchors.instanceHasAnchor(AnchorLine::Top) && !qmlAnchors.instanceHasAnchor(AnchorLine::Bottom)) { + adjustAnchorLine(qmlItemNode, + containerFormEditorItem()->qmlItemNode(), + containerFormEditorItem()->horizontalCenterSnappingLines(), + SnapLineMap(), + AnchorLine::VerticalCenter, + AnchorLine::VerticalCenter); + } +} + //static void alignLine(QLineF &line) //{ // line.setP1(QPointF(std::floor(line.p1().x()) + 0.5, diff --git a/src/plugins/qmldesigner/components/formeditor/snapper.h b/src/plugins/qmldesigner/components/formeditor/snapper.h index 6f41c44bcf..975cc78f2d 100644 --- a/src/plugins/qmldesigner/components/formeditor/snapper.h +++ b/src/plugins/qmldesigner/components/formeditor/snapper.h @@ -42,6 +42,12 @@ namespace QmlDesigner { class Snapper { public: + enum Snapping { + UseSnapping, + UseSnappingAndAnchoring, + NoSnapping + }; + Snapper(); void setContainerFormEditorItem(FormEditorItem *formEditorItem); @@ -73,6 +79,9 @@ public: QList<QGraphicsItem*> generateSnappingLines(const QRectF &boundingRect, QGraphicsItem *layerItem, const QTransform &transform); + + void adjustAnchoringOfItem(FormEditorItem *formEditorItem); + protected: double snappedOffsetForLines(const SnapLineMap &snappingLineMap, double value) const; diff --git a/src/plugins/qmldesigner/designercore/include/qmlanchors.h b/src/plugins/qmldesigner/designercore/include/qmlanchors.h index a0565677df..0508ca9967 100644 --- a/src/plugins/qmldesigner/designercore/include/qmlanchors.h +++ b/src/plugins/qmldesigner/designercore/include/qmlanchors.h @@ -92,6 +92,14 @@ public: void removeAnchors(); bool instanceHasAnchor(AnchorLine::Type sourceAnchorLineType) const; bool instanceHasAnchors() const; + double instanceLeftAnchorLine() const; + double instanceTopAnchorLine() const; + double instanceRightAnchorLine() const; + double instanceBottomAnchorLine() const; + double instanceHorizontalCenterAnchorLine() const; + double instanceVerticalCenterAnchorLine() const; + double instanceAnchorLine(AnchorLine::Type anchorLine) const; + void setMargin(AnchorLine::Type sourceAnchorLineType, double margin) const; bool instanceHasMargin(AnchorLine::Type sourceAnchorLineType) const; double instanceMargin(AnchorLine::Type sourceAnchorLineType) const; @@ -101,8 +109,13 @@ public: void fill(); void centerIn(); -protected: + bool checkForCycle(AnchorLine::Type anchorLineTyp, const QmlItemNode &sourceItem) const; + bool checkForHorizontalCycle(const QmlItemNode &sourceItem) const; + bool checkForVerticalCycle(const QmlItemNode &sourceItem) const; + QmlItemNode qmlItemNode() const; + +protected: void beautify(); private: diff --git a/src/plugins/qmldesigner/designercore/model/qmlanchors.cpp b/src/plugins/qmldesigner/designercore/model/qmlanchors.cpp index bcbcd30028..74e3ad3cf6 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlanchors.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlanchors.cpp @@ -393,7 +393,52 @@ bool QmlAnchors::instanceHasAnchors() const instanceHasAnchor(AnchorLine::Bottom) || instanceHasAnchor(AnchorLine::HorizontalCenter) || instanceHasAnchor(AnchorLine::VerticalCenter) || - instanceHasAnchor(AnchorLine::Baseline); + instanceHasAnchor(AnchorLine::Baseline); +} + +double QmlAnchors::instanceLeftAnchorLine() const +{ + return qmlItemNode().nodeInstance().position().x(); +} + +double QmlAnchors::instanceTopAnchorLine() const +{ + return qmlItemNode().nodeInstance().position().y(); +} + +double QmlAnchors::instanceRightAnchorLine() const +{ + return qmlItemNode().nodeInstance().position().x() + qmlItemNode().nodeInstance().size().width(); +} + +double QmlAnchors::instanceBottomAnchorLine() const +{ + return qmlItemNode().nodeInstance().position().y() + qmlItemNode().nodeInstance().size().height(); +} + +double QmlAnchors::instanceHorizontalCenterAnchorLine() const +{ + return (instanceLeftAnchorLine() + instanceRightAnchorLine()) / 2.0; +} + +double QmlAnchors::instanceVerticalCenterAnchorLine() const +{ + return (instanceBottomAnchorLine() + instanceTopAnchorLine()) / 2.0; +} + +double QmlAnchors::instanceAnchorLine(AnchorLine::Type anchorLine) const +{ + switch (anchorLine) { + case AnchorLine::Left: return instanceLeftAnchorLine(); + case AnchorLine::Top: return instanceTopAnchorLine(); + case AnchorLine::Bottom: return instanceBottomAnchorLine(); + case AnchorLine::Right: return instanceRightAnchorLine(); + case AnchorLine::HorizontalCenter: return instanceHorizontalCenterAnchorLine(); + case AnchorLine::VerticalCenter: return instanceVerticalCenterAnchorLine(); + default: return 0; + } + + return 0.0; } void QmlAnchors::setMargin(AnchorLine::Type sourceAnchorLineType, double margin) const @@ -407,6 +452,71 @@ bool QmlAnchors::instanceHasMargin(AnchorLine::Type sourceAnchorLineType) const return !qIsNull(instanceMargin(sourceAnchorLineType)); } +static bool checkForHorizontalCycleRecusive(const QmlAnchors &anchors, QList<QmlItemNode> &visitedItems) +{ + visitedItems.append(anchors.qmlItemNode()); + if (anchors.instanceHasAnchor(AnchorLine::Left)) { + AnchorLine leftAnchorLine = anchors.instanceAnchor(AnchorLine::Left); + if (visitedItems.contains(leftAnchorLine.qmlItemNode()) || checkForHorizontalCycleRecusive(leftAnchorLine.qmlItemNode().anchors(), visitedItems)) + return true; + } + + if (anchors.instanceHasAnchor(AnchorLine::Right)) { + AnchorLine rightAnchorLine = anchors.instanceAnchor(AnchorLine::Right); + if (visitedItems.contains(rightAnchorLine.qmlItemNode()) || checkForHorizontalCycleRecusive(rightAnchorLine.qmlItemNode().anchors(), visitedItems)) + return true; + } + + if (anchors.instanceHasAnchor(AnchorLine::HorizontalCenter)) { + AnchorLine horizontalCenterAnchorLine = anchors.instanceAnchor(AnchorLine::HorizontalCenter); + if (visitedItems.contains(horizontalCenterAnchorLine.qmlItemNode()) || checkForHorizontalCycleRecusive(horizontalCenterAnchorLine.qmlItemNode().anchors(), visitedItems)) + return true; + } + + return false; +} + +static bool checkForVerticalCycleRecusive(const QmlAnchors &anchors, QList<QmlItemNode> &visitedItems) +{ + visitedItems.append(anchors.qmlItemNode()); + + if (anchors.instanceHasAnchor(AnchorLine::Top)) { + AnchorLine topAnchorLine = anchors.instanceAnchor(AnchorLine::Top); + if (visitedItems.contains(topAnchorLine.qmlItemNode()) || checkForVerticalCycleRecusive(topAnchorLine.qmlItemNode().anchors(), visitedItems)) + return true; + } + + if (anchors.instanceHasAnchor(AnchorLine::Bottom)) { + AnchorLine bottomAnchorLine = anchors.instanceAnchor(AnchorLine::Bottom); + if (visitedItems.contains(bottomAnchorLine.qmlItemNode()) || checkForVerticalCycleRecusive(bottomAnchorLine.qmlItemNode().anchors(), visitedItems)) + return true; + } + + if (anchors.instanceHasAnchor(AnchorLine::VerticalCenter)) { + AnchorLine verticalCenterAnchorLine = anchors.instanceAnchor(AnchorLine::VerticalCenter); + if (visitedItems.contains(verticalCenterAnchorLine.qmlItemNode()) || checkForVerticalCycleRecusive(verticalCenterAnchorLine.qmlItemNode().anchors(), visitedItems)) + return true; + } + + return false; +} + +bool QmlAnchors::checkForHorizontalCycle(const QmlItemNode &sourceItem) const +{ + QList<QmlItemNode> visitedItems; + visitedItems.append(sourceItem); + + return checkForHorizontalCycleRecusive(*this, visitedItems); +} + +bool QmlAnchors::checkForVerticalCycle(const QmlItemNode &sourceItem) const +{ + QList<QmlItemNode> visitedItems; + visitedItems.append(sourceItem); + + return checkForVerticalCycleRecusive(*this, visitedItems); +} + double QmlAnchors::instanceMargin(AnchorLine::Type sourceAnchorLineType) const { return qmlItemNode().nodeInstance().property(marginPropertyName(sourceAnchorLineType)).toDouble(); @@ -452,4 +562,12 @@ void QmlAnchors::centerIn() qmlItemNode().modelNode().bindingProperty("anchors.centerIn").setExpression("parent"); } +bool QmlAnchors::checkForCycle(AnchorLine::Type anchorLineTyp, const QmlItemNode &sourceItem) const +{ + if (anchorLineTyp & AnchorLine::HorizontalMask) + return checkForHorizontalCycle(sourceItem); + else + return checkForVerticalCycle(sourceItem); +} + } //QmlDesigner |