Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Toggle main menu visibility
Loading...
Searching...
No Matches
qgraphicslayout.cpp
Go to the documentation of this file.
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
// Qt-Security score:significant reason:default
4
5
#
include
"qapplication.h"
6
7
#
include
"qgraphicslayout.h"
8
#
include
"qgraphicslayout_p.h"
9
#
include
"qgraphicslayoutitem.h"
10
#
include
"qgraphicslayoutitem_p.h"
11
#
include
"qgraphicswidget.h"
12
#
include
"qgraphicswidget_p.h"
13
#
include
"qgraphicsscene.h"
14
15
QT_BEGIN_NAMESPACE
16
17
/*!
18
\class QGraphicsLayout
19
\brief The QGraphicsLayout class provides the base class for all layouts
20
in Graphics View.
21
\since 4.4
22
\ingroup graphicsview-api
23
\inmodule QtWidgets
24
25
QGraphicsLayout is an abstract class that defines a virtual API for
26
arranging QGraphicsWidget children and other QGraphicsLayoutItem objects
27
for a QGraphicsWidget. QGraphicsWidget assigns responsibility to a
28
QGraphicsLayout through QGraphicsWidget::setLayout(). As the widget
29
is resized, the layout will automatically arrange the widget's children.
30
QGraphicsLayout inherits QGraphicsLayoutItem, so, it can be managed by
31
any layout, including its own subclasses.
32
33
\section1 Writing a Custom Layout
34
35
You can use QGraphicsLayout as a base to write your own custom layout
36
(e.g., a flowlayout), but it is more common to use one of its subclasses
37
instead - QGraphicsLinearLayout or QGraphicsGridLayout. When creating
38
a custom layout, the following functions must be reimplemented as a bare
39
minimum:
40
41
\table
42
\header \li Function \li Description
43
\row \li QGraphicsLayoutItem::setGeometry()
44
\li Notifies you when the geometry of the layout is set. You can
45
store the geometry in your own layout class in a reimplementation
46
of this function.
47
\row \li QGraphicsLayoutItem::sizeHint()
48
\li Returns the layout's size hints.
49
\row \li QGraphicsLayout::count()
50
\li Returns the number of items in your layout.
51
\row \li QGraphicsLayout::itemAt()
52
\li Returns a pointer to an item in your layout.
53
\row \li QGraphicsLayout::removeAt()
54
\li Removes an item from your layout without destroying it.
55
\endtable
56
57
For more details on how to implement each function, refer to the individual
58
function documentation.
59
60
Each layout defines its own API for arranging widgets and layout items.
61
For example, with a grid layout, you require a row and a
62
column index with optional row and column spans, alignment, spacing, and more.
63
A linear layout, however, requires a single row or column index to position its
64
items. For a grid layout, the order of insertion does not affect the layout in
65
any way, but for a linear layout, the order is essential. When writing your own
66
layout subclass, you are free to choose the API that best suits your layout.
67
68
QGraphicsLayout provides the addChildLayoutItem() convenience function to add
69
layout items to a custom layout. The function will automatically reparent
70
graphics items, if required.
71
72
\section1 Activating the Layout
73
74
When the layout's geometry changes, QGraphicsLayout immediately rearranges
75
all of its managed items by calling setGeometry() on each item. This
76
rearrangement is called \e activating the layout.
77
78
QGraphicsLayout updates its own geometry to match the contentsRect() of the
79
QGraphicsLayoutItem it is managing. Thus, it will automatically rearrange all
80
its items when the widget is resized. QGraphicsLayout caches the sizes of all
81
its managed items to avoid calling setGeometry() too often.
82
83
\note A QGraphicsLayout will have the same geometry as the contentsRect()
84
of the widget (not the layout) it is assigned to.
85
86
\section2 Activating the Layout Implicitly
87
88
The layout can be activated implicitly using one of two ways: by calling
89
activate() or by calling invalidate(). Calling activate() activates the layout
90
immediately. In contrast, calling invalidate() is delayed, as it posts a
91
\l{QEvent::LayoutRequest}{LayoutRequest} event to the managed widget. Due
92
to event compression, the activate() will only be called once after control has
93
returned to the event loop. This is referred to as \e invalidating the layout.
94
Invalidating the layout also invalidates any cached information. Also, the
95
invalidate() function is a virtual function. So, you can invalidate your own
96
cache in a subclass of QGraphicsLayout by reimplementing this function.
97
98
\section1 Event Handling
99
100
QGraphicsLayout listens to events for the widget it manages through the
101
virtual widgetEvent() event handler. When the layout is assigned to a
102
widget, all events delivered to the widget are first processed by
103
widgetEvent(). This allows the layout to be aware of any relevant state
104
changes on the widget such as visibility changes or layout direction changes.
105
106
\section1 Margin Handling
107
108
The margins of a QGraphicsLayout can be modified by reimplementing
109
setContentsMargins() and getContentsMargins().
110
111
*/
112
113
/*!
114
Constructs a QGraphicsLayout object.
115
116
\a parent is passed to QGraphicsLayoutItem's constructor and the
117
QGraphicsLayoutItem's isLayout argument is set to \e true.
118
119
If \a parent is a QGraphicsWidget the layout will be installed
120
on that widget. (Note that installing a layout will delete the old one
121
installed.)
122
*/
123
QGraphicsLayout::QGraphicsLayout(QGraphicsLayoutItem *parent)
124
: QGraphicsLayoutItem(*
new
QGraphicsLayoutPrivate)
125
{
126
setParentLayoutItem(parent);
127
if
(parent && !parent->isLayout()) {
128
// If a layout has a parent that is not a layout it must be a QGraphicsWidget.
129
QGraphicsItem *itemParent = parent->graphicsItem();
130
if
(itemParent && itemParent->isWidget()) {
131
static_cast
<QGraphicsWidget *>(itemParent)->d_func()->setLayout_helper(
this
);
132
}
else
{
133
qWarning(
"QGraphicsLayout::QGraphicsLayout: Attempt to create a layout with a parent that is"
134
" neither a QGraphicsWidget nor QGraphicsLayout"
);
135
}
136
}
137
d_func()->sizePolicy = QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding, QSizePolicy::DefaultType);
138
setOwnedByLayout(
true
);
139
}
140
141
/*!
142
\internal
143
*/
144
QGraphicsLayout::QGraphicsLayout(QGraphicsLayoutPrivate &dd, QGraphicsLayoutItem *parent)
145
: QGraphicsLayoutItem(dd)
146
{
147
setParentLayoutItem(parent);
148
if
(parent && !parent->isLayout()) {
149
// If a layout has a parent that is not a layout it must be a QGraphicsWidget.
150
QGraphicsItem *itemParent = parent->graphicsItem();
151
if
(itemParent && itemParent->isWidget()) {
152
static_cast
<QGraphicsWidget *>(itemParent)->d_func()->setLayout_helper(
this
);
153
}
else
{
154
qWarning(
"QGraphicsLayout::QGraphicsLayout: Attempt to create a layout with a parent that is"
155
" neither a QGraphicsWidget nor QGraphicsLayout"
);
156
}
157
}
158
d_func()->sizePolicy = QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding, QSizePolicy::DefaultType);
159
setOwnedByLayout(
true
);
160
}
161
162
/*!
163
Destroys the QGraphicsLayout object.
164
*/
165
QGraphicsLayout::~QGraphicsLayout()
166
{
167
}
168
169
/*!
170
Sets the contents margins to \a left, \a top, \a right and \a bottom. The
171
default contents margins for toplevel layouts are style dependent
172
(by querying the pixelMetric for QStyle::PM_LayoutLeftMargin,
173
QStyle::PM_LayoutTopMargin, QStyle::PM_LayoutRightMargin and
174
QStyle::PM_LayoutBottomMargin).
175
176
For sublayouts the default margins are 0.
177
178
Changing the contents margins automatically invalidates the layout.
179
180
\sa invalidate()
181
*/
182
void
QGraphicsLayout::setContentsMargins(qreal left, qreal top, qreal right, qreal bottom)
183
{
184
Q_D(QGraphicsLayout);
185
if
(d->left == left && d->top == top && d->right == right && d->bottom == bottom)
186
return
;
187
d->left = left;
188
d->right = right;
189
d->top = top;
190
d->bottom = bottom;
191
invalidate();
192
}
193
194
/*!
195
\reimp
196
*/
197
void
QGraphicsLayout::getContentsMargins(qreal *left, qreal *top, qreal *right, qreal *bottom)
const
198
{
199
Q_D(
const
QGraphicsLayout);
200
d->getMargin(left, d->left, QStyle::PM_LayoutLeftMargin);
201
d->getMargin(top, d->top, QStyle::PM_LayoutTopMargin);
202
d->getMargin(right, d->right, QStyle::PM_LayoutRightMargin);
203
d->getMargin(bottom, d->bottom, QStyle::PM_LayoutBottomMargin);
204
}
205
206
/*!
207
Activates the layout, causing all items in the layout to be immediately
208
rearranged. This function is based on calling count() and itemAt(), and
209
then calling setGeometry() on all items sequentially. When activated,
210
the layout will adjust its geometry to its parent's contentsRect().
211
The parent will then invalidate any layout of its own.
212
213
If called in sequence or recursively, e.g., by one of the arranged items
214
in response to being resized, this function will do nothing.
215
216
Note that the layout is free to use geometry caching to optimize this
217
process. To forcefully invalidate any such cache, you can call
218
invalidate() before calling activate().
219
220
\sa invalidate()
221
*/
222
void
QGraphicsLayout::activate()
223
{
224
Q_D(QGraphicsLayout);
225
if
(d->activated)
226
return
;
227
228
d->activateRecursive(
this
);
229
230
// we don't call activate on a sublayout, but somebody might.
231
// Therefore, we walk to the parentitem of the toplevel layout.
232
QGraphicsLayoutItem *parentItem =
this
;
233
while
(parentItem && parentItem->isLayout())
234
parentItem = parentItem->parentLayoutItem();
235
if
(!parentItem)
236
return
;
237
Q_ASSERT(!parentItem->isLayout());
238
239
if
(QGraphicsLayout::instantInvalidatePropagation()) {
240
QGraphicsWidget *parentWidget =
static_cast
<QGraphicsWidget*>(parentItem);
241
if
(!parentWidget->parentLayoutItem()) {
242
// we've reached the topmost widget, resize it
243
bool
wasResized = parentWidget->testAttribute(Qt::WA_Resized);
244
parentWidget->resize(parentWidget->size());
245
parentWidget->setAttribute(Qt::WA_Resized, wasResized);
246
}
247
248
setGeometry(parentItem->contentsRect());
// relayout children
249
}
else
{
250
setGeometry(parentItem->contentsRect());
// relayout children
251
parentLayoutItem()->updateGeometry();
252
}
253
}
254
255
/*!
256
Returns \c true if the layout is currently being activated; otherwise,
257
returns \c false. If the layout is being activated, this means that it is
258
currently in the process of rearranging its items (i.e., the activate()
259
function has been called, and has not yet returned).
260
261
\sa activate(), invalidate()
262
*/
263
bool
QGraphicsLayout::isActivated()
const
264
{
265
Q_D(
const
QGraphicsLayout);
266
return
d->activated;
267
}
268
269
/*!
270
Clears any cached geometry and size hint information in the layout, and
271
posts a \l{QEvent::LayoutRequest}{LayoutRequest} event to the managed
272
parent QGraphicsLayoutItem.
273
274
\sa activate(), setGeometry()
275
*/
276
void
QGraphicsLayout::invalidate()
277
{
278
if
(QGraphicsLayout::instantInvalidatePropagation()) {
279
updateGeometry();
280
}
else
{
281
// only mark layouts as invalid (activated = false) if we can post a LayoutRequest event.
282
QGraphicsLayoutItem *layoutItem =
this
;
283
while
(layoutItem && layoutItem->isLayout()) {
284
// we could call updateGeometry(), but what if that method
285
// does not call the base implementation? In addition, updateGeometry()
286
// does more than we need.
287
layoutItem->d_func()->sizeHintCacheDirty =
true
;
288
layoutItem->d_func()->sizeHintWithConstraintCacheDirty =
true
;
289
layoutItem = layoutItem->parentLayoutItem();
290
}
291
if
(layoutItem) {
292
layoutItem->d_func()->sizeHintCacheDirty =
true
;
293
layoutItem->d_func()->sizeHintWithConstraintCacheDirty =
true
;
294
}
295
296
bool
postIt = layoutItem ? !layoutItem->isLayout() :
false
;
297
if
(postIt) {
298
layoutItem =
this
;
299
while
(layoutItem && layoutItem->isLayout()
300
&&
static_cast
<QGraphicsLayout*>(layoutItem)->d_func()->activated) {
301
static_cast
<QGraphicsLayout*>(layoutItem)->d_func()->activated =
false
;
302
layoutItem = layoutItem->parentLayoutItem();
303
}
304
if
(layoutItem && !layoutItem->isLayout()) {
305
// If a layout has a parent that is not a layout it must be a QGraphicsWidget.
306
QCoreApplication::postEvent(
static_cast
<QGraphicsWidget *>(layoutItem),
new
QEvent(QEvent::LayoutRequest));
307
}
308
}
309
}
310
}
311
312
/*!
313
\reimp
314
*/
315
void
QGraphicsLayout::updateGeometry()
316
{
317
Q_D(QGraphicsLayout);
318
if
(QGraphicsLayout::instantInvalidatePropagation()) {
319
d->activated =
false
;
320
QGraphicsLayoutItem::updateGeometry();
321
322
QGraphicsLayoutItem *parentItem = parentLayoutItem();
323
if
(!parentItem)
324
return
;
325
326
if
(parentItem->isLayout())
327
static_cast
<QGraphicsLayout *>(parentItem)->invalidate();
328
else
329
parentItem->updateGeometry();
330
}
else
{
331
QGraphicsLayoutItem::updateGeometry();
332
if
(QGraphicsLayoutItem *parentItem = parentLayoutItem()) {
333
if
(parentItem->isLayout()) {
334
parentItem->updateGeometry();
335
}
else
{
336
invalidate();
337
}
338
}
339
}
340
}
341
342
/*!
343
This virtual event handler receives all events for the managed
344
widget. QGraphicsLayout uses this event handler to listen for layout
345
related events such as geometry changes, layout changes or layout
346
direction changes.
347
348
\a e is a pointer to the event.
349
350
You can reimplement this event handler to track similar events for your
351
own custom layout.
352
353
\sa QGraphicsWidget::event(), QGraphicsItem::sceneEvent()
354
*/
355
void
QGraphicsLayout::widgetEvent(QEvent *e)
356
{
357
switch
(e->type()) {
358
case
QEvent::GraphicsSceneResize:
359
if
(isActivated()) {
360
setGeometry(parentLayoutItem()->contentsRect());
361
}
else
{
362
activate();
// relies on that activate() will call updateGeometry()
363
}
364
break
;
365
case
QEvent::LayoutRequest:
366
activate();
367
break
;
368
case
QEvent::LayoutDirectionChange:
369
invalidate();
370
break
;
371
default
:
372
break
;
373
}
374
}
375
376
/*!
377
\fn virtual int QGraphicsLayout::count() const = 0
378
379
This pure virtual function must be reimplemented in a subclass of
380
QGraphicsLayout to return the number of items in the layout.
381
382
The subclass is free to decide how to store the items.
383
384
\sa itemAt(), removeAt()
385
*/
386
387
/*!
388
\fn virtual QGraphicsLayoutItem *QGraphicsLayout::itemAt(int i) const = 0
389
390
This pure virtual function must be reimplemented in a subclass of
391
QGraphicsLayout to return a pointer to the item at index \a i. The
392
reimplementation can assume that \a i is valid (i.e., it respects the
393
value of count()).
394
Together with count(), it is provided as a means of iterating over all items in a layout.
395
396
The subclass is free to decide how to store the items, and the visual arrangement
397
does not have to be reflected through this function.
398
399
\sa count(), removeAt()
400
*/
401
402
/*!
403
\fn virtual void QGraphicsLayout::removeAt(int index) = 0
404
405
This pure virtual function must be reimplemented in a subclass of
406
QGraphicsLayout to remove the item at \a index. The
407
reimplementation can assume that \a index is valid (i.e., it
408
respects the value of count()).
409
410
The implementation must ensure that the parentLayoutItem() of
411
the removed item does not point to this layout, since the item is
412
considered to be removed from the layout hierarchy.
413
414
If the layout is to be reused between applications, we recommend
415
that the layout deletes the item, but the graphics view framework
416
does not depend on this.
417
418
The subclass is free to decide how to store the items.
419
420
\sa itemAt(), count()
421
*/
422
423
/*!
424
\since 4.6
425
426
This function is a convenience function provided for custom layouts, and will go through
427
all items in the layout and reparent their graphics items to the closest QGraphicsWidget
428
ancestor of the layout.
429
430
If \a layoutItem is already in a different layout, it will be removed from that layout.
431
432
If custom layouts want special behaviour they can ignore to use this function, and implement
433
their own behaviour.
434
435
\sa graphicsItem()
436
*/
437
void
QGraphicsLayout::addChildLayoutItem(QGraphicsLayoutItem *layoutItem)
438
{
439
Q_D(QGraphicsLayout);
440
d->addChildLayoutItem(layoutItem);
441
}
442
443
static
bool
g_instantInvalidatePropagation
=
false
;
444
445
/*!
446
\internal
447
\since 4.8
448
\sa instantInvalidatePropagation()
449
450
Calling this function with \a enable set to true will enable a feature that
451
makes propagation of invalidation up to ancestor layout items to be done in
452
one go. It will propagate up the parentLayoutItem() hierarchy until it has
453
reached the root. If the root item is a QGraphicsWidget, it will *post* a
454
layout request to it. When the layout request is consumed it will traverse
455
down the hierarchy of layouts and widgets and activate all layouts that is
456
invalid (not activated). This is the recommended behaviour.
457
458
If not set it will also propagate up the parentLayoutItem() hierarchy, but
459
it will stop at the \e{first widget} it encounters, and post a layout
460
request to the widget. When the layout request is consumed, this might
461
cause it to continue propagation up to the parentLayoutItem() of the
462
widget. It will continue in this fashion until it has reached a widget with
463
no parentLayoutItem(). This strategy might cause drawing artifacts, since
464
it is not done in one go, and the consumption of layout requests might be
465
interleaved by consumption of paint events, which might cause significant
466
flicker.
467
Note, this is not the recommended behavior, but for compatibility reasons
468
this is the default behaviour.
469
*/
470
void
QGraphicsLayout::setInstantInvalidatePropagation(
bool
enable)
471
{
472
g_instantInvalidatePropagation = enable;
473
}
474
475
/*!
476
\internal
477
\since 4.8
478
\sa setInstantInvalidatePropagation()
479
480
returns \c true if the complete widget/layout hierarchy is rearranged in one go.
481
*/
482
bool
QGraphicsLayout::instantInvalidatePropagation()
483
{
484
return
g_instantInvalidatePropagation;
485
}
486
487
QT_END_NAMESPACE
g_instantInvalidatePropagation
static bool g_instantInvalidatePropagation
Definition
qgraphicslayout.cpp:443
qtbase
src
widgets
graphicsview
qgraphicslayout.cpp
Generated on
for Qt by
1.18.0