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
qstackedlayout.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
"qstackedlayout.h"
6
#
include
"qlayout_p.h"
7
8
#
include
<
qlist
.
h
>
9
#
include
"private/qwidget_p.h"
10
#
include
"private/qlayoutengine_p.h"
11
12
#
include
<
QtCore
/
qpointer
.
h
>
13
14
#
include
<
memory
>
15
16
QT_BEGIN_NAMESPACE
17
18
class
QStackedLayoutPrivate :
public
QLayoutPrivate
19
{
20
Q_DECLARE_PUBLIC(QStackedLayout)
21
public
:
22
QStackedLayoutPrivate() : index(-1), stackingMode(QStackedLayout::StackOne) {}
23
QLayoutItem* replaceAt(
int
index, QLayoutItem *newitem) override;
24
QList<QLayoutItem *> list;
25
int
index;
26
QStackedLayout::StackingMode stackingMode;
27
};
28
29
QLayoutItem* QStackedLayoutPrivate::replaceAt(
int
idx, QLayoutItem *newitem)
30
{
31
Q_Q(QStackedLayout);
32
if
(idx < 0 || idx >= list.size() || !newitem)
33
return
nullptr
;
34
QWidget *wdg = newitem->widget();
35
if
(Q_UNLIKELY(!wdg)) {
36
qWarning(
"QStackedLayout::replaceAt: Only widgets can be added"
);
37
return
nullptr
;
38
}
39
QLayoutItem *orgitem = list.at(idx);
40
list.replace(idx, newitem);
41
if
(idx == index)
42
q->setCurrentIndex(index);
43
return
orgitem;
44
}
45
46
/*!
47
\class QStackedLayout
48
49
\brief The QStackedLayout class provides a stack of widgets where
50
only one widget is visible at a time.
51
52
\ingroup geomanagement
53
\inmodule QtWidgets
54
55
QStackedLayout can be used to create a user interface similar to
56
the one provided by QTabWidget. There is also a convenience
57
QStackedWidget class built on top of QStackedLayout.
58
59
A QStackedLayout can be populated with a number of child widgets
60
("pages"). For example:
61
62
\snippet qstackedlayout/main.cpp 0
63
\codeline
64
\snippet qstackedlayout/main.cpp 2
65
\snippet qstackedlayout/main.cpp 3
66
67
QStackedLayout provides no intrinsic means for the user to switch
68
page. This is typically done through a QComboBox or a QListWidget
69
that stores the titles of the QStackedLayout's pages. For
70
example:
71
72
\snippet qstackedlayout/main.cpp 1
73
74
When populating a layout, the widgets are added to an internal
75
list. The indexOf() function returns the index of a widget in that
76
list. The widgets can either be added to the end of the list using
77
the addWidget() function, or inserted at a given index using the
78
insertWidget() function. The removeWidget() function removes the
79
widget at the given index from the layout. The number of widgets
80
contained in the layout, can be obtained using the count()
81
function.
82
83
The widget() function returns the widget at a given index
84
position. The index of the widget that is shown on screen is given
85
by currentIndex() and can be changed using setCurrentIndex(). In a
86
similar manner, the currently shown widget can be retrieved using
87
the currentWidget() function, and altered using the
88
setCurrentWidget() function.
89
90
Whenever the current widget in the layout changes or a widget is
91
removed from the layout, the currentChanged() and widgetRemoved()
92
signals are emitted respectively.
93
94
\sa QStackedWidget, QTabWidget
95
*/
96
97
/*!
98
\fn void QStackedLayout::currentChanged(int index)
99
100
This signal is emitted whenever the current widget in the layout
101
changes. The \a index specifies the index of the new current
102
widget, or -1 if there isn't a new one (for example, if there
103
are no widgets in the QStackedLayout)
104
105
\sa currentWidget(), setCurrentWidget()
106
*/
107
108
/*!
109
\fn void QStackedLayout::widgetRemoved(int index)
110
111
This signal is emitted whenever a widget is removed from the
112
layout. The widget's \a index is passed as parameter.
113
114
\sa removeWidget()
115
*/
116
117
/*!
118
\fn void QStackedLayout::widgetAdded(int index)
119
120
\since 6.9
121
122
This signal is emitted whenever a widget is added or inserted.
123
The widget's \a index is passed as parameter.
124
125
\sa addWidget(), insertWidget()
126
*/
127
128
/*!
129
Constructs a QStackedLayout with no parent.
130
131
This QStackedLayout must be installed on a widget later on to
132
become effective.
133
134
\sa addWidget(), insertWidget()
135
*/
136
QStackedLayout::QStackedLayout()
137
: QLayout(*
new
QStackedLayoutPrivate,
nullptr
,
nullptr
)
138
{
139
}
140
141
/*!
142
Constructs a new QStackedLayout with the given \a parent.
143
144
This layout will install itself on the \a parent widget and
145
manage the geometry of its children.
146
*/
147
QStackedLayout::QStackedLayout(QWidget *parent)
148
: QLayout(*
new
QStackedLayoutPrivate,
nullptr
, parent)
149
{
150
}
151
152
/*!
153
Constructs a new QStackedLayout and inserts it into
154
the given \a parentLayout.
155
*/
156
QStackedLayout::QStackedLayout(QLayout *parentLayout)
157
: QLayout(*
new
QStackedLayoutPrivate, parentLayout,
nullptr
)
158
{
159
}
160
161
/*!
162
Destroys this QStackedLayout. Note that the layout's widgets are
163
\e not destroyed.
164
*/
165
QStackedLayout::~QStackedLayout()
166
{
167
Q_D(QStackedLayout);
168
qDeleteAll(d->list);
169
}
170
171
/*!
172
Adds the given \a widget to the end of this layout and returns the
173
index position of the \a widget.
174
175
If the QStackedLayout is empty before this function is called,
176
the given \a widget becomes the current widget.
177
178
\sa insertWidget(), removeWidget(), setCurrentWidget()
179
*/
180
int
QStackedLayout::addWidget(QWidget *widget)
181
{
182
Q_D(QStackedLayout);
183
return
insertWidget(d->list.size(), widget);
184
}
185
186
/*!
187
Inserts the given \a widget at the given \a index in this
188
QStackedLayout. If \a index is out of range, the widget is
189
appended (in which case it is the actual index of the \a widget
190
that is returned).
191
192
If the QStackedLayout is empty before this function is called, the
193
given \a widget becomes the current widget.
194
195
Inserting a new widget at an index less than or equal to the current index
196
will increment the current index, but keep the current widget.
197
198
\sa addWidget(), removeWidget(), setCurrentWidget()
199
*/
200
int
QStackedLayout::insertWidget(
int
index, QWidget *widget)
201
{
202
Q_D(QStackedLayout);
203
addChildWidget(widget);
204
index = qMin(index, d->list.size());
205
if
(index < 0)
206
index = d->list.size();
207
QWidgetItem *wi = QLayoutPrivate::createWidgetItem(
this
, widget);
208
d->list.insert(index, wi);
209
invalidate();
210
if
(d->index < 0) {
211
setCurrentIndex(index);
212
}
else
{
213
if
(index <= d->index)
214
++d->index;
215
if
(d->stackingMode == StackOne)
216
widget->hide();
217
widget->lower();
218
}
219
emit widgetAdded(index);
220
return
index;
221
}
222
223
/*!
224
\reimp
225
*/
226
QLayoutItem *QStackedLayout::itemAt(
int
index)
const
227
{
228
Q_D(
const
QStackedLayout);
229
return
d->list.value(index);
230
}
231
232
// Code that enables proper handling of the case that takeAt() is
233
// called somewhere inside QObject destructor (can't call hide()
234
// on the object then)
235
static
bool
qt_wasDeleted
(
const
QWidget *w)
236
{
237
return
QObjectPrivate::get(w)->wasDeleted;
238
}
239
240
241
/*!
242
\reimp
243
*/
244
QLayoutItem *QStackedLayout::takeAt(
int
index)
245
{
246
Q_D(QStackedLayout);
247
if
(index <0 || index >= d->list.size())
248
return
nullptr
;
249
QLayoutItem *item = d->list.takeAt(index);
250
if
(index == d->index) {
251
d->index = -1;
252
if
( d->list.size() > 0 ) {
253
int
newIndex = (index == d->list.size()) ? index-1 : index;
254
setCurrentIndex(newIndex);
255
}
else
{
256
emit currentChanged(-1);
257
}
258
}
else
if
(index < d->index) {
259
--d->index;
260
}
261
emit widgetRemoved(index);
262
if
(item->widget() && !qt_wasDeleted(item->widget()))
263
item->widget()->hide();
264
return
item;
265
}
266
267
/*!
268
\property QStackedLayout::currentIndex
269
\brief the index position of the widget that is visible
270
271
The current index is -1 if there is no current widget.
272
273
\sa currentWidget(), indexOf()
274
*/
275
void
QStackedLayout::setCurrentIndex(
int
index)
276
{
277
Q_D(QStackedLayout);
278
QWidget *prev = currentWidget();
279
QWidget *next = widget(index);
280
if
(!next || next == prev)
281
return
;
282
283
bool
reenableUpdates =
false
;
284
QWidget *parent = parentWidget();
285
286
if
(parent && parent->updatesEnabled()) {
287
reenableUpdates =
true
;
288
parent->setUpdatesEnabled(
false
);
289
}
290
291
QPointer<QWidget> fw = parent ? parent->window()->focusWidget() :
nullptr
;
292
const
bool
focusWasOnOldPage = fw && (prev && prev->isAncestorOf(fw));
293
294
if
(prev) {
295
prev->clearFocus();
296
if
(d->stackingMode == StackOne)
297
prev->hide();
298
}
299
300
d->index = index;
301
next->raise();
302
next->show();
303
304
// try to move focus onto the incoming widget if focus
305
// was somewhere on the outgoing widget.
306
307
if
(parent) {
308
if
(focusWasOnOldPage) {
309
// look for the best focus widget we can find
310
if
(QWidget *nfw = next->focusWidget())
311
nfw->setFocus();
312
else
{
313
// second best: first child widget in the focus chain
314
if
(QWidget *i = fw) {
315
while
((i = i->nextInFocusChain()) != fw) {
316
if
(((i->focusPolicy() & Qt::TabFocus) == Qt::TabFocus)
317
&& !i->focusProxy() && i->isVisibleTo(next) && i->isEnabled()
318
&& next->isAncestorOf(i)) {
319
i->setFocus();
320
break
;
321
}
322
}
323
// third best: incoming widget
324
if
(i == fw )
325
next->setFocus();
326
}
327
}
328
}
329
}
330
if
(reenableUpdates)
331
parent->setUpdatesEnabled(
true
);
332
emit currentChanged(index);
333
}
334
335
int
QStackedLayout::currentIndex()
const
336
{
337
Q_D(
const
QStackedLayout);
338
return
d->index;
339
}
340
341
342
/*!
343
\fn void QStackedLayout::setCurrentWidget(QWidget *widget)
344
345
Sets the current widget to be the specified \a widget. The new
346
current widget must already be contained in this stacked layout.
347
348
\sa setCurrentIndex(), currentWidget()
349
*/
350
void
QStackedLayout::setCurrentWidget(QWidget *widget)
351
{
352
int
index = indexOf(widget);
353
if
(Q_UNLIKELY(index == -1)) {
354
qWarning(
"QStackedLayout::setCurrentWidget: Widget %p not contained in stack"
, widget);
355
return
;
356
}
357
setCurrentIndex(index);
358
}
359
360
361
/*!
362
Returns the current widget, or \nullptr if there are no widgets
363
in this layout.
364
365
\sa currentIndex(), setCurrentWidget()
366
*/
367
QWidget *QStackedLayout::currentWidget()
const
368
{
369
Q_D(
const
QStackedLayout);
370
return
d->index >= 0 ? d->list.at(d->index)->widget() :
nullptr
;
371
}
372
373
/*!
374
Returns the widget at the given \a index, or \nullptr if there is
375
no widget at the given position.
376
377
\sa currentWidget(), indexOf()
378
*/
379
QWidget *QStackedLayout::widget(
int
index)
const
380
{
381
Q_D(
const
QStackedLayout);
382
if
(index < 0 || index >= d->list.size())
383
return
nullptr
;
384
return
d->list.at(index)->widget();
385
}
386
387
/*!
388
\property QStackedLayout::count
389
\brief the number of widgets contained in the layout
390
391
\sa currentIndex(), widget()
392
*/
393
int
QStackedLayout::count()
const
394
{
395
Q_D(
const
QStackedLayout);
396
return
d->list.size();
397
}
398
399
400
/*!
401
\reimp
402
*/
403
void
QStackedLayout::addItem(QLayoutItem *item)
404
{
405
std::unique_ptr<QLayoutItem> guard(item);
406
QWidget *widget = item->widget();
407
if
(Q_UNLIKELY(!widget)) {
408
qWarning(
"QStackedLayout::addItem: Only widgets can be added"
);
409
return
;
410
}
411
addWidget(widget);
412
}
413
414
/*!
415
\reimp
416
*/
417
QSize QStackedLayout::sizeHint()
const
418
{
419
Q_D(
const
QStackedLayout);
420
QSize s(0, 0);
421
int
n = d->list.size();
422
423
for
(
int
i = 0; i < n; ++i)
424
if
(QWidget *widget = d->list.at(i)->widget()) {
425
QSize ws(widget->sizeHint());
426
if
(widget->sizePolicy().horizontalPolicy() == QSizePolicy::Ignored)
427
ws.setWidth(0);
428
if
(widget->sizePolicy().verticalPolicy() == QSizePolicy::Ignored)
429
ws.setHeight(0);
430
s = s.expandedTo(ws);
431
}
432
return
s;
433
}
434
435
/*!
436
\reimp
437
*/
438
QSize QStackedLayout::minimumSize()
const
439
{
440
Q_D(
const
QStackedLayout);
441
QSize s(0, 0);
442
int
n = d->list.size();
443
444
for
(
int
i = 0; i < n; ++i)
445
if
(QWidget *widget = d->list.at(i)->widget())
446
s = s.expandedTo(qSmartMinSize(widget));
447
return
s;
448
}
449
450
/*!
451
\reimp
452
*/
453
void
QStackedLayout::setGeometry(
const
QRect &rect)
454
{
455
Q_D(QStackedLayout);
456
switch
(d->stackingMode) {
457
case
StackOne:
458
if
(QWidget *widget = currentWidget())
459
widget->setGeometry(rect);
460
break
;
461
case
StackAll:
462
if
(
const
int
n = d->list.size())
463
for
(
int
i = 0; i < n; ++i)
464
if
(QWidget *widget = d->list.at(i)->widget())
465
widget->setGeometry(rect);
466
break
;
467
}
468
}
469
470
/*!
471
\reimp
472
*/
473
bool
QStackedLayout::hasHeightForWidth()
const
474
{
475
const
int
n = count();
476
477
for
(
int
i = 0; i < n; ++i) {
478
if
(QLayoutItem *item = itemAt(i)) {
479
if
(item->hasHeightForWidth())
480
return
true
;
481
}
482
}
483
return
false
;
484
}
485
486
/*!
487
\reimp
488
*/
489
int
QStackedLayout::heightForWidth(
int
width)
const
490
{
491
const
int
n = count();
492
493
int
hfw = 0;
494
for
(
int
i = 0; i < n; ++i) {
495
if
(QLayoutItem *item = itemAt(i)) {
496
if
(QWidget *w = item->widget())
497
/*
498
Note: Does not query the layout item, but bypasses it and asks the widget
499
directly. This is consistent with how QStackedLayout::sizeHint() is
500
implemented. This also avoids an issue where QWidgetItem::heightForWidth()
501
returns -1 if the widget is hidden.
502
*/
503
hfw = qMax(hfw, w->heightForWidth(width));
504
}
505
}
506
hfw = qMax(hfw, minimumSize().height());
507
return
hfw;
508
}
509
510
/*!
511
\enum QStackedLayout::StackingMode
512
\since 4.4
513
514
This enum specifies how the layout handles its child widgets
515
regarding their visibility.
516
517
\value StackOne
518
Only the current widget is visible. This is the default.
519
520
\value StackAll
521
All widgets are visible. The current widget is merely raised.
522
*/
523
524
525
/*!
526
\property QStackedLayout::stackingMode
527
\brief determines the way visibility of child widgets are handled.
528
\since 4.4
529
530
The default value is StackOne. Setting the property to StackAll
531
allows you to make use of the layout for overlay widgets
532
that do additional drawing on top of other widgets, for example,
533
graphical editors.
534
*/
535
536
QStackedLayout::StackingMode QStackedLayout::stackingMode()
const
537
{
538
Q_D(
const
QStackedLayout);
539
return
d->stackingMode;
540
}
541
542
void
QStackedLayout::setStackingMode(StackingMode stackingMode)
543
{
544
Q_D(QStackedLayout);
545
if
(d->stackingMode == stackingMode)
546
return
;
547
d->stackingMode = stackingMode;
548
549
const
int
n = d->list.size();
550
if
(n == 0)
551
return
;
552
553
switch
(d->stackingMode) {
554
case
StackOne:
555
if
(
const
int
idx = currentIndex())
556
for
(
int
i = 0; i < n; ++i)
557
if
(QWidget *widget = d->list.at(i)->widget())
558
widget->setVisible(i == idx);
559
break
;
560
case
StackAll: {
// Turn overlay on: Make sure all widgets are the same size
561
QRect geometry;
562
if
(
const
QWidget *widget = currentWidget())
563
geometry = widget->geometry();
564
for
(
int
i = 0; i < n; ++i)
565
if
(QWidget *widget = d->list.at(i)->widget()) {
566
if
(!geometry.isNull())
567
widget->setGeometry(geometry);
568
widget->setVisible(
true
);
569
}
570
}
571
break
;
572
}
573
}
574
575
QT_END_NAMESPACE
576
577
#
include
"moc_qstackedlayout.cpp"
qt_wasDeleted
static bool qt_wasDeleted(const QWidget *w)
Definition
qstackedlayout.cpp:235
qtbase
src
widgets
kernel
qstackedlayout.cpp
Generated on
for Qt by
1.18.0