1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the Qt Quick Layouts module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #ifndef QQUICKLAYOUT_P_H
41 #define QQUICKLAYOUT_P_H
42 
43 #include <QPointer>
44 #include <QQuickItem>
45 #include <private/qquickitem_p.h>
46 #include <QtQuick/private/qquickitemchangelistener_p.h>
47 #include <QtGui/private/qlayoutpolicy_p.h>
48 
49 QT_BEGIN_NAMESPACE
50 
51 class QQuickLayoutAttached;
Q_DECLARE_LOGGING_CATEGORY(lcQuickLayouts)52 Q_DECLARE_LOGGING_CATEGORY(lcQuickLayouts)
53 
54 class QQuickLayoutPrivate;
55 class QQuickLayout : public QQuickItem, public QQuickItemChangeListener
56 
57 {
58     Q_OBJECT
59     QML_NAMED_ELEMENT(Layout)
60     QML_UNCREATABLE("Do not create objects of type Layout.")
61     QML_ATTACHED(QQuickLayoutAttached)
62 
63 public:
64     enum SizeHint {
65         MinimumSize = 0,
66         PreferredSize,
67         MaximumSize,
68         NSizes
69     };
70 
71     explicit QQuickLayout(QQuickLayoutPrivate &dd, QQuickItem *parent = 0);
72     ~QQuickLayout();
73 
74     static QQuickLayoutAttached *qmlAttachedProperties(QObject *object);
75 
76 
77     void componentComplete() override;
78     virtual QSizeF sizeHint(Qt::SizeHint whichSizeHint) const = 0;
79     virtual void setAlignment(QQuickItem *item, Qt::Alignment align) = 0;
80     virtual void invalidate(QQuickItem * childItem = 0);
81     virtual void updateLayoutItems() = 0;
82     void ensureLayoutItemsUpdated() const;
83 
84     // iterator
85     virtual QQuickItem *itemAt(int index) const = 0;
86     virtual int itemCount() const = 0;
87 
88     virtual void rearrange(const QSizeF &);
89 
90     static void effectiveSizeHints_helper(QQuickItem *item, QSizeF *cachedSizeHints, QQuickLayoutAttached **info, bool useFallbackToWidthOrHeight);
91     static QLayoutPolicy::Policy effectiveSizePolicy_helper(QQuickItem *item, Qt::Orientation orientation, QQuickLayoutAttached *info);
92     bool shouldIgnoreItem(QQuickItem *child, QQuickLayoutAttached *&info, QSizeF *sizeHints) const;
93     void checkAnchors(QQuickItem *item) const;
94 
95     void itemChange(ItemChange change, const ItemChangeData &value) override;
96     void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)  override;
97     bool isReady() const;
98     void deactivateRecur();
99 
100     bool invalidated() const;
101     bool invalidatedArrangement() const;
102     bool isMirrored() const;
103 
104     /* QQuickItemChangeListener */
105     void itemSiblingOrderChanged(QQuickItem *item) override;
106     void itemImplicitWidthChanged(QQuickItem *item) override;
107     void itemImplicitHeightChanged(QQuickItem *item) override;
108     void itemDestroyed(QQuickItem *item) override;
109     void itemVisibilityChanged(QQuickItem *item) override;
110 
111     Q_INVOKABLE void _q_dumpLayoutTree() const;
112     void dumpLayoutTreeRecursive(int level, QString &buf) const;
113 
114 protected:
115     void updatePolish() override;
116 
117     enum Orientation {
118         Vertical = 0,
119         Horizontal,
120         NOrientations
121     };
122 
123 protected slots:
124     void invalidateSenderItem();
125 
126 private:
127     unsigned m_inUpdatePolish : 1;
128     unsigned m_polishInsideUpdatePolish : 2;
129 
130     Q_DECLARE_PRIVATE(QQuickLayout)
131 
132     friend class QQuickLayoutAttached;
133 };
134 
135 
136 class QQuickLayoutPrivate : public QQuickItemPrivate
137 {
Q_DECLARE_PUBLIC(QQuickLayout)138     Q_DECLARE_PUBLIC(QQuickLayout)
139 public:
140     QQuickLayoutPrivate() : m_dirty(true), m_dirtyArrangement(true), m_isReady(false), m_disableRearrange(true), m_hasItemChangeListeners(false) {}
141 
142     qreal getImplicitWidth() const override;
143     qreal getImplicitHeight() const override;
144 
145     void applySizeHints() const;
146 
147 protected:
148     /* m_dirty == true means that something in the layout was changed,
149        but its state has not been synced to the internal grid layout engine. It is usually:
150        1. A child item was added or removed from the layout (or made visible/invisble)
151        2. A child item got one of its size hints changed
152     */
153     mutable unsigned m_dirty : 1;
154     /* m_dirtyArrangement == true means that the layout still needs a rearrange despite that
155      * m_dirty == false. This is only used for the case that a layout has been invalidated,
156      * but its new size is the same as the old size (in that case the child layout won't get
157      * a geometryChanged() notification, which rearrange() usually reacts to)
158      */
159     mutable unsigned m_dirtyArrangement : 1;
160     unsigned m_isReady : 1;
161     unsigned m_disableRearrange : 1;
162     unsigned m_hasItemChangeListeners : 1;      // if false, we don't need to remove its item change listeners...
163     mutable QSet<QQuickItem *> m_ignoredItems;
164 };
165 
166 
167 class QQuickLayoutAttached : public QObject
168 {
169     Q_OBJECT
170     Q_PROPERTY(qreal minimumWidth READ minimumWidth WRITE setMinimumWidth NOTIFY minimumWidthChanged)
171     Q_PROPERTY(qreal minimumHeight READ minimumHeight WRITE setMinimumHeight NOTIFY minimumHeightChanged)
172     Q_PROPERTY(qreal preferredWidth READ preferredWidth WRITE setPreferredWidth NOTIFY preferredWidthChanged)
173     Q_PROPERTY(qreal preferredHeight READ preferredHeight WRITE setPreferredHeight NOTIFY preferredHeightChanged)
174     Q_PROPERTY(qreal maximumWidth READ maximumWidth WRITE setMaximumWidth NOTIFY maximumWidthChanged)
175     Q_PROPERTY(qreal maximumHeight READ maximumHeight WRITE setMaximumHeight NOTIFY maximumHeightChanged)
176     Q_PROPERTY(bool fillHeight READ fillHeight WRITE setFillHeight NOTIFY fillHeightChanged)
177     Q_PROPERTY(bool fillWidth READ fillWidth WRITE setFillWidth NOTIFY fillWidthChanged)
178     Q_PROPERTY(int row READ row WRITE setRow NOTIFY rowChanged)
179     Q_PROPERTY(int column READ column WRITE setColumn NOTIFY columnChanged)
180     Q_PROPERTY(int rowSpan READ rowSpan WRITE setRowSpan NOTIFY rowSpanChanged)
181     Q_PROPERTY(int columnSpan READ columnSpan WRITE setColumnSpan NOTIFY columnSpanChanged)
182     Q_PROPERTY(Qt::Alignment alignment READ alignment WRITE setAlignment NOTIFY alignmentChanged)
183 
184     Q_PROPERTY(qreal margins READ margins WRITE setMargins NOTIFY marginsChanged)
185     Q_PROPERTY(qreal leftMargin READ leftMargin WRITE setLeftMargin RESET resetLeftMargin NOTIFY leftMarginChanged)
186     Q_PROPERTY(qreal topMargin READ topMargin WRITE setTopMargin RESET resetTopMargin NOTIFY topMarginChanged)
187     Q_PROPERTY(qreal rightMargin READ rightMargin WRITE setRightMargin RESET resetRightMargin NOTIFY rightMarginChanged)
188     Q_PROPERTY(qreal bottomMargin READ bottomMargin WRITE setBottomMargin RESET resetBottomMargin NOTIFY bottomMarginChanged)
189 
190 public:
191     QQuickLayoutAttached(QObject *object);
192 
minimumWidth()193     qreal minimumWidth() const { return !m_isMinimumWidthSet ? sizeHint(Qt::MinimumSize, Qt::Horizontal) : m_minimumWidth; }
194     void setMinimumWidth(qreal width);
195 
minimumHeight()196     qreal minimumHeight() const { return !m_isMinimumHeightSet ? sizeHint(Qt::MinimumSize, Qt::Vertical) : m_minimumHeight; }
197     void setMinimumHeight(qreal height);
198 
preferredWidth()199     qreal preferredWidth() const { return m_preferredWidth; }
200     void setPreferredWidth(qreal width);
201 
preferredHeight()202     qreal preferredHeight() const { return m_preferredHeight; }
203     void setPreferredHeight(qreal width);
204 
maximumWidth()205     qreal maximumWidth() const { return !m_isMaximumWidthSet ? sizeHint(Qt::MaximumSize, Qt::Horizontal) : m_maximumWidth; }
206     void setMaximumWidth(qreal width);
207 
maximumHeight()208     qreal maximumHeight() const { return !m_isMaximumHeightSet ? sizeHint(Qt::MaximumSize, Qt::Vertical) : m_maximumHeight; }
209     void setMaximumHeight(qreal height);
210 
211     void setMinimumImplicitSize(const QSizeF &sz);
212     void setMaximumImplicitSize(const QSizeF &sz);
213 
fillWidth()214     bool fillWidth() const { return m_fillWidth; }
215     void setFillWidth(bool fill);
isFillWidthSet()216     bool isFillWidthSet() const { return m_isFillWidthSet; }
217 
fillHeight()218     bool fillHeight() const { return m_fillHeight; }
219     void setFillHeight(bool fill);
isFillHeightSet()220     bool isFillHeightSet() const { return m_isFillHeightSet; }
221 
row()222     int row() const { return qMax(m_row, 0); }
223     void setRow(int row);
isRowSet()224     bool isRowSet() const { return m_row >= 0; }
column()225     int column() const { return qMax(m_column, 0); }
226     void setColumn(int column);
isColumnSet()227     bool isColumnSet() const { return m_column >= 0; }
228 
rowSpan()229     int rowSpan() const { return m_rowSpan; }
230     void setRowSpan(int span);
columnSpan()231     int columnSpan() const { return m_columnSpan; }
232     void setColumnSpan(int span);
233 
alignment()234     Qt::Alignment alignment() const { return m_alignment; }
235     void setAlignment(Qt::Alignment align);
236 
margins()237     qreal margins() const { return m_defaultMargins; }
238     void setMargins(qreal m);
239 
leftMargin()240     qreal leftMargin() const { return m_isLeftMarginSet ? m_margins.left() : m_defaultMargins; }
241     void setLeftMargin(qreal m);
242     void resetLeftMargin();
243 
topMargin()244     qreal topMargin() const { return m_isTopMarginSet ? m_margins.top() : m_defaultMargins; }
245     void setTopMargin(qreal m);
246     void resetTopMargin();
247 
rightMargin()248     qreal rightMargin() const { return m_isRightMarginSet ? m_margins.right() : m_defaultMargins; }
249     void setRightMargin(qreal m);
250     void resetRightMargin();
251 
bottomMargin()252     qreal bottomMargin() const { return m_isBottomMarginSet ? m_margins.bottom() : m_defaultMargins; }
253     void setBottomMargin(qreal m);
254     void resetBottomMargin();
255 
qMargins()256     QMarginsF qMargins() const {
257         return QMarginsF(leftMargin(), topMargin(), rightMargin(), bottomMargin());
258     }
259 
effectiveQMargins()260     QMarginsF effectiveQMargins() const {
261         bool mirrored = parentLayout() && parentLayout()->isMirrored();
262         if (mirrored)
263             return QMarginsF(rightMargin(), topMargin(), leftMargin(), bottomMargin());
264         else
265             return qMargins();
266     }
267 
setChangesNotificationEnabled(bool enabled)268     bool setChangesNotificationEnabled(bool enabled)
269     {
270         const bool old = m_changesNotificationEnabled;
271         m_changesNotificationEnabled = enabled;
272         return old;
273     }
274 
275     qreal sizeHint(Qt::SizeHint which, Qt::Orientation orientation) const;
276 
isExtentExplicitlySet(Qt::Orientation o,Qt::SizeHint whichSize)277     bool isExtentExplicitlySet(Qt::Orientation o, Qt::SizeHint whichSize) const
278     {
279         switch (whichSize) {
280         case Qt::MinimumSize:
281             return o == Qt::Horizontal ? m_isMinimumWidthSet : m_isMinimumHeightSet;
282         case Qt::MaximumSize:
283             return o == Qt::Horizontal ? m_isMaximumWidthSet : m_isMaximumHeightSet;
284         case Qt::PreferredSize:
285             return true;            // Layout.preferredWidth is always explicitly set
286         case Qt::MinimumDescent:    // Not supported
287         case Qt::NSizeHints:
288             return false;
289         }
290         return false;
291     }
292 
293 signals:
294     void minimumWidthChanged();
295     void minimumHeightChanged();
296     void preferredWidthChanged();
297     void preferredHeightChanged();
298     void maximumWidthChanged();
299     void maximumHeightChanged();
300     void fillWidthChanged();
301     void fillHeightChanged();
302     void leftMarginChanged();
303     void topMarginChanged();
304     void rightMarginChanged();
305     void bottomMarginChanged();
306     void marginsChanged();
307     void rowChanged();
308     void columnChanged();
309     void rowSpanChanged();
310     void columnSpanChanged();
311     void alignmentChanged();
312 
313 private:
314     void invalidateItem();
315     QQuickLayout *parentLayout() const;
316     QQuickItem *item() const;
317 private:
318     qreal m_minimumWidth;
319     qreal m_minimumHeight;
320     qreal m_preferredWidth;
321     qreal m_preferredHeight;
322     qreal m_maximumWidth;
323     qreal m_maximumHeight;
324 
325     qreal m_defaultMargins;
326     QMarginsF m_margins;
327 
328     qreal m_fallbackWidth;
329     qreal m_fallbackHeight;
330 
331     // GridLayout specific properties
332     int m_row;
333     int m_column;
334     int m_rowSpan;
335     int m_columnSpan;
336 
337     unsigned m_fillWidth : 1;
338     unsigned m_fillHeight : 1;
339     unsigned m_isFillWidthSet : 1;
340     unsigned m_isFillHeightSet : 1;
341     unsigned m_isMinimumWidthSet : 1;
342     unsigned m_isMinimumHeightSet : 1;
343     // preferredWidth and preferredHeight are always explicit, since
344     // their implicit equivalent is implicitWidth and implicitHeight
345     unsigned m_isMaximumWidthSet : 1;
346     unsigned m_isMaximumHeightSet : 1;
347     unsigned m_changesNotificationEnabled : 1;
348     unsigned m_isLeftMarginSet : 1;
349     unsigned m_isTopMarginSet : 1;
350     unsigned m_isRightMarginSet : 1;
351     unsigned m_isBottomMarginSet : 1;
352     Qt::Alignment m_alignment;
353     friend class QQuickLayout;
354 };
355 
356 inline QQuickLayoutAttached *attachedLayoutObject(QQuickItem *item, bool create = true)
357 {
358     return static_cast<QQuickLayoutAttached *>(qmlAttachedPropertiesObject<QQuickLayout>(item, create));
359 }
360 
361 QT_END_NAMESPACE
362 
363 QML_DECLARE_TYPE(QQuickLayout)
364 
365 #endif // QQUICKLAYOUT_P_H
366