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 QtGui 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 QGRIDLAYOUTENGINE_P_H
41 #define QGRIDLAYOUTENGINE_P_H
42 
43 //
44 //  W A R N I N G
45 //  -------------
46 //
47 // This file is not part of the Qt API.  It exists for the convenience
48 // of the graphics view layout classes.  This header
49 // file may change from version to version without notice, or even be removed.
50 //
51 // We mean it.
52 //
53 
54 #include <QtGui/private/qtguiglobal_p.h>
55 #include "qalgorithms.h"
56 #include "qbitarray.h"
57 #include "qlist.h"
58 #include "qmap.h"
59 #include "qpair.h"
60 #include <QtCore/qvector.h>
61 #include <QtCore/qsize.h>
62 #include <QtCore/qrect.h>
63 #include <float.h>
64 #include "qlayoutpolicy_p.h"
65 #include "qabstractlayoutstyleinfo_p.h"
66 
67 // #define QGRIDLAYOUTENGINE_DEBUG
68 
69 QT_BEGIN_NAMESPACE
70 
71 class QStyle;
72 class QWidget;
73 
74 // ### deal with Descent in a similar way
75 enum {
76     MinimumSize = Qt::MinimumSize,
77     PreferredSize = Qt::PreferredSize,
78     MaximumSize = Qt::MaximumSize,
79     NSizes
80 };
81 
82 // do not reorder
83 enum {
84     Hor,
85     Ver,
86     NOrientations
87 };
88 
89 // do not reorder
90 enum LayoutSide {
91     Left,
92     Top,
93     Right,
94     Bottom
95 };
96 
97 enum {
98     NoConstraint,
99     HorizontalConstraint,   // Width depends on the height
100     VerticalConstraint,     // Height depends on the width
101     UnknownConstraint,      // need to update cache
102     UnfeasibleConstraint    // not feasible, it be has some items with Vertical and others with Horizontal constraints
103 };
104 
105 template <typename T>
106 class QLayoutParameter
107 {
108 public:
109     enum State { Default, User, Cached };
110 
QLayoutParameter()111     inline QLayoutParameter() : q_value(T()), q_state(Default) {}
q_value(value)112     inline QLayoutParameter(T value, State state = Default) : q_value(value), q_state(state) {}
113 
setUserValue(T value)114     inline void setUserValue(T value) {
115         q_value = value;
116         q_state = User;
117     }
setCachedValue(T value)118     inline void setCachedValue(T value) const {
119         if (q_state != User) {
120             q_value = value;
121             q_state = Cached;
122         }
123     }
value()124     inline T value() const { return q_value; }
value(T defaultValue)125     inline T value(T defaultValue) const { return isUser() ? q_value : defaultValue; }
isDefault()126     inline bool isDefault() const { return q_state == Default; }
isUser()127     inline bool isUser() const { return q_state == User; }
isCached()128     inline bool isCached() const { return q_state == Cached; }
129 
130 private:
131     mutable T q_value;
132     mutable State q_state;
133 };
134 
135 class QStretchParameter : public QLayoutParameter<int>
136 {
137 public:
QStretchParameter()138     QStretchParameter() : QLayoutParameter<int>(-1) {}
139 
140 };
141 
142 class Q_GUI_EXPORT QGridLayoutBox
143 {
144 public:
QGridLayoutBox()145     inline QGridLayoutBox()
146         : q_minimumSize(0), q_preferredSize(0), q_maximumSize(FLT_MAX),
147           q_minimumDescent(-1), q_minimumAscent(-1) {}
148 
149     void add(const QGridLayoutBox &other, int stretch, qreal spacing);
150     void combine(const QGridLayoutBox &other);
151     void normalize();
152 
153 #ifdef QGRIDLAYOUTENGINE_DEBUG
154     void dump(int indent = 0) const;
155 #endif
156     // This code could use the union-struct-array trick, but a compiler
157     // bug prevents this from working.
158     qreal q_minimumSize;
159     qreal q_preferredSize;
160     qreal q_maximumSize;
161     qreal q_minimumDescent;
162     qreal q_minimumAscent;
q_sizes(int which)163     inline qreal &q_sizes(int which)
164     {
165         qreal *t;
166         switch (which) {
167         case Qt::MinimumSize:
168             t = &q_minimumSize;
169             break;
170         case Qt::PreferredSize:
171             t = &q_preferredSize;
172             break;
173         case Qt::MaximumSize:
174             t = &q_maximumSize;
175             break;
176         case Qt::MinimumDescent:
177             t = &q_minimumDescent;
178             break;
179         case (Qt::MinimumDescent + 1):
180             t = &q_minimumAscent;
181             break;
182         default:
183             t = nullptr;
184             break;
185         }
186         return *t;
187     }
q_sizes(int which)188     inline const qreal &q_sizes(int which) const
189     {
190         const qreal *t;
191         switch (which) {
192         case Qt::MinimumSize:
193             t = &q_minimumSize;
194             break;
195         case Qt::PreferredSize:
196             t = &q_preferredSize;
197             break;
198         case Qt::MaximumSize:
199             t = &q_maximumSize;
200             break;
201         case Qt::MinimumDescent:
202             t = &q_minimumDescent;
203             break;
204         case (Qt::MinimumDescent + 1):
205             t = &q_minimumAscent;
206             break;
207         default:
208             t = nullptr;
209             break;
210         }
211         return *t;
212     }
213 };
214 Q_DECLARE_TYPEINFO(QGridLayoutBox, Q_MOVABLE_TYPE); // cannot be Q_PRIMITIVE_TYPE, as q_maximumSize, say, is != 0
215 
216 bool operator==(const QGridLayoutBox &box1, const QGridLayoutBox &box2);
217 inline bool operator!=(const QGridLayoutBox &box1, const QGridLayoutBox &box2)
218     { return !operator==(box1, box2); }
219 
220 class QGridLayoutMultiCellData
221 {
222 public:
QGridLayoutMultiCellData()223     inline QGridLayoutMultiCellData() : q_stretch(-1) {}
224 
225     QGridLayoutBox q_box;
226     int q_stretch;
227 };
228 
229 typedef QMap<QPair<int, int>, QGridLayoutMultiCellData> MultiCellMap;
230 
231 class QGridLayoutRowInfo;
232 
233 class QGridLayoutRowData
234 {
235 public:
236     void reset(int count);
237     void distributeMultiCells(const QGridLayoutRowInfo &rowInfo, bool snapToPixelGrid);
238     void calculateGeometries(int start, int end, qreal targetSize, qreal *positions, qreal *sizes,
239                              qreal *descents, const QGridLayoutBox &totalBox,
240                              const QGridLayoutRowInfo &rowInfo, bool snapToPixelGrid);
241     QGridLayoutBox totalBox(int start, int end) const;
242     void stealBox(int start, int end, int which, qreal *positions, qreal *sizes);
243 
244 #ifdef QGRIDLAYOUTENGINE_DEBUG
245     void dump(int indent = 0) const;
246 #endif
247 
248     QBitArray ignore;   // ### rename q_
249     QVector<QGridLayoutBox> boxes;
250     MultiCellMap multiCellMap;
251     QVector<int> stretches;
252     QVector<qreal> spacings;
253     bool hasIgnoreFlag;
254 };
255 
256 class QGridLayoutRowInfo
257 {
258 public:
QGridLayoutRowInfo()259     inline QGridLayoutRowInfo() : count(0) {}
260 
261     void insertOrRemoveRows(int row, int delta);
262 
263 #ifdef QGRIDLAYOUTENGINE_DEBUG
264     void dump(int indent = 0) const;
265 #endif
266 
267     int count;
268     QVector<QStretchParameter> stretches;
269     QVector<QLayoutParameter<qreal> > spacings;
270     QVector<Qt::Alignment> alignments;
271     QVector<QGridLayoutBox> boxes;
272 };
273 
274 
275 class Q_GUI_EXPORT QGridLayoutItem
276 {
277 public:
278     QGridLayoutItem(int row, int column, int rowSpan = 1, int columnSpan = 1,
279                     Qt::Alignment alignment = { });
~QGridLayoutItem()280     virtual ~QGridLayoutItem() {}
281 
firstRow()282     inline int firstRow() const { return q_firstRows[Ver]; }
firstColumn()283     inline int firstColumn() const { return q_firstRows[Hor]; }
rowSpan()284     inline int rowSpan() const { return q_rowSpans[Ver]; }
columnSpan()285     inline int columnSpan() const { return q_rowSpans[Hor]; }
lastRow()286     inline int lastRow() const { return firstRow() + rowSpan() - 1; }
lastColumn()287     inline int lastColumn() const { return firstColumn() + columnSpan() - 1; }
288 
289     int firstRow(Qt::Orientation orientation) const;
290     int firstColumn(Qt::Orientation orientation) const;
291     int lastRow(Qt::Orientation orientation) const;
292     int lastColumn(Qt::Orientation orientation) const;
293     int rowSpan(Qt::Orientation orientation) const;
294     int columnSpan(Qt::Orientation orientation) const;
295     void setFirstRow(int row, Qt::Orientation orientation = Qt::Vertical);
296     void setRowSpan(int rowSpan, Qt::Orientation orientation = Qt::Vertical);
297 
298     int stretchFactor(Qt::Orientation orientation) const;
299     void setStretchFactor(int stretch, Qt::Orientation orientation);
300 
alignment()301     inline Qt::Alignment alignment() const { return q_alignment; }
setAlignment(Qt::Alignment alignment)302     inline void setAlignment(Qt::Alignment alignment) { q_alignment = alignment; }
303 
304     virtual QLayoutPolicy::Policy sizePolicy(Qt::Orientation orientation) const = 0;
305     virtual QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint) const = 0;
isIgnored()306     virtual bool isIgnored() const { return false; }
307 
308     virtual void setGeometry(const QRectF &rect) = 0;
309     /*
310       returns true if the size policy returns true for either hasHeightForWidth()
311       or hasWidthForHeight()
312      */
hasDynamicConstraint()313     virtual bool hasDynamicConstraint() const { return false; }
dynamicConstraintOrientation()314     virtual Qt::Orientation dynamicConstraintOrientation() const { return Qt::Horizontal; }
315 
316 
317     virtual QLayoutPolicy::ControlTypes controlTypes(LayoutSide side) const;
318 
319     QRectF geometryWithin(qreal x, qreal y, qreal width, qreal height, qreal rowDescent, Qt::Alignment align, bool snapToPixelGrid) const;
320     QGridLayoutBox box(Qt::Orientation orientation, bool snapToPixelGrid, qreal constraint = -1.0) const;
321 
322 
323     void transpose();
324     void insertOrRemoveRows(int row, int delta, Qt::Orientation orientation = Qt::Vertical);
325     QSizeF effectiveMaxSize(const QSizeF &constraint) const;
326 
327 #ifdef QGRIDLAYOUTENGINE_DEBUG
328     void dump(int indent = 0) const;
329 #endif
330 
331 private:
332     int q_firstRows[NOrientations];
333     int q_rowSpans[NOrientations];
334     int q_stretches[NOrientations];
335     Qt::Alignment q_alignment;
336 
337 };
338 
339 class Q_GUI_EXPORT QGridLayoutEngine
340 {
341 public:
342     QGridLayoutEngine(Qt::Alignment defaultAlignment = { }, bool snapToPixelGrid = false);
~QGridLayoutEngine()343     inline ~QGridLayoutEngine() { qDeleteAll(q_items); }
344 
345     int rowCount(Qt::Orientation orientation) const;
346     int columnCount(Qt::Orientation orientation) const;
rowCount()347     inline int rowCount() const { return q_infos[Ver].count; }
columnCount()348     inline int columnCount() const { return q_infos[Hor].count; }
349     // returns the number of items inserted, which may be less than (rowCount * columnCount)
350     int itemCount() const;
351     QGridLayoutItem *itemAt(int index) const;
352 
353     int effectiveFirstRow(Qt::Orientation orientation = Qt::Vertical) const;
354     int effectiveLastRow(Qt::Orientation orientation = Qt::Vertical) const;
355 
356     void setSpacing(qreal spacing, Qt::Orientations orientations);
357     qreal spacing(Qt::Orientation orientation, const QAbstractLayoutStyleInfo *styleInfo) const;
358     // ### setSpacingAfterRow(), spacingAfterRow()
359     void setRowSpacing(int row, qreal spacing, Qt::Orientation orientation = Qt::Vertical);
360     qreal rowSpacing(int row, Qt::Orientation orientation = Qt::Vertical) const;
361 
362     void setRowStretchFactor(int row, int stretch, Qt::Orientation orientation = Qt::Vertical);
363     int rowStretchFactor(int row, Qt::Orientation orientation = Qt::Vertical) const;
364 
365     void setRowSizeHint(Qt::SizeHint which, int row, qreal size,
366                         Qt::Orientation orientation = Qt::Vertical);
367     qreal rowSizeHint(Qt::SizeHint which, int row,
368                       Qt::Orientation orientation = Qt::Vertical) const;
369 
370     void setRowAlignment(int row, Qt::Alignment alignment, Qt::Orientation orientation);
371     Qt::Alignment rowAlignment(int row, Qt::Orientation orientation) const;
372 
373     Qt::Alignment effectiveAlignment(const QGridLayoutItem *layoutItem) const;
374 
375 
376     void insertItem(QGridLayoutItem *item, int index);
377     void addItem(QGridLayoutItem *item);
378     void removeItem(QGridLayoutItem *item);
deleteItems()379     void deleteItems()
380     {
381         const QList<QGridLayoutItem *> oldItems = q_items;
382         q_items.clear();    // q_items are used as input when the grid is regenerated in removeRows
383         // The following calls to removeRows are suboptimal
384         int rows = rowCount(Qt::Vertical);
385         removeRows(0, rows, Qt::Vertical);
386         rows = rowCount(Qt::Horizontal);
387         removeRows(0, rows, Qt::Horizontal);
388         qDeleteAll(oldItems);
389     }
390 
391     QGridLayoutItem *itemAt(int row, int column, Qt::Orientation orientation = Qt::Vertical) const;
392     inline void insertRow(int row, Qt::Orientation orientation = Qt::Vertical)
393         { insertOrRemoveRows(row, +1, orientation); }
removeRows(int row,int count,Qt::Orientation orientation)394     inline void removeRows(int row, int count, Qt::Orientation orientation)
395         { insertOrRemoveRows(row, -count, orientation); }
396 
397     void invalidate();
398     void setGeometries(const QRectF &contentsGeometry, const QAbstractLayoutStyleInfo *styleInfo);
399     QRectF cellRect(const QRectF &contentsGeometry, int row, int column, int rowSpan, int columnSpan,
400                     const QAbstractLayoutStyleInfo *styleInfo) const;
401     QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint,
402                     const QAbstractLayoutStyleInfo *styleInfo) const;
403 
404     // heightForWidth / widthForHeight support
405     QSizeF dynamicallyConstrainedSizeHint(Qt::SizeHint which, const QSizeF &constraint) const;
406     bool ensureDynamicConstraint() const;
407     bool hasDynamicConstraint() const;
408     Qt::Orientation constraintOrientation() const;
409 
410 
411     QLayoutPolicy::ControlTypes controlTypes(LayoutSide side) const;
412     void transpose();
413     void setVisualDirection(Qt::LayoutDirection direction);
414     Qt::LayoutDirection visualDirection() const;
415 #ifdef QGRIDLAYOUTENGINE_DEBUG
416     void dump(int indent = 0) const;
417 #endif
418 
419 private:
grossRoundUp(int n)420     static int grossRoundUp(int n) { return ((n + 2) | 0x3) - 2; }
421 
422     void maybeExpandGrid(int row, int column, Qt::Orientation orientation = Qt::Vertical);
423     void regenerateGrid();
internalGridRowCount()424     inline int internalGridRowCount() const { return grossRoundUp(rowCount()); }
internalGridColumnCount()425     inline int internalGridColumnCount() const { return grossRoundUp(columnCount()); }
426     void setItemAt(int row, int column, QGridLayoutItem *item);
427     void insertOrRemoveRows(int row, int delta, Qt::Orientation orientation = Qt::Vertical);
428     void fillRowData(QGridLayoutRowData *rowData,
429                      const qreal *colPositions, const qreal *colSizes,
430                      Qt::Orientation orientation,
431                      const QAbstractLayoutStyleInfo *styleInfo) const;
432     void ensureEffectiveFirstAndLastRows() const;
433     void ensureColumnAndRowData(QGridLayoutRowData *rowData, QGridLayoutBox *totalBox,
434                                             const qreal *colPositions, const qreal *colSizes,
435                                             Qt::Orientation orientation,
436                                             const QAbstractLayoutStyleInfo *styleInfo) const;
437 
438     void ensureGeometries(const QSizeF &size, const QAbstractLayoutStyleInfo *styleInfo) const;
439 protected:
440     QList<QGridLayoutItem *> q_items;
441 private:
442     // User input
443     QVector<QGridLayoutItem *> q_grid;
444     QLayoutParameter<qreal> q_defaultSpacings[NOrientations];
445     QGridLayoutRowInfo q_infos[NOrientations];
446     Qt::LayoutDirection m_visualDirection;
447 
448     // Configuration
449     Qt::Alignment m_defaultAlignment;
450     unsigned m_snapToPixelGrid : 1;
451 
452     // Lazily computed from the above user input
453     mutable int q_cachedEffectiveFirstRows[NOrientations];
454     mutable int q_cachedEffectiveLastRows[NOrientations];
455     mutable quint8 q_cachedConstraintOrientation : 3;
456 
457     // this is useful to cache
458     mutable QGridLayoutBox q_totalBoxes[NOrientations];
459     enum {
460         NotCached = -2,             // Cache is empty. Happens when the engine is invalidated.
461         CachedWithNoConstraint = -1 // cache has a totalBox without any HFW/WFH constraints.
462         // >= 0                     // cache has a totalBox with this specific constraint.
463     };
464     mutable qreal q_totalBoxCachedConstraints[NOrientations];   // holds the constraint used for the cached totalBox
465 
466     // Layout item input
467     mutable QGridLayoutRowData q_columnData;
468     mutable QGridLayoutRowData q_rowData;
469 
470     // Output
471     mutable QSizeF q_cachedSize;
472     mutable QVector<qreal> q_xx;
473     mutable QVector<qreal> q_yy;
474     mutable QVector<qreal> q_widths;
475     mutable QVector<qreal> q_heights;
476     mutable QVector<qreal> q_descents;
477 
478     friend class QGridLayoutItem;
479 };
480 
481 QT_END_NAMESPACE
482 
483 #endif
484