src/gui/graphicsview/qgraphicslayout.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
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 "qapplication.h"
       
    43 
       
    44 #ifndef QT_NO_GRAPHICSVIEW
       
    45 #include "qgraphicslayout.h"
       
    46 #include "qgraphicslayout_p.h"
       
    47 #include "qgraphicslayoutitem.h"
       
    48 #include "qgraphicslayoutitem_p.h"
       
    49 #include "qgraphicswidget.h"
       
    50 #include "qgraphicswidget_p.h"
       
    51 #include "qgraphicsscene.h"
       
    52 
       
    53 QT_BEGIN_NAMESPACE
       
    54 
       
    55 /*!
       
    56     \class QGraphicsLayout
       
    57     \brief The QGraphicsLayout class provides the base class for all layouts
       
    58     in Graphics View.
       
    59     \since 4.4
       
    60     \ingroup graphicsview-api
       
    61 
       
    62     QGraphicsLayout is an abstract class that defines a virtual API for
       
    63     arranging QGraphicsWidget children and other QGraphicsLayoutItem objects
       
    64     for a QGraphicsWidget. QGraphicsWidget assigns responsibility to a
       
    65     QGraphicsLayout through QGraphicsWidget::setLayout(). As the widget
       
    66     is resized, the layout will automatically arrange the widget's children.
       
    67     QGraphicsLayout inherits QGraphicsLayoutItem, so, it can be managed by
       
    68     any layout, including its own subclasses.
       
    69 
       
    70     \section1 Writing a Custom Layout
       
    71 
       
    72     You can use QGraphicsLayout as a base to write your own custom layout
       
    73     (e.g., a flowlayout), but it is more common to use one of its subclasses
       
    74     instead - QGraphicsLinearLayout or QGraphicsGridLayout. When creating
       
    75     a custom layout, the following functions must be reimplemented as a bare
       
    76     minimum:
       
    77 
       
    78     \table
       
    79     \header \o Function                     \o Description
       
    80     \row     \o QGraphicsLayoutItem::setGeometry()
       
    81                \o Notifies you when the geometry of the layout is set. You can
       
    82                    store the geometry in your own layout class in a reimplementation
       
    83                    of this function.
       
    84     \row    \o QGraphicsLayoutItem::sizeHint()
       
    85                \o Returns the layout's size hints.
       
    86     \row    \o QGraphicsLayout::count()
       
    87               \o Returns the number of items in your layout.
       
    88     \row    \o QGraphicsLayout::itemAt()
       
    89               \o Returns a pointer to an item in your layout.
       
    90     \row    \o QGraphicsLayout::removeAt()
       
    91               \o Removes an item from your layout without destroying it.
       
    92     \endtable
       
    93 
       
    94     For more details on how to implement each function, refer to the individual
       
    95     function documentation.
       
    96 
       
    97     Each layout defines its own API for arranging widgets and layout items.
       
    98     For example, with a grid layout, you require a row and a
       
    99     column index with optional row and column spans, alignment, spacing, and more.
       
   100     A linear layout, however, requires a single row or column index to position its
       
   101     items. For a grid layout, the order of insertion does not affect the layout in
       
   102     any way, but for a linear layout, the order is essential. When writing your own
       
   103     layout subclass, you are free to choose the API that best suits your layout.
       
   104 
       
   105     \section1 Activating the Layout
       
   106 
       
   107     When the layout's geometry changes, QGraphicsLayout immediately rearranges
       
   108     all of its managed items by calling setGeometry() on each item. This
       
   109     rearrangement is called \e activating the layout.
       
   110 
       
   111     QGraphicsLayout updates its own geometry to match the contentsRect() of the
       
   112     QGraphicsLayoutItem it is managing. Thus, it will automatically rearrange all
       
   113     its items when the widget is resized. QGraphicsLayout caches the sizes of all
       
   114     its managed items to avoid calling setGeometry() too often.
       
   115 
       
   116     \note A QGraphicsLayout will have the same geometry as the contentsRect()
       
   117     of the widget (not the layout) it is assigned to.
       
   118 
       
   119     \section2 Activating the Layout Implicitly
       
   120 
       
   121     The layout can be activated implicitly using one of two ways: by calling
       
   122     activate() or by calling invalidate(). Calling activate() activates the layout
       
   123     immediately. In contrast, calling invalidate() is delayed, as it posts a
       
   124     \l{QEvent::LayoutRequest}{LayoutRequest} event to the managed widget. Due
       
   125     to event compression, the activate() will only be called once after control has
       
   126     returned to the event loop. This is referred to as \e invalidating the layout.
       
   127     Invalidating the layout also invalidates any cached information. Also, the
       
   128     invalidate() function is a virtual function. So, you can invalidate your own
       
   129     cache in a subclass of QGraphicsLayout by reimplementing this function.
       
   130 
       
   131     \section1 Event Handling
       
   132 
       
   133     QGraphicsLayout listens to events for the widget it manages through the
       
   134     virtual widgetEvent() event handler. When the layout is assigned to a
       
   135     widget, all events delivered to the widget are first processed by
       
   136     widgetEvent(). This allows the layout to be aware of any relevant state
       
   137     changes on the widget such as visibility changes or layout direction changes.
       
   138 
       
   139     \section1 Margin Handling
       
   140 
       
   141     The margins of a QGraphicsLayout can be modified by reimplementing
       
   142     setContentsMargins() and getContentsMargins().
       
   143 
       
   144 */
       
   145 
       
   146 /*!
       
   147     Contructs a QGraphicsLayout object.
       
   148     
       
   149     \a parent is passed to QGraphicsLayoutItem's constructor and the
       
   150     QGraphicsLayoutItem's isLayout argument is set to \e true.
       
   151 */
       
   152 QGraphicsLayout::QGraphicsLayout(QGraphicsLayoutItem *parent)
       
   153     : QGraphicsLayoutItem(*new QGraphicsLayoutPrivate)
       
   154 {
       
   155     setParentLayoutItem(parent);
       
   156     if (parent && !parent->isLayout()) {
       
   157         // If a layout has a parent that is not a layout it must be a QGraphicsWidget.
       
   158         QGraphicsItem *itemParent = parent->graphicsItem();
       
   159         if (itemParent && itemParent->isWidget()) {
       
   160             static_cast<QGraphicsWidget *>(itemParent)->d_func()->setLayout_helper(this);
       
   161         } else {
       
   162             qWarning("QGraphicsLayout::QGraphicsLayout: Attempt to create a layout with a parent that is"
       
   163                     " neither a QGraphicsWidget nor QGraphicsLayout");
       
   164         }
       
   165     }
       
   166     setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding, QSizePolicy::DefaultType);
       
   167     setOwnedByLayout(true);
       
   168 }
       
   169 
       
   170 /*!
       
   171     \internal
       
   172 */
       
   173 QGraphicsLayout::QGraphicsLayout(QGraphicsLayoutPrivate &dd, QGraphicsLayoutItem *parent)
       
   174     : QGraphicsLayoutItem(dd)
       
   175 {
       
   176     setParentLayoutItem(parent);
       
   177     if (parent && !parent->isLayout()) {
       
   178         // If a layout has a parent that is not a layout it must be a QGraphicsWidget.
       
   179         QGraphicsItem *itemParent = parent->graphicsItem();
       
   180         if (itemParent && itemParent->isWidget()) {
       
   181             static_cast<QGraphicsWidget *>(itemParent)->d_func()->setLayout_helper(this);
       
   182         } else {
       
   183             qWarning("QGraphicsLayout::QGraphicsLayout: Attempt to create a layout with a parent that is"
       
   184                     " neither a QGraphicsWidget nor QGraphicsLayout");
       
   185         }
       
   186     }
       
   187     setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding, QSizePolicy::DefaultType);
       
   188     setOwnedByLayout(true);
       
   189 }
       
   190 
       
   191 /*!
       
   192     Destroys the QGraphicsLayout object.
       
   193 */
       
   194 QGraphicsLayout::~QGraphicsLayout()
       
   195 {
       
   196 }
       
   197 
       
   198 /*!
       
   199     Sets the contents margins to \a left, \a top, \a right and \a bottom. The
       
   200     default contents margins for toplevel layouts are style dependent
       
   201     (by querying the pixelMetric for QStyle::PM_LayoutLeftMargin,
       
   202     QStyle::PM_LayoutTopMargin, QStyle::PM_LayoutRightMargin and
       
   203     QStyle::PM_LayoutBottomMargin).
       
   204 
       
   205     For sublayouts the default margins are 0.
       
   206 
       
   207     Changing the contents margins automatically invalidates the layout.
       
   208 
       
   209     \sa invalidate()
       
   210 */
       
   211 void QGraphicsLayout::setContentsMargins(qreal left, qreal top, qreal right, qreal bottom)
       
   212 {
       
   213     Q_D(QGraphicsLayout);
       
   214     if (d->left == left && d->top == top && d->right == right && d->bottom == bottom)
       
   215         return;
       
   216     d->left = left;
       
   217     d->right = right;
       
   218     d->top = top;
       
   219     d->bottom = bottom;
       
   220     invalidate();
       
   221 }
       
   222 
       
   223 /*!
       
   224     \reimp
       
   225 */
       
   226 void QGraphicsLayout::getContentsMargins(qreal *left, qreal *top, qreal *right, qreal *bottom) const
       
   227 {
       
   228     Q_D(const QGraphicsLayout);
       
   229     d->getMargin(left, d->left, QStyle::PM_LayoutLeftMargin);
       
   230     d->getMargin(top, d->top, QStyle::PM_LayoutTopMargin);
       
   231     d->getMargin(right, d->right, QStyle::PM_LayoutRightMargin);
       
   232     d->getMargin(bottom, d->bottom, QStyle::PM_LayoutBottomMargin);
       
   233 }
       
   234 
       
   235 /*!
       
   236     Activates the layout, causing all items in the layout to be immediately
       
   237     rearranged. This function is based on calling count() and itemAt(), and
       
   238     then calling setGeometry() on all items sequentially. When activated,
       
   239     the layout will adjust its geometry to its parent's contentsRect().
       
   240     The parent will then invalidate any layout of its own.
       
   241 
       
   242     If called in sequence or recursively, e.g., by one of the arranged items
       
   243     in response to being resized, this function will do nothing.
       
   244 
       
   245     Note that the layout is free to use geometry caching to optimize this
       
   246     process.  To forcefully invalidate any such cache, you can call
       
   247     invalidate() before calling activate().
       
   248 
       
   249     \sa invalidate()
       
   250 */
       
   251 void QGraphicsLayout::activate()
       
   252 {
       
   253     Q_D(QGraphicsLayout);
       
   254     if (d->activated)
       
   255         return;
       
   256 
       
   257     d->activateRecursive(this);
       
   258     
       
   259     // we don't call activate on a sublayout, but somebody might.
       
   260     // Therefore, we walk to the parentitem of the toplevel layout.
       
   261     QGraphicsLayoutItem *parentItem = this;
       
   262     while (parentItem && parentItem->isLayout())
       
   263         parentItem = parentItem->parentLayoutItem();
       
   264     if (!parentItem)
       
   265         return;
       
   266     Q_ASSERT(!parentItem->isLayout());
       
   267 
       
   268     setGeometry(parentItem->contentsRect());    // relayout children
       
   269     
       
   270     // ### bug, should be parentItem ?
       
   271     parentLayoutItem()->updateGeometry();            // bubble up; will set activated to false
       
   272     // ### too many resizes? maybe we should walk up the chain to the
       
   273     // ### top-level layouted layoutItem and call activate there.
       
   274 }
       
   275 
       
   276 /*!
       
   277     Returns true if the layout is currently being activated; otherwise,
       
   278     returns false. If the layout is being activated, this means that it is
       
   279     currently in the process of rearranging its items (i.e., the activate()
       
   280     function has been called, and has not yet returned).
       
   281 
       
   282     \sa activate(), invalidate()
       
   283 */
       
   284 bool QGraphicsLayout::isActivated() const
       
   285 {
       
   286     Q_D(const QGraphicsLayout);
       
   287     return d->activated;
       
   288 }
       
   289 
       
   290 /*!
       
   291     Clears any cached geometry and size hint information in the layout, and
       
   292     posts a \l{QEvent::LayoutRequest}{LayoutRequest} event to the managed
       
   293     parent QGraphicsLayoutItem.
       
   294 
       
   295     \sa activate(), setGeometry()
       
   296 */
       
   297 void QGraphicsLayout::invalidate()
       
   298 {
       
   299     // only mark layouts as invalid (activated = false) if we can post a LayoutRequest event.
       
   300     QGraphicsLayoutItem *layoutItem = this;
       
   301     while (layoutItem && layoutItem->isLayout()) {
       
   302         // we could call updateGeometry(), but what if that method
       
   303         // does not call the base implementation? In addition, updateGeometry()
       
   304         // does more than we need.
       
   305         layoutItem->d_func()->sizeHintCacheDirty = true;
       
   306         layoutItem = layoutItem->parentLayoutItem();        
       
   307     }
       
   308     if (layoutItem)
       
   309         layoutItem->d_func()->sizeHintCacheDirty = true;
       
   310 
       
   311     bool postIt = layoutItem ? !layoutItem->isLayout() : false;
       
   312     if (postIt) {
       
   313         layoutItem = this;
       
   314         while (layoutItem && layoutItem->isLayout()
       
   315                 && static_cast<QGraphicsLayout*>(layoutItem)->d_func()->activated) {
       
   316             static_cast<QGraphicsLayout*>(layoutItem)->d_func()->activated = false;
       
   317             layoutItem = layoutItem->parentLayoutItem();
       
   318         }
       
   319         if (layoutItem && !layoutItem->isLayout()) {
       
   320             // If a layout has a parent that is not a layout it must be a QGraphicsWidget.
       
   321             QApplication::postEvent(static_cast<QGraphicsWidget *>(layoutItem), new QEvent(QEvent::LayoutRequest));
       
   322         }
       
   323     }
       
   324 }
       
   325 
       
   326 /*!
       
   327     \reimp
       
   328 */
       
   329 void QGraphicsLayout::updateGeometry()
       
   330 {
       
   331     QGraphicsLayoutItem::updateGeometry();
       
   332     if (QGraphicsLayoutItem *parentItem = parentLayoutItem()) {
       
   333         if (parentItem->isLayout()) {
       
   334             parentItem->updateGeometry();
       
   335         } else {
       
   336             invalidate();
       
   337         }
       
   338     }
       
   339 }
       
   340 
       
   341 /*!
       
   342     This virtual event handler receives all events for the managed
       
   343     widget. QGraphicsLayout uses this event handler to listen for layout
       
   344     related events such as geometry changes, layout changes or layout
       
   345     direction changes.
       
   346     
       
   347     \a e is a pointer to the event.
       
   348 
       
   349     You can reimplement this event handler to track similar events for your
       
   350     own custom layout.
       
   351 
       
   352     \sa QGraphicsWidget::event(), QGraphicsItem::sceneEvent()
       
   353 */
       
   354 void QGraphicsLayout::widgetEvent(QEvent *e)
       
   355 {
       
   356     switch (e->type()) {
       
   357     case QEvent::GraphicsSceneResize:
       
   358         if (isActivated()) {
       
   359             setGeometry(parentLayoutItem()->contentsRect());
       
   360         } else {
       
   361             activate(); // relies on that activate() will call updateGeometry()
       
   362         }
       
   363         break;
       
   364     case QEvent::LayoutRequest:
       
   365         activate();
       
   366         break;
       
   367     case QEvent::LayoutDirectionChange:
       
   368         invalidate();
       
   369         break;
       
   370     default:
       
   371         break;
       
   372     }
       
   373 }
       
   374 
       
   375 /*!
       
   376     \fn virtual int QGraphicsLayout::count() const = 0
       
   377 
       
   378     This pure virtual function must be reimplemented in a subclass of
       
   379     QGraphicsLayout to return the number of items in the layout.
       
   380 
       
   381     The subclass is free to decide how to store the items.
       
   382 
       
   383     \sa itemAt(), removeAt()
       
   384 */
       
   385 
       
   386 /*!
       
   387     \fn virtual QGraphicsLayoutItem *QGraphicsLayout::itemAt(int i) const = 0
       
   388 
       
   389     This pure virtual function must be reimplemented in a subclass of
       
   390     QGraphicsLayout to return a pointer to the item at index \a i. The
       
   391     reimplementation can assume that \a i is valid (i.e., it respects the
       
   392     value of count()).
       
   393     Together with count(), it is provided as a means of iterating over all items in a layout.
       
   394 
       
   395     The subclass is free to decide how to store the items, and the visual arrangement
       
   396     does not have to be reflected through this function.
       
   397 
       
   398     \sa count(), removeAt()
       
   399 */
       
   400 
       
   401 /*!
       
   402     \fn virtual void QGraphicsLayout::removeAt(int index) = 0
       
   403 
       
   404     This pure virtual function must be reimplemented in a subclass of
       
   405     QGraphicsLayout to remove the item at \a index. The
       
   406     reimplementation can assume that \a index is valid (i.e., it
       
   407     respects the value of count()).
       
   408 
       
   409     The implementation must ensure that the parentLayoutItem() of
       
   410     the removed item does not point to this layout, since the item is
       
   411     considered to be removed from the layout hierarchy.
       
   412 
       
   413     If the layout is to be reused between applications, we recommend
       
   414     that the layout deletes the item, but the graphics view framework
       
   415     does not depend on this.
       
   416 
       
   417     The subclass is free to decide how to store the items.
       
   418 
       
   419     \sa itemAt(), count()
       
   420 */
       
   421 
       
   422 /*!
       
   423     \since 4.6
       
   424 
       
   425     This function is a convenience function provided for custom layouts, and will go through
       
   426     all items in the layout and reparent their graphics items to the closest QGraphicsWidget
       
   427     ancestor of the layout.
       
   428 
       
   429     If \a layoutItem is already in a different layout, it will be removed  from that layout.
       
   430 
       
   431     If custom layouts want special behaviour they can ignore to use this function, and implement
       
   432     their own behaviour.
       
   433 
       
   434     \sa graphicsItem()
       
   435  */
       
   436 void QGraphicsLayout::addChildLayoutItem(QGraphicsLayoutItem *layoutItem)
       
   437 {
       
   438     Q_D(QGraphicsLayout);
       
   439     d->addChildLayoutItem(layoutItem);
       
   440 }
       
   441 
       
   442 QT_END_NAMESPACE
       
   443 
       
   444 #endif //QT_NO_GRAPHICSVIEW