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

Last change on this file since 476 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

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