5#include <QtQuick/private/qquicksafearea_p.h>
7#include <QtQuick/private/qquickanchors_p_p.h>
8#include <QtQuick/private/qquickitem_p.h>
9#include <QtQuick/private/qquickflickable_p.h>
10#include <QtQuick/qquickwindow.h>
11#include <QtQuick/qquickitem.h>
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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
84
85
86
87
89QQuickSafeArea *QQuickSafeArea::qmlAttachedProperties(QObject *attachee)
91 auto *item = qobject_cast<QQuickItem*>(attachee);
93 if (
auto *window = qobject_cast<QQuickWindow*>(attachee))
94 item = window->contentItem();
97 if (
auto *safeAreaAttachable = qobject_cast<QQuickSafeAreaAttachable*>(attachee))
98 item = safeAreaAttachable->safeAreaAttachmentItem();
101 qmlWarning(attachee) <<
"SafeArea can not be attached to this type";
109 if (
auto *safeArea = item->findChild<QQuickSafeArea*>(Qt::FindDirectChildrenOnly))
112 return new QQuickSafeArea(item);
115QQuickSafeArea::QQuickSafeArea(QQuickItem *item)
118 qCInfo(lcSafeArea) <<
"Creating" <<
this;
120 connect(item, &QQuickItem::windowChanged,
121 this, &QQuickSafeArea::windowChanged);
123 item->setFlag(QQuickItem::ItemObservesViewport);
124 QQuickItemPrivate::get(item)->addItemChangeListener(
125 this, QQuickItemPrivate::Matrix);
130QQuickSafeArea::~QQuickSafeArea()
132 qCInfo(lcSafeArea) <<
"Destroying" <<
this;
134 const auto listenedItems = m_listenedItems;
135 for (
const auto &item : listenedItems) {
138 auto *itemPrivate = QQuickItemPrivate::get(item);
139 itemPrivate->removeItemChangeListener(
this,
140 QQuickItemPrivate::Matrix);
141 itemPrivate->removeItemChangeListener(
this,
142 QQuickItemPrivate::Geometry);
147
148
149
150
151
152
153
154
155
156
157
158
159QMarginsF QQuickSafeArea::margins()
const
161 return m_safeAreaMargins;
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
183void QQuickSafeArea::setAdditionalMargins(
const QMarginsF &additionalMargins)
186 auto newMargins = additionalMargins | QMarginsF();
188 if (newMargins == m_additionalMargins)
191 m_additionalMargins = newMargins;
193 emit additionalMarginsChanged();
195 auto *attachedItem = qobject_cast<QQuickItem*>(parent());
196 updateSafeAreasRecursively(attachedItem);
199QMarginsF QQuickSafeArea::additionalMargins()
const
201 return m_additionalMargins;
205
206
209 if (margins.isNull())
212 const auto localMarginRect = fromItem->mapRectToItem(toItem,
213 QRectF(margins.left(), margins.top(),
214 fromItem->width() - margins.left() - margins.right(),
215 fromItem->height() - margins.top() - margins.bottom()));
219 margins.left() > 0 ? localMarginRect.left() : 0,
220 margins.top() > 0 ? localMarginRect.top() : 0,
221 margins.right() > 0 ? toItem->width() - localMarginRect.right() : 0,
222 margins.bottom() > 0 ? toItem->height() - localMarginRect.bottom() : 0
226void QQuickSafeArea::updateSafeArea()
228 qCDebug(lcSafeArea) <<
"✨ Updating" <<
this;
230 auto *attachedItem = qobject_cast<QQuickItem*>(parent());
231 if (!QQuickItemPrivate::get(attachedItem)->componentComplete) {
232 qCDebug(lcSafeArea) << attachedItem <<
"is not complete. Deferring";
236 QMarginsF inheritedMargins;
237 auto *parentItem = attachedItem->parentItem();
239 if (qobject_cast<QQuickFlickable*>(parentItem)) {
245 qCDebug(lcSafeArea) <<
"Stopping safe area margin propagation on" << parentItem;
255 if (
auto *safeArea = parentItem->findChild<QQuickSafeArea*>(Qt::FindDirectChildrenOnly)) {
256 inheritedMargins = safeArea->margins();
260 parentItem = parentItem->parentItem();
263 const auto *window = attachedItem->window();
264 if (!parentItem && window) {
267 parentItem = window->contentItem();
268 inheritedMargins = window->safeAreaMargins();
271 auto inheritedMarginsMapped = toLocalMargins(inheritedMargins, parentItem, attachedItem);
274 const QMarginsF newMargins = QMarginsF() | (inheritedMarginsMapped + additionalMargins());
276 if (newMargins != m_safeAreaMargins) {
277 qCDebug(lcSafeArea) <<
"Margins changed from" << m_safeAreaMargins
278 <<
"to" << newMargins
279 <<
"based on inherited" << inheritedMargins
280 <<
"mapped to local" << inheritedMarginsMapped
281 <<
"and additional" << additionalMargins();
283 m_safeAreaMargins = newMargins;
285 if (emittingMarginsUpdate) {
290 qCDebug(lcSafeArea) <<
"Already emitting update for" <<
this;
294 QScopedValueRollback blocker(emittingMarginsUpdate,
true);
295 emit marginsChanged();
297 if (m_safeAreaMargins != newMargins) {
298 qCDebug(lcSafeArea) <<
"⚠️ Possible binding loop for" <<
this
299 << newMargins <<
"changed to" << m_safeAreaMargins;
301 QScopedValueRollback blocker(detectedPossibleBindingLoop,
true);
303 for (
int i = 0; i < 5; ++i) {
304 auto marginsBeforeEmit = m_safeAreaMargins;
305 emit marginsChanged();
306 if (m_safeAreaMargins == marginsBeforeEmit) {
307 qCDebug(lcSafeArea) <<
"✅ Margins stabilized for" <<
this;
311 qCDebug(lcSafeArea) << qPrintable(QStringLiteral(
"‼️").repeated(i + 1))
312 << marginsBeforeEmit <<
"changed to" << m_safeAreaMargins;
315 qmlWarning(attachedItem) <<
"Safe area binding loop detected";
320void QQuickSafeArea::windowChanged()
325void QQuickSafeArea::itemTransformChanged(QQuickItem *item, QQuickItem *transformedItem)
327 Q_ASSERT(item == parent());
329 auto *transformedItemPrivate = QQuickItemPrivate::get(transformedItem);
330 qCDebug(lcSafeArea) <<
"📏 Transform changed for" << transformedItem
331 <<
"with dirty state" << transformedItemPrivate->dirtyToString();
333 if (qobject_cast<QQuickFlickable*>(transformedItem->parentItem())) {
334 qCDebug(lcSafeArea) <<
"Ignoring transform change for Flickable content item";
342 if (transformedItem != item) {
343 for (
auto *parent = item->parentItem(); parent; parent = parent->parentItem()) {
344 if (parent->findChild<QQuickSafeArea*>(Qt::FindDirectChildrenOnly))
347 if (parent == transformedItem)
352 if (item != parent()) {
353 qCDebug(lcSafeArea) <<
"Found" << item <<
"closer to transformed item than" <<
this;
366 auto dirtyAttributes = transformedItemPrivate->dirtyAttributes;
367 if (dirtyAttributes & (QQuickItemPrivate::Position | QQuickItemPrivate::Size)) {
368 qCDebug(lcSafeArea) <<
"Deferring update of" <<
this <<
"until geometry change";
369 transformedItemPrivate->addItemChangeListener(
370 this, QQuickItemPrivate::Geometry);
374 updateSafeAreasRecursively(item);
377void QQuickSafeArea::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change,
const QRectF &oldGeometry)
380 Q_UNUSED(oldGeometry);
382 auto *itemPrivate = QQuickItemPrivate::get(item);
383 itemPrivate->removeItemChangeListener(
this, QQuickItemPrivate::Geometry);
385 qCDebug(lcSafeArea) <<
"📐 Geometry changed for" << item <<
"from" << oldGeometry
386 <<
"to" << QRectF(item->position(), item->size());
388 updateSafeAreasRecursively(item);
391void QQuickSafeArea::updateSafeAreasRecursively(QQuickItem *item)
395 if (
auto *safeArea = item->findChild<QQuickSafeArea*>(Qt::FindDirectChildrenOnly))
396 safeArea->updateSafeArea();
398 auto *itemPrivate = QQuickItemPrivate::get(item);
399 const auto paintOrderChildItems = itemPrivate->paintOrderChildItems();
400 for (
auto *child : paintOrderChildItems)
401 updateSafeAreasRecursively(child);
404void QQuickSafeArea::addSourceItem(QQuickItem *item)
406 m_listenedItems << item;
409void QQuickSafeArea::removeSourceItem(QQuickItem *item)
411 m_listenedItems.removeAll(item);
414#ifndef QT_NO_DEBUG_STREAM
417 QDebugStateSaver saver(debug);
421 debug <<
"QQuickSafeArea(nullptr)";
425 debug << safeArea->metaObject()->className() <<
'(' <<
static_cast<
const void *>(safeArea);
427 debug <<
", attachedItem=" << safeArea->parent();
428 debug <<
", safeAreaMargins=" << safeArea->m_safeAreaMargins;
429 debug <<
", additionalMargins=" << safeArea->additionalMargins();
436QQuickSafeAreaAttachable::~QQuickSafeAreaAttachable() =
default;
440#include "moc_qquicksafearea_p.cpp"
QDebug operator<<(QDebug dbg, const NSObject *nsObject)
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)
static QMarginsF toLocalMargins(const QMarginsF &margins, QQuickItem *fromItem, QQuickItem *toItem)