diff -r 000000000000 -r 1918ee327afb src/gui/kernel/qstackedlayout.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/gui/kernel/qstackedlayout.cpp Mon Jan 11 14:00:40 2010 +0000 @@ -0,0 +1,543 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qstackedlayout.h" +#include "qlayout_p.h" + +#include +#include +#include "private/qlayoutengine_p.h" + +QT_BEGIN_NAMESPACE + +class QStackedLayoutPrivate : public QLayoutPrivate +{ + Q_DECLARE_PUBLIC(QStackedLayout) +public: + QStackedLayoutPrivate() : index(-1), stackingMode(QStackedLayout::StackOne) {} + QList list; + int index; + QStackedLayout::StackingMode stackingMode; +}; + +/*! + \class QStackedLayout + + \brief The QStackedLayout class provides a stack of widgets where + only one widget is visible at a time. + + \ingroup geomanagement + + QStackedLayout can be used to create a user interface similar to + the one provided by QTabWidget. There is also a convenience + QStackedWidget class built on top of QStackedLayout. + + A QStackedLayout can be populated with a number of child widgets + ("pages"). For example: + + \snippet doc/src/snippets/qstackedlayout/main.cpp 0 + \codeline + \snippet doc/src/snippets/qstackedlayout/main.cpp 2 + \snippet doc/src/snippets/qstackedlayout/main.cpp 3 + + QStackedLayout provides no intrinsic means for the user to switch + page. This is typically done through a QComboBox or a QListWidget + that stores the titles of the QStackedLayout's pages. For + example: + + \snippet doc/src/snippets/qstackedlayout/main.cpp 1 + + When populating a layout, the widgets are added to an internal + list. The indexOf() function returns the index of a widget in that + list. The widgets can either be added to the end of the list using + the addWidget() function, or inserted at a given index using the + insertWidget() function. The removeWidget() function removes the + widget at the given index from the layout. The number of widgets + contained in the layout, can be obtained using the count() + function. + + The widget() function returns the widget at a given index + position. The index of the widget that is shown on screen is given + by currentIndex() and can be changed using setCurrentIndex(). In a + similar manner, the currently shown widget can be retrieved using + the currentWidget() function, and altered using the + setCurrentWidget() function. + + Whenever the current widget in the layout changes or a widget is + removed from the layout, the currentChanged() and widgetRemoved() + signals are emitted respectively. + + \sa QStackedWidget, QTabWidget +*/ + +/*! + \fn void QStackedLayout::currentChanged(int index) + + This signal is emitted whenever the current widget in the layout + changes. The \a index specifies the index of the new current + widget, or -1 if there isn't a new one (for example, if there + are no widgets in the QStackedLayout) + + \sa currentWidget(), setCurrentWidget() +*/ + +/*! + \fn void QStackedLayout::widgetRemoved(int index) + + This signal is emitted whenever a widget is removed from the + layout. The widget's \a index is passed as parameter. + + \sa removeWidget() +*/ + +/*! + \fn QWidget *QStackedLayout::widget() + \internal +*/ + +/*! + Constructs a QStackedLayout with no parent. + + This QStackedLayout must be installed on a widget later on to + become effective. + + \sa addWidget(), insertWidget() +*/ +QStackedLayout::QStackedLayout() + : QLayout(*new QStackedLayoutPrivate, 0, 0) +{ +} + +/*! + Constructs a new QStackedLayout with the given \a parent. + + This layout will install itself on the \a parent widget and + manage the geometry of its children. +*/ +QStackedLayout::QStackedLayout(QWidget *parent) + : QLayout(*new QStackedLayoutPrivate, 0, parent) +{ +} + +/*! + Constructs a new QStackedLayout and inserts it into + the given \a parentLayout. +*/ +QStackedLayout::QStackedLayout(QLayout *parentLayout) + : QLayout(*new QStackedLayoutPrivate, parentLayout, 0) +{ +} + +/*! + Destroys this QStackedLayout. Note that the layout's widgets are + \e not destroyed. +*/ +QStackedLayout::~QStackedLayout() +{ + Q_D(QStackedLayout); + qDeleteAll(d->list); +} + +/*! + Adds the given \a widget to the end of this layout and returns the + index position of the \a widget. + + If the QStackedLayout is empty before this function is called, + the given \a widget becomes the current widget. + + \sa insertWidget(), removeWidget(), setCurrentWidget() +*/ +int QStackedLayout::addWidget(QWidget *widget) +{ + Q_D(QStackedLayout); + return insertWidget(d->list.count(), widget); +} + +/*! + Inserts the given \a widget at the given \a index in this + QStackedLayout. If \a index is out of range, the widget is + appended (in which case it is the actual index of the \a widget + that is returned). + + If the QStackedLayout is empty before this function is called, the + given \a widget becomes the current widget. + + Inserting a new widget at an index less than or equal to the current index + will increment the current index, but keep the current widget. + + \sa addWidget(), removeWidget(), setCurrentWidget() +*/ +int QStackedLayout::insertWidget(int index, QWidget *widget) +{ + Q_D(QStackedLayout); + addChildWidget(widget); + index = qMin(index, d->list.count()); + if (index < 0) + index = d->list.count(); + QWidgetItem *wi = QLayoutPrivate::createWidgetItem(this, widget); + d->list.insert(index, wi); + invalidate(); + if (d->index < 0) { + setCurrentIndex(index); + } else { + if (index <= d->index) + ++d->index; + if (d->stackingMode == StackOne) + widget->hide(); + widget->lower(); + } + return index; +} + +/*! + \reimp +*/ +QLayoutItem *QStackedLayout::itemAt(int index) const +{ + Q_D(const QStackedLayout); + return d->list.value(index); +} + +// Code that enables proper handling of the case that takeAt() is +// called somewhere inside QObject destructor (can't call hide() +// on the object then) + +class QtFriendlyLayoutWidget : public QWidget +{ +public: + inline bool wasDeleted() const { return d_ptr->wasDeleted; } +}; + +static bool qt_wasDeleted(const QWidget *w) { return static_cast(w)->wasDeleted(); } + + +/*! + \reimp +*/ +QLayoutItem *QStackedLayout::takeAt(int index) +{ + Q_D(QStackedLayout); + if (index <0 || index >= d->list.size()) + return 0; + QLayoutItem *item = d->list.takeAt(index); + if (index == d->index) { + d->index = -1; + if ( d->list.count() > 0 ) { + int newIndex = (index == d->list.count()) ? index-1 : index; + setCurrentIndex(newIndex); + } else { + emit currentChanged(-1); + } + } else if (index < d->index) { + --d->index; + } + emit widgetRemoved(index); + if (item->widget() && !qt_wasDeleted(item->widget())) + item->widget()->hide(); + return item; +} + +/*! + \property QStackedLayout::currentIndex + \brief the index position of the widget that is visible + + The current index is -1 if there is no current widget. + + \sa currentWidget(), indexOf() +*/ +void QStackedLayout::setCurrentIndex(int index) +{ + Q_D(QStackedLayout); + QWidget *prev = currentWidget(); + QWidget *next = widget(index); + if (!next || next == prev) + return; + + bool reenableUpdates = false; + QWidget *parent = parentWidget(); + + if (parent && parent->updatesEnabled()) { + reenableUpdates = true; + parent->setUpdatesEnabled(false); + } + + QWidget *fw = parent ? parent->window()->focusWidget() : 0; + if (prev) { + prev->clearFocus(); + if (d->stackingMode == StackOne) + prev->hide(); + } + + d->index = index; + next->raise(); + next->show(); + + // try to move focus onto the incoming widget if focus + // was somewhere on the outgoing widget. + + if (parent) { + if (fw && (prev && prev->isAncestorOf(fw))) { // focus was on old page + // look for the best focus widget we can find + if (QWidget *nfw = next->focusWidget()) + nfw->setFocus(); + else { + // second best: first child widget in the focus chain + QWidget *i = fw; + while ((i = i->nextInFocusChain()) != fw) { + if (((i->focusPolicy() & Qt::TabFocus) == Qt::TabFocus) + && !i->focusProxy() && i->isVisibleTo(next) && i->isEnabled() + && next->isAncestorOf(i)) { + i->setFocus(); + break; + } + } + // third best: incoming widget + if (i == fw ) + next->setFocus(); + } + } + } + if (reenableUpdates) + parent->setUpdatesEnabled(true); + emit currentChanged(index); +} + +int QStackedLayout::currentIndex() const +{ + Q_D(const QStackedLayout); + return d->index; +} + + +/*! + \fn void QStackedLayout::setCurrentWidget(QWidget *widget) + + Sets the current widget to be the specified \a widget. The new + current widget must already be contained in this stacked layout. + + \sa setCurrentIndex(), currentWidget() + */ +void QStackedLayout::setCurrentWidget(QWidget *widget) +{ + int index = indexOf(widget); + if (index == -1) { + qWarning("QStackedLayout::setCurrentWidget: Widget %p not contained in stack", widget); + return; + } + setCurrentIndex(index); +} + + +/*! + Returns the current widget, or 0 if there are no widgets in this + layout. + + \sa currentIndex(), setCurrentWidget() +*/ +QWidget *QStackedLayout::currentWidget() const +{ + Q_D(const QStackedLayout); + return d->index >= 0 ? d->list.at(d->index)->widget() : 0; +} + +/*! + Returns the widget at the given \a index, or 0 if there is no + widget at the given position. + + \sa currentWidget(), indexOf() +*/ +QWidget *QStackedLayout::widget(int index) const +{ + Q_D(const QStackedLayout); + if (index < 0 || index >= d->list.size()) + return 0; + return d->list.at(index)->widget(); +} + +/*! + \property QStackedLayout::count + \brief the number of widgets contained in the layout + + \sa currentIndex(), widget() +*/ +int QStackedLayout::count() const +{ + Q_D(const QStackedLayout); + return d->list.size(); +} + + +/*! + \reimp +*/ +void QStackedLayout::addItem(QLayoutItem *item) +{ + QWidget *widget = item->widget(); + if (widget) { + addWidget(widget); + delete item; + } else { + qWarning("QStackedLayout::addItem: Only widgets can be added"); + } +} + +/*! + \reimp +*/ +QSize QStackedLayout::sizeHint() const +{ + Q_D(const QStackedLayout); + QSize s(0, 0); + int n = d->list.count(); + + for (int i = 0; i < n; ++i) + if (QWidget *widget = d->list.at(i)->widget()) { + QSize ws(widget->sizeHint()); + if (widget->sizePolicy().horizontalPolicy() == QSizePolicy::Ignored) + ws.setWidth(0); + if (widget->sizePolicy().verticalPolicy() == QSizePolicy::Ignored) + ws.setHeight(0); + s = s.expandedTo(ws); + } + return s; +} + +/*! + \reimp +*/ +QSize QStackedLayout::minimumSize() const +{ + Q_D(const QStackedLayout); + QSize s(0, 0); + int n = d->list.count(); + + for (int i = 0; i < n; ++i) + if (QWidget *widget = d->list.at(i)->widget()) + s = s.expandedTo(qSmartMinSize(widget)); + return s; +} + +/*! + \reimp +*/ +void QStackedLayout::setGeometry(const QRect &rect) +{ + Q_D(QStackedLayout); + switch (d->stackingMode) { + case StackOne: + if (QWidget *widget = currentWidget()) + widget->setGeometry(rect); + break; + case StackAll: + if (const int n = d->list.count()) + for (int i = 0; i < n; ++i) + if (QWidget *widget = d->list.at(i)->widget()) + widget->setGeometry(rect); + break; + } +} + +/*! + \enum QStackedLayout::StackingMode + \since 4.4 + + This enum specifies how the layout handles its child widgets + regarding their visibility. + + \value StackOne + Only the current widget is visible. This is the default. + + \value StackAll + All widgets are visible. The current widget is merely raised. +*/ + + +/*! + \property QStackedLayout::stackingMode + \brief determines the way visibility of child widgets are handled. + \since 4.4 + + The default value is StackOne. Setting the property to StackAll + allows you to make use of the layout for overlay widgets + that do additional drawing on top of other widgets, for example, + graphical editors. +*/ + +QStackedLayout::StackingMode QStackedLayout::stackingMode() const +{ + Q_D(const QStackedLayout); + return d->stackingMode; +} + +void QStackedLayout::setStackingMode(StackingMode stackingMode) +{ + Q_D(QStackedLayout); + if (d->stackingMode == stackingMode) + return; + d->stackingMode = stackingMode; + + const int n = d->list.count(); + if (n == 0) + return; + + switch (d->stackingMode) { + case StackOne: + if (const int idx = currentIndex()) + for (int i = 0; i < n; ++i) + if (QWidget *widget = d->list.at(i)->widget()) + widget->setVisible(i == idx); + break; + case StackAll: { // Turn overlay on: Make sure all widgets are the same size + QRect geometry; + if (const QWidget *widget = currentWidget()) + geometry = widget->geometry(); + for (int i = 0; i < n; ++i) + if (QWidget *widget = d->list.at(i)->widget()) { + if (!geometry.isNull()) + widget->setGeometry(geometry); + widget->setVisible(true); + } + } + break; + } +} + +QT_END_NAMESPACE