Qt 4.8
qlayoutengine.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include "qlayout.h"
43 #include "private/qlayoutengine_p.h"
44 
45 #include "qvector.h"
46 #include "qwidget.h"
47 
48 #include <qlist.h>
49 #include <qalgorithms.h>
50 
51 #include <qdebug.h>
52 
54 
55 //#define QLAYOUT_EXTRA_DEBUG
56 
57 typedef qint64 Fixed64;
58 static inline Fixed64 toFixed(int i) { return (Fixed64)i * 256; }
59 static inline int fRound(Fixed64 i) {
60  return (i % 256 < 128) ? i / 256 : 1 + i / 256;
61 }
62 
63 /*
64  This is the main workhorse of the QGridLayout. It portions out
65  available space to the chain's children.
66 
67  The calculation is done in fixed point: "fixed" variables are
68  scaled by a factor of 256.
69 
70  If the layout runs "backwards" (i.e. RightToLeft or Up) the layout
71  is computed mirror-reversed, and it's the caller's responsibility
72  do reverse the values before use.
73 
74  chain contains input and output parameters describing the geometry.
75  count is the count of items in the chain; pos and space give the
76  interval (relative to parentWidget topLeft).
77 */
78 void qGeomCalc(QVector<QLayoutStruct> &chain, int start, int count,
79  int pos, int space, int spacer)
80 {
81  int cHint = 0;
82  int cMin = 0;
83  int cMax = 0;
84  int sumStretch = 0;
85  int sumSpacing = 0;
86  int expandingCount = 0;
87 
88  bool allEmptyNonstretch = true;
89  int pendingSpacing = -1;
90  int spacerCount = 0;
91  int i;
92 
93  for (i = start; i < start + count; i++) {
94  QLayoutStruct *data = &chain[i];
95 
96  data->done = false;
97  cHint += data->smartSizeHint();
98  cMin += data->minimumSize;
99  cMax += data->maximumSize;
100  sumStretch += data->stretch;
101  if (!data->empty) {
102  /*
103  Using pendingSpacing, we ensure that the spacing for the last
104  (non-empty) item is ignored.
105  */
106  if (pendingSpacing >= 0) {
107  sumSpacing += pendingSpacing;
108  ++spacerCount;
109  }
110  pendingSpacing = data->effectiveSpacer(spacer);
111  }
112  if (data->expansive)
113  expandingCount++;
114  allEmptyNonstretch = allEmptyNonstretch && data->empty && !data->expansive && data->stretch <= 0;
115  }
116 
117  int extraspace = 0;
118 
119  if (space < cMin + sumSpacing) {
120  /*
121  Less space than minimumSize; take from the biggest first
122  */
123 
124  int minSize = cMin + sumSpacing;
125 
126  // shrink the spacers proportionally
127  if (spacer >= 0) {
128  spacer = minSize > 0 ? spacer * space / minSize : 0;
129  sumSpacing = spacer * spacerCount;
130  }
131 
132  QList<int> list;
133 
134  for (i = start; i < start + count; i++)
135  list << chain.at(i).minimumSize;
136 
137  qSort(list);
138 
139  int space_left = space - sumSpacing;
140 
141  int sum = 0;
142  int idx = 0;
143  int space_used=0;
144  int current = 0;
145  while (idx < count && space_used < space_left) {
146  current = list.at(idx);
147  space_used = sum + current * (count - idx);
148  sum += current;
149  ++idx;
150  }
151  --idx;
152  int deficit = space_used - space_left;
153 
154  int items = count - idx;
155  /*
156  * If we truncate all items to "current", we would get "deficit" too many pixels. Therefore, we have to remove
157  * deficit/items from each item bigger than maxval. The actual value to remove is deficitPerItem + remainder/items
158  * "rest" is the accumulated error from using integer arithmetic.
159  */
160  int deficitPerItem = deficit/items;
161  int remainder = deficit % items;
162  int maxval = current - deficitPerItem;
163 
164  int rest = 0;
165  for (i = start; i < start + count; i++) {
166  int maxv = maxval;
167  rest += remainder;
168  if (rest >= items) {
169  maxv--;
170  rest-=items;
171  }
172  QLayoutStruct *data = &chain[i];
173  data->size = qMin(data->minimumSize, maxv);
174  data->done = true;
175  }
176  } else if (space < cHint + sumSpacing) {
177  /*
178  Less space than smartSizeHint(), but more than minimumSize.
179  Currently take space equally from each, as in Qt 2.x.
180  Commented-out lines will give more space to stretchier
181  items.
182  */
183  int n = count;
184  int space_left = space - sumSpacing;
185  int overdraft = cHint - space_left;
186 
187  // first give to the fixed ones:
188  for (i = start; i < start + count; i++) {
189  QLayoutStruct *data = &chain[i];
190  if (!data->done
191  && data->minimumSize >= data->smartSizeHint()) {
192  data->size = data->smartSizeHint();
193  data->done = true;
194  space_left -= data->smartSizeHint();
195  // sumStretch -= data->stretch;
196  n--;
197  }
198  }
199  bool finished = n == 0;
200  while (!finished) {
201  finished = true;
202  Fixed64 fp_over = toFixed(overdraft);
203  Fixed64 fp_w = 0;
204 
205  for (i = start; i < start+count; i++) {
206  QLayoutStruct *data = &chain[i];
207  if (data->done)
208  continue;
209  // if (sumStretch <= 0)
210  fp_w += fp_over / n;
211  // else
212  // fp_w += (fp_over * data->stretch) / sumStretch;
213  int w = fRound(fp_w);
214  data->size = data->smartSizeHint() - w;
215  fp_w -= toFixed(w); // give the difference to the next
216  if (data->size < data->minimumSize) {
217  data->done = true;
218  data->size = data->minimumSize;
219  finished = false;
220  overdraft -= data->smartSizeHint() - data->minimumSize;
221  // sumStretch -= data->stretch;
222  n--;
223  break;
224  }
225  }
226  }
227  } else { // extra space
228  int n = count;
229  int space_left = space - sumSpacing;
230  // first give to the fixed ones, and handle non-expansiveness
231  for (i = start; i < start + count; i++) {
232  QLayoutStruct *data = &chain[i];
233  if (!data->done
234  && (data->maximumSize <= data->smartSizeHint()
235  || (!allEmptyNonstretch && data->empty &&
236  !data->expansive && data->stretch == 0))) {
237  data->size = data->smartSizeHint();
238  data->done = true;
239  space_left -= data->size;
240  sumStretch -= data->stretch;
241  if (data->expansive)
242  expandingCount--;
243  n--;
244  }
245  }
246  extraspace = space_left;
247 
248  /*
249  Do a trial distribution and calculate how much it is off.
250  If there are more deficit pixels than surplus pixels, give
251  the minimum size items what they need, and repeat.
252  Otherwise give to the maximum size items, and repeat.
253 
254  Paul Olav Tvete has a wonderful mathematical proof of the
255  correctness of this principle, but unfortunately this
256  comment is too small to contain it.
257  */
258  int surplus, deficit;
259  do {
260  surplus = deficit = 0;
261  Fixed64 fp_space = toFixed(space_left);
262  Fixed64 fp_w = 0;
263  for (i = start; i < start + count; i++) {
264  QLayoutStruct *data = &chain[i];
265  if (data->done)
266  continue;
267  extraspace = 0;
268  if (sumStretch > 0) {
269  fp_w += (fp_space * data->stretch) / sumStretch;
270  } else if (expandingCount > 0) {
271  fp_w += (fp_space * (data->expansive ? 1 : 0)) / expandingCount;
272  } else {
273  fp_w += fp_space * 1 / n;
274  }
275  int w = fRound(fp_w);
276  data->size = w;
277  fp_w -= toFixed(w); // give the difference to the next
278  if (w < data->smartSizeHint()) {
279  deficit += data->smartSizeHint() - w;
280  } else if (w > data->maximumSize) {
281  surplus += w - data->maximumSize;
282  }
283  }
284  if (deficit > 0 && surplus <= deficit) {
285  // give to the ones that have too little
286  for (i = start; i < start+count; i++) {
287  QLayoutStruct *data = &chain[i];
288  if (!data->done && data->size < data->smartSizeHint()) {
289  data->size = data->smartSizeHint();
290  data->done = true;
291  space_left -= data->smartSizeHint();
292  sumStretch -= data->stretch;
293  if (data->expansive)
294  expandingCount--;
295  n--;
296  }
297  }
298  }
299  if (surplus > 0 && surplus >= deficit) {
300  // take from the ones that have too much
301  for (i = start; i < start + count; i++) {
302  QLayoutStruct *data = &chain[i];
303  if (!data->done && data->size > data->maximumSize) {
304  data->size = data->maximumSize;
305  data->done = true;
306  space_left -= data->maximumSize;
307  sumStretch -= data->stretch;
308  if (data->expansive)
309  expandingCount--;
310  n--;
311  }
312  }
313  }
314  } while (n > 0 && surplus != deficit);
315  if (n == 0)
316  extraspace = space_left;
317  }
318 
319  /*
320  As a last resort, we distribute the unwanted space equally
321  among the spacers (counting the start and end of the chain). We
322  could, but don't, attempt a sub-pixel allocation of the extra
323  space.
324  */
325  int extra = extraspace / (spacerCount + 2);
326  int p = pos + extra;
327  for (i = start; i < start+count; i++) {
328  QLayoutStruct *data = &chain[i];
329  data->pos = p;
330  p += data->size;
331  if (!data->empty)
332  p += data->effectiveSpacer(spacer) + extra;
333  }
334 
335 #ifdef QLAYOUT_EXTRA_DEBUG
336  qDebug() << "qGeomCalc" << "start" << start << "count" << count << "pos" << pos
337  << "space" << space << "spacer" << spacer;
338  for (i = start; i < start + count; ++i) {
339  qDebug() << i << ':' << chain[i].minimumSize << chain[i].smartSizeHint()
340  << chain[i].maximumSize << "stretch" << chain[i].stretch
341  << "empty" << chain[i].empty << "expansive" << chain[i].expansive
342  << "spacing" << chain[i].spacing;
343  qDebug() << "result pos" << chain[i].pos << "size" << chain[i].size;
344  }
345 #endif
346 }
347 
348 Q_GUI_EXPORT QSize qSmartMinSize(const QSize &sizeHint, const QSize &minSizeHint,
349  const QSize &minSize, const QSize &maxSize,
350  const QSizePolicy &sizePolicy)
351 {
352  QSize s(0, 0);
353 
354  if (sizePolicy.horizontalPolicy() != QSizePolicy::Ignored) {
355  if (sizePolicy.horizontalPolicy() & QSizePolicy::ShrinkFlag)
356  s.setWidth(minSizeHint.width());
357  else
358  s.setWidth(qMax(sizeHint.width(), minSizeHint.width()));
359  }
360 
361  if (sizePolicy.verticalPolicy() != QSizePolicy::Ignored) {
362  if (sizePolicy.verticalPolicy() & QSizePolicy::ShrinkFlag) {
363  s.setHeight(minSizeHint.height());
364  } else {
365  s.setHeight(qMax(sizeHint.height(), minSizeHint.height()));
366  }
367  }
368 
369  s = s.boundedTo(maxSize);
370  if (minSize.width() > 0)
371  s.setWidth(minSize.width());
372  if (minSize.height() > 0)
373  s.setHeight(minSize.height());
374 
375  return s.expandedTo(QSize(0,0));
376 }
377 
379 {
380  QWidget *w = ((QWidgetItem *)i)->widget();
381  return qSmartMinSize(w->sizeHint(), w->minimumSizeHint(),
382  w->minimumSize(), w->maximumSize(),
383  w->sizePolicy());
384 }
385 
387 {
388  return qSmartMinSize(w->sizeHint(), w->minimumSizeHint(),
389  w->minimumSize(), w->maximumSize(),
390  w->sizePolicy());
391 }
392 
394  const QSize &minSize, const QSize &maxSize,
395  const QSizePolicy &sizePolicy, Qt::Alignment align)
396 {
397  if (align & Qt::AlignHorizontal_Mask && align & Qt::AlignVertical_Mask)
399  QSize s = maxSize;
400  QSize hint = sizeHint.expandedTo(minSize);
401  if (s.width() == QWIDGETSIZE_MAX && !(align & Qt::AlignHorizontal_Mask))
402  if (!(sizePolicy.horizontalPolicy() & QSizePolicy::GrowFlag))
403  s.setWidth(hint.width());
404 
405  if (s.height() == QWIDGETSIZE_MAX && !(align & Qt::AlignVertical_Mask))
406  if (!(sizePolicy.verticalPolicy() & QSizePolicy::GrowFlag))
407  s.setHeight(hint.height());
408 
409  if (align & Qt::AlignHorizontal_Mask)
411  if (align & Qt::AlignVertical_Mask)
413  return s;
414 }
415 
416 Q_GUI_EXPORT QSize qSmartMaxSize(const QWidgetItem *i, Qt::Alignment align)
417 {
418  QWidget *w = ((QWidgetItem*)i)->widget();
419 
421  w->sizePolicy(), align);
422 }
423 
424 Q_GUI_EXPORT QSize qSmartMaxSize(const QWidget *w, Qt::Alignment align)
425 {
427  w->sizePolicy(), align);
428 }
429 
431 {
432  QObject *parent = layout->parent();
433  if (!parent) {
434  return -1;
435  } else if (parent->isWidgetType()) {
436  QWidget *pw = static_cast<QWidget *>(parent);
437  return pw->style()->pixelMetric(pm, 0, pw);
438  } else {
439  return static_cast<QLayout *>(parent)->spacing();
440  }
441 }
442 
QSize maximumSize
the widget&#39;s maximum size in pixels
Definition: qwidget.h:173
QSize minimumSize
the widget&#39;s minimum size
Definition: qwidget.h:172
Q_DECL_CONSTEXPR const T & qMin(const T &a, const T &b)
Definition: qglobal.h:1215
#define QT_END_NAMESPACE
This macro expands to.
Definition: qglobal.h:90
static Fixed64 toFixed(int i)
Q_GUI_EXPORT QSize qSmartMinSize(const QSize &sizeHint, const QSize &minSizeHint, const QSize &minSize, const QSize &maxSize, const QSizePolicy &sizePolicy)
virtual int pixelMetric(PixelMetric metric, const QStyleOption *option=0, const QWidget *widget=0) const =0
Returns the value of the given pixel metric.
#define Q_GUI_EXPORT
Definition: qglobal.h:1450
Q_GUI_EXPORT QSize qSmartMaxSize(const QSize &sizeHint, const QSize &minSize, const QSize &maxSize, const QSizePolicy &sizePolicy, Qt::Alignment align)
Policy horizontalPolicy() const
Definition: qsizepolicy.h:118
#define QWIDGETSIZE_MAX
Defines the maximum size for a QWidget object.
Definition: qwidget.h:1087
The QWidget class is the base class of all user interface objects.
Definition: qwidget.h:150
PixelMetric
This enum describes the various available pixel metrics.
Definition: qstyle.h:474
QSize expandedTo(const QSize &) const
Returns a size holding the maximum width and height of this size and the given otherSize.
Definition: qsize.h:187
qint64 Fixed64
void setHeight(int h)
Sets the height to the given height.
Definition: qsize.h:135
The QObject class is the base class of all Qt objects.
Definition: qobject.h:111
bool empty() const
This function is provided for STL compatibility.
Definition: qvector.h:285
static int fRound(Fixed64 i)
int effectiveSpacer(int uniformSpacer) const
QSize boundedTo(const QSize &) const
Returns a size holding the minimum width and height of this size and the given otherSize.
Definition: qsize.h:192
Q_DECL_CONSTEXPR const T & qMax(const T &a, const T &b)
Definition: qglobal.h:1217
QStyle * style() const
Definition: qwidget.cpp:2742
void setWidth(int w)
Sets the width to the given width.
Definition: qsize.h:132
Q_CORE_EXPORT void qDebug(const char *,...)
int width() const
Returns the width.
Definition: qsize.h:126
#define QT_BEGIN_NAMESPACE
This macro expands to.
Definition: qglobal.h:89
The QLayout class is the base class of geometry managers.
Definition: qlayout.h:90
const T & at(int i) const
Returns the item at index position i in the list.
Definition: qlist.h:468
const char * layout
QSize minimumSizeHint
the recommended minimum size for the widget
Definition: qwidget.h:196
static const char * data(const QByteArray &arr)
__int64 qint64
Definition: qglobal.h:942
The QWidgetItem class is a layout item that represents a widget.
Definition: qlayoutitem.h:122
void qSort(RandomAccessIterator start, RandomAccessIterator end)
Definition: qalgorithms.h:177
const T & at(int i) const
Returns the item at index position i in the vector.
Definition: qvector.h:350
void qGeomCalc(QVector< QLayoutStruct > &chain, int start, int count, int pos, int space, int spacer)
bool isWidgetType() const
Returns true if the object is a widget; otherwise returns false.
Definition: qobject.h:146
Q_GUI_EXPORT int qSmartSpacing(const QLayout *layout, QStyle::PixelMetric pm)
QSizePolicy sizePolicy
the default layout behavior of the widget
Definition: qwidget.h:171
QObject * parent() const
Returns a pointer to the parent object.
Definition: qobject.h:273
Policy verticalPolicy() const
Definition: qsizepolicy.h:119
int height() const
Returns the height.
Definition: qsize.h:129
The QSize class defines the size of a two-dimensional object using integer point precision.
Definition: qsize.h:53
QSize sizeHint
the recommended size for the widget
Definition: qwidget.h:195
static const int QLAYOUTSIZE_MAX
Definition: qlayoutitem.h:56
int size() const
Returns the number of items in the vector.
Definition: qvector.h:137