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 |
|
---|
52 | QT_BEGIN_NAMESPACE
|
---|
53 |
|
---|
54 | /*
|
---|
55 | Returns true if the \a widget can be added to the \a layout;
|
---|
56 | otherwise returns false.
|
---|
57 | */
|
---|
58 | static 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 |
|
---|
68 | struct 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 |
|
---|
108 | class QBoxLayoutPrivate : public QLayoutPrivate
|
---|
109 | {
|
---|
110 | Q_DECLARE_PUBLIC(QBoxLayout)
|
---|
111 | public:
|
---|
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 |
|
---|
145 | QBoxLayoutPrivate::~QBoxLayoutPrivate()
|
---|
146 | {
|
---|
147 | }
|
---|
148 |
|
---|
149 | static 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 | */
|
---|
159 | void 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 | */
|
---|
269 | void 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;
|
---|
|
---|