source: trunk/src/gui/kernel/qstackedlayout.cpp@ 858

Last change on this file since 858 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

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