1
/* This file is part of the KDE project
2
Copyright (C) 2005 Cedric Pasteur <cedric.pasteur@free.fr>
3
Copyright (C) 2007-2012 JarosÅaw Staniek <staniek@kde.org>
5
This library is free software; you can redistribute it and/or
6
modify it under the terms of the GNU Library General Public
7
License as published by the Free Software Foundation; either
8
version 2 of the License, or (at your option) any later version.
10
This library is distributed in the hope that it will be useful,
11
but WITHOUT ANY WARRANTY; without even the implied warranty of
12
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
Library General Public License for more details.
15
You should have received a copy of the GNU Library General Public License
16
along with this library; see the file COPYING.LIB. If not, write to
17
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18
* Boston, MA 02110-1301, USA.
21
#include "FlowLayout.h"
22
#include "kexiutils_global.h"
26
class KexiFlowLayout::Private
32
QList<QLayoutItem*> list;
36
Qt::Orientation orientation;
37
QSize cached_sizeHint;
41
KexiFlowLayout::Private::Private()
42
: cached_width(0), cached_hfw(0), justify(false), orientation(Qt::Horizontal)
47
KexiFlowLayout::Private::~Private()
52
//// The layout itself
54
KexiFlowLayout::KexiFlowLayout(QWidget *parent, int margin, int spacing)
55
: QLayout(parent), d(new Private())
61
KexiFlowLayout::KexiFlowLayout(QLayout* parent, int margin, int spacing)
62
: QLayout(), d(new Private())
64
parent->addItem(this);
69
KexiFlowLayout::KexiFlowLayout(int margin, int spacing)
70
: QLayout(), d(new Private())
76
KexiFlowLayout::~KexiFlowLayout()
82
KexiFlowLayout::addItem(QLayoutItem *item)
87
void KexiFlowLayout::addSpacing(int size)
89
if (d->orientation == Qt::Horizontal)
90
addItem(new QSpacerItem(size, 0, QSizePolicy::Fixed, QSizePolicy::Minimum));
92
addItem(new QSpacerItem(0, size, QSizePolicy::Minimum, QSizePolicy::Fixed));
95
void KexiFlowLayout::insertWidget(int index, QWidget* widget, int stretch, Qt::Alignment alignment)
98
QWidgetItem *wi = new QWidgetItem(widget);
99
wi->setAlignment(alignment);
100
d->list.insert(index, wi);
104
QList<QWidget*>* KexiFlowLayout::widgetList() const
106
QList<QWidget*> *list = new QList<QWidget*>();
107
foreach(QLayoutItem* item, d->list) {
109
list->append(item->widget());
114
void KexiFlowLayout::invalidate()
116
QLayout::invalidate();
117
d->cached_sizeHint = QSize();
118
d->cached_minSize = QSize();
122
int KexiFlowLayout::count() const
124
return d->list.size();
127
bool KexiFlowLayout::isEmpty() const
129
return d->list.isEmpty();
132
bool KexiFlowLayout::hasHeightForWidth() const
134
return (d->orientation == Qt::Horizontal);
137
int KexiFlowLayout::heightForWidth(int w) const
139
if (d->cached_width != w) {
140
// workaround to allow this method to stay 'const'
141
KexiFlowLayout *mthis = (KexiFlowLayout*)this;
142
int h = mthis->simulateLayout(QRect(0, 0, w, 0));
143
mthis->d->cached_hfw = h;
144
mthis->d->cached_width = w;
147
return d->cached_hfw;
150
QSize KexiFlowLayout::sizeHint() const
152
if (d->cached_sizeHint.isEmpty()) {
153
KexiFlowLayout *mthis = (KexiFlowLayout*)this;
154
QRect r = QRect(0, 0, 2000, 2000);
155
mthis->simulateLayout(r);
157
return d->cached_sizeHint;
160
QSize KexiFlowLayout::minimumSize() const
162
//! @todo Do we really need to simulate layout here?
163
//! I commented this out because it was impossible to stretch layout conveniently.
164
//! Now, minimum size is computed automatically based on item's minimumSize...
166
if (d->cached_minSize.isEmpty()) {
167
KexiFlowLayout *mthis = (KexiFlowLayout*)this;
168
QRect r = QRect(0, 0, 2000, 2000);
169
mthis->simulateLayout(r);
172
return d->cached_minSize;
175
Qt::Orientations KexiFlowLayout::expandingDirections() const
177
if (d->orientation == Qt::Vertical)
180
return Qt::Horizontal;
183
void KexiFlowLayout::setGeometry(const QRect &r)
185
QLayout::setGeometry(r);
186
if (d->orientation == Qt::Horizontal)
187
doHorizontalLayout(r);
192
int KexiFlowLayout::simulateLayout(const QRect &r)
194
if (d->orientation == Qt::Horizontal)
195
return doHorizontalLayout(r, true);
197
return doVerticalLayout(r, true);
200
inline void doHorizontalLayoutForLine(const QRect &r, const QList<QLayoutItem*>& currentLine,
201
int spacing, bool justify, int& y, int& h, int& availableSpace, int& expandingWidgets,
202
int& sizeHintWidth, int& minSizeWidth, int& lineMinHeight, bool testOnly)
204
QListIterator<QLayoutItem*> it2(currentLine);
206
sizeHintWidth = 0 - spacing;
207
minSizeWidth = 0 - spacing;
209
while (it2.hasNext()) {
210
QLayoutItem *item = it2.next();
211
QSize itemSizeHint = item->sizeHint(); // we cache these ones because it can take
212
QSize itemMinSize = item->minimumSize(); // a while to get them
215
if (expandingWidgets != 0) {
216
if (item->expandingDirections() & Qt::Horizontal)
218
qMin(itemSizeHint.width() + availableSpace / expandingWidgets, r.width()),
219
itemSizeHint.height()
222
s = QSize(qMin(itemSizeHint.width(), r.width()), itemSizeHint.height());
225
qMin(itemSizeHint.width() + availableSpace / (int)currentLine.count(), r.width()),
226
itemSizeHint.height()
229
s = QSize(qMin(itemSizeHint.width(), r.width()), itemSizeHint.height());
231
// adjust vertical position depending on vertical alignment
233
if (item->alignment() & Qt::AlignBottom)
234
add_y = h - s.height() - 1;
235
else if (item->alignment() & Qt::AlignVCenter)
236
add_y = (h - s.height() - 1) / 2;
238
add_y = 0; // Qt::AlignTop
239
item->setGeometry(QRect(QPoint(wx, y + add_y), s));
241
wx = wx + s.width() + spacing;
242
minSizeWidth = minSizeWidth + spacing + itemMinSize.width();
243
sizeHintWidth = sizeHintWidth + spacing + itemSizeHint.width();
244
lineMinHeight = qMax(lineMinHeight, itemMinSize.height());
248
int KexiFlowLayout::doHorizontalLayout(const QRect &r, bool testOnly)
252
int h = 0; // height of this line
253
int availableSpace = r.width() + spacing();
254
int expandingWidgets = 0; // number of widgets in the line with QSizePolicy == Expanding
255
QListIterator<QLayoutItem*> it(d->list);
256
QList<QLayoutItem*> currentLine;
257
QSize minSize, sizeHint(20, 20);
258
int minSizeHeight = 0 - spacing();
260
while (it.hasNext()) {
261
QLayoutItem *o = it.next();
262
if (o->isEmpty()) // do not consider hidden widgets
265
// qDebug() << o->widget()->className() << " " << o->widget()->name();
266
QSize oSizeHint = o->sizeHint(); // we cache these ones because it can take
267
// a while to get it (eg for child layouts)
268
if ((x + oSizeHint.width()) > r.right() && h > 0) {
269
// do the layout of current line
270
int sizeHintWidth, minSizeWidth, lineMinHeight;
271
doHorizontalLayoutForLine(r, currentLine,
272
spacing(), d->justify, y, h, availableSpace, expandingWidgets,
273
sizeHintWidth, minSizeWidth, lineMinHeight, testOnly);
275
sizeHint = sizeHint.expandedTo(QSize(sizeHintWidth, 0));
276
minSize = minSize.expandedTo(QSize(minSizeWidth, 0));
277
minSizeHeight = minSizeHeight + spacing() + lineMinHeight;
279
y = y + spacing() + h;
283
expandingWidgets = 0;
284
availableSpace = r.width() + spacing();
287
x = x + spacing() + oSizeHint.width();
288
h = qMax(h, oSizeHint.height());
289
currentLine.append(o);
290
if (o->expandingDirections() & Qt::Horizontal)
292
availableSpace = qMax(0, availableSpace - spacing() - oSizeHint.width());
295
// don't forget to layout the last line
296
int sizeHintWidth, minSizeWidth, lineMinHeight;
297
doHorizontalLayoutForLine(r, currentLine,
298
spacing(), d->justify, y, h, availableSpace, expandingWidgets,
299
sizeHintWidth, minSizeWidth, lineMinHeight, testOnly);
301
sizeHint = sizeHint.expandedTo(QSize(sizeHintWidth, y + spacing() + h));
302
minSizeHeight = minSizeHeight + spacing() + lineMinHeight;
303
minSize = minSize.expandedTo(QSize(minSizeWidth, minSizeHeight));
305
// store sizeHint() and minimumSize()
306
d->cached_sizeHint = sizeHint + QSize(2 * margin(), 2 * margin());
307
d->cached_minSize = minSize + QSize(2 * margin() , 2 * margin());
309
return y + h - r.y();
312
inline void doVerticalLayoutForLine(const QRect &r, const QList<QLayoutItem*>& currentLine,
313
int spacing, bool justify, int& x, int& w, int& availableSpace, int& expandingWidgets,
314
int& sizeHintHeight, int& minSizeHeight, int& colMinWidth, bool testOnly)
316
QListIterator<QLayoutItem*> it2(currentLine);
318
sizeHintHeight = 0 - spacing;
319
minSizeHeight = 0 - spacing;
321
while (it2.hasNext()) {
322
QLayoutItem *item = it2.next();
323
QSize itemSizeHint = item->sizeHint(); // we cache these ones because it can take
324
QSize itemMinSize = item->minimumSize(); // a while to get them
327
if (expandingWidgets != 0) {
328
if (item->expandingDirections() & Qt::Vertical)
330
itemSizeHint.width(),
331
qMin(itemSizeHint.height() + availableSpace / expandingWidgets, r.height())
334
s = QSize(itemSizeHint.width(), qMin(itemSizeHint.height(), r.height()));
337
itemSizeHint.width(),
338
qMin(itemSizeHint.height() + availableSpace / (int)currentLine.count(), r.height())
341
s = QSize(itemSizeHint.width(), qMin(itemSizeHint.height(), r.height()));
343
// adjust horizontal position depending on vertical alignment
345
if (item->alignment() & Qt::AlignRight)
346
add_x = w - s.width() - 1;
347
else if (item->alignment() & Qt::AlignHCenter)
348
add_x = (w - s.width() - 1) / 2;
350
add_x = 0; // Qt::AlignLeft
351
item->setGeometry(QRect(QPoint(x + add_x, wy), s));
353
wy = wy + s.height() + spacing;
354
minSizeHeight = minSizeHeight + spacing + itemMinSize.height();
355
sizeHintHeight = sizeHintHeight + spacing + itemSizeHint.height();
356
colMinWidth = qMax(colMinWidth, itemMinSize.width());
360
int KexiFlowLayout::doVerticalLayout(const QRect &r, bool testOnly)
364
int w = 0; // width of this line
365
int availableSpace = r.height() + spacing();
366
int expandingWidgets = 0; // number of widgets in the line with QSizePolicy == Expanding
367
QListIterator<QLayoutItem*> it(d->list);
368
QList<QLayoutItem*> currentLine;
369
QSize minSize, sizeHint(20, 20);
370
int minSizeWidth = 0 - spacing();
372
while (it.hasNext()) {
373
QLayoutItem *o = it.next();
374
if (o->isEmpty()) // do not consider hidden widgets
377
QSize oSizeHint = o->sizeHint(); // we cache these ones because it can take
378
// a while to get it (eg for child layouts)
379
if (y + oSizeHint.height() > r.bottom() && w > 0) {
380
// do the layout of current line
381
int sizeHintHeight, minSizeHeight, colMinWidth;
382
doVerticalLayoutForLine(r, currentLine,
383
spacing(), d->justify, y, w, availableSpace, expandingWidgets,
384
sizeHintHeight, minSizeHeight, colMinWidth, testOnly);
386
sizeHint = sizeHint.expandedTo(QSize(0, sizeHintHeight));
387
minSize = minSize.expandedTo(QSize(0, minSizeHeight));
388
minSizeWidth = minSizeWidth + spacing() + colMinWidth;
389
// start a new column
390
x = x + spacing() + w;
394
expandingWidgets = 0;
395
availableSpace = r.height() + spacing();
398
y = y + spacing() + oSizeHint.height();
399
w = qMax(w, oSizeHint.width());
400
currentLine.append(o);
401
if (o->expandingDirections() & Qt::Vertical)
403
availableSpace = qMax(0, availableSpace - spacing() - oSizeHint.height());
406
// don't forget to layout the last line
407
int sizeHintHeight, minSizeHeight, colMinWidth;
408
doVerticalLayoutForLine(r, currentLine,
409
spacing(), d->justify, y, w, availableSpace, expandingWidgets,
410
sizeHintHeight, minSizeHeight, colMinWidth, testOnly);
412
sizeHint = sizeHint.expandedTo(QSize(x + spacing() + w, sizeHintHeight));
413
minSizeWidth = minSizeWidth + spacing() + colMinWidth;
414
minSize = minSize.expandedTo(QSize(minSizeWidth, minSizeHeight));
416
// store sizeHint() and minimumSize()
417
d->cached_sizeHint = sizeHint + QSize(2 * margin(), 2 * margin());
418
d->cached_minSize = minSize + QSize(2 * margin(), 2 * margin());
420
return x + w - r.x();
423
QLayoutItem *KexiFlowLayout::itemAt(int index) const
425
return d->list.value(index);
428
QLayoutItem *KexiFlowLayout::takeAt(int index)
430
if (index >= 0 && index < d->list.size())
431
return d->list.takeAt(index);
436
void KexiFlowLayout::setOrientation(Qt::Orientation orientation)
438
d->orientation = orientation;
441
Qt::Orientation KexiFlowLayout::orientation() const
443
return d->orientation;
446
void KexiFlowLayout::setJustified(bool justify)
448
d->justify = justify;
451
bool KexiFlowLayout::isJustified() const