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 QGraphicsLinearLayout
42 \brief The QGraphicsLinearLayout class provides a horizontal or vertical
43 layout for managing widgets in Graphics View.
44 \since 4.4
45 \ingroup graphicsview-api
46 \inmodule QtWidgets
47
48 The default orientation for a linear layout is Qt::Horizontal. You can
49 choose a vertical orientation either by calling setOrientation(), or by
50 passing Qt::Vertical to QGraphicsLinearLayout's constructor.
51
52 The most common way to use QGraphicsLinearLayout is to construct an object
53 on the heap with no parent, add widgets and layouts by calling addItem(),
54 and finally assign the layout to a widget by calling
55 QGraphicsWidget::setLayout().
56
57 \snippet code/src_gui_graphicsview_qgraphicslinearlayout.cpp 0
58
59 You can add widgets, layouts, stretches (addStretch(), insertStretch() or
60 setStretchFactor()), and spacings (setItemSpacing()) to a linear
61 layout. The layout takes ownership of the items. In some cases when the layout
62 item also inherits from QGraphicsItem (such as QGraphicsWidget) there will be a
63 ambiguity in ownership because the layout item belongs to two ownership hierarchies.
64 See the documentation of QGraphicsLayoutItem::setOwnedByLayout() how to handle
65 this.
66 You can access each item in the layout by calling count() and itemAt(). Calling
67 removeAt() or removeItem() will remove an item from the layout, without
68 destroying it.
69
70 \section1 Size Hints and Size Policies in QGraphicsLinearLayout
71
72 QGraphicsLinearLayout respects each item's size hints and size policies,
73 and when the layout contains more space than the items can fill, each item
74 is arranged according to the layout's alignment for that item. You can set
75 an alignment for each item by calling setAlignment(), and check the
76 alignment for any item by calling alignment(). By default, items are
77 aligned to the top left.
78
79 \section1 Spacing within QGraphicsLinearLayout
80
81 Between the items, the layout distributes some space. The actual amount of
82 space depends on the managed widget's current style, but the common
83 spacing is 4. You can also set your own spacing by calling setSpacing(),
84 and get the current spacing value by calling spacing(). If you want to
85 configure individual spacing for your items, you can call setItemSpacing().
86
87 \section1 Stretch Factor in QGraphicsLinearLayout
88
89 You can assign a stretch factor to each item to control how much space it
90 will get compared to the other items. By default, two identical widgets
91 arranged in a linear layout will have the same size, but if the first
92 widget has a stretch factor of 1 and the second widget has a stretch
93 factor of 2, the first widget will get 1/3 of the available space, and the
94 second will get 2/3.
95
96 QGraphicsLinearLayout calculates the distribution of sizes by adding up
97 the stretch factors of all items, and then dividing the available space
98 accordingly. The default stretch factor is 0 for all items; a factor of 0
99 means the item does not have any defined stretch factor; effectively this
100 is the same as setting the stretch factor to 1. The stretch factor only
101 applies to the available space in the lengthwise direction of the layout
102 (following its orientation). If you want to control both the item's
103 horizontal and vertical stretch, you can use QGraphicsGridLayout instead.
104
105 \section1 QGraphicsLinearLayout Compared to Other Layouts
106
107 QGraphicsLinearLayout is very similar to QVBoxLayout and QHBoxLayout, but
108 in contrast to these classes, it is used to manage QGraphicsWidget and
109 QGraphicsLayout instead of QWidget and QLayout.
110
111 \sa QGraphicsGridLayout, QGraphicsWidget
112*/
113
114#include "qapplication.h"
115
116#include "qwidget.h"
117#include "qgraphicslayout_p.h"
118#include "qgraphicslayoutitem.h"
119#include "qgraphicslinearlayout.h"
120#include "qgraphicswidget.h"
121#include "qgraphicsgridlayoutengine_p.h"
122#include "qgraphicslayoutstyleinfo_p.h"
123#include "qscopedpointer.h"
124#ifdef QT_DEBUG
125#include <QtCore/qdebug.h>
126#endif
127
128QT_BEGIN_NAMESPACE
129
130class QGraphicsLinearLayoutPrivate : public QGraphicsLayoutPrivate
131{
132public:
133 QGraphicsLinearLayoutPrivate(Qt::Orientation orientation)
134 : orientation(orientation)
135 { }
136
137 void removeGridItem(QGridLayoutItem *gridItem);
138 QGraphicsLayoutStyleInfo *styleInfo() const;
139 void fixIndex(int *index) const;
140 int gridRow(int index) const;
141 int gridColumn(int index) const;
142
143 Qt::Orientation orientation;
144 mutable QScopedPointer<QGraphicsLayoutStyleInfo> m_styleInfo;
145 QGraphicsGridLayoutEngine engine;
146};
147
148void QGraphicsLinearLayoutPrivate::removeGridItem(QGridLayoutItem *gridItem)
149{
150 int index = gridItem->firstRow(orientation);
151 engine.removeItem(gridItem);
152 engine.removeRows(index, 1, orientation);
153}
154
155void QGraphicsLinearLayoutPrivate::fixIndex(int *index) const
156{
157 int count = engine.rowCount(orientation);
158 if (uint(*index) > uint(count))
159 *index = count;
160}
161
162int QGraphicsLinearLayoutPrivate::gridRow(int index) const
163{
164 if (orientation == Qt::Horizontal)
165 return 0;
166 return int(qMin(uint(index), uint(engine.rowCount())));
167}
168
169int QGraphicsLinearLayoutPrivate::gridColumn(int index) const
170{
171 if (orientation == Qt::Vertical)
172 return 0;
173 return int(qMin(uint(index), uint(engine.columnCount())));
174}
175
176QGraphicsLayoutStyleInfo *QGraphicsLinearLayoutPrivate::styleInfo() const
177{
178 if (!m_styleInfo)
179 m_styleInfo.reset(new QGraphicsLayoutStyleInfo(this));
180 return m_styleInfo.data();
181}
182
183/*!
184 Constructs a QGraphicsLinearLayout instance. You can pass the
185 \a orientation for the layout, either horizontal or vertical, and
186 \a parent is passed to QGraphicsLayout's constructor.
187*/
188QGraphicsLinearLayout::QGraphicsLinearLayout(Qt::Orientation orientation, QGraphicsLayoutItem *parent)
189 : QGraphicsLayout(*new QGraphicsLinearLayoutPrivate(orientation), parent)
190{
191}
192
193/*!
194 Constructs a QGraphicsLinearLayout instance using Qt::Horizontal
195 orientation. \a parent is passed to QGraphicsLayout's constructor.
196*/
197QGraphicsLinearLayout::QGraphicsLinearLayout(QGraphicsLayoutItem *parent)
198 : QGraphicsLayout(*new QGraphicsLinearLayoutPrivate(Qt::Horizontal), parent)
199{
200}
201
202/*!
203 Destroys the QGraphicsLinearLayout object.
204*/
205QGraphicsLinearLayout::~QGraphicsLinearLayout()
206{
207 for (int i = count() - 1; i >= 0; --i) {
208 QGraphicsLayoutItem *item = itemAt(i);
209 // The following lines can be removed, but this removes the item
210 // from the layout more efficiently than the implementation of
211 // ~QGraphicsLayoutItem.
212 removeAt(i);
213 if (item) {
214 item->setParentLayoutItem(nullptr);
215 if (item->ownedByLayout())
216 delete item;
217 }
218 }
219}
220
221/*!
222 Change the layout orientation to \a orientation. Changing the layout
223 orientation will automatically invalidate the layout.
224
225 \sa orientation()
226*/
227void QGraphicsLinearLayout::setOrientation(Qt::Orientation orientation)
228{
229 Q_D(QGraphicsLinearLayout);
230 if (orientation != d->orientation) {
231 d->engine.transpose();
232 d->orientation = orientation;
233 invalidate();
234 }
235}
236
237/*!
238 Returns the layout orientation.
239 \sa setOrientation()
240 */
241Qt::Orientation QGraphicsLinearLayout::orientation() const
242{
243 Q_D(const QGraphicsLinearLayout);
244 return d->orientation;
245}
246
247/*!
248 \fn void QGraphicsLinearLayout::addItem(QGraphicsLayoutItem *item)
249
250 This convenience function is equivalent to calling
251 insertItem(-1, \a item).
252*/
253
254/*!
255 \fn void QGraphicsLinearLayout::addStretch(int stretch)
256
257 This convenience function is equivalent to calling
258 insertStretch(-1, \a stretch).
259*/
260
261/*!
262 Inserts \a item into the layout at \a index, or before any item that is
263 currently at \a index.
264
265 \sa addItem(), itemAt(), insertStretch(), setItemSpacing()
266*/
267void QGraphicsLinearLayout::insertItem(int index, QGraphicsLayoutItem *item)
268{
269 Q_D(QGraphicsLinearLayout);
270 if (!item) {
271 qWarning("QGraphicsLinearLayout::insertItem: cannot insert null item");
272 return;
273 }
274 if (item == this) {
275 qWarning("QGraphicsLinearLayout::insertItem: cannot insert itself");
276 return;
277 }
278 d->addChildLayoutItem(item);
279
280 Q_ASSERT(item);
281 d->fixIndex(&index);
282 d->engine.insertRow(index, d->orientation);
283 QGraphicsGridLayoutEngineItem *gridEngineItem = new QGraphicsGridLayoutEngineItem(item, d->gridRow(index), d->gridColumn(index), 1, 1, { });
284 d->engine.insertItem(gridEngineItem, index);
285 invalidate();
286}
287
288/*!
289 Inserts a stretch of \a stretch at \a index, or before any item that is
290 currently at \a index.
291
292 \sa addStretch(), setStretchFactor(), setItemSpacing(), insertItem()
293*/
294void QGraphicsLinearLayout::insertStretch(int index, int stretch)
295{
296 Q_D(QGraphicsLinearLayout);
297 d->fixIndex(&index);
298 d->engine.insertRow(index, d->orientation);
299 d->engine.setRowStretchFactor(index, stretch, d->orientation);
300 invalidate();
301}
302
303/*!
304 Removes \a item from the layout without destroying it. Ownership of
305 \a item is transferred to the caller.
306
307 \sa removeAt(), insertItem()
308*/
309void QGraphicsLinearLayout::removeItem(QGraphicsLayoutItem *item)
310{
311 Q_D(QGraphicsLinearLayout);
312 if (QGraphicsGridLayoutEngineItem *gridItem = d->engine.findLayoutItem(item)) {
313 item->setParentLayoutItem(nullptr);
314 d->removeGridItem(gridItem);
315 delete gridItem;
316 invalidate();
317 }
318}
319
320/*!
321 Removes the item at \a index without destroying it. Ownership of the item
322 is transferred to the caller.
323
324 \sa removeItem(), insertItem()
325*/
326void QGraphicsLinearLayout::removeAt(int index)
327{
328 Q_D(QGraphicsLinearLayout);
329 if (index < 0 || index >= d->engine.itemCount()) {
330 qWarning("QGraphicsLinearLayout::removeAt: invalid index %d", index);
331 return;
332 }
333
334 if (QGraphicsGridLayoutEngineItem *gridItem = static_cast<QGraphicsGridLayoutEngineItem*>(d->engine.itemAt(index))) {
335 if (QGraphicsLayoutItem *layoutItem = gridItem->layoutItem())
336 layoutItem->setParentLayoutItem(nullptr);
337 d->removeGridItem(gridItem);
338 delete gridItem;
339 invalidate();
340 }
341}
342
343/*!
344 Sets the layout's spacing to \a spacing. Spacing refers to the
345 vertical and horizontal distances between items.
346
347 \sa setItemSpacing(), setStretchFactor(), QGraphicsGridLayout::setSpacing()
348*/
349void QGraphicsLinearLayout::setSpacing(qreal spacing)
350{
351 Q_D(QGraphicsLinearLayout);
352 if (spacing < 0) {
353 qWarning("QGraphicsLinearLayout::setSpacing: invalid spacing %g", spacing);
354 return;
355 }
356 d->engine.setSpacing(spacing, Qt::Horizontal | Qt::Vertical);
357 invalidate();
358}
359
360/*!
361 Returns the layout's spacing. Spacing refers to the
362 vertical and horizontal distances between items.
363
364 \sa setSpacing()
365 */
366qreal QGraphicsLinearLayout::spacing() const
367{
368 Q_D(const QGraphicsLinearLayout);
369 return d->engine.spacing(d->orientation, d->styleInfo());
370}
371
372/*!
373 Sets the spacing after item at \a index to \a spacing.
374*/
375void QGraphicsLinearLayout::setItemSpacing(int index, qreal spacing)
376{
377 Q_D(QGraphicsLinearLayout);
378 d->engine.setRowSpacing(index, spacing, d->orientation);
379 invalidate();
380}
381/*!
382 Returns the spacing after item at \a index.
383*/
384qreal QGraphicsLinearLayout::itemSpacing(int index) const
385{
386 Q_D(const QGraphicsLinearLayout);
387 return d->engine.rowSpacing(index, d->orientation);
388}
389
390/*!
391 Sets the stretch factor for \a item to \a stretch. If an item's stretch
392 factor changes, this function will invalidate the layout.
393
394 Setting \a stretch to 0 removes the stretch factor from the item, and is
395 effectively equivalent to setting \a stretch to 1.
396
397 \sa stretchFactor()
398*/
399void QGraphicsLinearLayout::setStretchFactor(QGraphicsLayoutItem *item, int stretch)
400{
401 Q_D(QGraphicsLinearLayout);
402 if (!item) {
403 qWarning("QGraphicsLinearLayout::setStretchFactor: cannot assign"
404 " a stretch factor to a null item");
405 return;
406 }
407 if (stretchFactor(item) == stretch)
408 return;
409 d->engine.setStretchFactor(item, stretch, d->orientation);
410 invalidate();
411}
412
413/*!
414 Returns the stretch factor for \a item. The default stretch factor is 0,
415 meaning that the item has no assigned stretch factor.
416
417 \sa setStretchFactor()
418*/
419int QGraphicsLinearLayout::stretchFactor(QGraphicsLayoutItem *item) const
420{
421 Q_D(const QGraphicsLinearLayout);
422 if (!item) {
423 qWarning("QGraphicsLinearLayout::setStretchFactor: cannot return"
424 " a stretch factor for a null item");
425 return 0;
426 }
427 return d->engine.stretchFactor(item, d->orientation);
428}
429
430/*!
431 Sets the alignment of \a item to \a alignment. If \a item's alignment
432 changes, the layout is automatically invalidated.
433
434 \sa alignment(), invalidate()
435*/
436void QGraphicsLinearLayout::setAlignment(QGraphicsLayoutItem *item, Qt::Alignment alignment)
437{
438 Q_D(QGraphicsLinearLayout);
439 if (this->alignment(item) == alignment)
440 return;
441 d->engine.setAlignment(item, alignment);
442 invalidate();
443}
444
445/*!
446 Returns the alignment for \a item. The default alignment is
447 Qt::AlignTop | Qt::AlignLeft.
448
449 The alignment decides how the item is positioned within its assigned space
450 in the case where there's more space available in the layout than the
451 widgets can occupy.
452
453 \sa setAlignment()
454*/
455Qt::Alignment QGraphicsLinearLayout::alignment(QGraphicsLayoutItem *item) const
456{
457 Q_D(const QGraphicsLinearLayout);
458 return d->engine.alignment(item);
459}
460
461#if 0 // ###
462QSizePolicy::ControlTypes QGraphicsLinearLayout::controlTypes(LayoutSide side) const
463{
464 return d->engine.controlTypes(side);
465}
466#endif
467
468/*!
469 \reimp
470*/
471int QGraphicsLinearLayout::count() const
472{
473 Q_D(const QGraphicsLinearLayout);
474 return d->engine.itemCount();
475}
476
477/*!
478 \reimp
479 When iterating from 0 and up, it will return the items in the visual arranged order.
480*/
481QGraphicsLayoutItem *QGraphicsLinearLayout::itemAt(int index) const
482{
483 Q_D(const QGraphicsLinearLayout);
484 if (index < 0 || index >= d->engine.itemCount()) {
485 qWarning("QGraphicsLinearLayout::itemAt: invalid index %d", index);
486 return nullptr;
487 }
488 QGraphicsLayoutItem *item = nullptr;
489 if (QGraphicsGridLayoutEngineItem *gridItem = static_cast<QGraphicsGridLayoutEngineItem *>(d->engine.itemAt(index)))
490 item = gridItem->layoutItem();
491 return item;
492}
493
494/*!
495 \reimp
496*/
497void QGraphicsLinearLayout::setGeometry(const QRectF &rect)
498{
499 Q_D(QGraphicsLinearLayout);
500 QGraphicsLayout::setGeometry(rect);
501 QRectF effectiveRect = geometry();
502 qreal left, top, right, bottom;
503 getContentsMargins(&left, &top, &right, &bottom);
504 Qt::LayoutDirection visualDir = d->visualDirection();
505 d->engine.setVisualDirection(visualDir);
506 if (visualDir == Qt::RightToLeft)
507 qSwap(left, right);
508 effectiveRect.adjust(+left, +top, -right, -bottom);
509#ifdef QGRIDLAYOUTENGINE_DEBUG
510 if (qt_graphicsLayoutDebug()) {
511 static int counter = 0;
512 qDebug() << counter++ << "QGraphicsLinearLayout::setGeometry - " << rect;
513 dump(1);
514 }
515#endif
516 d->engine.setGeometries(effectiveRect, d->styleInfo());
517#ifdef QGRIDLAYOUTENGINE_DEBUG
518 if (qt_graphicsLayoutDebug()) {
519 qDebug("post dump");
520 dump(1);
521 }
522#endif
523}
524
525/*!
526 \reimp
527*/
528QSizeF QGraphicsLinearLayout::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
529{
530 Q_D(const QGraphicsLinearLayout);
531 qreal left, top, right, bottom;
532 getContentsMargins(&left, &top, &right, &bottom);
533 const QSizeF extraMargins(left + right, top + bottom);
534 return d->engine.sizeHint(which , constraint - extraMargins, d->styleInfo()) + extraMargins;
535}
536
537/*!
538 \reimp
539*/
540void QGraphicsLinearLayout::invalidate()
541{
542 Q_D(QGraphicsLinearLayout);
543 d->engine.invalidate();
544 if (d->m_styleInfo)
545 d->m_styleInfo->invalidate();
546 QGraphicsLayout::invalidate();
547}
548
549/*!
550 \internal
551*/
552void QGraphicsLinearLayout::dump(int indent) const
553{
554#ifdef QGRIDLAYOUTENGINE_DEBUG
555 if (qt_graphicsLayoutDebug()) {
556 Q_D(const QGraphicsLinearLayout);
557 qDebug("%*s%s layout", indent, "",
558 d->orientation == Qt::Horizontal ? "Horizontal" : "Vertical");
559 d->engine.dump(indent + 1);
560 }
561#else
562 Q_UNUSED(indent);
563#endif
564}
565
566QT_END_NAMESPACE
567