source: trunk/src/gui/kernel/qboxlayout.cpp@ 808

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

trunk: Merged in qt 4.6.2 sources.

File size: 44.1 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 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 "qboxlayout.h"
43#include "qapplication.h"
44#include "qwidget.h"
45#include "qlist.h"
46#include "qsizepolicy.h"
47#include "qvector.h"
48
49#include "qlayoutengine_p.h"
50#include "qlayout_p.h"
51
52QT_BEGIN_NAMESPACE
53
54/*
55 Returns true if the \a widget can be added to the \a layout;
56 otherwise returns false.
57*/
58static bool checkWidget(QLayout *layout, QWidget *widget)
59{
60 if (!widget) {
61 qWarning("QLayout: Cannot add null widget to %s/%s", layout->metaObject()->className(),
62 layout->objectName().toLocal8Bit().data());
63 return false;
64 }
65 return true;
66}
67
68struct QBoxLayoutItem
69{
70 QBoxLayoutItem(QLayoutItem *it, int stretch_ = 0)
71 : item(it), stretch(stretch_), magic(false) { }
72 ~QBoxLayoutItem() { delete item; }
73
74 int hfw(int w) {
75 if (item->hasHeightForWidth()) {
76 return item->heightForWidth(w);
77 } else {
78 return item->sizeHint().height();
79 }
80 }
81 int mhfw(int w) {
82 if (item->hasHeightForWidth()) {
83 return item->heightForWidth(w);
84 } else {
85 return item->minimumSize().height();
86 }
87 }
88 int hStretch() {
89 if (stretch == 0 && item->widget()) {
90 return item->widget()->sizePolicy().horizontalStretch();
91 } else {
92 return stretch;
93 }
94 }
95 int vStretch() {
96 if (stretch == 0 && item->widget()) {
97 return item->widget()->sizePolicy().verticalStretch();
98 } else {
99 return stretch;
100 }
101 }
102
103 QLayoutItem *item;
104 int stretch;
105 bool magic;
106};
107
108class QBoxLayoutPrivate : public QLayoutPrivate
109{
110 Q_DECLARE_PUBLIC(QBoxLayout)
111public:
112 QBoxLayoutPrivate() : hfwWidth(-1), dirty(true), spacing(-1) { }
113 ~QBoxLayoutPrivate();
114
115 void setDirty() {
116 geomArray.clear();
117 hfwWidth = -1;
118 hfwHeight = -1;
119 dirty = true;
120 }
121
122 QList<QBoxLayoutItem *> list;
123 QVector<QLayoutStruct> geomArray;
124 int hfwWidth;
125 int hfwHeight;
126 int hfwMinHeight;
127 QSize sizeHint;
128 QSize minSize;
129 QSize maxSize;
130 int leftMargin, topMargin, rightMargin, bottomMargin;
131 Qt::Orientations expanding;
132 uint hasHfw : 1;
133 uint dirty : 1;
134 QBoxLayout::Direction dir;
135 int spacing;
136
137 inline void deleteAll() { while (!list.isEmpty()) delete list.takeFirst(); }
138
139 void setupGeom();
140 void calcHfw(int);
141
142 void effectiveMargins(int *left, int *top, int *right, int *bottom) const;
143};
144
145QBoxLayoutPrivate::~QBoxLayoutPrivate()
146{
147}
148
149static inline bool horz(QBoxLayout::Direction dir)
150{
151 return dir == QBoxLayout::RightToLeft || dir == QBoxLayout::LeftToRight;
152}
153
154/**
155 * The purpose of this function is to make sure that widgets are not laid out outside its layout.
156 * E.g. the layoutItemRect margins are only meant to take of the surrounding margins/spacings.
157 * However, if the margin is 0, it can easily cover the area of a widget above it.
158 */
159void QBoxLayoutPrivate::effectiveMargins(int *left, int *top, int *right, int *bottom) const
160{
161 int l = leftMargin;
162 int t = topMargin;
163 int r = rightMargin;
164 int b = bottomMargin;
165#ifdef Q_WS_MAC
166 Q_Q(const QBoxLayout);
167 if (horz(dir)) {
168 QBoxLayoutItem *leftBox = 0;
169 QBoxLayoutItem *rightBox = 0;
170
171 if (left || right) {
172 leftBox = list.value(0);
173 rightBox = list.value(list.count() - 1);
174 if (dir == QBoxLayout::RightToLeft)
175 qSwap(leftBox, rightBox);
176
177 int leftDelta = 0;
178 int rightDelta = 0;
179 if (leftBox) {
180 QLayoutItem *itm = leftBox->item;
181 if (QWidget *w = itm->widget())
182 leftDelta = itm->geometry().left() - w->geometry().left();
183 }
184 if (rightBox) {
185 QLayoutItem *itm = rightBox->item;
186 if (QWidget *w = itm->widget())
187 rightDelta = w->geometry().right() - itm->geometry().right();
188 }
189 QWidget *w = q->parentWidget();
190 Qt::LayoutDirection layoutDirection = w ? w->layoutDirection() : QApplication::layoutDirection();
191 if (layoutDirection == Qt::RightToLeft)
192 qSwap(leftDelta, rightDelta);
193
194 l = qMax(l, leftDelta);
195 r = qMax(r, rightDelta);
196 }
197
198 int count = top || bottom ? list.count() : 0;
199 for (int i = 0; i < count; ++i) {
200 QBoxLayoutItem *box = list.at(i);
201 QLayoutItem *itm = box->item;
202 QWidget *w = itm->widget();
203 if (w) {
204 QRect lir = itm->geometry();
205 QRect wr = w->geometry();
206 if (top)
207 t = qMax(t, lir.top() - wr.top());
208 if (bottom)
209 b = qMax(b, wr.bottom() - lir.bottom());
210 }
211 }
212 } else { // vertical layout
213 QBoxLayoutItem *topBox = 0;
214 QBoxLayoutItem *bottomBox = 0;
215
216 if (top || bottom) {
217 topBox = list.value(0);
218 bottomBox = list.value(list.count() - 1);
219 if (dir == QBoxLayout::BottomToTop) {
220 qSwap(topBox, bottomBox);
221 }
222
223 if (top && topBox) {
224 QLayoutItem *itm = topBox->item;
225 QWidget *w = itm->widget();
226 if (w)
227 t = qMax(t, itm->geometry().top() - w->geometry().top());
228 }
229
230 if (bottom && bottomBox) {
231 QLayoutItem *itm = bottomBox->item;
232 QWidget *w = itm->widget();
233 if (w)
234 b = qMax(b, w->geometry().bottom() - itm->geometry().bottom());
235 }
236 }
237
238 int count = left || right ? list.count() : 0;
239 for (int i = 0; i < count; ++i) {
240 QBoxLayoutItem *box = list.at(i);
241 QLayoutItem *itm = box->item;
242 QWidget *w = itm->widget();
243 if (w) {
244 QRect lir = itm->geometry();
245 QRect wr = w->geometry();
246 if (left)
247 l = qMax(l, lir.left() - wr.left());
248 if (right)
249 r = qMax(r, wr.right() - lir.right());
250 }
251 }
252 }
253#endif
254 if (left)
255 *left = l;
256 if (top)
257 *top = t;
258 if (right)
259 *right = r;
260 if (bottom)
261 *bottom = b;
262}
263
264
265/*
266 Initializes the data structure needed by qGeomCalc and
267 recalculates max/min and size hint.
268*/
269void QBoxLayoutPrivate::setupGeom()
270{
271 if (!dirty)
272 return;
273
274 Q_Q(QBoxLayout);
275 int maxw = horz(dir) ? 0 : QLAYOUTSIZE_MAX;
276 int maxh = horz(dir) ? QLAYOUTSIZE_MAX : 0;
277 int minw = 0;
278 int minh = 0;
279 int hintw = 0;
280 int hinth = 0;
281
282 bool horexp = false;
283 bool verexp = false;
284
285 hasHfw = false;
286
287 int n = list.count();
288 geomArray.clear();
289 QVector<QLayoutStruct> a(n);
290
291 QSizePolicy::ControlTypes controlTypes1;
292 QSizePolicy::ControlTypes controlTypes2;
293 int fixedSpacing = q->spacing();
294 int previousNonEmptyIndex = -1;
295
296 QStyle *style = 0;
297 if (fixedSpacing < 0) {
298 if (QWidget *parentWidget = q->parentWidget())
299 style = parentWidget->style();
300 }
301
302 for (int i = 0; i < n; i++) {
303 QBoxLayoutItem *box = list.at(i);
304 QSize max = box->item->maximumSize();
305 QSize min = box->item->minimumSize();
306 QSize hint = box->item->sizeHint();
307 Qt::Orientations exp = box->item->expandingDirections();
308 bool empty = box->item->isEmpty();
309 int spacing = 0;
310
311 if (!empty) {
312 if (fixedSpacing >= 0) {
313 spacing = (previousNonEmptyIndex >= 0) ? fixedSpacing : 0;
314#ifdef Q_WS_MAC
315 if (!horz(dir) && previousNonEmptyIndex >= 0) {
316 QBoxLayoutItem *sibling = (dir == QBoxLayout::TopToBottom ? box : list.at(previousNonEmptyIndex));
317 if (sibling) {
318 QWidget *wid = sibling->item->widget();
319 if (wid)
320 spacing = qMax(spacing, sibling->item->geometry().top() - wid->geometry().top());
321 }
322 }
323#endif
324 } else {
325 controlTypes1 = controlTypes2;
326 controlTypes2 = box->item->controlTypes();
327 if (previousNonEmptyIndex >= 0) {
328 QSizePolicy::ControlTypes actual1 = controlTypes1;
329 QSizePolicy::ControlTypes actual2 = controlTypes2;
330 if (dir == QBoxLayout::RightToLeft || dir == QBoxLayout::BottomToTop)
331 qSwap(actual1, actual2);
332
333 if (style) {
334 spacing = style->combinedLayoutSpacing(actual1, actual2,
335 horz(dir) ? Qt::Horizontal : Qt::Vertical,
336 0, q->parentWidget());
337 if (spacing < 0)
338 spacing = 0;
339 }
340 }
341 }
342
343 if (previousNonEmptyIndex >= 0)
344 a[previousNonEmptyIndex].spacing = spacing;
345 previousNonEmptyIndex = i;
346 }
347
348 bool ignore = empty && box->item->widget(); // ignore hidden widgets
349 bool dummy = true;
350 if (horz(dir)) {
351 bool expand = (exp & Qt::Horizontal || box->stretch > 0);
352 horexp = horexp || expand;
353 maxw += spacing + max.width();
354 minw += spacing + min.width();
355 hintw += spacing + hint.width();
356 if (!ignore)
357 qMaxExpCalc(maxh, verexp, dummy,
358 max.height(), exp & Qt::Vertical, box->item->isEmpty());
359 minh = qMax(minh, min.height());
360 hinth = qMax(hinth, hint.height());
361
362 a[i].sizeHint = hint.width();
363 a[i].maximumSize = max.width();
364 a[i].minimumSize = min.width();
365 a[i].expansive = expand;
366 a[i].stretch = box->stretch ? box->stretch : box->hStretch();
367 } else {
368 bool expand = (exp & Qt::Vertical || box->stretch > 0);
369 verexp = verexp || expand;
370 maxh += spacing + max.height();
371 minh += spacing + min.height();
372 hinth += spacing + hint.height();
373 if (!ignore)
374 qMaxExpCalc(maxw, horexp, dummy,
375 max.width(), exp & Qt::Horizontal, box->item->isEmpty());
376 minw = qMax(minw, min.width());
377 hintw = qMax(hintw, hint.width());
378
379 a[i].sizeHint = hint.height();
380 a[i].maximumSize = max.height();
381 a[i].minimumSize = min.height();
382 a[i].expansive = expand;
383 a[i].stretch = box->stretch ? box->stretch : box->vStretch();
384 }
385
386 a[i].empty = empty;
387 a[i].spacing = 0; // might be be initialized with a non-zero value in a later iteration
388 hasHfw = hasHfw || box->item->hasHeightForWidth();
389 }
390
391 geomArray = a;
392
393 expanding = (Qt::Orientations)
394 ((horexp ? Qt::Horizontal : 0)
395 | (verexp ? Qt::Vertical : 0));
396
397 minSize = QSize(minw, minh);
398 maxSize = QSize(maxw, maxh).expandedTo(minSize);
399 sizeHint = QSize(hintw, hinth).expandedTo(minSize).boundedTo(maxSize);
400
401 q->getContentsMargins(&leftMargin, &topMargin, &rightMargin, &bottomMargin);
402 int left, top, right, bottom;
403 effectiveMargins(&left, &top, &right, &bottom);
404 QSize extra(left + right, top + bottom);
405
406 minSize += extra;
407 maxSize += extra;
408 sizeHint += extra;
409
410 dirty = false;
411}
412
413/*
414 Calculates and stores the preferred height given the width \a w.
415*/
416void QBoxLayoutPrivate::calcHfw(int w)
417{
418 QVector<QLayoutStruct> &a = geomArray;
419 int n = a.count();
420 int h = 0;
421 int mh = 0;
422
423 Q_ASSERT(n == list.size());
424
425 if (horz(dir)) {
426 qGeomCalc(a, 0, n, 0, w);
427 for (int i = 0; i < n; i++) {
428 QBoxLayoutItem *box = list.at(i);
429 h = qMax(h, box->hfw(a.at(i).size));
430 mh = qMax(mh, box->mhfw(a.at(i).size));
431 }
432 } else {
433 for (int i = 0; i < n; ++i) {
434 QBoxLayoutItem *box = list.at(i);
435 int spacing = a.at(i).spacing;
436 h += box->hfw(w);
437 mh += box->mhfw(w);
438 h += spacing;
439 mh += spacing;
440 }
441 }
442 hfwWidth = w;
443 hfwHeight = h;
444 hfwMinHeight = mh;
445}
446
447
448/*!
449 \class QBoxLayout
450
451 \brief The QBoxLayout class lines up child widgets horizontally or
452 vertically.
453
454 \ingroup geomanagement
455
456 QBoxLayout takes the space it gets (from its parent layout or from
457 the parentWidget()), divides it up into a row of boxes, and makes
458 each managed widget fill one box.
459
460 \image qhboxlayout-with-5-children.png Horizontal box layout with five child widgets
461
462 If the QBoxLayout's orientation is Qt::Horizontal the boxes are
463 placed in a row, with suitable sizes. Each widget (or other box)
464 will get at least its minimum size and at most its maximum size.
465 Any excess space is shared according to the stretch factors (more
466 about that below).
467
468 \image qvboxlayout-with-5-children.png Vertical box layout with five child widgets
469
470 If the QBoxLayout's orientation is Qt::Vertical, the boxes are
471 placed in a column, again with suitable sizes.
472
473 The easiest way to create a QBoxLayout is to use one of the
474 convenience classes, e.g. QHBoxLayout (for Qt::Horizontal boxes)
475 or QVBoxLayout (for Qt::Vertical boxes). You can also use the
476 QBoxLayout constructor directly, specifying its direction as
477 LeftToRight, RightToLeft, TopToBottom, or BottomToTop.
478
479 If the QBoxLayout is not the top-level layout (i.e. it is not
480 managing all of the widget's area and children), you must add it
481 to its parent layout before you can do anything with it. The
482 normal way to add a layout is by calling
483 parentLayout-\>addLayout().
484
485 Once you have done this, you can add boxes to the QBoxLayout using
486 one of four functions:
487
488 \list
489 \o addWidget() to add a widget to the QBoxLayout and set the
490 widget's stretch factor. (The stretch factor is along the row of
491 boxes.)
492
493 \o addSpacing() to create an empty box; this is one of the
494 functions you use to create nice and spacious dialogs. See below
495 for ways to set margins.
496
497 \o addStretch() to create an empty, stretchable box.
498
499 \o addLayout() to add a box containing another QLayout to the row
500 and set that layout's stretch factor.
501 \endlist
502
503 Use insertWidget(), insertSpacing(), insertStretch() or
504 insertLayout() to insert a box at a specified position in the
505 layout.
506
507 QBoxLayout also includes two margin widths:
508
509 \list
510 \o setContentsMargins() sets the width of the outer border on
511 each side of the widget. This is the width of the reserved space
512 along each of the QBoxLayout's four sides.
513 \o setSpacing() sets the width between neighboring boxes. (You
514 can use addSpacing() to get more space at a particular spot.)
515 \endlist
516
517 The margin default is provided by the style. The default margin
518 most Qt styles specify is 9 for child widgets and 11 for windows.
519 The spacing defaults to the same as the margin width for a
520 top-level layout, or to the same as the parent layout.
521
522 To remove a widget from a layout, call removeWidget(). Calling
523 QWidget::hide() on a widget also effectively removes the widget
524 from the layout until QWidget::show() is called.
525
526 You will almost always want to use QVBoxLayout and QHBoxLayout
527 rather than QBoxLayout because of their convenient constructors.
528
529 \sa QGridLayout, QStackedLayout, {Layout Management}
530*/
531
532/*!
533 \enum QBoxLayout::Direction
534
535 This type is used to determine the direction of a box layout.
536
537 \value LeftToRight Horizontal from left to right.
538 \value RightToLeft Horizontal from right to left.
539 \value TopToBottom Vertical from top to bottom.
540 \value BottomToTop Vertical from bottom to top.
541
542 \omitvalue Down
543 \omitvalue Up
544*/
545
546/*!
547 Constructs a new QBoxLayout with direction \a dir and parent widget \a
548 parent.
549
550 \sa direction()
551*/
552QBoxLayout::QBoxLayout(Direction dir, QWidget *parent)
553 : QLayout(*new QBoxLayoutPrivate, 0, parent)
554{
555 Q_D(QBoxLayout);
556 d->dir = dir;
557}