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 | #ifndef QQUICKLAYOUT_P_H |
5 | #define QQUICKLAYOUT_P_H |
6 | |
7 | // |
8 | // W A R N I N G |
9 | // ------------- |
10 | // |
11 | // This file is not part of the Qt API. It exists purely as an |
12 | // implementation detail. This header file may change from version to |
13 | // version without notice, or even be removed. |
14 | // |
15 | // We mean it. |
16 | // |
17 | |
18 | #include <QPointer> |
19 | #include <QQuickItem> |
20 | #include <QtCore/qflags.h> |
21 | |
22 | #include <QtQuickLayouts/private/qquicklayoutglobal_p.h> |
23 | #include <private/qquickitem_p.h> |
24 | #include <QtQuick/private/qquickitemchangelistener_p.h> |
25 | #include <QtGui/private/qlayoutpolicy_p.h> |
26 | |
27 | QT_BEGIN_NAMESPACE |
28 | |
29 | class QQuickLayoutAttached; |
30 | Q_DECLARE_LOGGING_CATEGORY(lcQuickLayouts) |
31 | |
32 | class QQuickLayoutPrivate; |
33 | class Q_QUICKLAYOUTS_PRIVATE_EXPORT QQuickLayout : public QQuickItem, public QQuickItemChangeListener |
34 | |
35 | { |
36 | Q_OBJECT |
37 | QML_NAMED_ELEMENT(Layout) |
38 | QML_ADDED_IN_VERSION(1, 0) |
39 | QML_UNCREATABLE("Do not create objects of type Layout." ) |
40 | QML_ATTACHED(QQuickLayoutAttached) |
41 | |
42 | public: |
43 | enum SizeHint { |
44 | MinimumSize = 0, |
45 | PreferredSize, |
46 | MaximumSize, |
47 | NSizes |
48 | }; |
49 | |
50 | enum EnsureLayoutItemsUpdatedOption { |
51 | Recursive = 0b001, |
52 | ApplySizeHints = 0b010 |
53 | }; |
54 | |
55 | Q_DECLARE_FLAGS(EnsureLayoutItemsUpdatedOptions, EnsureLayoutItemsUpdatedOption) |
56 | |
57 | explicit QQuickLayout(QQuickLayoutPrivate &dd, QQuickItem *parent = nullptr); |
58 | ~QQuickLayout(); |
59 | |
60 | static QQuickLayoutAttached *qmlAttachedProperties(QObject *object); |
61 | |
62 | |
63 | void componentComplete() override; |
64 | virtual QSizeF sizeHint(Qt::SizeHint whichSizeHint) const = 0; |
65 | virtual void setAlignment(QQuickItem *item, Qt::Alignment align) = 0; |
66 | virtual void setStretchFactor(QQuickItem *item, int stretchFactor, Qt::Orientation orient) = 0; |
67 | |
68 | virtual void invalidate(QQuickItem * childItem = nullptr); |
69 | virtual void updateLayoutItems() = 0; |
70 | |
71 | void ensureLayoutItemsUpdated(EnsureLayoutItemsUpdatedOptions options = {}) const; |
72 | |
73 | // iterator |
74 | virtual QQuickItem *itemAt(int index) const = 0; |
75 | virtual int itemCount() const = 0; |
76 | |
77 | virtual void rearrange(const QSizeF &); |
78 | |
79 | static void effectiveSizeHints_helper(QQuickItem *item, QSizeF *cachedSizeHints, QQuickLayoutAttached **info, bool useFallbackToWidthOrHeight); |
80 | static QLayoutPolicy::Policy effectiveSizePolicy_helper(QQuickItem *item, Qt::Orientation orientation, QQuickLayoutAttached *info); |
81 | bool shouldIgnoreItem(QQuickItem *child) const; |
82 | void checkAnchors(QQuickItem *item) const; |
83 | |
84 | void itemChange(ItemChange change, const ItemChangeData &value) override; |
85 | void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override; |
86 | bool isReady() const; |
87 | void deactivateRecur(); |
88 | |
89 | bool invalidated() const; |
90 | bool invalidatedArrangement() const; |
91 | bool isMirrored() const; |
92 | |
93 | /* QQuickItemChangeListener */ |
94 | void itemSiblingOrderChanged(QQuickItem *item) override; |
95 | void itemImplicitWidthChanged(QQuickItem *item) override; |
96 | void itemImplicitHeightChanged(QQuickItem *item) override; |
97 | void itemDestroyed(QQuickItem *item) override; |
98 | void itemVisibilityChanged(QQuickItem *item) override; |
99 | |
100 | void maybeSubscribeToBaseLineOffsetChanges(QQuickItem *item); |
101 | |
102 | Q_INVOKABLE void _q_dumpLayoutTree() const; |
103 | void dumpLayoutTreeRecursive(int level, QString &buf) const; |
104 | |
105 | protected: |
106 | void updatePolish() override; |
107 | |
108 | enum Orientation { |
109 | Vertical = 0, |
110 | Horizontal, |
111 | NOrientations |
112 | }; |
113 | |
114 | protected Q_SLOTS: |
115 | void invalidateSenderItem(); |
116 | |
117 | private: |
118 | unsigned m_inUpdatePolish : 1; |
119 | unsigned m_polishInsideUpdatePolish : 2; |
120 | |
121 | Q_DECLARE_PRIVATE(QQuickLayout) |
122 | |
123 | friend class QQuickLayoutAttached; |
124 | }; |
125 | |
126 | Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickLayout::EnsureLayoutItemsUpdatedOptions) |
127 | |
128 | class QQuickLayoutPrivate : public QQuickItemPrivate |
129 | { |
130 | Q_DECLARE_PUBLIC(QQuickLayout) |
131 | public: |
132 | QQuickLayoutPrivate() : m_dirty(true) |
133 | , m_dirtyArrangement(true) |
134 | , m_isReady(false) |
135 | , m_disableRearrange(true) |
136 | , m_hasItemChangeListeners(false) {} |
137 | |
138 | void applySizeHints() const; |
139 | |
140 | protected: |
141 | /* m_dirty == true means that something in the layout was changed, |
142 | but its state has not been synced to the internal grid layout engine. It is usually: |
143 | 1. A child item was added or removed from the layout (or made visible/invisble) |
144 | 2. A child item got one of its size hints changed |
145 | */ |
146 | mutable unsigned m_dirty : 1; |
147 | /* m_dirtyArrangement == true means that the layout still needs a rearrange despite that |
148 | * m_dirty == false. This is only used for the case that a layout has been invalidated, |
149 | * but its new size is the same as the old size (in that case the child layout won't get |
150 | * a geometryChanged() notification, which rearrange() usually reacts to) |
151 | */ |
152 | mutable unsigned m_dirtyArrangement : 1; |
153 | unsigned m_isReady : 1; |
154 | unsigned m_disableRearrange : 1; |
155 | unsigned m_hasItemChangeListeners : 1; // if false, we don't need to remove its item change listeners... |
156 | }; |
157 | |
158 | |
159 | class Q_QUICKLAYOUTS_PRIVATE_EXPORT QQuickLayoutAttached : public QObject |
160 | { |
161 | Q_OBJECT |
162 | Q_PROPERTY(qreal minimumWidth READ minimumWidth WRITE setMinimumWidth NOTIFY minimumWidthChanged FINAL) |
163 | Q_PROPERTY(qreal minimumHeight READ minimumHeight WRITE setMinimumHeight NOTIFY minimumHeightChanged FINAL) |
164 | Q_PROPERTY(qreal preferredWidth READ preferredWidth WRITE setPreferredWidth NOTIFY preferredWidthChanged FINAL) |
165 | Q_PROPERTY(qreal preferredHeight READ preferredHeight WRITE setPreferredHeight NOTIFY preferredHeightChanged FINAL) |
166 | Q_PROPERTY(qreal maximumWidth READ maximumWidth WRITE setMaximumWidth NOTIFY maximumWidthChanged FINAL) |
167 | Q_PROPERTY(qreal maximumHeight READ maximumHeight WRITE setMaximumHeight NOTIFY maximumHeightChanged FINAL) |
168 | Q_PROPERTY(bool fillHeight READ fillHeight WRITE setFillHeight NOTIFY fillHeightChanged FINAL) |
169 | Q_PROPERTY(bool fillWidth READ fillWidth WRITE setFillWidth NOTIFY fillWidthChanged FINAL) |
170 | Q_PROPERTY(int row READ row WRITE setRow NOTIFY rowChanged FINAL) |
171 | Q_PROPERTY(int column READ column WRITE setColumn NOTIFY columnChanged FINAL) |
172 | Q_PROPERTY(int rowSpan READ rowSpan WRITE setRowSpan NOTIFY rowSpanChanged FINAL) |
173 | Q_PROPERTY(int columnSpan READ columnSpan WRITE setColumnSpan NOTIFY columnSpanChanged FINAL) |
174 | Q_PROPERTY(Qt::Alignment alignment READ alignment WRITE setAlignment NOTIFY alignmentChanged FINAL) |
175 | Q_PROPERTY(int horizontalStretchFactor READ horizontalStretchFactor WRITE setHorizontalStretchFactor NOTIFY horizontalStretchFactorChanged FINAL) |
176 | Q_PROPERTY(int verticalStretchFactor READ verticalStretchFactor WRITE setVerticalStretchFactor NOTIFY verticalStretchFactorChanged FINAL) |
177 | |
178 | Q_PROPERTY(qreal margins READ margins WRITE setMargins NOTIFY marginsChanged FINAL) |
179 | Q_PROPERTY(qreal leftMargin READ leftMargin WRITE setLeftMargin RESET resetLeftMargin NOTIFY leftMarginChanged FINAL) |
180 | Q_PROPERTY(qreal topMargin READ topMargin WRITE setTopMargin RESET resetTopMargin NOTIFY topMarginChanged FINAL) |
181 | Q_PROPERTY(qreal rightMargin READ rightMargin WRITE setRightMargin RESET resetRightMargin NOTIFY rightMarginChanged FINAL) |
182 | Q_PROPERTY(qreal bottomMargin READ bottomMargin WRITE setBottomMargin RESET resetBottomMargin NOTIFY bottomMarginChanged FINAL) |
183 | |
184 | public: |
185 | QQuickLayoutAttached(QObject *object); |
186 | |
187 | qreal minimumWidth() const { return !m_isMinimumWidthSet ? sizeHint(which: Qt::MinimumSize, orientation: Qt::Horizontal) : m_minimumWidth; } |
188 | void setMinimumWidth(qreal width); |
189 | bool isMinimumWidthSet() const {return m_isMinimumWidthSet; } |
190 | |
191 | qreal minimumHeight() const { return !m_isMinimumHeightSet ? sizeHint(which: Qt::MinimumSize, orientation: Qt::Vertical) : m_minimumHeight; } |
192 | void setMinimumHeight(qreal height); |
193 | bool isMinimumHeightSet() const {return m_isMinimumHeightSet; } |
194 | |
195 | qreal preferredWidth() const { return m_preferredWidth; } |
196 | void setPreferredWidth(qreal width); |
197 | bool isPreferredWidthSet() const {return m_preferredWidth > -1; } |
198 | |
199 | qreal preferredHeight() const { return m_preferredHeight; } |
200 | void setPreferredHeight(qreal width); |
201 | bool isPreferredHeightSet() const {return m_preferredHeight > -1; } |
202 | |
203 | qreal maximumWidth() const { return !m_isMaximumWidthSet ? sizeHint(which: Qt::MaximumSize, orientation: Qt::Horizontal) : m_maximumWidth; } |
204 | void setMaximumWidth(qreal width); |
205 | bool isMaximumWidthSet() const {return m_isMaximumWidthSet; } |
206 | |
207 | qreal maximumHeight() const { return !m_isMaximumHeightSet ? sizeHint(which: Qt::MaximumSize, orientation: Qt::Vertical) : m_maximumHeight; } |
208 | void setMaximumHeight(qreal height); |
209 | bool isMaximumHeightSet() const {return m_isMaximumHeightSet; } |
210 | |
211 | void setMinimumImplicitSize(const QSizeF &sz); |
212 | void setMaximumImplicitSize(const QSizeF &sz); |
213 | |
214 | bool fillWidth() const { return m_fillWidth; } |
215 | void setFillWidth(bool fill); |
216 | bool isFillWidthSet() const { return m_isFillWidthSet; } |
217 | |
218 | bool fillHeight() const { return m_fillHeight; } |
219 | void setFillHeight(bool fill); |
220 | bool isFillHeightSet() const { return m_isFillHeightSet; } |
221 | |
222 | int row() const { return qMax(a: m_row, b: 0); } |
223 | void setRow(int row); |
224 | bool isRowSet() const { return m_row >= 0; } |
225 | int column() const { return qMax(a: m_column, b: 0); } |
226 | void setColumn(int column); |
227 | bool isColumnSet() const { return m_column >= 0; } |
228 | |
229 | int rowSpan() const { return m_rowSpan; } |
230 | void setRowSpan(int span); |
231 | int columnSpan() const { return m_columnSpan; } |
232 | void setColumnSpan(int span); |
233 | |
234 | Qt::Alignment alignment() const { return m_alignment; } |
235 | void setAlignment(Qt::Alignment align); |
236 | bool isAlignmentSet() const {return m_isAlignmentSet; } |
237 | |
238 | int horizontalStretchFactor() const { return m_horizontalStretch; } |
239 | void setHorizontalStretchFactor(int stretchFactor); |
240 | bool isHorizontalStretchFactorSet() const { return m_horizontalStretch > -1; } |
241 | int verticalStretchFactor() const { return m_verticalStretch; } |
242 | void setVerticalStretchFactor(int stretchFactor); |
243 | bool isVerticalStretchFactorSet() const { return m_verticalStretch > -1; } |
244 | |
245 | qreal margins() const { return m_defaultMargins; } |
246 | void setMargins(qreal m); |
247 | bool isMarginsSet() const { return m_isMarginsSet; } |
248 | |
249 | qreal leftMargin() const { return m_isLeftMarginSet ? m_margins.left() : m_defaultMargins; } |
250 | void setLeftMargin(qreal m); |
251 | void resetLeftMargin(); |
252 | bool isLeftMarginSet() const { return m_isLeftMarginSet; } |
253 | |
254 | qreal topMargin() const { return m_isTopMarginSet ? m_margins.top() : m_defaultMargins; } |
255 | void setTopMargin(qreal m); |
256 | void resetTopMargin(); |
257 | bool isTopMarginSet() const {return m_isTopMarginSet; } |
258 | |
259 | qreal rightMargin() const { return m_isRightMarginSet ? m_margins.right() : m_defaultMargins; } |
260 | void setRightMargin(qreal m); |
261 | void resetRightMargin(); |
262 | bool isRightMarginSet() const { return m_isRightMarginSet; } |
263 | |
264 | qreal bottomMargin() const { return m_isBottomMarginSet ? m_margins.bottom() : m_defaultMargins; } |
265 | void setBottomMargin(qreal m); |
266 | void resetBottomMargin(); |
267 | bool isBottomMarginSet() const { return m_isBottomMarginSet; } |
268 | |
269 | QMarginsF qMargins() const { |
270 | return QMarginsF(leftMargin(), topMargin(), rightMargin(), bottomMargin()); |
271 | } |
272 | |
273 | QMarginsF effectiveQMargins() const { |
274 | bool mirrored = parentLayout() && parentLayout()->isMirrored(); |
275 | if (mirrored) |
276 | return QMarginsF(rightMargin(), topMargin(), leftMargin(), bottomMargin()); |
277 | else |
278 | return qMargins(); |
279 | } |
280 | |
281 | bool setChangesNotificationEnabled(bool enabled) |
282 | { |
283 | const bool old = m_changesNotificationEnabled; |
284 | m_changesNotificationEnabled = enabled; |
285 | return old; |
286 | } |
287 | |
288 | qreal sizeHint(Qt::SizeHint which, Qt::Orientation orientation) const; |
289 | |
290 | bool isExtentExplicitlySet(Qt::Orientation o, Qt::SizeHint whichSize) const |
291 | { |
292 | switch (whichSize) { |
293 | case Qt::MinimumSize: |
294 | return o == Qt::Horizontal ? m_isMinimumWidthSet : m_isMinimumHeightSet; |
295 | case Qt::MaximumSize: |
296 | return o == Qt::Horizontal ? m_isMaximumWidthSet : m_isMaximumHeightSet; |
297 | case Qt::PreferredSize: |
298 | return true; // Layout.preferredWidth is always explicitly set |
299 | case Qt::MinimumDescent: // Not supported |
300 | case Qt::NSizeHints: |
301 | return false; |
302 | } |
303 | return false; |
304 | } |
305 | |
306 | Q_SIGNALS: |
307 | void minimumWidthChanged(); |
308 | void minimumHeightChanged(); |
309 | void preferredWidthChanged(); |
310 | void preferredHeightChanged(); |
311 | void maximumWidthChanged(); |
312 | void maximumHeightChanged(); |
313 | void fillWidthChanged(); |
314 | void fillHeightChanged(); |
315 | void leftMarginChanged(); |
316 | void topMarginChanged(); |
317 | void rightMarginChanged(); |
318 | void bottomMarginChanged(); |
319 | void marginsChanged(); |
320 | void rowChanged(); |
321 | void columnChanged(); |
322 | void rowSpanChanged(); |
323 | void columnSpanChanged(); |
324 | void alignmentChanged(); |
325 | void horizontalStretchFactorChanged(); |
326 | void verticalStretchFactorChanged(); |
327 | |
328 | private: |
329 | void invalidateItem(); |
330 | QQuickLayout *parentLayout() const; |
331 | QQuickItem *item() const; |
332 | private: |
333 | qreal m_minimumWidth; |
334 | qreal m_minimumHeight; |
335 | qreal m_preferredWidth; |
336 | qreal m_preferredHeight; |
337 | qreal m_maximumWidth; |
338 | qreal m_maximumHeight; |
339 | |
340 | qreal m_defaultMargins; |
341 | QMarginsF m_margins; |
342 | |
343 | qreal m_fallbackWidth; |
344 | qreal m_fallbackHeight; |
345 | |
346 | // GridLayout specific properties |
347 | int m_row; |
348 | int m_column; |
349 | int m_rowSpan; |
350 | int m_columnSpan; |
351 | |
352 | unsigned m_fillWidth : 1; |
353 | unsigned m_fillHeight : 1; |
354 | unsigned m_isFillWidthSet : 1; |
355 | unsigned m_isFillHeightSet : 1; |
356 | unsigned m_isMinimumWidthSet : 1; |
357 | unsigned m_isMinimumHeightSet : 1; |
358 | // preferredWidth and preferredHeight are always explicit, since |
359 | // their implicit equivalent is implicitWidth and implicitHeight |
360 | unsigned m_isMaximumWidthSet : 1; |
361 | unsigned m_isMaximumHeightSet : 1; |
362 | unsigned m_changesNotificationEnabled : 1; |
363 | unsigned m_isMarginsSet : 1; |
364 | unsigned m_isLeftMarginSet : 1; |
365 | unsigned m_isTopMarginSet : 1; |
366 | unsigned m_isRightMarginSet : 1; |
367 | unsigned m_isBottomMarginSet : 1; |
368 | unsigned m_isAlignmentSet : 1; |
369 | Qt::Alignment m_alignment; |
370 | int m_horizontalStretch; |
371 | int m_verticalStretch; |
372 | friend class QQuickLayout; |
373 | }; |
374 | |
375 | inline QQuickLayoutAttached *attachedLayoutObject(QQuickItem *item, bool create = true) |
376 | { |
377 | return static_cast<QQuickLayoutAttached *>(qmlAttachedPropertiesObject<QQuickLayout>(obj: item, create)); |
378 | } |
379 | |
380 | QT_END_NAMESPACE |
381 | |
382 | QML_DECLARE_TYPE(QQuickLayout) |
383 | |
384 | #endif // QQUICKLAYOUT_P_H |
385 | |