1 | // Copyright (C) 2016 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qquickdesignersupport_p.h" |
5 | #include <private/qquickitem_p.h> |
6 | |
7 | #if QT_CONFIG(quick_shadereffect) |
8 | #include <QtQuick/private/qquickshadereffectsource_p.h> |
9 | #endif |
10 | #include <QtQuick/private/qquickrectangle_p.h> |
11 | #include <QtQml/private/qabstractanimationjob_p.h> |
12 | #include <private/qqmlengine_p.h> |
13 | #include <private/qquickview_p.h> |
14 | #include <QtQuick/private/qquickstategroup_p.h> |
15 | #include <QtGui/QImage> |
16 | #include <private/qqmlvme_p.h> |
17 | #include <private/qqmlcomponentattached_p.h> |
18 | #include <private/qqmldata_p.h> |
19 | #include <private/qsgadaptationlayer_p.h> |
20 | |
21 | QT_BEGIN_NAMESPACE |
22 | |
23 | QQuickDesignerSupport::QQuickDesignerSupport() |
24 | { |
25 | } |
26 | |
27 | QQuickDesignerSupport::~QQuickDesignerSupport() |
28 | { |
29 | typedef QHash<QQuickItem*, QSGLayer*>::iterator ItemTextureHashIt; |
30 | |
31 | for (ItemTextureHashIt iterator = m_itemTextureHash.begin(), end = m_itemTextureHash.end(); iterator != end; ++iterator) { |
32 | QSGLayer *texture = iterator.value(); |
33 | QQuickItem *item = iterator.key(); |
34 | QQuickItemPrivate::get(item)->derefFromEffectItem(unhide: true); |
35 | delete texture; |
36 | } |
37 | } |
38 | |
39 | void QQuickDesignerSupport::refFromEffectItem(QQuickItem *referencedItem, bool hide) |
40 | { |
41 | if (referencedItem == nullptr) |
42 | return; |
43 | |
44 | QQuickItemPrivate::get(item: referencedItem)->refFromEffectItem(hide); |
45 | QQuickWindowPrivate::get(c: referencedItem->window())->updateDirtyNode(referencedItem); |
46 | |
47 | Q_ASSERT(QQuickItemPrivate::get(referencedItem)->rootNode()); |
48 | |
49 | if (!m_itemTextureHash.contains(key: referencedItem)) { |
50 | QSGRenderContext *rc = QQuickWindowPrivate::get(c: referencedItem->window())->context; |
51 | QSGLayer *texture = rc->sceneGraphContext()->createLayer(renderContext: rc); |
52 | |
53 | QSizeF itemSize = referencedItem->size(); |
54 | texture->setLive(true); |
55 | texture->setItem(QQuickItemPrivate::get(item: referencedItem)->rootNode()); |
56 | texture->setRect(QRectF(QPointF(0, 0), itemSize)); |
57 | texture->setSize(itemSize.toSize()); |
58 | texture->setRecursive(true); |
59 | texture->setFormat(QSGLayer::RGBA8); |
60 | texture->setHasMipmaps(false); |
61 | |
62 | m_itemTextureHash.insert(key: referencedItem, value: texture); |
63 | } |
64 | } |
65 | |
66 | void QQuickDesignerSupport::derefFromEffectItem(QQuickItem *referencedItem, bool unhide) |
67 | { |
68 | if (referencedItem == nullptr) |
69 | return; |
70 | |
71 | delete m_itemTextureHash.take(key: referencedItem); |
72 | QQuickItemPrivate::get(item: referencedItem)->derefFromEffectItem(unhide); |
73 | } |
74 | |
75 | QImage QQuickDesignerSupport::renderImageForItem(QQuickItem *referencedItem, const QRectF &boundingRect, const QSize &imageSize) |
76 | { |
77 | if (referencedItem == nullptr || referencedItem->parentItem() == nullptr) { |
78 | qDebug() << __FILE__ << __LINE__ << "Warning: Item can be rendered." ; |
79 | return QImage(); |
80 | } |
81 | |
82 | QSGLayer *renderTexture = m_itemTextureHash.value(key: referencedItem); |
83 | |
84 | Q_ASSERT(renderTexture); |
85 | if (renderTexture == nullptr) |
86 | return QImage(); |
87 | renderTexture->setRect(boundingRect); |
88 | renderTexture->setSize(imageSize); |
89 | renderTexture->setItem(QQuickItemPrivate::get(item: referencedItem)->rootNode()); |
90 | renderTexture->markDirtyTexture(); |
91 | renderTexture->updateTexture(); |
92 | |
93 | QImage renderImage = renderTexture->toImage(); |
94 | renderImage = renderImage.mirrored(horizontally: false, vertically: true); |
95 | |
96 | if (renderImage.size().isEmpty()) |
97 | qDebug() << __FILE__ << __LINE__ << "Warning: Image is empty." ; |
98 | |
99 | return renderImage; |
100 | } |
101 | |
102 | bool QQuickDesignerSupport::isDirty(QQuickItem *referencedItem, DirtyType dirtyType) |
103 | { |
104 | if (referencedItem == nullptr) |
105 | return false; |
106 | |
107 | return QQuickItemPrivate::get(item: referencedItem)->dirtyAttributes & dirtyType; |
108 | } |
109 | |
110 | void QQuickDesignerSupport::addDirty(QQuickItem *referencedItem, QQuickDesignerSupport::DirtyType dirtyType) |
111 | { |
112 | if (referencedItem == nullptr) |
113 | return; |
114 | |
115 | QQuickItemPrivate::get(item: referencedItem)->dirtyAttributes |= dirtyType; |
116 | } |
117 | |
118 | void QQuickDesignerSupport::resetDirty(QQuickItem *referencedItem) |
119 | { |
120 | if (referencedItem == nullptr) |
121 | return; |
122 | |
123 | QQuickItemPrivate::get(item: referencedItem)->dirtyAttributes = 0x0; |
124 | QQuickItemPrivate::get(item: referencedItem)->removeFromDirtyList(); |
125 | } |
126 | |
127 | QTransform QQuickDesignerSupport::windowTransform(QQuickItem *referencedItem) |
128 | { |
129 | if (referencedItem == nullptr) |
130 | return QTransform(); |
131 | |
132 | return QQuickItemPrivate::get(item: referencedItem)->itemToWindowTransform(); |
133 | } |
134 | |
135 | QTransform QQuickDesignerSupport::parentTransform(QQuickItem *referencedItem) |
136 | { |
137 | if (referencedItem == nullptr) |
138 | return QTransform(); |
139 | |
140 | QTransform parentTransform; |
141 | |
142 | QQuickItemPrivate::get(item: referencedItem)->itemToParentTransform(&parentTransform); |
143 | |
144 | return parentTransform; |
145 | } |
146 | |
147 | QString propertyNameForAnchorLine(const QQuickAnchors::Anchor &anchorLine) |
148 | { |
149 | switch (anchorLine) { |
150 | case QQuickAnchors::LeftAnchor: return QLatin1String("left" ); |
151 | case QQuickAnchors::RightAnchor: return QLatin1String("right" ); |
152 | case QQuickAnchors::TopAnchor: return QLatin1String("top" ); |
153 | case QQuickAnchors::BottomAnchor: return QLatin1String("bottom" ); |
154 | case QQuickAnchors::HCenterAnchor: return QLatin1String("horizontalCenter" ); |
155 | case QQuickAnchors::VCenterAnchor: return QLatin1String("verticalCenter" ); |
156 | case QQuickAnchors::BaselineAnchor: return QLatin1String("baseline" ); |
157 | case QQuickAnchors::InvalidAnchor: // fallthrough: |
158 | default: return QString(); |
159 | } |
160 | } |
161 | |
162 | bool isValidAnchorName(const QString &name) |
163 | { |
164 | static QStringList anchorNameList(QStringList() << QLatin1String("anchors.top" ) |
165 | << QLatin1String("anchors.left" ) |
166 | << QLatin1String("anchors.right" ) |
167 | << QLatin1String("anchors.bottom" ) |
168 | << QLatin1String("anchors.verticalCenter" ) |
169 | << QLatin1String("anchors.horizontalCenter" ) |
170 | << QLatin1String("anchors.fill" ) |
171 | << QLatin1String("anchors.centerIn" ) |
172 | << QLatin1String("anchors.baseline" )); |
173 | |
174 | return anchorNameList.contains(str: name); |
175 | } |
176 | |
177 | bool QQuickDesignerSupport::isAnchoredTo(QQuickItem *fromItem, QQuickItem *toItem) |
178 | { |
179 | QQuickItemPrivate *fromItemPrivate = QQuickItemPrivate::get(item: fromItem); |
180 | QQuickAnchors *anchors = fromItemPrivate->anchors(); |
181 | return anchors->fill() == toItem |
182 | || anchors->centerIn() == toItem |
183 | || anchors->bottom().item == toItem |
184 | || anchors->top().item == toItem |
185 | || anchors->left().item == toItem |
186 | || anchors->right().item == toItem |
187 | || anchors->verticalCenter().item == toItem |
188 | || anchors->horizontalCenter().item == toItem |
189 | || anchors->baseline().item == toItem; |
190 | } |
191 | |
192 | bool QQuickDesignerSupport::areChildrenAnchoredTo(QQuickItem *fromItem, QQuickItem *toItem) |
193 | { |
194 | const auto childItems = fromItem->childItems(); |
195 | for (QQuickItem *childItem : childItems) { |
196 | if (childItem) { |
197 | if (isAnchoredTo(fromItem: childItem, toItem)) |
198 | return true; |
199 | |
200 | if (areChildrenAnchoredTo(fromItem: childItem, toItem)) |
201 | return true; |
202 | } |
203 | } |
204 | |
205 | return false; |
206 | } |
207 | |
208 | QQuickAnchors *anchors(QQuickItem *item) |
209 | { |
210 | QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); |
211 | return itemPrivate->anchors(); |
212 | } |
213 | |
214 | QQuickAnchors::Anchor anchorLineFlagForName(const QString &name) |
215 | { |
216 | if (name == QLatin1String("anchors.top" )) |
217 | return QQuickAnchors::TopAnchor; |
218 | |
219 | if (name == QLatin1String("anchors.left" )) |
220 | return QQuickAnchors::LeftAnchor; |
221 | |
222 | if (name == QLatin1String("anchors.bottom" )) |
223 | return QQuickAnchors::BottomAnchor; |
224 | |
225 | if (name == QLatin1String("anchors.right" )) |
226 | return QQuickAnchors::RightAnchor; |
227 | |
228 | if (name == QLatin1String("anchors.horizontalCenter" )) |
229 | return QQuickAnchors::HCenterAnchor; |
230 | |
231 | if (name == QLatin1String("anchors.verticalCenter" )) |
232 | return QQuickAnchors::VCenterAnchor; |
233 | |
234 | if (name == QLatin1String("anchors.baseline" )) |
235 | return QQuickAnchors::BaselineAnchor; |
236 | |
237 | |
238 | Q_ASSERT_X(false, Q_FUNC_INFO, "wrong anchor name - this should never happen" ); |
239 | return QQuickAnchors::LeftAnchor; |
240 | } |
241 | |
242 | bool QQuickDesignerSupport::hasAnchor(QQuickItem *item, const QString &name) |
243 | { |
244 | if (!isValidAnchorName(name)) |
245 | return false; |
246 | |
247 | if (name == QLatin1String("anchors.fill" )) |
248 | return anchors(item)->fill() != nullptr; |
249 | |
250 | if (name == QLatin1String("anchors.centerIn" )) |
251 | return anchors(item)->centerIn() != nullptr; |
252 | |
253 | if (name == QLatin1String("anchors.right" )) |
254 | return anchors(item)->right().item != nullptr; |
255 | |
256 | if (name == QLatin1String("anchors.top" )) |
257 | return anchors(item)->top().item != nullptr; |
258 | |
259 | if (name == QLatin1String("anchors.left" )) |
260 | return anchors(item)->left().item != nullptr; |
261 | |
262 | if (name == QLatin1String("anchors.bottom" )) |
263 | return anchors(item)->bottom().item != nullptr; |
264 | |
265 | if (name == QLatin1String("anchors.horizontalCenter" )) |
266 | return anchors(item)->horizontalCenter().item != nullptr; |
267 | |
268 | if (name == QLatin1String("anchors.verticalCenter" )) |
269 | return anchors(item)->verticalCenter().item != nullptr; |
270 | |
271 | if (name == QLatin1String("anchors.baseline" )) |
272 | return anchors(item)->baseline().item != nullptr; |
273 | |
274 | return anchors(item)->usedAnchors().testFlag(flag: anchorLineFlagForName(name)); |
275 | } |
276 | |
277 | QQuickItem *QQuickDesignerSupport::anchorFillTargetItem(QQuickItem *item) |
278 | { |
279 | return anchors(item)->fill(); |
280 | } |
281 | |
282 | QQuickItem *QQuickDesignerSupport::anchorCenterInTargetItem(QQuickItem *item) |
283 | { |
284 | return anchors(item)->centerIn(); |
285 | } |
286 | |
287 | |
288 | |
289 | QPair<QString, QObject*> QQuickDesignerSupport::anchorLineTarget(QQuickItem *item, const QString &name, QQmlContext *context) |
290 | { |
291 | QObject *targetObject = nullptr; |
292 | QString targetName; |
293 | |
294 | if (name == QLatin1String("anchors.fill" )) { |
295 | targetObject = anchors(item)->fill(); |
296 | } else if (name == QLatin1String("anchors.centerIn" )) { |
297 | targetObject = anchors(item)->centerIn(); |
298 | } else { |
299 | QQmlProperty metaProperty(item, name, context); |
300 | if (!metaProperty.isValid()) |
301 | return QPair<QString, QObject*>(); |
302 | |
303 | QQuickAnchorLine anchorLine = metaProperty.read().value<QQuickAnchorLine>(); |
304 | if (anchorLine.anchorLine != QQuickAnchors::InvalidAnchor) { |
305 | targetObject = anchorLine.item; |
306 | targetName = propertyNameForAnchorLine(anchorLine: anchorLine.anchorLine); |
307 | } |
308 | |
309 | } |
310 | |
311 | return QPair<QString, QObject*>(targetName, targetObject); |
312 | } |
313 | |
314 | void QQuickDesignerSupport::resetAnchor(QQuickItem *item, const QString &name) |
315 | { |
316 | if (name == QLatin1String("anchors.fill" )) { |
317 | anchors(item)->resetFill(); |
318 | } else if (name == QLatin1String("anchors.centerIn" )) { |
319 | anchors(item)->resetCenterIn(); |
320 | } else if (name == QLatin1String("anchors.top" )) { |
321 | anchors(item)->resetTop(); |
322 | } else if (name == QLatin1String("anchors.left" )) { |
323 | anchors(item)->resetLeft(); |
324 | } else if (name == QLatin1String("anchors.right" )) { |
325 | anchors(item)->resetRight(); |
326 | } else if (name == QLatin1String("anchors.bottom" )) { |
327 | anchors(item)->resetBottom(); |
328 | } else if (name == QLatin1String("anchors.horizontalCenter" )) { |
329 | anchors(item)->resetHorizontalCenter(); |
330 | } else if (name == QLatin1String("anchors.verticalCenter" )) { |
331 | anchors(item)->resetVerticalCenter(); |
332 | } else if (name == QLatin1String("anchors.baseline" )) { |
333 | anchors(item)->resetBaseline(); |
334 | } |
335 | } |
336 | |
337 | void QQuickDesignerSupport::emitComponentCompleteSignalForAttachedProperty(QObject *object) |
338 | { |
339 | if (!object) |
340 | return; |
341 | |
342 | QQmlData *data = QQmlData::get(object); |
343 | if (data && data->context) { |
344 | QQmlComponentAttached *componentAttached = data->context->componentAttacheds(); |
345 | while (componentAttached) { |
346 | if (componentAttached->parent()) |
347 | if (componentAttached->parent() == object) |
348 | emit componentAttached->completed(); |
349 | |
350 | componentAttached = componentAttached->next(); |
351 | } |
352 | } |
353 | } |
354 | |
355 | QList<QObject*> QQuickDesignerSupport::statesForItem(QQuickItem *item) |
356 | { |
357 | QList<QObject*> objectList; |
358 | const QList<QQuickState *> stateList = QQuickItemPrivate::get(item)->_states()->states(); |
359 | |
360 | objectList.reserve(asize: stateList.size()); |
361 | for (QQuickState* state : stateList) |
362 | objectList.append(t: state); |
363 | |
364 | return objectList; |
365 | } |
366 | |
367 | bool QQuickDesignerSupport::isComponentComplete(QQuickItem *item) |
368 | { |
369 | return QQuickItemPrivate::get(item)->componentComplete; |
370 | } |
371 | |
372 | int QQuickDesignerSupport::borderWidth(QQuickItem *item) |
373 | { |
374 | QQuickRectangle *rectangle = qobject_cast<QQuickRectangle*>(object: item); |
375 | if (rectangle) |
376 | return rectangle->border()->width(); |
377 | |
378 | return 0; |
379 | } |
380 | |
381 | void QQuickDesignerSupport::refreshExpressions(QQmlContext *context) |
382 | { |
383 | QQmlContextData::get(context)->refreshExpressions(); |
384 | } |
385 | |
386 | void QQuickDesignerSupport::setRootItem(QQuickView *view, QQuickItem *item) |
387 | { |
388 | QQuickViewPrivate::get(view)->setRootObject(item); |
389 | } |
390 | |
391 | bool QQuickDesignerSupport::isValidWidth(QQuickItem *item) |
392 | { |
393 | return QQuickItemPrivate::get(item)->heightValid(); |
394 | } |
395 | |
396 | bool QQuickDesignerSupport::isValidHeight(QQuickItem *item) |
397 | { |
398 | return QQuickItemPrivate::get(item)->widthValid(); |
399 | } |
400 | |
401 | void QQuickDesignerSupport::updateDirtyNode(QQuickItem *item) |
402 | { |
403 | if (item->window()) |
404 | QQuickWindowPrivate::get(c: item->window())->updateDirtyNode(item); |
405 | } |
406 | |
407 | void QQuickDesignerSupport::activateDesignerMode() |
408 | { |
409 | QQmlEnginePrivate::activateDesignerMode(); |
410 | } |
411 | |
412 | void QQuickDesignerSupport::disableComponentComplete() |
413 | { |
414 | QQmlVME::disableComponentComplete(); |
415 | } |
416 | |
417 | void QQuickDesignerSupport::enableComponentComplete() |
418 | { |
419 | QQmlVME::enableComponentComplete(); |
420 | } |
421 | |
422 | void QQuickDesignerSupport::polishItems(QQuickWindow *window) |
423 | { |
424 | QQuickWindowPrivate::get(c: window)->polishItems(); |
425 | } |
426 | |
427 | ComponentCompleteDisabler::ComponentCompleteDisabler() |
428 | { |
429 | QQuickDesignerSupport::disableComponentComplete(); |
430 | } |
431 | |
432 | ComponentCompleteDisabler::~ComponentCompleteDisabler() |
433 | { |
434 | QQuickDesignerSupport::enableComponentComplete(); |
435 | } |
436 | |
437 | |
438 | QT_END_NAMESPACE |
439 | |