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
69QT_BEGIN_NAMESPACE
70
71class QStyle;
72class QWidget;
73
74// ### deal with Descent in a similar way
75enum {
76 MinimumSize = Qt::MinimumSize,
77 PreferredSize = Qt::PreferredSize,
78 MaximumSize = Qt::MaximumSize,
79 NSizes
80};
81
82// do not reorder
83enum {
84 Hor,
85 Ver,
86 NOrientations
87};
88
89// do not reorder
90enum LayoutSide {
91 Left,
92 Top,
93 Right,
94 Bottom
95};
96
97enum {
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
105template <typename T>
106class QLayoutParameter
107{
108public:
109 enum State { Default, User, Cached };
110
111 inline QLayoutParameter() : q_value(T()), q_state(Default) {}
112 inline QLayoutParameter(T value, State state = Default) : q_value(value), q_state(state) {}
113
114 inline void setUserValue(T value) {
115 q_value = value;
116 q_state = User;
117 }
118 inline void setCachedValue(T value) const {
119 if (q_state != User) {
120 q_value = value;
121 q_state = Cached;
122 }
123 }
124 inline T value() const { return q_value; }
125 inline T value(T defaultValue) const { return isUser() ? q_value : defaultValue; }
126 inline bool isDefault() const { return q_state == Default; }
127 inline bool isUser() const { return q_state == User; }
128 inline bool isCached() const { return q_state == Cached; }
129
130private:
131 mutable T q_value;
132 mutable State q_state;
133};
134
135class QStretchParameter : public QLayoutParameter<int>
136{
137public:
138 QStretchParameter() : QLayoutParameter<int>(-1) {}
139
140};
141
142class Q_GUI_EXPORT QGridLayoutBox
143{
144public:
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;
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 }
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};
214Q_DECLARE_TYPEINFO(QGridLayoutBox, Q_MOVABLE_TYPE); // cannot be Q_PRIMITIVE_TYPE, as q_maximumSize, say, is != 0
215
216bool operator==(const QGridLayoutBox &box1, const QGridLayoutBox &box2);
217inline bool operator!=(const QGridLayoutBox &box1, const QGridLayoutBox &box2)
218 { return !operator==(box1, box2); }
219
220class QGridLayoutMultiCellData
221{
222public:
223 inline QGridLayoutMultiCellData() : q_stretch(-1) {}
224
225 QGridLayoutBox q_box;
226 int q_stretch;
227};
228
229typedef QMap<QPair<int, int>, QGridLayoutMultiCellData> MultiCellMap;
230
231class QGridLayoutRowInfo;
232
233class QGridLayoutRowData
234{
235public:
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
256class QGridLayoutRowInfo
257{
258public:
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
275class Q_GUI_EXPORT QGridLayoutItem
276{
277public:
278 QGridLayoutItem(int row, int column, int rowSpan = 1, int columnSpan = 1,
279 Qt::Alignment alignment = nullptr);
280 virtual ~QGridLayoutItem() {}
281
282 inline int firstRow() const { return q_firstRows[Ver]; }
283 inline int firstColumn() const { return q_firstRows[Hor]; }
284 inline int rowSpan() const { return q_rowSpans[Ver]; }
285 inline int columnSpan() const { return q_rowSpans[Hor]; }
286 inline int lastRow() const { return firstRow() + rowSpan() - 1; }
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
301 inline Qt::Alignment alignment() const { return q_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;
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 */
313 virtual bool hasDynamicConstraint() const { return false; }
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
331private:
332 int q_firstRows[NOrientations];
333 int q_rowSpans[NOrientations];
334 int q_stretches[NOrientations];
335 Qt::Alignment q_alignment;
336
337};
338
339class Q_GUI_EXPORT QGridLayoutEngine
340{
341public:
342 QGridLayoutEngine(Qt::Alignment defaultAlignment = Qt::Alignment(nullptr), bool snapToPixelGrid = false);
343 inline ~QGridLayoutEngine() { qDeleteAll(q_items); }
344
345 int rowCount(Qt::Orientation orientation) const;
346 int columnCount(Qt::Orientation orientation) const;
347 inline int rowCount() const { return q_infos[Ver].count; }
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);
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); }
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
419private:
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();
424 inline int internalGridRowCount() const { return grossRoundUp(rowCount()); }
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;
439protected:
440 QList<QGridLayoutItem *> q_items;
441private:
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
481QT_END_NAMESPACE
482
483#endif
484