movemanipulator.cpp 16.8 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
2
**
3
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
7
**
hjk's avatar
hjk committed
8 9 10 11 12 13 14
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
15 16
**
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17 18 19 20 21 22
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23
**
hjk's avatar
hjk committed
24 25
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
con's avatar
con committed
26 27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
29 30 31 32 33 34 35

#include "movemanipulator.h"
#include "layeritem.h"
#include "formeditoritem.h"
#include "formeditorscene.h"

#include <QPointF>
hjk's avatar
hjk committed
36
#include <QDebug>
37 38 39

#include <limits>
#include <qmlanchors.h>
40
#include <nodemetainfo.h>
41 42
#include <variantproperty.h>
#include <nodeabstractproperty.h>
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83


namespace QmlDesigner {

MoveManipulator::MoveManipulator(LayerItem *layerItem, FormEditorView *view)
    : m_layerItem(layerItem),
    m_view(view),
    m_isActive(false)
{
}

MoveManipulator::~MoveManipulator()
{
    deleteSnapLines();
}

QPointF MoveManipulator::beginPoint() const
{
    return m_beginPoint;
}

void MoveManipulator::setItem(FormEditorItem* item)
{
    QList<FormEditorItem*> itemList;
    itemList.append(item);

    setItems(itemList);
}

void MoveManipulator::setItems(const QList<FormEditorItem*> &itemList)
{
    m_itemList = itemList;
    if (!m_itemList.isEmpty()) {
        if (m_itemList.first()->parentItem())
            m_snapper.setContainerFormEditorItem(m_itemList.first()->parentItem());
        else
            m_snapper.setContainerFormEditorItem(m_itemList.first());
        m_snapper.setTransformtionSpaceFormEditorItem(m_snapper.containerFormEditorItem());
    }
}

84

85
void MoveManipulator::synchronizeParent(const QList<FormEditorItem*> &itemList, const ModelNode &parentNode)
86
{
87 88 89 90 91 92 93
    bool snapperUpdated = false;

    foreach (FormEditorItem *item, itemList) {
        if (m_itemList.contains(item)) {
            QmlItemNode parentItemNode = QmlItemNode(parentNode);
            if (parentItemNode.isValid()) {
                if (snapperUpdated == false && m_snapper.containerFormEditorItem() != m_view->scene()->itemForQmlItemNode(parentItemNode)) {
94 95 96 97
                    m_snapper.setContainerFormEditorItem(m_view->scene()->itemForQmlItemNode(parentItemNode));
                    m_snapper.setTransformtionSpaceFormEditorItem(m_snapper.containerFormEditorItem());
                    m_snapper.updateSnappingLines(m_itemList);
                    updateHashes();
98
                    snapperUpdated = true;
99 100 101 102
                }
            }
        }
    }
103

104 105
    if (!parentNode.metaInfo().isSubclassOf("<cpp>.QDeclarativeBasePositioner", -1, -1))
        update(m_lastPosition, NoSnapping, UseBaseState);
106 107 108 109
}

void MoveManipulator::synchronizeInstanceParent(const QList<FormEditorItem*> &itemList)
{
Thomas Hartmann's avatar
Thomas Hartmann committed
110
    if (m_view->model() && !m_itemList.isEmpty() && m_itemList.first()->qmlItemNode().instanceParent().isValid())
111
        synchronizeParent(itemList, m_itemList.first()->qmlItemNode().instanceParent());
112 113
}

114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
void MoveManipulator::updateHashes()
{
//    foreach (FormEditorItem* item, m_itemList)
//        m_beginItemRectHash[item] = item->mapRectToParent(item->qmlItemNode().instanceBoundingRect());

    foreach (FormEditorItem* item, m_itemList) {
        QPointF positionInParentSpace = m_snapper.containerFormEditorItem()->mapFromScene(m_beginPositionInSceneSpaceHash.value(item));
        m_beginItemRectHash[item].translate(positionInParentSpace - m_beginPositionHash.value(item));
        m_beginPositionHash.insert(item, positionInParentSpace);
    }
}

bool MoveManipulator::itemsCanReparented() const
{
    foreach (FormEditorItem* item, m_itemList) {
129 130 131
        if (item
            && item->qmlItemNode().isValid()
            && !item->qmlItemNode().canReparent())
132 133 134 135 136 137 138 139 140 141 142 143 144 145
            return false;
    }

    return true;
}

void MoveManipulator::begin(const QPointF &beginPoint)
{
    m_isActive = true;

    m_snapper.updateSnappingLines(m_itemList);


    foreach (FormEditorItem* item, m_itemList) {
146 147
        if (item && item->qmlItemNode().isValid())
            m_beginItemRectHash.insert(item, m_snapper.containerFormEditorItem()->mapRectFromItem(item, item->qmlItemNode().instanceBoundingRect()));
148 149 150
    }

    foreach (FormEditorItem* item, m_itemList) {
151 152 153 154 155 156
        if (item && item->qmlItemNode().isValid()) {
            QPointF positionInParentSpace(item->qmlItemNode().instancePosition());
            QPointF positionInScenesSpace = m_snapper.containerFormEditorItem()->mapToScene(positionInParentSpace);
            m_beginPositionInSceneSpaceHash.insert(item, positionInScenesSpace);
        }
    }
157

158 159 160 161 162 163 164 165 166 167 168 169 170
    foreach (FormEditorItem* item, m_itemList) {
        if (item && item->qmlItemNode().isValid()) {
            QPointF positionInParentSpace = m_snapper.containerFormEditorItem()->mapFromScene(m_beginPositionInSceneSpaceHash.value(item));
            m_beginPositionHash.insert(item, positionInParentSpace);

            QmlAnchors anchors(item->qmlItemNode().anchors());
            m_beginTopMarginHash.insert(item, anchors.instanceMargin(AnchorLine::Top));
            m_beginLeftMarginHash.insert(item, anchors.instanceMargin(AnchorLine::Left));
            m_beginRightMarginHash.insert(item, anchors.instanceMargin(AnchorLine::Right));
            m_beginBottomMarginHash.insert(item, anchors.instanceMargin(AnchorLine::Bottom));
            m_beginHorizontalCenterHash.insert(item, anchors.instanceMargin(AnchorLine::HorizontalCenter));
            m_beginVerticalCenterHash.insert(item, anchors.instanceMargin(AnchorLine::VerticalCenter));
        }
171 172 173 174 175 176 177 178 179 180 181 182 183
    }

    m_beginPoint = beginPoint;

//    setOpacityForAllElements(0.62);

    m_rewriterTransaction = m_view->beginRewriterTransaction();
}





Marco Bubke's avatar
Marco Bubke committed
184
QPointF MoveManipulator::findSnappingOffset(const QHash<FormEditorItem*, QRectF> &boundingRectHash)
185 186 187 188
{
    QPointF offset;

    QMap<double, double> verticalOffsetMap;
Marco Bubke's avatar
Marco Bubke committed
189 190 191
    QMap<double, double> horizontalOffsetMap;

    QHashIterator<FormEditorItem*, QRectF> hashIterator(boundingRectHash);
192
    while (hashIterator.hasNext()) {
Marco Bubke's avatar
Marco Bubke committed
193 194 195
        hashIterator.next();
        FormEditorItem *formEditorItem = hashIterator.key();
        QRectF boundingRect = hashIterator.value();
196 197 198 199

        if (!formEditorItem || !formEditorItem->qmlItemNode().isValid())
            continue;

Marco Bubke's avatar
Marco Bubke committed
200 201 202 203 204 205 206 207 208 209 210
        if (!formEditorItem->qmlItemNode().hasBindingProperty("x")) {
            double verticalOffset = m_snapper.snappedVerticalOffset(boundingRect);
            if (verticalOffset < std::numeric_limits<double>::max())
                verticalOffsetMap.insert(qAbs(verticalOffset), verticalOffset);
        }

        if (!formEditorItem->qmlItemNode().hasBindingProperty("y")) {
            double horizontalOffset = m_snapper.snappedHorizontalOffset(boundingRect);
            if (horizontalOffset < std::numeric_limits<double>::max())
                horizontalOffsetMap.insert(qAbs(horizontalOffset), horizontalOffset);
        }
211 212 213 214 215 216 217 218 219 220 221 222 223 224
    }


    if (!verticalOffsetMap.isEmpty())
        offset.rx() = verticalOffsetMap.begin().value();


    if (!horizontalOffsetMap.isEmpty())
        offset.ry() = horizontalOffsetMap.begin().value();

    return offset;
}


Marco Bubke's avatar
Marco Bubke committed
225
void MoveManipulator::generateSnappingLines(const QHash<FormEditorItem*, QRectF> &boundingRectHash)
226
{
Marco Bubke's avatar
Marco Bubke committed
227
    m_graphicsLineList = m_snapper.generateSnappingLines(boundingRectHash.values(),
228 229 230 231 232 233
                                                         m_layerItem.data(),
                                                         m_snapper.transformtionSpaceFormEditorItem()->sceneTransform());
}



Marco Bubke's avatar
Marco Bubke committed
234
QHash<FormEditorItem*, QRectF> MoveManipulator::tanslatedBoundingRects(const QHash<FormEditorItem*, QRectF> &boundingRectHash, const QPointF& offsetVector)
235
{
Marco Bubke's avatar
Marco Bubke committed
236 237 238
    QHash<FormEditorItem*, QRectF> translatedBoundingRectHash;

    QHashIterator<FormEditorItem*, QRectF> hashIterator(boundingRectHash);
239
    while (hashIterator.hasNext()) {
Marco Bubke's avatar
Marco Bubke committed
240 241 242 243
        QPointF alignedOffset(offsetVector);
        hashIterator.next();
        FormEditorItem *formEditorItem = hashIterator.key();
        QRectF boundingRect = hashIterator.value();
244 245 246 247

        if (!formEditorItem || !formEditorItem->qmlItemNode().isValid())
            continue;

Marco Bubke's avatar
Marco Bubke committed
248 249 250 251 252 253
        if (formEditorItem->qmlItemNode().hasBindingProperty("x"))
            alignedOffset.setX(0);
        if (formEditorItem->qmlItemNode().hasBindingProperty("y"))
            alignedOffset.setY(0);
        translatedBoundingRectHash.insert(formEditorItem, boundingRect.translated(offsetVector));
    }
254

Marco Bubke's avatar
Marco Bubke committed
255
    return translatedBoundingRectHash;
256 257 258 259 260 261 262
}



/*
  /brief updates the position of the items.
*/
263
void MoveManipulator::update(const QPointF& updatePoint, Snapping useSnapping, State stateToBeManipulated)
264
{
265
    m_lastPosition = updatePoint;
266 267 268 269 270 271 272 273 274 275 276
    deleteSnapLines(); //Since they position is changed and the item is moved the snapping lines are
                       //are obsolete. The new updated snapping lines (color and visibility) will be
                       //calculated in snapPoint() called in moveNode() later

    if (m_itemList.isEmpty()) {
        return;
    } else {
        QPointF updatePointInContainerSpace(m_snapper.containerFormEditorItem()->mapFromScene(updatePoint));
        QPointF beginPointInContainerSpace(m_snapper.containerFormEditorItem()->mapFromScene(m_beginPoint));

        QPointF offsetVector(updatePointInContainerSpace - beginPointInContainerSpace);
277 278 279 280 281 282 283

        if (useSnapping == UseSnappingAndAnchoring)
        {

        }

        if (useSnapping == UseSnapping || useSnapping == UseSnappingAndAnchoring) {
Marco Bubke's avatar
Marco Bubke committed
284 285
            offsetVector -= findSnappingOffset(tanslatedBoundingRects(m_beginItemRectHash, offsetVector));
            generateSnappingLines(tanslatedBoundingRects(m_beginItemRectHash, offsetVector));
286 287 288 289 290
        }

        foreach (FormEditorItem* item, m_itemList) {
            QPointF positionInContainerSpace(m_beginPositionHash.value(item) + offsetVector);

291 292 293
            if (!item || !item->qmlItemNode().isValid())
                continue;

294 295 296
            // don't support anchors for base state because it is not needed by the droptool
            if (stateToBeManipulated == UseActualState) {
                QmlAnchors anchors(item->qmlItemNode().anchors());
297

298
                if (anchors.instanceHasAnchor(AnchorLine::Top))
299
                    anchors.setMargin(AnchorLine::Top, m_beginTopMarginHash.value(item) + offsetVector.y());
300

301
                if (anchors.instanceHasAnchor(AnchorLine::Left))
302
                    anchors.setMargin(AnchorLine::Left, m_beginLeftMarginHash.value(item) + offsetVector.x());
303

304
                if (anchors.instanceHasAnchor(AnchorLine::Bottom))
305
                    anchors.setMargin(AnchorLine::Bottom, m_beginBottomMarginHash.value(item) - offsetVector.y());
306

307
                if (anchors.instanceHasAnchor(AnchorLine::Right))
308
                    anchors.setMargin(AnchorLine::Right, m_beginRightMarginHash.value(item) - offsetVector.x());
309

310
                if (anchors.instanceHasAnchor(AnchorLine::HorizontalCenter))
311
                    anchors.setMargin(AnchorLine::HorizontalCenter, m_beginHorizontalCenterHash.value(item) + offsetVector.x());
312

313
                if (anchors.instanceHasAnchor(AnchorLine::VerticalCenter))
314
                    anchors.setMargin(AnchorLine::VerticalCenter, m_beginVerticalCenterHash.value(item) + offsetVector.y());
315

316
                setPosition(item->qmlItemNode(), positionInContainerSpace);
317 318 319 320 321
            } else {
                item->qmlItemNode().modelNode().variantProperty("x").setValue(qRound(positionInContainerSpace.x()));
                item->qmlItemNode().modelNode().variantProperty("y").setValue(qRound(positionInContainerSpace.y()));
            }
        }
322 323 324 325 326 327 328 329 330 331
    }
}

void MoveManipulator::clear()
{
    deleteSnapLines();
    m_beginItemRectHash.clear();
    m_beginPositionHash.clear();
    m_beginPositionInSceneSpaceHash.clear();
    m_itemList.clear();
332
    m_lastPosition = QPointF();
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353

    m_rewriterTransaction.commit();

    m_beginTopMarginHash.clear();
    m_beginLeftMarginHash.clear();
    m_beginRightMarginHash.clear();
    m_beginBottomMarginHash.clear();
    m_beginHorizontalCenterHash.clear();
    m_beginVerticalCenterHash.clear();
}

void MoveManipulator::reparentTo(FormEditorItem *newParent)
{
    deleteSnapLines();

    if (!newParent)
        return;

    if (!itemsCanReparented())
        return;

354
    if (!newParent->qmlItemNode().modelNode().metaInfo().isPositioner()
355 356
            && newParent->qmlItemNode().modelNode().hasParentProperty()) {
        ModelNode grandParent = newParent->qmlItemNode().modelNode().parentProperty().parentModelNode();
357
        if (grandParent.metaInfo().isPositioner())
358 359 360 361 362
            newParent = m_view.data()->scene()->itemForQmlItemNode(QmlItemNode(grandParent));
    }



363 364 365 366 367
    QVector<ModelNode> nodeReparentVector;
    NodeAbstractProperty parentProperty;

    QmlItemNode parent(newParent->qmlItemNode());
    if (parent.isValid()) {
368
        if (parent.hasDefaultProperty())
369
            parentProperty = parent.nodeAbstractProperty(parent.defaultProperty());
370
        else
371 372 373 374 375 376 377 378
            parentProperty = parent.nodeAbstractProperty("data");

        foreach (FormEditorItem* item, m_itemList) {
            if (!item || !item->qmlItemNode().isValid())
                continue;

            if (parentProperty != item->qmlItemNode().modelNode().parentProperty())
                nodeReparentVector.append(item->qmlItemNode().modelNode());
379

380
        }
381 382 383 384 385

        foreach (const ModelNode &nodeToReparented, nodeReparentVector)
            parentProperty.reparentHere(nodeToReparented);

        synchronizeParent(m_itemList, parentProperty.parentModelNode());
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400
    }
}


void MoveManipulator::end(const QPointF &/*endPoint*/)
{
    m_isActive = false;
    deleteSnapLines();
//    setOpacityForAllElements(1.0);
    clear();
}

void MoveManipulator::moveBy(double deltaX, double deltaY)
{
    foreach (FormEditorItem* item, m_itemList) {
401 402 403
        if (!item || !item->qmlItemNode().isValid())
            continue;

404 405
        QmlAnchors anchors(item->qmlItemNode().anchors());

406
        if (anchors.instanceHasAnchor(AnchorLine::Top))
407
            anchors.setMargin(AnchorLine::Top, anchors.instanceMargin(AnchorLine::Top) + deltaY);
408

409
        if (anchors.instanceHasAnchor(AnchorLine::Left))
410 411
            anchors.setMargin(AnchorLine::Left, anchors.instanceMargin(AnchorLine::Left) + deltaX);

412
        if (anchors.instanceHasAnchor(AnchorLine::Bottom))
413
            anchors.setMargin(AnchorLine::Bottom, anchors.instanceMargin(AnchorLine::Bottom) - deltaY);
414

415
        if (anchors.instanceHasAnchor(AnchorLine::Right))
416 417
            anchors.setMargin(AnchorLine::Right, anchors.instanceMargin(AnchorLine::Right) - deltaX);

418
        if (anchors.instanceHasAnchor(AnchorLine::HorizontalCenter))
419 420
            anchors.setMargin(AnchorLine::HorizontalCenter, anchors.instanceMargin(AnchorLine::HorizontalCenter) + deltaX);

421
        if (anchors.instanceHasAnchor(AnchorLine::VerticalCenter))
422 423
            anchors.setMargin(AnchorLine::VerticalCenter, anchors.instanceMargin(AnchorLine::VerticalCenter) + deltaY);

Marco Bubke's avatar
Marco Bubke committed
424 425
        setPosition(item->qmlItemNode(), QPointF(item->qmlItemNode().instanceValue("x").toDouble() + deltaX,
                                                  item->qmlItemNode().instanceValue("y").toDouble() + deltaY));
426 427 428
    }
}

429 430 431 432 433 434 435 436 437 438
void MoveManipulator::beginRewriterTransaction()
{
    m_rewriterTransaction = m_view->beginRewriterTransaction();
}

void MoveManipulator::endRewriterTransaction()
{
    m_rewriterTransaction.commit();
}

439 440 441 442 443 444 445 446 447
void MoveManipulator::setOpacityForAllElements(qreal opacity)
{
    foreach (FormEditorItem* item, m_itemList)
        item->setOpacity(opacity);
}

void MoveManipulator::deleteSnapLines()
{
    if (m_layerItem) {
448
        foreach (QGraphicsItem *item, m_graphicsLineList) {
449
            m_layerItem->scene()->removeItem(item);
450 451
            delete item;
        }
452 453 454 455 456 457 458 459 460 461
    }
    m_graphicsLineList.clear();
    m_view->scene()->update();
}

bool MoveManipulator::isActive() const
{
    return m_isActive;
}

Marco Bubke's avatar
Marco Bubke committed
462 463 464 465 466 467 468 469 470
void MoveManipulator::setPosition(QmlItemNode itemNode, const QPointF &position)
{
    if (!itemNode.hasBindingProperty("x"))
        itemNode.setVariantProperty("x", qRound(position.x()));

    if (!itemNode.hasBindingProperty("y"))
        itemNode.setVariantProperty("y", qRound(position.y()));
}

471
}