1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
10 ** GNU Lesser General Public License Usage
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
31 **************************************************************************/
33 #include "movemanipulator.h"
34 #include "itemutilfunctions.h"
35 #include "layeritem.h"
36 #include "formeditoritem.h"
37 #include "formeditorscene.h"
43 #include <QApplication>
47 #include <qmlanchors.h>
48 #include <variantproperty.h>
49 #include <nodeabstractproperty.h>
52 namespace QmlDesigner {
54 MoveManipulator::MoveManipulator(LayerItem *layerItem, FormEditorView *view)
55 : m_layerItem(layerItem),
61 MoveManipulator::~MoveManipulator()
66 QPointF MoveManipulator::beginPoint() const
71 void MoveManipulator::setItem(FormEditorItem* item)
73 QList<FormEditorItem*> itemList;
74 itemList.append(item);
79 void MoveManipulator::setItems(const QList<FormEditorItem*> &itemList)
81 m_itemList = itemList;
82 if (!m_itemList.isEmpty()) {
83 if (m_itemList.first()->parentItem())
84 m_snapper.setContainerFormEditorItem(m_itemList.first()->parentItem());
86 m_snapper.setContainerFormEditorItem(m_itemList.first());
87 m_snapper.setTransformtionSpaceFormEditorItem(m_snapper.containerFormEditorItem());
91 void MoveManipulator::updateHashes()
93 // foreach (FormEditorItem* item, m_itemList)
94 // m_beginItemRectHash[item] = item->mapRectToParent(item->qmlItemNode().instanceBoundingRect());
96 foreach (FormEditorItem* item, m_itemList) {
97 QPointF positionInParentSpace = m_snapper.containerFormEditorItem()->mapFromScene(m_beginPositionInSceneSpaceHash.value(item));
98 m_beginItemRectHash[item].translate(positionInParentSpace - m_beginPositionHash.value(item));
99 m_beginPositionHash.insert(item, positionInParentSpace);
103 bool MoveManipulator::itemsCanReparented() const
105 foreach (FormEditorItem* item, m_itemList) {
107 && item->qmlItemNode().isValid()
108 && !item->qmlItemNode().canReparent())
115 void MoveManipulator::begin(const QPointF &beginPoint)
119 m_snapper.updateSnappingLines(m_itemList);
122 foreach (FormEditorItem* item, m_itemList) {
123 if (item && item->qmlItemNode().isValid())
124 m_beginItemRectHash.insert(item, m_snapper.containerFormEditorItem()->mapRectFromItem(item, item->qmlItemNode().instanceBoundingRect()));
127 foreach (FormEditorItem* item, m_itemList) {
128 if (item && item->qmlItemNode().isValid()) {
129 QPointF positionInParentSpace(item->qmlItemNode().instancePosition());
130 QPointF positionInScenesSpace = m_snapper.containerFormEditorItem()->mapToScene(positionInParentSpace);
131 m_beginPositionInSceneSpaceHash.insert(item, positionInScenesSpace);
135 foreach (FormEditorItem* item, m_itemList) {
136 if (item && item->qmlItemNode().isValid()) {
137 QPointF positionInParentSpace = m_snapper.containerFormEditorItem()->mapFromScene(m_beginPositionInSceneSpaceHash.value(item));
138 m_beginPositionHash.insert(item, positionInParentSpace);
140 QmlAnchors anchors(item->qmlItemNode().anchors());
141 m_beginTopMarginHash.insert(item, anchors.instanceMargin(AnchorLine::Top));
142 m_beginLeftMarginHash.insert(item, anchors.instanceMargin(AnchorLine::Left));
143 m_beginRightMarginHash.insert(item, anchors.instanceMargin(AnchorLine::Right));
144 m_beginBottomMarginHash.insert(item, anchors.instanceMargin(AnchorLine::Bottom));
145 m_beginHorizontalCenterHash.insert(item, anchors.instanceMargin(AnchorLine::HorizontalCenter));
146 m_beginVerticalCenterHash.insert(item, anchors.instanceMargin(AnchorLine::VerticalCenter));
150 m_beginPoint = beginPoint;
152 // setOpacityForAllElements(0.62);
154 m_rewriterTransaction = m_view->beginRewriterTransaction();
161 QPointF MoveManipulator::findSnappingOffset(const QHash<FormEditorItem*, QRectF> &boundingRectHash)
165 QMap<double, double> verticalOffsetMap;
166 QMap<double, double> horizontalOffsetMap;
168 QHashIterator<FormEditorItem*, QRectF> hashIterator(boundingRectHash);
169 while(hashIterator.hasNext()) {
171 FormEditorItem *formEditorItem = hashIterator.key();
172 QRectF boundingRect = hashIterator.value();
174 if (!formEditorItem || !formEditorItem->qmlItemNode().isValid())
177 if (!formEditorItem->qmlItemNode().hasBindingProperty("x")) {
178 double verticalOffset = m_snapper.snappedVerticalOffset(boundingRect);
179 if (verticalOffset < std::numeric_limits<double>::max())
180 verticalOffsetMap.insert(qAbs(verticalOffset), verticalOffset);
183 if (!formEditorItem->qmlItemNode().hasBindingProperty("y")) {
184 double horizontalOffset = m_snapper.snappedHorizontalOffset(boundingRect);
185 if (horizontalOffset < std::numeric_limits<double>::max())
186 horizontalOffsetMap.insert(qAbs(horizontalOffset), horizontalOffset);
191 if (!verticalOffsetMap.isEmpty())
192 offset.rx() = verticalOffsetMap.begin().value();
195 if (!horizontalOffsetMap.isEmpty())
196 offset.ry() = horizontalOffsetMap.begin().value();
202 void MoveManipulator::generateSnappingLines(const QHash<FormEditorItem*, QRectF> &boundingRectHash)
204 m_graphicsLineList = m_snapper.generateSnappingLines(boundingRectHash.values(),
206 m_snapper.transformtionSpaceFormEditorItem()->sceneTransform());
211 QHash<FormEditorItem*, QRectF> MoveManipulator::tanslatedBoundingRects(const QHash<FormEditorItem*, QRectF> &boundingRectHash, const QPointF& offsetVector)
213 QHash<FormEditorItem*, QRectF> translatedBoundingRectHash;
215 QHashIterator<FormEditorItem*, QRectF> hashIterator(boundingRectHash);
216 while(hashIterator.hasNext()) {
217 QPointF alignedOffset(offsetVector);
219 FormEditorItem *formEditorItem = hashIterator.key();
220 QRectF boundingRect = hashIterator.value();
222 if (!formEditorItem || !formEditorItem->qmlItemNode().isValid())
225 if (formEditorItem->qmlItemNode().hasBindingProperty("x"))
226 alignedOffset.setX(0);
227 if (formEditorItem->qmlItemNode().hasBindingProperty("y"))
228 alignedOffset.setY(0);
229 translatedBoundingRectHash.insert(formEditorItem, boundingRect.translated(offsetVector));
232 return translatedBoundingRectHash;
238 /brief updates the position of the items.
240 void MoveManipulator::update(const QPointF& updatePoint, Snapping useSnapping, State stateToBeManipulated)
242 deleteSnapLines(); //Since they position is changed and the item is moved the snapping lines are
243 //are obsolete. The new updated snapping lines (color and visibility) will be
244 //calculated in snapPoint() called in moveNode() later
246 if (m_itemList.isEmpty()) {
249 QPointF updatePointInContainerSpace(m_snapper.containerFormEditorItem()->mapFromScene(updatePoint));
250 QPointF beginPointInContainerSpace(m_snapper.containerFormEditorItem()->mapFromScene(m_beginPoint));
252 QPointF offsetVector(updatePointInContainerSpace - beginPointInContainerSpace);
254 if (useSnapping == UseSnappingAndAnchoring)
259 if (useSnapping == UseSnapping || useSnapping == UseSnappingAndAnchoring) {
260 offsetVector -= findSnappingOffset(tanslatedBoundingRects(m_beginItemRectHash, offsetVector));
261 generateSnappingLines(tanslatedBoundingRects(m_beginItemRectHash, offsetVector));
264 foreach (FormEditorItem* item, m_itemList) {
265 QPointF positionInContainerSpace(m_beginPositionHash.value(item) + offsetVector);
267 if (!item || !item->qmlItemNode().isValid())
270 // don't support anchors for base state because it is not needed by the droptool
271 if (stateToBeManipulated == UseActualState) {
272 QmlAnchors anchors(item->qmlItemNode().anchors());
274 if (anchors.instanceHasAnchor(AnchorLine::Top)) {
275 anchors.setMargin(AnchorLine::Top, m_beginTopMarginHash.value(item) + offsetVector.y());
278 if (anchors.instanceHasAnchor(AnchorLine::Left)) {
279 anchors.setMargin(AnchorLine::Left, m_beginLeftMarginHash.value(item) + offsetVector.x());
282 if (anchors.instanceHasAnchor(AnchorLine::Bottom)) {
283 anchors.setMargin(AnchorLine::Bottom, m_beginBottomMarginHash.value(item) - offsetVector.y());
286 if (anchors.instanceHasAnchor(AnchorLine::Right)) {
287 anchors.setMargin(AnchorLine::Right, m_beginRightMarginHash.value(item) - offsetVector.x());
290 if (anchors.instanceHasAnchor(AnchorLine::HorizontalCenter)) {
291 anchors.setMargin(AnchorLine::HorizontalCenter, m_beginHorizontalCenterHash.value(item) + offsetVector.x());
294 if (anchors.instanceHasAnchor(AnchorLine::VerticalCenter)) {
295 anchors.setMargin(AnchorLine::VerticalCenter, m_beginVerticalCenterHash.value(item) + offsetVector.y());
298 setPosition(item->qmlItemNode(), positionInContainerSpace);
300 item->qmlItemNode().modelNode().variantProperty("x").setValue(qRound(positionInContainerSpace.x()));
301 item->qmlItemNode().modelNode().variantProperty("y").setValue(qRound(positionInContainerSpace.y()));
307 void MoveManipulator::clear()
310 m_beginItemRectHash.clear();
311 m_beginPositionHash.clear();
312 m_beginPositionInSceneSpaceHash.clear();
315 m_rewriterTransaction.commit();
317 m_beginTopMarginHash.clear();
318 m_beginLeftMarginHash.clear();
319 m_beginRightMarginHash.clear();
320 m_beginBottomMarginHash.clear();
321 m_beginHorizontalCenterHash.clear();
322 m_beginVerticalCenterHash.clear();
325 void MoveManipulator::reparentTo(FormEditorItem *newParent)
332 if (!itemsCanReparented())
335 foreach (FormEditorItem* item, m_itemList) {
336 if (!item || !item->qmlItemNode().isValid())
339 QmlItemNode parent(newParent->qmlItemNode());
340 if (parent.isValid()) {
341 if (parent.hasDefaultProperty())
342 item->qmlItemNode().setParentProperty(parent.nodeAbstractProperty(parent.defaultProperty()));
344 item->qmlItemNode().setParentProperty(parent.nodeAbstractProperty("data"));
348 if (m_view->model()) {
349 m_snapper.setContainerFormEditorItem(newParent);
350 m_snapper.setTransformtionSpaceFormEditorItem(m_snapper.containerFormEditorItem());
351 m_snapper.updateSnappingLines(m_itemList);
357 void MoveManipulator::end(const QPointF &/*endPoint*/)
361 // setOpacityForAllElements(1.0);
365 void MoveManipulator::moveBy(double deltaX, double deltaY)
367 foreach (FormEditorItem* item, m_itemList) {
368 if (!item || !item->qmlItemNode().isValid())
371 QmlAnchors anchors(item->qmlItemNode().anchors());
373 if (anchors.instanceHasAnchor(AnchorLine::Top)) {
374 anchors.setMargin(AnchorLine::Top, anchors.instanceMargin(AnchorLine::Top) + deltaY);
377 if (anchors.instanceHasAnchor(AnchorLine::Left)) {
378 anchors.setMargin(AnchorLine::Left, anchors.instanceMargin(AnchorLine::Left) + deltaX);
381 if (anchors.instanceHasAnchor(AnchorLine::Bottom)) {
382 anchors.setMargin(AnchorLine::Bottom, anchors.instanceMargin(AnchorLine::Bottom) - deltaY);
385 if (anchors.instanceHasAnchor(AnchorLine::Right)) {
386 anchors.setMargin(AnchorLine::Right, anchors.instanceMargin(AnchorLine::Right) - deltaX);
389 if (anchors.instanceHasAnchor(AnchorLine::HorizontalCenter)) {
390 anchors.setMargin(AnchorLine::HorizontalCenter, anchors.instanceMargin(AnchorLine::HorizontalCenter) + deltaX);
393 if (anchors.instanceHasAnchor(AnchorLine::VerticalCenter)) {
394 anchors.setMargin(AnchorLine::VerticalCenter, anchors.instanceMargin(AnchorLine::VerticalCenter) + deltaY);
397 setPosition(item->qmlItemNode(), QPointF(item->qmlItemNode().instanceValue("x").toDouble() + deltaX,
398 item->qmlItemNode().instanceValue("y").toDouble() + deltaY));
402 void MoveManipulator::beginRewriterTransaction()
404 m_rewriterTransaction = m_view->beginRewriterTransaction();
407 void MoveManipulator::endRewriterTransaction()
409 m_rewriterTransaction.commit();
412 void MoveManipulator::setOpacityForAllElements(qreal opacity)
414 foreach (FormEditorItem* item, m_itemList)
415 item->setOpacity(opacity);
418 void MoveManipulator::deleteSnapLines()
421 foreach (QGraphicsItem *item, m_graphicsLineList) {
422 m_layerItem->scene()->removeItem(item);
426 m_graphicsLineList.clear();
427 m_view->scene()->update();
430 bool MoveManipulator::isActive() const
435 void MoveManipulator::setPosition(QmlItemNode itemNode, const QPointF &position)
437 if (!itemNode.hasBindingProperty("x"))
438 itemNode.setVariantProperty("x", qRound(position.x()));
440 if (!itemNode.hasBindingProperty("y"))
441 itemNode.setVariantProperty("y", qRound(position.y()));