1
/****************************************************************************
3
** Copyright (C) 1992-2005 Trolltech AS. All rights reserved.
5
** This file is part of the gui module of the Qt Toolkit.
7
** This file may be distributed under the terms of the Q Public License
8
** as defined by Trolltech AS of Norway and appearing in the file
9
** LICENSE.QPL included in the packaging of this file.
11
** This file may be distributed and/or modified under the terms of the
12
** GNU General Public License version 2 as published by the Free Software
13
** Foundation and appearing in the file LICENSE.GPL included in the
14
** packaging of this file.
16
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
17
** information about Qt Commercial License Agreements.
18
** See http://www.trolltech.com/qpl/ for QPL licensing information.
19
** See http://www.trolltech.com/gpl/ for GPL licensing information.
21
** Contact info@trolltech.com if any conditions of this licensing are
24
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
25
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27
****************************************************************************/
30
#include "private/qlayoutengine_p.h"
36
#include <qalgorithms.h>
41
static inline Fixed toFixed(int i) { return (Fixed)i * 256; }
42
static inline int fRound(Fixed i) {
43
return (i % 256 < 128) ? i / 256 : 1 + i / 256;
47
This is the main workhorse of the QGridLayout. It portions out
48
available space to the chain's children.
50
The calculation is done in fixed point: "fixed" variables are
51
scaled by a factor of 256.
53
If the layout runs "backwards" (i.e. RightToLeft or Up) the layout
54
is computed mirror-reversed, and it's the caller's responsibility
55
do reverse the values before use.
57
chain contains input and output parameters describing the geometry.
58
count is the count of items in the chain; pos and space give the
59
interval (relative to parentWidget topLeft).
61
void qGeomCalc(QVector<QLayoutStruct> &chain, int start, int count,
62
int pos, int space, int spacer)
70
bool wannaGrow = false; // anyone who really wants to grow?
71
// bool canShrink = false; // anyone who could be persuaded to shrink?
74
for (i = start; i < start + count; i++) {
75
chain[i].done = false;
76
cHint += chain[i].smartSizeHint();
77
cMin += chain[i].minimumSize;
78
cMax += chain[i].maximumSize;
79
sumStretch += chain[i].stretch;
82
wannaGrow = wannaGrow || chain[i].expansive || chain[i].stretch > 0;
87
spacerCount--; // only spacers between things
89
if (space < cMin + spacerCount * spacer) {
91
Less space than minimumSize; take from the biggest first
94
int minSize = cMin + spacerCount * spacer;
96
//shrink the spacers proportionally
97
spacer = minSize > 0 ? (spacer * space) / minSize : 0;
101
for (i = start; i < start+count; i++) {
102
list << chain[i].minimumSize;
107
int space_left = space - spacerCount*spacer;
113
while (idx < count && space_used < space_left) {
114
current = list.at(idx);
115
space_used = sum + current * (count - idx);
120
int deficit = space_used - space_left;
122
int items = count - idx;
123
int maxval = current - deficit/items;
125
for (i = start; i < start+count; i++) {
126
chain[i].size = qMin(chain[i].minimumSize, maxval);
127
chain[i].done = true;
129
} else if (space < cHint + spacerCount*spacer) {
131
Less space than smartSizeHint(), but more than minimumSize.
132
Currently take space equally from each, as in Qt 2.x.
133
Commented-out lines will give more space to stretchier
137
int space_left = space - spacerCount*spacer;
138
int overdraft = cHint - space_left;
140
// first give to the fixed ones:
141
for (i = start; i < start + count; i++) {
143
&& chain[i].minimumSize >= chain[i].smartSizeHint()) {
144
chain[i].size = chain[i].smartSizeHint();
145
chain[i].done = true;
146
space_left -= chain[i].smartSizeHint();
147
// sumStretch -= chain[i].stretch;
151
bool finished = n == 0;
154
Fixed fp_over = toFixed(overdraft);
157
for (i = start; i < start+count; i++) {
160
// if (sumStretch <= 0)
163
// fp_w += (fp_over * chain[i].stretch) / sumStretch;
164
int w = fRound(fp_w);
165
chain[i].size = chain[i].smartSizeHint() - w;
166
fp_w -= toFixed(w); // give the difference to the next
167
if (chain[i].size < chain[i].minimumSize) {
168
chain[i].done = true;
169
chain[i].size = chain[i].minimumSize;
171
overdraft -= (chain[i].smartSizeHint()
172
- chain[i].minimumSize);
173
// sumStretch -= chain[i].stretch;
179
} else { // extra space
181
int space_left = space - spacerCount*spacer;
182
// first give to the fixed ones, and handle non-expansiveness
183
for (i = start; i < start + count; i++) {
185
&& (chain[i].maximumSize <= chain[i].smartSizeHint()
186
|| (wannaGrow && !chain[i].expansive && chain[i].stretch == 0))) {
187
chain[i].size = chain[i].smartSizeHint();
188
chain[i].done = true;
189
space_left -= chain[i].smartSizeHint();
190
sumStretch -= chain[i].stretch;
194
extraspace = space_left;
197
Do a trial distribution and calculate how much it is off.
198
If there are more deficit pixels than surplus pixels, give
199
the minimum size items what they need, and repeat.
200
Otherwise give to the maximum size items, and repeat.
202
Paul Olav Tvete has a wonderful mathematical proof of the
203
correctness of this principle, but unfortunately this
204
comment is too small to contain it.
206
int surplus, deficit;
208
surplus = deficit = 0;
209
Fixed fp_space = toFixed(space_left);
211
for (i = start; i < start+count; i++) {
216
fp_w += fp_space / n;
218
fp_w += (fp_space * chain[i].stretch) / sumStretch;
219
int w = fRound(fp_w);
221
fp_w -= toFixed(w); // give the difference to the next
222
if (w < chain[i].smartSizeHint()) {
223
deficit += chain[i].smartSizeHint() - w;
224
} else if (w > chain[i].maximumSize) {
225
surplus += w - chain[i].maximumSize;
228
if (deficit > 0 && surplus <= deficit) {
229
// give to the ones that have too little
230
for (i = start; i < start+count; i++) {
231
if (!chain[i].done &&
232
chain[i].size < chain[i].smartSizeHint()) {
233
chain[i].size = chain[i].smartSizeHint();
234
chain[i].done = true;
235
space_left -= chain[i].smartSizeHint();
236
sumStretch -= chain[i].stretch;
241
if (surplus > 0 && surplus >= deficit) {
242
// take from the ones that have too much
243
for (i = start; i < start+count; i++) {
244
if (!chain[i].done &&
245
chain[i].size > chain[i].maximumSize) {
246
chain[i].size = chain[i].maximumSize;
247
chain[i].done = true;
248
space_left -= chain[i].maximumSize;
249
sumStretch -= chain[i].stretch;
254
} while (n > 0 && surplus != deficit);
256
extraspace = space_left;
260
As a last resort, we distribute the unwanted space equally
261
among the spacers (counting the start and end of the chain). We
262
could, but don't, attempt a sub-pixel allocation of the extra
265
int extra = extraspace / (spacerCount + 2);
267
for (i = start; i < start+count; i++) {
269
p = p + chain[i].size;
275
Q_GUI_EXPORT QSize qSmartMinSize(const QWidgetItem *i)
277
QWidget *w = ((QWidgetItem *)i)->widget();
280
QSize msh(w->minimumSizeHint());
281
QSize sh(w->sizeHint());
283
if (w->sizePolicy().horizontalPolicy() != QSizePolicy::Ignored) {
284
if (w->sizePolicy().horizontalPolicy() & QSizePolicy::ShrinkFlag)
285
s.setWidth(msh.width());
287
s.setWidth(qMax(sh.width(), msh.width()));
290
if (w->sizePolicy().verticalPolicy() != QSizePolicy::Ignored) {
291
if (w->sizePolicy().verticalPolicy() & QSizePolicy::ShrinkFlag) {
292
s.setHeight(msh.height());
294
s.setHeight(qMax(sh.height(), msh.height()));
298
s = s.boundedTo(w->maximumSize());
299
QSize min = w->minimumSize();
301
s.setWidth(min.width());
302
if (min.height() > 0)
303
s.setHeight(min.height());
304
else if (i->hasHeightForWidth() && s.width() > 0)
305
s = s.expandedTo(QSize(0, i->heightForWidth(s.width())));
307
return s.expandedTo(QSize(0,0));
310
Q_GUI_EXPORT QSize qSmartMinSize(const QWidget *w)
312
QWidgetItem item(const_cast<QWidget *>(w));
313
return qSmartMinSize(&item);
316
Q_GUI_EXPORT QSize qSmartMaxSize(const QWidgetItem *i, Qt::Alignment align)
318
QWidget *w = ((QWidgetItem*)i)->widget();
319
if (align & Qt::AlignHorizontal_Mask && align & Qt::AlignVertical_Mask)
320
return QSize(QLAYOUTSIZE_MAX, QLAYOUTSIZE_MAX);
321
QSize s = w->maximumSize();
322
if (s.width() == QWIDGETSIZE_MAX && !(align & Qt::AlignHorizontal_Mask))
323
if (!(w->sizePolicy().horizontalPolicy() & QSizePolicy::GrowFlag))
324
s.setWidth(w->sizeHint().width());
326
if (s.height() == QWIDGETSIZE_MAX && !(align & Qt::AlignVertical_Mask))
327
if (!(w->sizePolicy().verticalPolicy() & QSizePolicy::GrowFlag))
328
s.setHeight(w->sizeHint().height());
330
s = s.expandedTo(w->minimumSize());
332
if (align & Qt::AlignHorizontal_Mask)
333
s.setWidth(QLAYOUTSIZE_MAX);
334
if (align & Qt::AlignVertical_Mask)
335
s.setHeight(QLAYOUTSIZE_MAX);
339
Q_GUI_EXPORT QSize qSmartMaxSize(const QWidget *w, Qt::Alignment align)
341
QWidgetItem item(const_cast<QWidget *>(w));
342
return qSmartMaxSize(&item, align);
345
#endif // QT_NO_LAYOUT