summaryrefslogtreecommitdiff
path: root/src/plugins/qmldesigner
diff options
context:
space:
mode:
authorMarco Bubke <marco.bubke@digia.com>2013-05-14 16:21:29 +0200
committerMarco Bubke <marco.bubke@digia.com>2013-05-14 18:07:15 +0200
commitfc6d51aff103cfad5c64124077bf3e26b987bcd5 (patch)
tree54c2325464a89778628c101cd5323406a07f4eed /src/plugins/qmldesigner
parent81cbe76c8502acfa4b17d4376d6202eb7f0ca50f (diff)
downloadqt-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')
-rw-r--r--src/plugins/qmldesigner/components/formeditor/abstractformeditortool.cpp18
-rw-r--r--src/plugins/qmldesigner/components/formeditor/abstractformeditortool.h3
-rw-r--r--src/plugins/qmldesigner/components/formeditor/dragtool.cpp22
-rw-r--r--src/plugins/qmldesigner/components/formeditor/dragtool.h3
-rw-r--r--src/plugins/qmldesigner/components/formeditor/formeditorwidget.cpp39
-rw-r--r--src/plugins/qmldesigner/components/formeditor/formeditorwidget.h1
-rw-r--r--src/plugins/qmldesigner/components/formeditor/movemanipulator.cpp27
-rw-r--r--src/plugins/qmldesigner/components/formeditor/movemanipulator.h13
-rw-r--r--src/plugins/qmldesigner/components/formeditor/movetool.cpp35
-rw-r--r--src/plugins/qmldesigner/components/formeditor/resizemanipulator.cpp13
-rw-r--r--src/plugins/qmldesigner/components/formeditor/resizemanipulator.h10
-rw-r--r--src/plugins/qmldesigner/components/formeditor/resizetool.cpp18
-rw-r--r--src/plugins/qmldesigner/components/formeditor/snapper.cpp155
-rw-r--r--src/plugins/qmldesigner/components/formeditor/snapper.h9
-rw-r--r--src/plugins/qmldesigner/designercore/include/qmlanchors.h15
-rw-r--r--src/plugins/qmldesigner/designercore/model/qmlanchors.cpp120
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