util/src/gui/graphicsview/qgraphicslayout.cpp
changeset 7 f7bc934e204c
equal deleted inserted replaced
3:41300fa6a67c 7:f7bc934e204c
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 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     If \a parent is a QGraphicsWidget the layout will be installed
       
   153     on that widget. (Note that installing a layout will delete the old one
       
   154     installed.)
       
   155 */
       
   156 QGraphicsLayout::QGraphicsLayout(QGraphicsLayoutItem *parent)
       
   157     : QGraphicsLayoutItem(*new QGraphicsLayoutPrivate)
       
   158 {
       
   159     setParentLayoutItem(parent);
       
   160     if (parent && !parent->isLayout()) {
       
   161         // If a layout has a parent that is not a layout it must be a QGraphicsWidget.
       
   162         QGraphicsItem *itemParent = parent->graphicsItem();
       
   163         if (itemParent && itemParent->isWidget()) {
       
   164             static_cast<QGraphicsWidget *>(itemParent)->d_func()->setLayout_helper(this);
       
   165         } else {
       
   166             qWarning("QGraphicsLayout::QGraphicsLayout: Attempt to create a layout with a parent that is"
       
   167                     " neither a QGraphicsWidget nor QGraphicsLayout");
       
   168         }
       
   169     }
       
   170     setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding, QSizePolicy::DefaultType);
       
   171     setOwnedByLayout(true);
       
   172 }
       
   173 
       
   174 /*!
       
   175     \internal
       
   176 */
       
   177 QGraphicsLayout::QGraphicsLayout(QGraphicsLayoutPrivate &dd, QGraphicsLayoutItem *parent)
       
   178     : QGraphicsLayoutItem(dd)
       
   179 {
       
   180     setParentLayoutItem(parent);
       
   181     if (parent && !parent->isLayout()) {
       
   182         // If a layout has a parent that is not a layout it must be a QGraphicsWidget.
       
   183         QGraphicsItem *itemParent = parent->graphicsItem();
       
   184         if (itemParent && itemParent->isWidget()) {
       
   185             static_cast<QGraphicsWidget *>(itemParent)->d_func()->setLayout_helper(this);
       
   186         } else {
       
   187             qWarning("QGraphicsLayout::QGraphicsLayout: Attempt to create a layout with a parent that is"
       
   188                     " neither a QGraphicsWidget nor QGraphicsLayout");
       
   189         }
       
   190     }
       
   191     setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding, QSizePolicy::DefaultType);
       
   192     setOwnedByLayout(true);
       
   193 }
       
   194 
       
   195 /*!
       
   196     Destroys the QGraphicsLayout object.
       
   197 */
       
   198 QGraphicsLayout::~QGraphicsLayout()
       
   199 {
       
   200 }
       
   201 
       
   202 /*!
       
   203     Sets the contents margins to \a left, \a top, \a right and \a bottom. The
       
   204     default contents margins for toplevel layouts are style dependent
       
   205     (by querying the pixelMetric for QStyle::PM_LayoutLeftMargin,
       
   206     QStyle::PM_LayoutTopMargin, QStyle::PM_LayoutRightMargin and
       
   207     QStyle::PM_LayoutBottomMargin).
       
   208 
       
   209     For sublayouts the default margins are 0.
       
   210 
       
   211     Changing the contents margins automatically invalidates the layout.
       
   212 
       
   213     \sa invalidate()
       
   214 */
       
   215 void QGraphicsLayout::setContentsMargins(qreal left, qreal top, qreal right, qreal bottom)
       
   216 {
       
   217     Q_D(QGraphicsLayout);
       
   218     if (d->left == left && d->top == top && d->right == right && d->bottom == bottom)
       
   219         return;
       
   220     d->left = left;
       
   221     d->right = right;
       
   222     d->top = top;
       
   223     d->bottom = bottom;
       
   224     invalidate();
       
   225 }
       
   226 
       
   227 /*!
       
   228     \reimp
       
   229 */
       
   230 void QGraphicsLayout::getContentsMargins(qreal *left, qreal *top, qreal *right, qreal *bottom) const
       
   231 {
       
   232     Q_D(const QGraphicsLayout);
       
   233     d->getMargin(left, d->left, QStyle::PM_LayoutLeftMargin);
       
   234     d->getMargin(top, d->top, QStyle::PM_LayoutTopMargin);
       
   235     d->getMargin(right, d->right, QStyle::PM_LayoutRightMargin);
       
   236     d->getMargin(bottom, d->bottom, QStyle::PM_LayoutBottomMargin);
       
   237 }
       
   238 
       
   239 /*!
       
   240     Activates the layout, causing all items in the layout to be immediately
       
   241     rearranged. This function is based on calling count() and itemAt(), and
       
   242     then calling setGeometry() on all items sequentially. When activated,
       
   243     the layout will adjust its geometry to its parent's contentsRect().
       
   244     The parent will then invalidate any layout of its own.
       
   245 
       
   246     If called in sequence or recursively, e.g., by one of the arranged items
       
   247     in response to being resized, this function will do nothing.
       
   248 
       
   249     Note that the layout is free to use geometry caching to optimize this
       
   250     process.  To forcefully invalidate any such cache, you can call
       
   251     invalidate() before calling activate().
       
   252 
       
   253     \sa invalidate()
       
   254 */
       
   255 void QGraphicsLayout::activate()
       
   256 {
       
   257     Q_D(QGraphicsLayout);
       
   258     if (d->activated)
       
   259         return;
       
   260 
       
   261     d->activateRecursive(this);
       
   262     
       
   263     // we don't call activate on a sublayout, but somebody might.
       
   264     // Therefore, we walk to the parentitem of the toplevel layout.
       
   265     QGraphicsLayoutItem *parentItem = this;
       
   266     while (parentItem && parentItem->isLayout())
       
   267         parentItem = parentItem->parentLayoutItem();
       
   268     if (!parentItem)
       
   269         return;
       
   270     Q_ASSERT(!parentItem->isLayout());
       
   271 
       
   272     setGeometry(parentItem->contentsRect());    // relayout children
       
   273     
       
   274     // ### bug, should be parentItem ?
       
   275     parentLayoutItem()->updateGeometry();            // bubble up; will set activated to false
       
   276     // ### too many resizes? maybe we should walk up the chain to the
       
   277     // ### top-level layouted layoutItem and call activate there.
       
   278 }
       
   279 
       
   280 /*!
       
   281     Returns true if the layout is currently being activated; otherwise,
       
   282     returns false. If the layout is being activated, this means that it is
       
   283     currently in the process of rearranging its items (i.e., the activate()
       
   284     function has been called, and has not yet returned).
       
   285 
       
   286     \sa activate(), invalidate()
       
   287 */
       
   288 bool QGraphicsLayout::isActivated() const
       
   289 {
       
   290     Q_D(const QGraphicsLayout);
       
   291     return d->activated;
       
   292 }
       
   293 
       
   294 /*!
       
   295     Clears any cached geometry and size hint information in the layout, and
       
   296     posts a \l{QEvent::LayoutRequest}{LayoutRequest} event to the managed
       
   297     parent QGraphicsLayoutItem.
       
   298 
       
   299     \sa activate(), setGeometry()
       
   300 */
       
   301 void QGraphicsLayout::invalidate()
       
   302 {
       
   303     // only mark layouts as invalid (activated = false) if we can post a LayoutRequest event.
       
   304     QGraphicsLayoutItem *layoutItem = this;
       
   305     while (layoutItem && layoutItem->isLayout()) {
       
   306         // we could call updateGeometry(), but what if that method
       
   307         // does not call the base implementation? In addition, updateGeometry()
       
   308         // does more than we need.
       
   309         layoutItem->d_func()->sizeHintCacheDirty = true;
       
   310         layoutItem = layoutItem->parentLayoutItem();        
       
   311     }
       
   312     if (layoutItem)
       
   313         layoutItem->d_func()->sizeHintCacheDirty = true;
       
   314 
       
   315     bool postIt = layoutItem ? !layoutItem->isLayout() : false;
       
   316     if (postIt) {
       
   317         layoutItem = this;
       
   318         while (layoutItem && layoutItem->isLayout()
       
   319                 && static_cast<QGraphicsLayout*>(layoutItem)->d_func()->activated) {
       
   320             static_cast<QGraphicsLayout*>(layoutItem)->d_func()->activated = false;
       
   321             layoutItem = layoutItem->parentLayoutItem();
       
   322         }
       
   323         if (layoutItem && !layoutItem->isLayout()) {
       
   324             // If a layout has a parent that is not a layout it must be a QGraphicsWidget.
       
   325             QApplication::postEvent(static_cast<QGraphicsWidget *>(layoutItem), new QEvent(QEvent::LayoutRequest));
       
   326         }
       
   327     }
       
   328 }
       
   329 
       
   330 /*!
       
   331     \reimp
       
   332 */
       
   333 void QGraphicsLayout::updateGeometry()
       
   334 {
       
   335     QGraphicsLayoutItem::updateGeometry();
       
   336     if (QGraphicsLayoutItem *parentItem = parentLayoutItem()) {
       
   337         if (parentItem->isLayout()) {
       
   338             parentItem->updateGeometry();
       
   339         } else {
       
   340             invalidate();
       
   341         }
       
   342     }
       
   343 }
       
   344 
       
   345 /*!
       
   346     This virtual event handler receives all events for the managed
       
   347     widget. QGraphicsLayout uses this event handler to listen for layout
       
   348     related events such as geometry changes, layout changes or layout
       
   349     direction changes.
       
   350     
       
   351     \a e is a pointer to the event.
       
   352 
       
   353     You can reimplement this event handler to track similar events for your
       
   354     own custom layout.
       
   355 
       
   356     \sa QGraphicsWidget::event(), QGraphicsItem::sceneEvent()
       
   357 */
       
   358 void QGraphicsLayout::widgetEvent(QEvent *e)
       
   359 {
       
   360     switch (e->type()) {
       
   361     case QEvent::GraphicsSceneResize:
       
   362         if (isActivated()) {
       
   363             setGeometry(parentLayoutItem()->contentsRect());
       
   364         } else {
       
   365             activate(); // relies on that activate() will call updateGeometry()
       
   366         }
       
   367         break;
       
   368     case QEvent::LayoutRequest:
       
   369         activate();
       
   370         break;
       
   371     case QEvent::LayoutDirectionChange:
       
   372         invalidate();
       
   373         break;
       
   374     default:
       
   375         break;
       
   376     }
       
   377 }
       
   378 
       
   379 /*!
       
   380     \fn virtual int QGraphicsLayout::count() const = 0
       
   381 
       
   382     This pure virtual function must be reimplemented in a subclass of
       
   383     QGraphicsLayout to return the number of items in the layout.
       
   384 
       
   385     The subclass is free to decide how to store the items.
       
   386 
       
   387     \sa itemAt(), removeAt()
       
   388 */
       
   389 
       
   390 /*!
       
   391     \fn virtual QGraphicsLayoutItem *QGraphicsLayout::itemAt(int i) const = 0
       
   392 
       
   393     This pure virtual function must be reimplemented in a subclass of
       
   394     QGraphicsLayout to return a pointer to the item at index \a i. The
       
   395     reimplementation can assume that \a i is valid (i.e., it respects the
       
   396     value of count()).
       
   397     Together with count(), it is provided as a means of iterating over all items in a layout.
       
   398 
       
   399     The subclass is free to decide how to store the items, and the visual arrangement
       
   400     does not have to be reflected through this function.
       
   401 
       
   402     \sa count(), removeAt()
       
   403 */
       
   404 
       
   405 /*!
       
   406     \fn virtual void QGraphicsLayout::removeAt(int index) = 0
       
   407 
       
   408     This pure virtual function must be reimplemented in a subclass of
       
   409     QGraphicsLayout to remove the item at \a index. The
       
   410     reimplementation can assume that \a index is valid (i.e., it
       
   411     respects the value of count()).
       
   412 
       
   413     The implementation must ensure that the parentLayoutItem() of
       
   414     the removed item does not point to this layout, since the item is
       
   415     considered to be removed from the layout hierarchy.
       
   416 
       
   417     If the layout is to be reused between applications, we recommend
       
   418     that the layout deletes the item, but the graphics view framework
       
   419     does not depend on this.
       
   420 
       
   421     The subclass is free to decide how to store the items.
       
   422 
       
   423     \sa itemAt(), count()
       
   424 */
       
   425 
       
   426 /*!
       
   427     \since 4.6
       
   428 
       
   429     This function is a convenience function provided for custom layouts, and will go through
       
   430     all items in the layout and reparent their graphics items to the closest QGraphicsWidget
       
   431     ancestor of the layout.
       
   432 
       
   433     If \a layoutItem is already in a different layout, it will be removed  from that layout.
       
   434 
       
   435     If custom layouts want special behaviour they can ignore to use this function, and implement
       
   436     their own behaviour.
       
   437 
       
   438     \sa graphicsItem()
       
   439  */
       
   440 void QGraphicsLayout::addChildLayoutItem(QGraphicsLayoutItem *layoutItem)
       
   441 {
       
   442     Q_D(QGraphicsLayout);
       
   443     d->addChildLayoutItem(layoutItem);
       
   444 }
       
   445 
       
   446 QT_END_NAMESPACE
       
   447 
       
   448 #endif //QT_NO_GRAPHICSVIEW