src/gui/kernel/qstackedlayout.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the QtGui module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    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 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    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 
       
    49 QT_BEGIN_NAMESPACE
       
    50 
       
    51 class QStackedLayoutPrivate : public QLayoutPrivate
       
    52 {
       
    53     Q_DECLARE_PUBLIC(QStackedLayout)
       
    54 public:
       
    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 */
       
   144 QStackedLayout::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 */
       
   155 QStackedLayout::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 */
       
   164 QStackedLayout::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 */
       
   173 QStackedLayout::~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 */
       
   188 int 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 */
       
   208 int 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 */
       
   233 QLayoutItem *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 
       
   243 class QtFriendlyLayoutWidget : public QWidget
       
   244 {
       
   245 public:
       
   246     inline bool wasDeleted() const { return d_ptr->wasDeleted; }
       
   247 };
       
   248 
       
   249 static bool qt_wasDeleted(const QWidget *w) { return static_cast<const QtFriendlyLayoutWidget*>(w)->wasDeleted(); }
       
   250 
       
   251 
       
   252 /*!
       
   253     \reimp
       
   254 */
       
   255 QLayoutItem *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 */
       
   286 void 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 
       
   343 int 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  */
       
   358 void 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 */
       
   375 QWidget *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 */
       
   387 QWidget *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 */
       
   401 int QStackedLayout::count() const
       
   402 {
       
   403     Q_D(const QStackedLayout);
       
   404     return d->list.size();
       
   405 }
       
   406 
       
   407 
       
   408 /*!
       
   409     \reimp
       
   410 */
       
   411 void 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 */
       
   425 QSize 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 */
       
   446 QSize 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 */
       
   461 void 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 
       
   504 QStackedLayout::StackingMode QStackedLayout::stackingMode() const
       
   505 {
       
   506     Q_D(const QStackedLayout);
       
   507     return d->stackingMode;
       
   508 }
       
   509 
       
   510 void 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 
       
   543 QT_END_NAMESPACE