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
87QQuickSafeArea *QQuickSafeArea::qmlAttachedProperties(QObject *attachee)
89 auto *item = qobject_cast<QQuickItem*>(attachee);
91 if (
auto *window = qobject_cast<QQuickWindow*>(attachee))
92 item = window->contentItem();
95 if (
auto *safeAreaAttachable = qobject_cast<QQuickSafeAreaAttachable*>(attachee))
96 item = safeAreaAttachable->safeAreaAttachmentItem();
99 qmlWarning(attachee) <<
"SafeArea can not be attached to this type";
107 if (
auto *safeArea = item->findChild<QQuickSafeArea*>(Qt::FindDirectChildrenOnly))
110 return new QQuickSafeArea(item);
113QQuickSafeArea::QQuickSafeArea(QQuickItem *item)
116 qCInfo(lcSafeArea) <<
"Creating" <<
this;
118 connect(item, &QQuickItem::windowChanged,
119 this, &QQuickSafeArea::windowChanged);
121 item->setFlag(QQuickItem::ItemObservesViewport);
122 QQuickItemPrivate::get(item)->addItemChangeListener(
123 this, QQuickItemPrivate::Matrix);
128QQuickSafeArea::~QQuickSafeArea()
130 qCInfo(lcSafeArea) <<
"Destroying" <<
this;
132 const auto listenedItems = m_listenedItems;
133 for (
const auto &item : listenedItems) {
136 auto *itemPrivate = QQuickItemPrivate::get(item);
137 itemPrivate->removeItemChangeListener(
this,
138 QQuickItemPrivate::Matrix);
139 itemPrivate->removeItemChangeListener(
this,
140 QQuickItemPrivate::Geometry);
145
146
147
148
149
150
151
152
153
154
155
156
157QMarginsF QQuickSafeArea::margins()
const
159 return m_safeAreaMargins;
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
181void QQuickSafeArea::setAdditionalMargins(
const QMarginsF &additionalMargins)
184 auto newMargins = additionalMargins | QMarginsF();
186 if (newMargins == m_additionalMargins)
189 m_additionalMargins = newMargins;
191 emit additionalMarginsChanged();
193 auto *attachedItem = qobject_cast<QQuickItem*>(parent());
194 updateSafeAreasRecursively(attachedItem);
197QMarginsF QQuickSafeArea::additionalMargins()
const
199 return m_additionalMargins;
203
204
207 if (margins.isNull())
210 const auto localMarginRect = fromItem->mapRectToItem(toItem,
211 QRectF(margins.left(), margins.top(),
212 fromItem->width() - margins.left() - margins.right(),
213 fromItem->height() - margins.top() - margins.bottom()));
217 margins.left() > 0 ? localMarginRect.left() : 0,
218 margins.top() > 0 ? localMarginRect.top() : 0,
219 margins.right() > 0 ? toItem->width() - localMarginRect.right() : 0,
220 margins.bottom() > 0 ? toItem->height() - localMarginRect.bottom() : 0
224void QQuickSafeArea::updateSafeArea()
226 qCDebug(lcSafeArea) <<
"✨ Updating" <<
this;
228 auto *attachedItem = qobject_cast<QQuickItem*>(parent());
229 if (!QQuickItemPrivate::get(attachedItem)->componentComplete) {
230 qCDebug(lcSafeArea) << attachedItem <<
"is not complete. Deferring";
234 QMarginsF inheritedMargins;
235 auto *parentItem = attachedItem->parentItem();
237 if (qobject_cast<QQuickFlickable*>(parentItem)) {
243 qCDebug(lcSafeArea) <<
"Stopping safe area margin propagation on" << parentItem;
253 if (
auto *safeArea = parentItem->findChild<QQuickSafeArea*>(Qt::FindDirectChildrenOnly)) {
254 inheritedMargins = safeArea->margins();
258 parentItem = parentItem->parentItem();
261 const auto *window = attachedItem->window();
262 if (!parentItem && window) {
265 parentItem = window->contentItem();
266 inheritedMargins = window->safeAreaMargins();
269 auto inheritedMarginsMapped = toLocalMargins(inheritedMargins, parentItem, attachedItem);
272 const QMarginsF newMargins = QMarginsF() | (inheritedMarginsMapped + additionalMargins());
274 if (newMargins != m_safeAreaMargins) {
275 qCDebug(lcSafeArea) <<
"Margins changed from" << m_safeAreaMargins
276 <<
"to" << newMargins
277 <<
"based on inherited" << inheritedMargins
278 <<
"mapped to local" << inheritedMarginsMapped
279 <<
"and additional" << additionalMargins();
281 m_safeAreaMargins = newMargins;
283 if (emittingMarginsUpdate) {
288 qCDebug(lcSafeArea) <<
"Already emitting update for" <<
this;
292 QScopedValueRollback blocker(emittingMarginsUpdate,
true);
293 emit marginsChanged();
295 if (m_safeAreaMargins != newMargins) {
296 qCDebug(lcSafeArea) <<
"⚠️ Possible binding loop for" <<
this
297 << newMargins <<
"changed to" << m_safeAreaMargins;
299 QScopedValueRollback blocker(detectedPossibleBindingLoop,
true);
301 for (
int i = 0; i < 5; ++i) {
302 auto marginsBeforeEmit = m_safeAreaMargins;
303 emit marginsChanged();
304 if (m_safeAreaMargins == marginsBeforeEmit) {
305 qCDebug(lcSafeArea) <<
"✅ Margins stabilized for" <<
this;
309 qCDebug(lcSafeArea) << qPrintable(QStringLiteral(
"‼️").repeated(i + 1))
310 << marginsBeforeEmit <<
"changed to" << m_safeAreaMargins;
313 qmlWarning(attachedItem) <<
"Safe area binding loop detected";
318void QQuickSafeArea::windowChanged()
323void QQuickSafeArea::itemTransformChanged(QQuickItem *item, QQuickItem *transformedItem)
325 Q_ASSERT(item == parent());
327 auto *transformedItemPrivate = QQuickItemPrivate::get(transformedItem);
328 qCDebug(lcSafeArea) <<
"📏 Transform changed for" << transformedItem
329 <<
"with dirty state" << transformedItemPrivate->dirtyToString();
331 if (qobject_cast<QQuickFlickable*>(transformedItem->parentItem())) {
332 qCDebug(lcSafeArea) <<
"Ignoring transform change for Flickable content item";
340 if (transformedItem != item) {
341 for (
auto *parent = item->parentItem(); parent; parent = parent->parentItem()) {
342 if (parent->findChild<QQuickSafeArea*>(Qt::FindDirectChildrenOnly))
345 if (parent == transformedItem)
350 if (item != parent()) {
351 qCDebug(lcSafeArea) <<
"Found" << item <<
"closer to transformed item than" <<
this;
364 auto dirtyAttributes = transformedItemPrivate->dirtyAttributes;
365 if (dirtyAttributes & (QQuickItemPrivate::Position | QQuickItemPrivate::Size)) {
366 qCDebug(lcSafeArea) <<
"Deferring update of" <<
this <<
"until geometry change";
367 transformedItemPrivate->addItemChangeListener(
368 this, QQuickItemPrivate::Geometry);
372 updateSafeAreasRecursively(item);
375void QQuickSafeArea::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change,
const QRectF &oldGeometry)
378 Q_UNUSED(oldGeometry);
380 auto *itemPrivate = QQuickItemPrivate::get(item);
381 itemPrivate->removeItemChangeListener(
this, QQuickItemPrivate::Geometry);
383 qCDebug(lcSafeArea) <<
"📐 Geometry changed for" << item <<
"from" << oldGeometry
384 <<
"to" << QRectF(item->position(), item->size());
386 updateSafeAreasRecursively(item);
389void QQuickSafeArea::updateSafeAreasRecursively(QQuickItem *item)
393 if (
auto *safeArea = item->findChild<QQuickSafeArea*>(Qt::FindDirectChildrenOnly))
394 safeArea->updateSafeArea();
396 auto *itemPrivate = QQuickItemPrivate::get(item);
397 const auto paintOrderChildItems = itemPrivate->paintOrderChildItems();
398 for (
auto *child : paintOrderChildItems)
399 updateSafeAreasRecursively(child);
402void QQuickSafeArea::addSourceItem(QQuickItem *item)
404 m_listenedItems << item;
407void QQuickSafeArea::removeSourceItem(QQuickItem *item)
409 m_listenedItems.removeAll(item);
412#ifndef QT_NO_DEBUG_STREAM
415 QDebugStateSaver saver(debug);
419 debug <<
"QQuickSafeArea(nullptr)";
423 debug << safeArea->metaObject()->className() <<
'(' <<
static_cast<
const void *>(safeArea);
425 debug <<
", attachedItem=" << safeArea->parent();
426 debug <<
", safeAreaMargins=" << safeArea->m_safeAreaMargins;
427 debug <<
", additionalMargins=" << safeArea->additionalMargins();
434QQuickSafeAreaAttachable::~QQuickSafeAreaAttachable() =
default;
438#include "moc_qquicksafearea_p.cpp"
Q_STATIC_LOGGING_CATEGORY(lcAccessibilityCore, "qt.accessibility.core")
QDebug operator<<(QDebug dbg, const NSObject *nsObject)
static QMarginsF toLocalMargins(const QMarginsF &margins, QQuickItem *fromItem, QQuickItem *toItem)