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/*!
5 \class QGraphicsGridLayout
6 \brief The QGraphicsGridLayout class provides a grid layout for managing
7 widgets in Graphics View.
8 \since 4.4
9
10 \ingroup graphicsview-api
11 \inmodule QtWidgets
12
13 The most common way to use QGraphicsGridLayout is to construct an object
14 on the heap, passing a parent widget to the constructor, then add widgets
15 and layouts by calling addItem(). QGraphicsGridLayout automatically computes
16 the dimensions of the grid as you add items.
17
18 \snippet code/src_gui_graphicsview_qgraphicsgridlayout.cpp 0
19
20 Alternatively, if you do not pass a parent widget to the layout's constructor,
21 you will need to call QGraphicsWidget::setLayout() to set this layout as the
22 top-level layout for that widget, the widget will take ownership of the layout.
23
24 The layout takes ownership of the items. In some cases when the layout
25 item also inherits from QGraphicsItem (such as QGraphicsWidget) there will be a
26 ambiguity in ownership because the layout item belongs to two ownership hierarchies.
27 See the documentation of QGraphicsLayoutItem::setOwnedByLayout() how to handle
28 this.
29 You can access each item in the layout by calling count() and itemAt(). Calling
30 removeAt() will remove an item from the layout, without
31 destroying it.
32
33 \section1 Size Hints and Size Policies in QGraphicsGridLayout
34
35 QGraphicsGridLayout respects each item's size hints and size policies,
36 and when a cell in the grid has more space than the items can fill, each item
37 is arranged according to the layout's alignment for that item. You can set
38 an alignment for each item by calling setAlignment(), and check the
39 alignment for any item by calling alignment(). You can also set the alignment
40 for an entire row or column by calling setRowAlignment() and setColumnAlignment()
41 respectively. By default, items are aligned to the top left.
42
43
44 \sa QGraphicsLinearLayout, QGraphicsWidget
45*/
46
47#include "qglobal.h"
48
49#include "qapplication.h"
50#include "qwidget.h"
51#include "qgraphicslayout_p.h"
52#include "qgraphicslayoutitem.h"
53#include "qgraphicsgridlayout.h"
54#include "qgraphicswidget.h"
55#include "qgraphicsgridlayoutengine_p.h"
56#include "qgraphicslayoutstyleinfo_p.h"
57#include "qscopedpointer.h"
58#ifdef QT_DEBUG
59# include <QtCore/qdebug.h>
60#endif
61
62QT_BEGIN_NAMESPACE
63
64class QGraphicsGridLayoutPrivate : public QGraphicsLayoutPrivate
65{
66public:
67 QGraphicsGridLayoutPrivate() { }
68 QGraphicsLayoutStyleInfo *styleInfo() const;
69
70 mutable QScopedPointer<QGraphicsLayoutStyleInfo> m_styleInfo;
71 QGraphicsGridLayoutEngine engine;
72
73#ifdef QGRIDLAYOUTENGINE_DEBUG
74 void dump(int indent) const;
75#endif
76};
77
78
79QGraphicsLayoutStyleInfo *QGraphicsGridLayoutPrivate::styleInfo() const
80{
81 if (!m_styleInfo)
82 m_styleInfo.reset(other: new QGraphicsLayoutStyleInfo(this));
83 return m_styleInfo.data();
84}
85
86/*!
87 Constructs a QGraphicsGridLayout instance. \a parent is passed to
88 QGraphicsLayout's constructor.
89*/
90QGraphicsGridLayout::QGraphicsGridLayout(QGraphicsLayoutItem *parent)
91 : QGraphicsLayout(*new QGraphicsGridLayoutPrivate(), parent)
92{
93}
94
95/*!
96 Destroys the QGraphicsGridLayout object.
97*/
98QGraphicsGridLayout::~QGraphicsGridLayout()
99{
100 for (int i = count() - 1; i >= 0; --i) {
101 QGraphicsLayoutItem *item = itemAt(index: i);
102 // The following lines can be removed, but this removes the item
103 // from the layout more efficiently than the implementation of
104 // ~QGraphicsLayoutItem.
105 removeAt(index: i);
106 if (item) {
107 item->setParentLayoutItem(nullptr);
108 if (item->ownedByLayout())
109 delete item;
110 }
111 }
112}
113
114/*!
115 Adds \a item to the grid on \a row and \a column. You can specify a
116 \a rowSpan and \a columnSpan and an optional \a alignment.
117*/
118void QGraphicsGridLayout::addItem(QGraphicsLayoutItem *item, int row, int column,
119 int rowSpan, int columnSpan, Qt::Alignment alignment)
120{
121 Q_D(QGraphicsGridLayout);
122 if (row < 0 || column < 0) {
123 qWarning(msg: "QGraphicsGridLayout::addItem: invalid row/column: %d",
124 row < 0 ? row : column);
125 return;
126 }
127 if (columnSpan < 1 || rowSpan < 1) {
128 qWarning(msg: "QGraphicsGridLayout::addItem: invalid row span/column span: %d",
129 rowSpan < 1 ? rowSpan : columnSpan);
130 return;
131 }
132 if (!item) {
133 qWarning(msg: "QGraphicsGridLayout::addItem: cannot add null item");
134 return;
135 }
136 if (item == this) {
137 qWarning(msg: "QGraphicsGridLayout::addItem: cannot insert itself");
138 return;
139 }
140
141 d->addChildLayoutItem(item);
142
143 QGraphicsGridLayoutEngineItem *gridEngineItem = new QGraphicsGridLayoutEngineItem(item, row, column, rowSpan, columnSpan, alignment);
144 d->engine.insertItem(item: gridEngineItem, index: -1);
145 invalidate();
146}
147
148/*!
149 \fn QGraphicsGridLayout::addItem(QGraphicsLayoutItem *item, int row, int column, Qt::Alignment alignment = 0)
150
151 Adds \a item to the grid on \a row and \a column. You can specify
152 an optional \a alignment for \a item.
153*/
154
155/*!
156 Sets the default horizontal spacing for the grid layout to \a spacing.
157*/
158void QGraphicsGridLayout::setHorizontalSpacing(qreal spacing)
159{
160 Q_D(QGraphicsGridLayout);
161 d->engine.setSpacing(spacing, orientations: Qt::Horizontal);
162 invalidate();
163}
164
165/*!
166 Returns the default horizontal spacing for the grid layout.
167*/
168qreal QGraphicsGridLayout::horizontalSpacing() const
169{
170 Q_D(const QGraphicsGridLayout);
171 return d->engine.spacing(orientation: Qt::Horizontal, styleInfo: d->styleInfo());
172}
173
174/*!
175 Sets the default vertical spacing for the grid layout to \a spacing.
176*/
177void QGraphicsGridLayout::setVerticalSpacing(qreal spacing)
178{
179 Q_D(QGraphicsGridLayout);
180 d->engine.setSpacing(spacing, orientations: Qt::Vertical);
181 invalidate();
182}
183
184/*!
185 Returns the default vertical spacing for the grid layout.
186*/
187qreal QGraphicsGridLayout::verticalSpacing() const
188{
189 Q_D(const QGraphicsGridLayout);
190 return d->engine.spacing(orientation: Qt::Vertical, styleInfo: d->styleInfo());
191}
192
193/*!
194 Sets the grid layout's default spacing, both vertical and
195 horizontal, to \a spacing.
196
197 \sa rowSpacing(), columnSpacing()
198*/
199void QGraphicsGridLayout::setSpacing(qreal spacing)
200{
201 Q_D(QGraphicsGridLayout);
202 d->engine.setSpacing(spacing, orientations: Qt::Horizontal | Qt::Vertical);
203 invalidate();
204}
205
206/*!
207 Sets the spacing for \a row to \a spacing.
208*/
209void QGraphicsGridLayout::setRowSpacing(int row, qreal spacing)
210{
211 Q_D(QGraphicsGridLayout);
212 d->engine.setRowSpacing(row, spacing, orientation: Qt::Vertical);
213 invalidate();
214}
215
216/*!
217 Returns the row spacing for \a row.
218*/
219qreal QGraphicsGridLayout::rowSpacing(int row) const
220{
221 Q_D(const QGraphicsGridLayout);
222 return d->engine.rowSpacing(row, orientation: Qt::Vertical);
223}
224
225/*!
226 Sets the spacing for \a column to \a spacing.
227*/
228void QGraphicsGridLayout::setColumnSpacing(int column, qreal spacing)
229{
230 Q_D(QGraphicsGridLayout);
231 d->engine.setRowSpacing(row: column, spacing, orientation: Qt::Horizontal);
232 invalidate();
233}
234
235/*!
236 Returns the column spacing for \a column.
237*/
238qreal QGraphicsGridLayout::columnSpacing(int column) const
239{
240 Q_D(const QGraphicsGridLayout);
241 return d->engine.rowSpacing(row: column, orientation: Qt::Horizontal);
242}
243
244/*!
245 Sets the stretch factor for \a row to \a stretch.
246*/
247void QGraphicsGridLayout::setRowStretchFactor(int row, int stretch)
248{
249 Q_D(QGraphicsGridLayout);
250 d->engine.setRowStretchFactor(row, stretch, orientation: Qt::Vertical);
251 invalidate();
252}
253
254/*!
255 Returns the stretch factor for \a row.
256*/
257int QGraphicsGridLayout::rowStretchFactor(int row) const
258{
259 Q_D(const QGraphicsGridLayout);
260 return d->engine.rowStretchFactor(row, orientation: Qt::Vertical);
261}
262
263/*!
264 Sets the stretch factor for \a column to \a stretch.
265*/
266void QGraphicsGridLayout::setColumnStretchFactor(int column, int stretch)
267{
268 Q_D(QGraphicsGridLayout);
269 d->engine.setRowStretchFactor(row: column, stretch, orientation: Qt::Horizontal);
270 invalidate();
271}
272
273/*!
274 Returns the stretch factor for \a column.
275*/
276int QGraphicsGridLayout::columnStretchFactor(int column) const
277{
278 Q_D(const QGraphicsGridLayout);
279 return d->engine.rowStretchFactor(row: column, orientation: Qt::Horizontal);
280}
281
282/*!
283 Sets the minimum height for row, \a row, to \a height.
284*/
285void QGraphicsGridLayout::setRowMinimumHeight(int row, qreal height)
286{
287 Q_D(QGraphicsGridLayout);
288 d->engine.setRowSizeHint(which: Qt::MinimumSize, row, size: height, orientation: Qt::Vertical);
289 invalidate();
290}
291
292/*!
293 Returns the minimum height for row, \a row.
294*/
295qreal QGraphicsGridLayout::rowMinimumHeight(int row) const
296{
297 Q_D(const QGraphicsGridLayout);
298 return d->engine.rowSizeHint(which: Qt::MinimumSize, row, orientation: Qt::Vertical);
299}
300
301/*!
302 Sets the preferred height for row, \a row, to \a height.
303*/
304void QGraphicsGridLayout::setRowPreferredHeight(int row, qreal height)
305{
306 Q_D(QGraphicsGridLayout);
307 d->engine.setRowSizeHint(which: Qt::PreferredSize, row, size: height, orientation: Qt::Vertical);
308 invalidate();
309}
310
311/*!
312 Returns the preferred height for row, \a row.
313*/
314qreal QGraphicsGridLayout::rowPreferredHeight(int row) const
315{
316 Q_D(const QGraphicsGridLayout);
317 return d->engine.rowSizeHint(which: Qt::PreferredSize, row, orientation: Qt::Vertical);
318}
319
320/*!
321 Sets the maximum height for row, \a row, to \a height.
322*/
323void QGraphicsGridLayout::setRowMaximumHeight(int row, qreal height)
324{
325 Q_D(QGraphicsGridLayout);
326 d->engine.setRowSizeHint(which: Qt::MaximumSize, row, size: height, orientation: Qt::Vertical);
327 invalidate();
328}
329
330/*!
331 Returns the maximum height for row, \a row.
332*/
333qreal QGraphicsGridLayout::rowMaximumHeight(int row) const
334{
335 Q_D(const QGraphicsGridLayout);
336 return d->engine.rowSizeHint(which: Qt::MaximumSize, row, orientation: Qt::Vertical);
337}
338
339/*!
340 Sets the fixed height for row, \a row, to \a height.
341*/
342void QGraphicsGridLayout::setRowFixedHeight(int row, qreal height)
343{
344 Q_D(QGraphicsGridLayout);
345 d->engine.setRowSizeHint(which: Qt::MinimumSize, row, size: height, orientation: Qt::Vertical);
346 d->engine.setRowSizeHint(which: Qt::MaximumSize, row, size: height, orientation: Qt::Vertical);
347 invalidate();
348}
349
350/*!
351 Sets the minimum width for \a column to \a width.
352*/
353void QGraphicsGridLayout::setColumnMinimumWidth(int column, qreal width)
354{
355 Q_D(QGraphicsGridLayout);
356 d->engine.setRowSizeHint(which: Qt::MinimumSize, row: column, size: width, orientation: Qt::Horizontal);
357 invalidate();
358}
359
360/*!
361 Returns the minimum width for \a column.
362*/
363qreal QGraphicsGridLayout::columnMinimumWidth(int column) const
364{
365 Q_D(const QGraphicsGridLayout);
366 return d->engine.rowSizeHint(which: Qt::MinimumSize, row: column, orientation: Qt::Horizontal);
367}
368
369/*!
370 Sets the preferred width for \a column to \a width.
371*/
372void QGraphicsGridLayout::setColumnPreferredWidth(int column, qreal width)
373{
374 Q_D(QGraphicsGridLayout);
375 d->engine.setRowSizeHint(which: Qt::PreferredSize, row: column, size: width, orientation: Qt::Horizontal);
376 invalidate();
377}
378
379/*!
380 Returns the preferred width for \a column.
381*/
382qreal QGraphicsGridLayout::columnPreferredWidth(int column) const
383{
384 Q_D(const QGraphicsGridLayout);
385 return d->engine.rowSizeHint(which: Qt::PreferredSize, row: column, orientation: Qt::Horizontal);
386}
387
388/*!
389 Sets the maximum width of \a column to \a width.
390*/
391void QGraphicsGridLayout::setColumnMaximumWidth(int column, qreal width)
392{
393 Q_D(QGraphicsGridLayout);
394 d->engine.setRowSizeHint(which: Qt::MaximumSize, row: column, size: width, orientation: Qt::Horizontal);
395 invalidate();
396}
397
398/*!
399 Returns the maximum width for \a column.
400*/
401qreal QGraphicsGridLayout::columnMaximumWidth(int column) const
402{
403 Q_D(const QGraphicsGridLayout);
404 return d->engine.rowSizeHint(which: Qt::MaximumSize, row: column, orientation: Qt::Horizontal);
405}
406
407/*!
408 Sets the fixed width of \a column to \a width.
409*/
410void QGraphicsGridLayout::setColumnFixedWidth(int column, qreal width)
411{
412 Q_D(QGraphicsGridLayout);
413 d->engine.setRowSizeHint(which: Qt::MinimumSize, row: column, size: width, orientation: Qt::Horizontal);
414 d->engine.setRowSizeHint(which: Qt::MaximumSize, row: column, size: width, orientation: Qt::Horizontal);
415 invalidate();
416}
417
418/*!
419 Sets the alignment of \a row to \a alignment.
420*/
421void QGraphicsGridLayout::setRowAlignment(int row, Qt::Alignment alignment)
422{
423 Q_D(QGraphicsGridLayout);
424 d->engine.setRowAlignment(row, alignment, orientation: Qt::Vertical);
425 invalidate();
426}
427
428/*!
429 Returns the alignment of \a row.
430*/
431Qt::Alignment QGraphicsGridLayout::rowAlignment(int row) const
432{
433 Q_D(const QGraphicsGridLayout);
434 return d->engine.rowAlignment(row, orientation: Qt::Vertical);
435}
436
437/*!
438 Sets the alignment for \a column to \a alignment.
439*/
440void QGraphicsGridLayout::setColumnAlignment(int column, Qt::Alignment alignment)
441{
442 Q_D(QGraphicsGridLayout);
443 d->engine.setRowAlignment(row: column, alignment, orientation: Qt::Horizontal);
444 invalidate();
445}
446
447/*!
448 Returns the alignment for \a column.
449*/
450Qt::Alignment QGraphicsGridLayout::columnAlignment(int column) const
451{
452 Q_D(const QGraphicsGridLayout);
453 return d->engine.rowAlignment(row: column, orientation: Qt::Horizontal);
454}
455
456/*!
457 Sets the alignment for \a item to \a alignment.
458*/
459void QGraphicsGridLayout::setAlignment(QGraphicsLayoutItem *item, Qt::Alignment alignment)
460{
461 Q_D(QGraphicsGridLayout);
462 d->engine.setAlignment(graphicsLayoutItem: item, alignment);
463 invalidate();
464}
465
466/*!
467 Returns the alignment for \a item.
468*/
469Qt::Alignment QGraphicsGridLayout::alignment(QGraphicsLayoutItem *item) const
470{
471 Q_D(const QGraphicsGridLayout);
472 return d->engine.alignment(graphicsLayoutItem: item);
473}
474
475/*!
476 Returns the number of rows in the grid layout. This is always one more
477 than the index of the last row that is occupied by a layout item (empty
478 rows are counted except for those at the end).
479*/
480int QGraphicsGridLayout::rowCount() const
481{
482 Q_D(const QGraphicsGridLayout);
483 return d->engine.effectiveLastRow(orientation: Qt::Vertical) + 1;
484}
485
486/*!
487 Returns the number of columns in the grid layout. This is always one more
488 than the index of the last column that is occupied by a layout item (empty
489 columns are counted except for those at the end).
490*/
491int QGraphicsGridLayout::columnCount() const
492{
493 Q_D(const QGraphicsGridLayout);
494 return d->engine.effectiveLastRow(orientation: Qt::Horizontal) + 1;
495}
496
497/*!
498 Returns a pointer to the layout item at (\a row, \a column).
499*/
500QGraphicsLayoutItem *QGraphicsGridLayout::itemAt(int row, int column) const
501{
502 Q_D(const QGraphicsGridLayout);
503 if (row < 0 || row >= rowCount() || column < 0 || column >= columnCount()) {
504 qWarning(msg: "QGraphicsGridLayout::itemAt: invalid row, column %d, %d", row, column);
505 return nullptr;
506 }
507 if (QGraphicsGridLayoutEngineItem *engineItem = static_cast<QGraphicsGridLayoutEngineItem*>(d->engine.itemAt(row, column)))
508 return engineItem->layoutItem();
509 return nullptr;
510}
511
512/*!
513 Returns the number of layout items in this grid layout.
514*/
515int QGraphicsGridLayout::count() const
516{
517 Q_D(const QGraphicsGridLayout);
518 return d->engine.itemCount();
519}
520
521/*!
522 Returns the layout item at \a index, or \nullptr if there is no
523 layout item at this index.
524*/
525QGraphicsLayoutItem *QGraphicsGridLayout::itemAt(int index) const
526{
527 Q_D(const QGraphicsGridLayout);
528 if (index < 0 || index >= d->engine.itemCount()) {
529 qWarning(msg: "QGraphicsGridLayout::itemAt: invalid index %d", index);
530 return nullptr;
531 }
532 if (QGraphicsGridLayoutEngineItem *engineItem = static_cast<QGraphicsGridLayoutEngineItem*>(d->engine.itemAt(index)))
533 return engineItem->layoutItem();
534 return nullptr;
535}
536
537/*!
538 Removes the layout item at \a index without destroying it. Ownership of
539 the item is transferred to the caller.
540
541 \sa addItem()
542*/
543void QGraphicsGridLayout::removeAt(int index)
544{
545 Q_D(QGraphicsGridLayout);
546 if (index < 0 || index >= d->engine.itemCount()) {
547 qWarning(msg: "QGraphicsGridLayout::removeAt: invalid index %d", index);
548 return;
549 }
550
551 if (QGraphicsGridLayoutEngineItem *gridItem = static_cast<QGraphicsGridLayoutEngineItem*>(d->engine.itemAt(index))) {
552 if (QGraphicsLayoutItem *layoutItem = gridItem->layoutItem())
553 layoutItem->setParentLayoutItem(nullptr);
554 d->engine.removeItem(item: gridItem);
555
556 // recalculate rowInfo.count if we remove an item that is on the right/bottommost row
557 for (const Qt::Orientation orient : {Qt::Horizontal, Qt::Vertical}) {
558 const int oldCount = d->engine.rowCount(orientation: orient);
559 if (gridItem->lastRow(orientation: orient) == oldCount - 1) {
560 const int newCount = d->engine.effectiveLastRow(orientation: orient) + 1;
561 d->engine.removeRows(row: newCount, count: oldCount - newCount, orientation: orient);
562 }
563 }
564
565 delete gridItem;
566 invalidate();
567 }
568}
569
570/*!
571 Removes the layout item \a item without destroying it.
572 Ownership of the item is transferred to the caller.
573
574 \sa addItem()
575*/
576void QGraphicsGridLayout::removeItem(QGraphicsLayoutItem *item)
577{
578 Q_D(QGraphicsGridLayout);
579 int index = d->engine.indexOf(item);
580 removeAt(index);
581}
582/*!
583 \reimp
584*/
585void QGraphicsGridLayout::invalidate()
586{
587 Q_D(QGraphicsGridLayout);
588 d->engine.invalidate();
589 if (d->m_styleInfo)
590 d->m_styleInfo->invalidate();
591 QGraphicsLayout::invalidate();
592}
593
594#ifdef QGRIDLAYOUTENGINE_DEBUG
595void QGraphicsGridLayoutPrivate::dump(int indent) const
596{
597 if (qt_graphicsLayoutDebug()) {
598 engine.dump(indent + 1);
599 }
600}
601#endif
602
603/*!
604 Sets the bounding geometry of the grid layout to \a rect.
605*/
606void QGraphicsGridLayout::setGeometry(const QRectF &rect)
607{
608 Q_D(QGraphicsGridLayout);
609 QGraphicsLayout::setGeometry(rect);
610 QRectF effectiveRect = geometry();
611 qreal left, top, right, bottom;
612 getContentsMargins(left: &left, top: &top, right: &right, bottom: &bottom);
613 Qt::LayoutDirection visualDir = d->visualDirection();
614 d->engine.setVisualDirection(visualDir);
615 if (visualDir == Qt::RightToLeft)
616 qSwap(value1&: left, value2&: right);
617 effectiveRect.adjust(xp1: +left, yp1: +top, xp2: -right, yp2: -bottom);
618 d->engine.setGeometries(contentsGeometry: effectiveRect, styleInfo: d->styleInfo());
619#ifdef QGRIDLAYOUTENGINE_DEBUG
620 if (qt_graphicsLayoutDebug()) {
621 static int counter = 0;
622 qDebug("==== BEGIN DUMP OF QGraphicsGridLayout (%d)====", counter++);
623 d->dump(1);
624 qDebug("==== END DUMP OF QGraphicsGridLayout ====");
625 }
626#endif
627}
628
629/*!
630 \reimp
631*/
632QSizeF QGraphicsGridLayout::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
633{
634 Q_D(const QGraphicsGridLayout);
635 qreal left, top, right, bottom;
636 getContentsMargins(left: &left, top: &top, right: &right, bottom: &bottom);
637 const QSizeF extraMargins(left + right, top + bottom);
638 return d->engine.sizeHint(which , constraint: constraint - extraMargins, styleInfo: d->styleInfo()) + extraMargins;
639}
640
641
642#if 0
643// ### kill? (implement and kill?)
644QRect QGraphicsGridLayout::cellRect(int row, int column, int rowSpan, int columnSpan) const
645{
646 Q_D(const QGraphicsGridLayout);
647 return QRect();
648// return d->engine.cellRect(parentLayoutable(), contentsGeometry(), row, column, rowSpan, columnSpan);
649}
650
651QSizePolicy::ControlTypes QGraphicsGridLayout::controlTypes(LayoutSide side) const
652{
653 Q_D(const QGraphicsGridLayout);
654 return d->engine.controlTypes(side);
655}
656#endif
657
658QT_END_NAMESPACE
659

source code of qtbase/src/widgets/graphicsview/qgraphicsgridlayout.cpp