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