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

Last change on this file since 67 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

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