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;