source: trunk/src/gui/graphicsview/qgraphicslayout.cpp@ 624

Last change on this file since 624 was 561, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.1 sources.

File size: 16.6 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtGui module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
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** 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 have questions regarding the use of this file, please contact
37** Nokia at [email protected].
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
53QT_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*/
156QGraphicsLayout::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*/
177QGraphicsLayout::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*/
198QGraphicsLayout::~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*/
215void 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*/
230void 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*/
255void 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*/
288bool 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*/
301void 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*/
333void 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*/
358void 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 */
440void QGraphicsLayout::addChildLayoutItem(QGraphicsLayoutItem *layoutItem)
441{
442 Q_D(QGraphicsLayout);
443 d->addChildLayoutItem(layoutItem);
444}
445
446QT_END_NAMESPACE
447
448#endif //QT_NO_GRAPHICSVIEW
Note: See TracBrowser for help on using the repository browser.