1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25
26 #include "dragtool.h"
27
28 #include "formeditorscene.h"
29 #include "formeditorview.h"
30 #include "itemlibrarywidget.h"
31 #include <metainfo.h>
32 #include <nodehints.h>
33 #include <rewritingexception.h>
34
35 #include <QDebug>
36 #include <QGraphicsSceneMouseEvent>
37 #include <QLoggingCategory>
38 #include <QMimeData>
39 #include <QTimer>
40 #include <QWidget>
41
42 static Q_LOGGING_CATEGORY(dragToolInfo, "qtc.qmldesigner.formeditor", QtWarningMsg);
43
44 namespace QmlDesigner {
45
DragTool(FormEditorView * editorView)46 DragTool::DragTool(FormEditorView *editorView)
47 : AbstractFormEditorTool(editorView),
48 m_moveManipulator(editorView->scene()->manipulatorLayerItem(), editorView),
49 m_selectionIndicator(editorView->scene()->manipulatorLayerItem())
50 {
51 }
52
53 DragTool::~DragTool() = default;
54
clear()55 void DragTool::clear()
56 {
57 m_moveManipulator.clear();
58 m_selectionIndicator.clear();
59 m_movingItems.clear();
60 }
61
mousePressEvent(const QList<QGraphicsItem * > &,QGraphicsSceneMouseEvent *)62 void DragTool::mousePressEvent(const QList<QGraphicsItem *> &, QGraphicsSceneMouseEvent *) {}
mouseMoveEvent(const QList<QGraphicsItem * > &,QGraphicsSceneMouseEvent *)63 void DragTool::mouseMoveEvent(const QList<QGraphicsItem *> &, QGraphicsSceneMouseEvent *) {}
hoverMoveEvent(const QList<QGraphicsItem * > &,QGraphicsSceneMouseEvent *)64 void DragTool::hoverMoveEvent(const QList<QGraphicsItem *> &, QGraphicsSceneMouseEvent *) {}
65
keyPressEvent(QKeyEvent * event)66 void DragTool::keyPressEvent(QKeyEvent *event)
67 {
68 if (event->key() == Qt::Key_Escape) {
69 abort();
70 event->accept();
71 commitTransaction();
72 view()->changeToSelectionTool();
73 }
74 }
75
keyReleaseEvent(QKeyEvent *)76 void DragTool::keyReleaseEvent(QKeyEvent *) {}
mouseReleaseEvent(const QList<QGraphicsItem * > &,QGraphicsSceneMouseEvent *)77 void DragTool::mouseReleaseEvent(const QList<QGraphicsItem *> &, QGraphicsSceneMouseEvent *) {}
mouseDoubleClickEvent(const QList<QGraphicsItem * > &,QGraphicsSceneMouseEvent *)78 void DragTool::mouseDoubleClickEvent(const QList<QGraphicsItem *> &, QGraphicsSceneMouseEvent *) {}
itemsAboutToRemoved(const QList<FormEditorItem * > &)79 void DragTool::itemsAboutToRemoved(const QList<FormEditorItem *> &) {}
selectedItemsChanged(const QList<FormEditorItem * > &)80 void DragTool::selectedItemsChanged(const QList<FormEditorItem *> &) {}
updateMoveManipulator()81 void DragTool::updateMoveManipulator() {}
82
beginWithPoint(const QPointF & beginPoint)83 void DragTool::beginWithPoint(const QPointF &beginPoint)
84 {
85 m_movingItems = scene()->itemsForQmlItemNodes(m_dragNodes);
86
87 m_moveManipulator.setItems(m_movingItems);
88 m_moveManipulator.begin(beginPoint);
89 }
90
createQmlItemNode(const ItemLibraryEntry & itemLibraryEntry,const QmlItemNode & parentNode,const QPointF & scenePosition)91 void DragTool::createQmlItemNode(const ItemLibraryEntry &itemLibraryEntry,
92 const QmlItemNode &parentNode,
93 const QPointF &scenePosition)
94 {
95 MetaInfo metaInfo = MetaInfo::global();
96
97 FormEditorItem *parentItem = scene()->itemForQmlItemNode(parentNode);
98 const QPointF positonInItemSpace = parentItem->qmlItemNode().instanceSceneContentItemTransform().inverted().map(scenePosition);
99 QPointF itemPos = positonInItemSpace;
100
101 const bool rootIsFlow = QmlItemNode(view()->rootModelNode()).isFlowView();
102
103 QmlItemNode adjustedParentNode = parentNode;
104
105 if (rootIsFlow) {
106 itemPos = QPointF();
107 adjustedParentNode = view()->rootModelNode();
108 }
109
110 m_dragNodes.append(QmlItemNode::createQmlItemNode(view(), itemLibraryEntry, itemPos, adjustedParentNode));
111
112 if (rootIsFlow) {
113 for (QmlItemNode &dragNode : m_dragNodes)
114 dragNode.setFlowItemPosition(positonInItemSpace);
115 }
116
117 m_selectionIndicator.setItems(scene()->itemsForQmlItemNodes(m_dragNodes));
118 }
119
createQmlItemNodeFromImage(const QString & imagePath,const QmlItemNode & parentNode,const QPointF & scenePosition)120 void DragTool::createQmlItemNodeFromImage(const QString &imagePath,
121 const QmlItemNode &parentNode,
122 const QPointF &scenePosition)
123 {
124 if (parentNode.isValid()) {
125 MetaInfo metaInfo = MetaInfo::global();
126
127 FormEditorItem *parentItem = scene()->itemForQmlItemNode(parentNode);
128 QPointF positonInItemSpace = parentItem->qmlItemNode().instanceSceneContentItemTransform().inverted().map(scenePosition);
129
130 m_dragNodes.append(QmlItemNode::createQmlItemNodeFromImage(view(), imagePath, positonInItemSpace, parentNode));
131 }
132 }
133
createQmlItemNodeFromFont(const QString & fontPath,const QmlItemNode & parentNode,const QPointF & scenePos)134 void DragTool::createQmlItemNodeFromFont(const QString &fontPath,
135 const QmlItemNode &parentNode,
136 const QPointF &scenePos)
137 {
138 if (parentNode.isValid()) {
139 MetaInfo metaInfo = MetaInfo::global();
140
141 FormEditorItem *parentItem = scene()->itemForQmlItemNode(parentNode);
142 QPointF positonInItemSpace = parentItem->qmlItemNode().instanceSceneContentItemTransform()
143 .inverted().map(scenePos);
144
145 const auto typeAndData = ItemLibraryWidget::getAssetTypeAndData(fontPath);
146 QString fontFamily = QString::fromUtf8(typeAndData.second);
147
148 m_dragNodes.append(QmlItemNode::createQmlItemNodeFromFont(view(), fontFamily,
149 positonInItemSpace, parentNode));
150 }
151 }
152
targetContainerOrRootItem(const QList<QGraphicsItem * > & itemList,const QList<FormEditorItem * > & currentItems)153 FormEditorItem *DragTool::targetContainerOrRootItem(const QList<QGraphicsItem *> &itemList,
154 const QList<FormEditorItem *> ¤tItems)
155 {
156 FormEditorItem *formEditorItem = containerFormEditorItem(itemList, currentItems);
157
158 if (!formEditorItem)
159 formEditorItem = scene()->rootFormEditorItem();
160
161 return formEditorItem;
162 }
163
formEditorItemsChanged(const QList<FormEditorItem * > & itemList)164 void DragTool::formEditorItemsChanged(const QList<FormEditorItem *> &itemList)
165 {
166 if (!m_movingItems.isEmpty()) {
167 for (auto item : std::as_const(m_movingItems)) {
168 if (itemList.contains(item)) {
169 m_selectionIndicator.updateItems(m_movingItems);
170 break;
171 }
172 }
173 }
174 }
175
instancesCompleted(const QList<FormEditorItem * > & itemList)176 void DragTool::instancesCompleted(const QList<FormEditorItem *> &itemList)
177 {
178 m_moveManipulator.synchronizeInstanceParent(itemList);
179 for (FormEditorItem *item : itemList) {
180 for (const QmlItemNode &dragNode : std::as_const(m_dragNodes)) {
181 if (item->qmlItemNode() == dragNode) {
182 clearMoveDelay();
183 break;
184 }
185 }
186 }
187 }
188
instancesParentChanged(const QList<FormEditorItem * > & itemList)189 void DragTool::instancesParentChanged(const QList<FormEditorItem *> &itemList)
190 {
191 m_moveManipulator.synchronizeInstanceParent(itemList);
192 }
193
instancePropertyChange(const QList<QPair<ModelNode,PropertyName>> &)194 void DragTool::instancePropertyChange(const QList<QPair<ModelNode, PropertyName> > &) {}
195
clearMoveDelay()196 void DragTool::clearMoveDelay()
197 {
198 if (m_blockMove) {
199 m_blockMove = false;
200 if (!m_dragNodes.isEmpty())
201 beginWithPoint(m_startPoint);
202 }
203 }
204
focusLost()205 void DragTool::focusLost() {}
206
abort()207 void DragTool::abort()
208 {
209 if (!m_isAborted) {
210 m_isAborted = true;
211
212 for (auto &node : m_dragNodes) {
213 if (node.isValid())
214 node.destroy();
215 }
216 m_dragNodes.clear();
217 }
218 }
219
itemLibraryEntryFromMimeData(const QMimeData * mimeData)220 static ItemLibraryEntry itemLibraryEntryFromMimeData(const QMimeData *mimeData)
221 {
222 QByteArray data = mimeData->data(QStringLiteral("application/vnd.bauhaus.itemlibraryinfo"));
223
224 QDataStream stream(data);
225
226 ItemLibraryEntry itemLibraryEntry;
227 stream >> itemLibraryEntry;
228
229 return itemLibraryEntry;
230 }
231
canBeDropped(const QMimeData * mimeData)232 static bool canBeDropped(const QMimeData *mimeData)
233 {
234 return NodeHints::fromItemLibraryEntry(itemLibraryEntryFromMimeData(mimeData)).canBeDroppedInFormEditor();
235 }
236
hasItemLibraryInfo(const QMimeData * mimeData)237 static bool hasItemLibraryInfo(const QMimeData *mimeData)
238 {
239 return mimeData->hasFormat(QStringLiteral("application/vnd.bauhaus.itemlibraryinfo"));
240 }
241
dropEvent(const QList<QGraphicsItem * > &,QGraphicsSceneDragDropEvent * event)242 void DragTool::dropEvent(const QList<QGraphicsItem *> &/*itemList*/, QGraphicsSceneDragDropEvent *event)
243 {
244 if (canBeDropped(event->mimeData())) {
245 event->accept();
246 end(generateUseSnapping(event->modifiers()));
247
248 bool resetPuppet = false;
249 for (auto &node : m_dragNodes) {
250 if (node.isValid()) {
251 if ((node.instanceParentItem().isValid()
252 && node.instanceParent().modelNode().metaInfo().isLayoutable())
253 || node.isFlowItem()) {
254 node.removeProperty("x");
255 node.removeProperty("y");
256 resetPuppet = true;
257 }
258 }
259 }
260 if (resetPuppet)
261 view()->resetPuppet(); // Otherwise the layout might not reposition the items
262
263 commitTransaction();
264
265 if (!m_dragNodes.isEmpty()) {
266 QList<ModelNode> nodeList;
267 for (auto &node : std::as_const(m_dragNodes)) {
268 if (node.isValid())
269 nodeList.append(node);
270 }
271 view()->setSelectedModelNodes(nodeList);
272 }
273 m_dragNodes.clear();
274
275 view()->changeToSelectionTool();
276 }
277 }
278
dragEnterEvent(const QList<QGraphicsItem * > &,QGraphicsSceneDragDropEvent * event)279 void DragTool::dragEnterEvent(const QList<QGraphicsItem *> &/*itemList*/, QGraphicsSceneDragDropEvent *event)
280 {
281 if (canBeDropped(event->mimeData())) {
282 m_blockMove = false;
283
284 if (hasItemLibraryInfo(event->mimeData())) {
285 view()->widgetInfo().widget->setFocus();
286 m_isAborted = false;
287 }
288
289 if (!m_rewriterTransaction.isValid()) {
290 view()->clearSelectedModelNodes();
291 m_rewriterTransaction = view()->beginRewriterTransaction(QByteArrayLiteral("DragTool::dragEnterEvent"));
292 }
293 }
294 }
295
dragLeaveEvent(const QList<QGraphicsItem * > &,QGraphicsSceneDragDropEvent * event)296 void DragTool::dragLeaveEvent(const QList<QGraphicsItem *> &/*itemList*/, QGraphicsSceneDragDropEvent *event)
297 {
298 if (canBeDropped(event->mimeData())) {
299 event->accept();
300
301 m_moveManipulator.end();
302 clear();
303
304 for (auto &node : m_dragNodes) {
305 if (node.isValid())
306 node.destroy();
307 }
308 m_dragNodes.clear();
309
310 commitTransaction();
311 }
312
313 view()->changeToSelectionTool();
314 }
315
createDragNodes(const QMimeData * mimeData,const QPointF & scenePosition,const QList<QGraphicsItem * > & itemList)316 void DragTool::createDragNodes(const QMimeData *mimeData, const QPointF &scenePosition,
317 const QList<QGraphicsItem *> &itemList)
318 {
319 if (m_dragNodes.isEmpty()) {
320 FormEditorItem *targetContainerFormEditorItem = targetContainerOrRootItem(itemList);
321 if (targetContainerFormEditorItem) {
322 QmlItemNode targetContainerQmlItemNode = targetContainerFormEditorItem->qmlItemNode();
323
324 if (hasItemLibraryInfo(mimeData)) {
325 createQmlItemNode(itemLibraryEntryFromMimeData(mimeData), targetContainerQmlItemNode,
326 scenePosition);
327 } else {
328 const QStringList assetPaths = QString::fromUtf8(mimeData
329 ->data("application/vnd.bauhaus.libraryresource")).split(",");
330 for (const QString &assetPath : assetPaths) {
331 QString assetType = ItemLibraryWidget::getAssetTypeAndData(assetPath).first;
332 if (assetType == "application/vnd.bauhaus.libraryresource.image")
333 createQmlItemNodeFromImage(assetPath, targetContainerQmlItemNode, scenePosition);
334 else if (assetType == "application/vnd.bauhaus.libraryresource.font")
335 createQmlItemNodeFromFont(assetPath, targetContainerQmlItemNode, scenePosition);
336 }
337
338 if (!m_dragNodes.isEmpty())
339 m_selectionIndicator.setItems(scene()->itemsForQmlItemNodes(m_dragNodes));
340 }
341
342 m_blockMove = true;
343 m_startPoint = scenePosition;
344 }
345 }
346 }
347
dragMoveEvent(const QList<QGraphicsItem * > & itemList,QGraphicsSceneDragDropEvent * event)348 void DragTool::dragMoveEvent(const QList<QGraphicsItem *> &itemList, QGraphicsSceneDragDropEvent *event)
349 {
350 if (!m_blockMove && !m_isAborted && canBeDropped(event->mimeData())) {
351 event->accept();
352 if (!m_dragNodes.isEmpty()) {
353 FormEditorItem *targetContainerItem = targetContainerOrRootItem(itemList);
354 if (targetContainerItem) {
355 move(event->scenePos(), itemList);
356 } else {
357 end();
358 for (auto &node : m_dragNodes) {
359 if (node.isValid())
360 node.destroy();
361 }
362 m_dragNodes.clear();
363 }
364 } else {
365 createDragNodes(event->mimeData(), event->scenePos(), itemList);
366 }
367 } else {
368 event->ignore();
369 }
370 }
371
end()372 void DragTool::end()
373 {
374 m_moveManipulator.end();
375 clear();
376 }
377
end(Snapper::Snapping useSnapping)378 void DragTool::end(Snapper::Snapping useSnapping)
379 {
380 m_moveManipulator.end(useSnapping);
381 clear();
382 }
383
move(const QPointF & scenePosition,const QList<QGraphicsItem * > & itemList)384 void DragTool::move(const QPointF &scenePosition, const QList<QGraphicsItem *> &itemList)
385 {
386 if (!m_movingItems.isEmpty()) {
387 FormEditorItem *containerItem = targetContainerOrRootItem(itemList, m_movingItems);
388 for (auto &movingItem : std::as_const(m_movingItems)) {
389 if (containerItem && movingItem->parentItem() &&
390 containerItem != movingItem->parentItem()) {
391 const QmlItemNode movingNode = movingItem->qmlItemNode();
392 const QmlItemNode containerNode = containerItem->qmlItemNode();
393
394 qCInfo(dragToolInfo()) << Q_FUNC_INFO << movingNode << containerNode << movingNode.canBereparentedTo(containerNode);
395
396 if (movingNode.canBereparentedTo(containerNode))
397 m_moveManipulator.reparentTo(containerItem);
398 }
399 }
400
401 Snapper::Snapping useSnapping = Snapper::UseSnapping;
402
403 m_moveManipulator.update(scenePosition, useSnapping, MoveManipulator::UseBaseState);
404 }
405 }
406
commitTransaction()407 void DragTool::commitTransaction()
408 {
409 try {
410 m_rewriterTransaction.commit();
411 } catch (const RewritingException &e) {
412 e.showException();
413 }
414 }
415
416 } // namespace QmlDesigner
417