|
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 |