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 "abstractformeditortool.h"
27 #include "formeditorview.h"
28 #include "formeditorwidget.h"
29 #include "formeditorscene.h"
30 #include "itemlibrarywidget.h"
31
32 #include <modelnodecontextmenu.h>
33
34 #include <QDebug>
35 #include <QGraphicsSceneDragDropEvent>
36 #include <QMimeData>
37 #include <nodemetainfo.h>
38 #include <QAction>
39
40 namespace QmlDesigner {
41
AbstractFormEditorTool(FormEditorView * editorView)42 AbstractFormEditorTool::AbstractFormEditorTool(FormEditorView *editorView) : m_view(editorView)
43 {
44 }
45
46 AbstractFormEditorTool::~AbstractFormEditorTool() = default;
47
view() const48 FormEditorView* AbstractFormEditorTool::view() const
49 {
50 return m_view;
51 }
52
setView(FormEditorView * view)53 void AbstractFormEditorTool::setView(FormEditorView *view)
54 {
55 m_view = view;
56 }
57
scene() const58 FormEditorScene* AbstractFormEditorTool::scene() const
59 {
60 return view()->scene();
61 }
62
setItems(const QList<FormEditorItem * > & itemList)63 void AbstractFormEditorTool::setItems(const QList<FormEditorItem*> &itemList)
64 {
65 m_itemList = itemList;
66 selectedItemsChanged(m_itemList);
67 }
68
items() const69 QList<FormEditorItem*> AbstractFormEditorTool::items() const
70 {
71 return m_itemList;
72 }
73
toFormEditorItemList(const QList<QGraphicsItem * > & itemList)74 QList<FormEditorItem *> AbstractFormEditorTool::toFormEditorItemList(const QList<QGraphicsItem *> &itemList)
75 {
76 QList<FormEditorItem *> formEditorItemList;
77
78 foreach (QGraphicsItem *graphicsItem, itemList) {
79 auto formEditorItem = qgraphicsitem_cast<FormEditorItem*>(graphicsItem);
80 if (formEditorItem)
81 formEditorItemList.append(formEditorItem);
82 }
83
84 return formEditorItemList;
85 }
86
topItemIsMovable(const QList<QGraphicsItem * > & itemList)87 bool AbstractFormEditorTool::topItemIsMovable(const QList<QGraphicsItem*> & itemList)
88 {
89 QGraphicsItem *firstSelectableItem = topMovableGraphicsItem(itemList);
90 if (firstSelectableItem == nullptr)
91 return false;
92
93 FormEditorItem *formEditorItem = FormEditorItem::fromQGraphicsItem(firstSelectableItem);
94 QList<ModelNode> selectedNodes = view()->selectedModelNodes();
95
96 if (formEditorItem != nullptr
97 && selectedNodes.contains(formEditorItem->qmlItemNode()))
98 return true;
99
100 return false;
101
102 }
103
topSelectedItemIsMovable(const QList<QGraphicsItem * > & itemList)104 bool AbstractFormEditorTool::topSelectedItemIsMovable(const QList<QGraphicsItem*> &itemList)
105 {
106 QList<ModelNode> selectedNodes = view()->selectedModelNodes();
107
108 foreach (QGraphicsItem *item, itemList) {
109 FormEditorItem *formEditorItem = FormEditorItem::fromQGraphicsItem(item);
110 if (formEditorItem
111 && selectedNodes.contains(formEditorItem->qmlItemNode())
112 && formEditorItem->qmlItemNode().instanceIsMovable()
113 && formEditorItem->qmlItemNode().modelIsMovable()
114 && !formEditorItem->qmlItemNode().instanceIsInLayoutable()
115 && (formEditorItem->qmlItemNode().instanceHasShowContent()))
116 return true;
117 }
118
119 foreach (QGraphicsItem *item, itemList) {
120 FormEditorItem *formEditorItem = FormEditorItem::fromQGraphicsItem(item);
121 if (formEditorItem
122 && formEditorItem->qmlItemNode().isValid()
123 && formEditorItem->qmlItemNode().instanceIsMovable()
124 && formEditorItem->qmlItemNode().modelIsMovable()
125 && !formEditorItem->qmlItemNode().instanceIsInLayoutable()
126 && selectedNodes.contains(formEditorItem->qmlItemNode()))
127 return true;
128 }
129
130 return false;
131 }
132
selectedItemCursorInMovableArea(const QPointF & pos)133 bool AbstractFormEditorTool::selectedItemCursorInMovableArea(const QPointF &pos)
134 {
135 if (!view()->hasSingleSelectedModelNode())
136 return false;
137
138 const ModelNode selectedNode = view()->singleSelectedModelNode();
139
140 FormEditorItem *item = scene()->itemForQmlItemNode(selectedNode);
141
142 if (!item)
143 return false;
144
145 if (!topSelectedItemIsMovable({item}))
146 return false;
147
148 const QPolygonF boundingRectInSceneSpace(item->mapToScene(item->qmlItemNode().instanceBoundingRect()));
149 QRectF boundingRect = boundingRectInSceneSpace.boundingRect();
150 QRectF innerRect = boundingRect;
151
152 innerRect.adjust(2, 2, -2, -2);
153 const int heightOffset = -20 / scene()->formLayerItem()->viewportTransform().m11();
154 boundingRect.adjust(-2, heightOffset, 2, 2);
155
156 return !innerRect.contains(pos) && boundingRect.contains(pos);
157 }
158
topItemIsResizeHandle(const QList<QGraphicsItem * > &)159 bool AbstractFormEditorTool::topItemIsResizeHandle(const QList<QGraphicsItem*> &/*itemList*/)
160 {
161 return false;
162 }
163
topMovableGraphicsItem(const QList<QGraphicsItem * > & itemList)164 QGraphicsItem *AbstractFormEditorTool::topMovableGraphicsItem(const QList<QGraphicsItem*> &itemList)
165 {
166 foreach (QGraphicsItem *item, itemList) {
167 if (item->flags().testFlag(QGraphicsItem::ItemIsMovable))
168 return item;
169 }
170
171 return nullptr;
172 }
173
topMovableFormEditorItem(const QList<QGraphicsItem * > & itemList,bool selectOnlyContentItems)174 FormEditorItem *AbstractFormEditorTool::topMovableFormEditorItem(const QList<QGraphicsItem*> &itemList, bool selectOnlyContentItems)
175 {
176 foreach (QGraphicsItem *item, itemList) {
177 FormEditorItem *formEditorItem = FormEditorItem::fromQGraphicsItem(item);
178 if (formEditorItem
179 && formEditorItem->qmlItemNode().isValid()
180 && !formEditorItem->qmlItemNode().instanceIsInLayoutable()
181 && formEditorItem->qmlItemNode().instanceIsMovable()
182 && formEditorItem->qmlItemNode().modelIsMovable()
183 && (formEditorItem->qmlItemNode().instanceHasShowContent() || !selectOnlyContentItems))
184 return formEditorItem;
185 }
186
187 return nullptr;
188 }
189
nearestFormEditorItem(const QPointF & point,const QList<QGraphicsItem * > & itemList)190 FormEditorItem* AbstractFormEditorTool::nearestFormEditorItem(const QPointF &point, const QList<QGraphicsItem*> &itemList)
191 {
192 FormEditorItem* nearestItem = nullptr;
193 for (QGraphicsItem *item : itemList) {
194 FormEditorItem *formEditorItem = FormEditorItem::fromQGraphicsItem(item);
195
196 if (formEditorItem && formEditorItem->flowHitTest(point))
197 return formEditorItem;
198
199 if (!formEditorItem || !formEditorItem->qmlItemNode().isValid())
200 continue;
201
202 if (formEditorItem->parentItem() && !formEditorItem->parentItem()->isContentVisible())
203 continue;
204
205 if (formEditorItem && ModelNode::isThisOrAncestorLocked(formEditorItem->qmlItemNode().modelNode()))
206 continue;
207
208 if (!nearestItem)
209 nearestItem = formEditorItem;
210 else if (formEditorItem->selectionWeigth(point, 1) < nearestItem->selectionWeigth(point, 0))
211 nearestItem = formEditorItem;
212 }
213
214 if (nearestItem && nearestItem->qmlItemNode().isInStackedContainer())
215 return nearestItem->parentItem();
216
217 return nearestItem;
218 }
219
filterSelectedModelNodes(const QList<FormEditorItem * > & itemList) const220 QList<FormEditorItem *> AbstractFormEditorTool::filterSelectedModelNodes(const QList<FormEditorItem *> &itemList) const
221 {
222 QList<FormEditorItem *> filteredItemList;
223
224 foreach (FormEditorItem *item, itemList) {
225 if (view()->isSelectedModelNode(item->qmlItemNode()))
226 filteredItemList.append(item);
227 }
228
229 return filteredItemList;
230 }
231
dropEvent(const QList<QGraphicsItem * > &,QGraphicsSceneDragDropEvent *)232 void AbstractFormEditorTool::dropEvent(const QList<QGraphicsItem*> &/*itemList*/, QGraphicsSceneDragDropEvent * /* event */)
233 {
234 }
235
dragEnterEvent(const QList<QGraphicsItem * > & itemList,QGraphicsSceneDragDropEvent * event)236 void AbstractFormEditorTool::dragEnterEvent(const QList<QGraphicsItem*> &itemList, QGraphicsSceneDragDropEvent *event)
237 {
238 bool hasValidAssets = false;
239 if (event->mimeData()->hasFormat("application/vnd.bauhaus.libraryresource")) {
240 const QStringList assetPaths = QString::fromUtf8(event->mimeData()
241 ->data("application/vnd.bauhaus.libraryresource")).split(",");
242 for (const QString &assetPath : assetPaths) {
243 QString assetType = ItemLibraryWidget::getAssetTypeAndData(assetPath).first;
244 if (assetType == "application/vnd.bauhaus.libraryresource.image"
245 || assetType == "application/vnd.bauhaus.libraryresource.font") {
246 hasValidAssets = true;
247 break;
248 }
249 }
250 }
251
252 if (event->mimeData()->hasFormat(QLatin1String("application/vnd.bauhaus.itemlibraryinfo")) || hasValidAssets) {
253 event->accept();
254 view()->changeToDragTool();
255 view()->currentTool()->dragEnterEvent(itemList, event);
256 } else {
257 event->ignore();
258 }
259 }
260
mousePressEvent(const QList<QGraphicsItem * > &,QGraphicsSceneMouseEvent * event)261 void AbstractFormEditorTool::mousePressEvent(const QList<QGraphicsItem*> & /*itemList*/, QGraphicsSceneMouseEvent *event)
262 {
263 if (event->button() == Qt::RightButton)
264 event->accept();
265 }
266
containsItemNode(const QList<QGraphicsItem * > & itemList,const QmlItemNode & itemNode)267 static bool containsItemNode(const QList<QGraphicsItem*> & itemList, const QmlItemNode &itemNode)
268 {
269 foreach (QGraphicsItem *item, itemList) {
270 FormEditorItem *formEditorItem = FormEditorItem::fromQGraphicsItem(item);
271 if (formEditorItem && formEditorItem->qmlItemNode() == itemNode)
272 return true;
273 }
274
275 return false;
276 }
277
mouseReleaseEvent(const QList<QGraphicsItem * > & itemList,QGraphicsSceneMouseEvent * event)278 void AbstractFormEditorTool::mouseReleaseEvent(const QList<QGraphicsItem*> & itemList, QGraphicsSceneMouseEvent *event)
279 {
280 if (event->button() == Qt::RightButton) {
281
282 QmlItemNode currentSelectedNode;
283
284 if (view()->selectedModelNodes().count() == 1) {
285 currentSelectedNode = view()->selectedModelNodes().constFirst();
286
287 if (!containsItemNode(itemList, currentSelectedNode)) {
288 QmlItemNode selectedNode;
289
290 FormEditorItem *formEditorItem = nearestFormEditorItem(event->scenePos(), itemList);
291
292 if (formEditorItem && formEditorItem->qmlItemNode().isValid())
293 selectedNode = formEditorItem->qmlItemNode();
294
295 if (selectedNode.isValid()) {
296 QList<ModelNode> nodeList;
297 nodeList.append(selectedNode);
298
299 view()->setSelectedModelNodes(nodeList);
300 }
301 }
302 }
303
304 showContextMenu(event);
305 event->accept();
306 }
307 }
308
mouseDoubleClickEvent(const QList<QGraphicsItem * > & itemList,QGraphicsSceneMouseEvent * event)309 void AbstractFormEditorTool::mouseDoubleClickEvent(const QList<QGraphicsItem*> &itemList, QGraphicsSceneMouseEvent *event)
310 {
311 if (event->button() == Qt::LeftButton) {
312 FormEditorItem *formEditorItem = nearestFormEditorItem(event->pos(), itemList);
313 if (formEditorItem) {
314 view()->setSelectedModelNode(formEditorItem->qmlItemNode().modelNode());
315 view()->changeToCustomTool();
316 }
317 }
318 }
319
showContextMenu(QGraphicsSceneMouseEvent * event)320 void AbstractFormEditorTool::showContextMenu(QGraphicsSceneMouseEvent *event)
321 {
322 ModelNodeContextMenu::showContextMenu(view(), event->screenPos(), event->scenePos().toPoint(), true);
323 }
324
generateUseSnapping(Qt::KeyboardModifiers keyboardModifier) const325 Snapper::Snapping AbstractFormEditorTool::generateUseSnapping(Qt::KeyboardModifiers keyboardModifier) const
326 {
327 bool shouldSnapping = view()->formEditorWidget()->snappingAction()->isChecked();
328 bool shouldSnappingAndAnchoring = view()->formEditorWidget()->snappingAndAnchoringAction()->isChecked();
329
330 Snapper::Snapping useSnapping = Snapper::NoSnapping;
331 if (keyboardModifier.testFlag(Qt::ControlModifier) != (shouldSnapping || shouldSnappingAndAnchoring)) {
332 if (shouldSnappingAndAnchoring)
333 useSnapping = Snapper::UseSnappingAndAnchoring;
334 else
335 useSnapping = Snapper::UseSnapping;
336 }
337
338 return useSnapping;
339 }
340
isNotAncestorOfItemInList(FormEditorItem * formEditorItem,const QList<FormEditorItem * > & itemList)341 static bool isNotAncestorOfItemInList(FormEditorItem *formEditorItem, const QList<FormEditorItem*> &itemList)
342 {
343 foreach (FormEditorItem *item, itemList) {
344 if (item
345 && item->qmlItemNode().isValid()
346 && item->qmlItemNode().isAncestorOf(formEditorItem->qmlItemNode()))
347 return false;
348 }
349
350 return true;
351 }
352
containerFormEditorItem(const QList<QGraphicsItem * > & itemUnderMouseList,const QList<FormEditorItem * > & selectedItemList) const353 FormEditorItem *AbstractFormEditorTool::containerFormEditorItem(const QList<QGraphicsItem *> &itemUnderMouseList, const QList<FormEditorItem *> &selectedItemList) const
354 {
355 foreach (QGraphicsItem* item, itemUnderMouseList) {
356 FormEditorItem *formEditorItem = FormEditorItem::fromQGraphicsItem(item);
357 if (formEditorItem
358 && !selectedItemList.contains(formEditorItem)
359 && isNotAncestorOfItemInList(formEditorItem, selectedItemList)
360 && formEditorItem->isContainer()
361 && formEditorItem->isContentVisible())
362 return formEditorItem;
363 }
364
365 return nullptr;
366 }
367
clear()368 void AbstractFormEditorTool::clear()
369 {
370 m_itemList.clear();
371 }
372
start()373 void AbstractFormEditorTool::start()
374 {
375
376 }
377 }
378