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

source code of qtbase/src/gui/util/qgridlayoutengine_p.h