1
/***************************************************************************
4
* Copyright (C) 2008 Jason Stubbs <jasonbstubbs@gmail.com> *
6
* This program is free software; you can redistribute it and/or modify *
7
* it under the terms of the GNU General Public License as published by *
8
* the Free Software Foundation; either version 2 of the License, or *
9
* (at your option) any later version. *
11
* This program is distributed in the hope that it will be useful, *
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14
* GNU General Public License for more details. *
16
* You should have received a copy of the GNU General Public License *
17
* along with this program; if not, write to the *
18
* Free Software Foundation, Inc., *
19
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
20
***************************************************************************/
22
#include "compactlayout.h"
24
#include <QtCore/QHash>
26
#include <QtGui/QGraphicsWidget>
33
class CompactLayout::Private
36
Private(CompactLayout *q)
42
QHash<QGraphicsLayoutItem*, QRectF> calculateGeometries(const QRectF &rect,
44
const QSizeF &constraint) const;
45
void addPadding(QHash<QGraphicsLayoutItem*, QRectF> &geometries,
46
const QRectF &constraint);
47
QSizeF hackedConstraint(const QSizeF &constraint) const;
48
void updateParentWidget(QGraphicsWidget *item);
49
QRectF boundingRect(const QList<QRectF> &rects) const;
53
QList<QGraphicsLayoutItem*> items;
57
CompactLayout::CompactLayout(QGraphicsLayoutItem *parent)
58
: QGraphicsLayout(parent),
64
CompactLayout::~CompactLayout()
66
foreach (QGraphicsLayoutItem* item, d->items) {
73
qreal CompactLayout::spacing() const
79
void CompactLayout::setSpacing(qreal spacing)
85
void CompactLayout::insertItem(int index, QGraphicsLayoutItem *item)
87
index = qBound(0, index, d->items.count());
89
item->setParentLayoutItem(this);
91
QGraphicsWidget *widget = dynamic_cast<QGraphicsWidget *>(item);
93
d->updateParentWidget(widget);
96
if (index == d->items.count()) {
97
d->items.append(item);
99
d->items.insert(index, item);
106
void CompactLayout::addItem(QGraphicsLayoutItem *item)
108
insertItem(d->items.count(), item);
111
void CompactLayout::Private::updateParentWidget(QGraphicsWidget *item)
113
QGraphicsLayoutItem *parentItem = q->parentLayoutItem();
114
while (parentItem && parentItem->isLayout()) {
115
parentItem = parentItem->parentLayoutItem();
119
item->setParentItem(static_cast<QGraphicsWidget*>(parentItem));
124
void CompactLayout::removeItem(QGraphicsLayoutItem *item)
126
d->items.removeAll(item);
127
item->setParentLayoutItem(0);
133
bool CompactLayout::containsItem(QGraphicsLayoutItem *item) const
135
return d->items.contains(item);
139
int CompactLayout::count() const
141
return d->items.count();
145
void CompactLayout::setGeometry(const QRectF &rect)
147
QGraphicsLayout::setGeometry(rect);
149
QHash<QGraphicsLayoutItem*, QRectF> geometries;
150
geometries = d->calculateGeometries(rect, Qt::PreferredSize, rect.size());
152
d->addPadding(geometries, rect);
154
QHashIterator<QGraphicsLayoutItem*, QRectF> i(geometries);
155
while (i.hasNext()) {
157
QGraphicsLayoutItem *item = i.key();
158
item->setGeometry(i.value());
163
void CompactLayout::Private::addPadding(QHash<QGraphicsLayoutItem*, QRectF> &geometries, const QRectF &constraint)
165
QRectF bRect = boundingRect(geometries.values());
166
QSizeF size = bRect.size();
168
qreal xAdjustment = (constraint.width() - size.width()) / 2.0;
169
qreal yAdjustment = (constraint.height() - size.height()) / 2.0;
171
bRect.moveTopLeft(bRect.topLeft() + QPointF(xAdjustment, yAdjustment));
172
if (bRect.left() < constraint.left()) {
175
if (bRect.top() < constraint.top()) {
179
if (xAdjustment || yAdjustment) {
180
foreach (QGraphicsLayoutItem *item, items) {
181
QGraphicsWidget *widget = dynamic_cast<QGraphicsWidget *>(item);
182
if (widget && !widget->isVisible()) {
186
geometries[item].moveLeft(geometries[item].left() + xAdjustment);
187
geometries[item].moveTop(geometries[item].top() + yAdjustment);
193
QGraphicsLayoutItem* CompactLayout::itemAt(int index) const
195
if (index > d->items.count()) {
199
return d->items.at(index);
203
void CompactLayout::removeAt(int index)
205
QGraphicsLayoutItem* item = itemAt(index);
207
item->setParentLayoutItem(0);
208
d->items.removeAt(index);
212
QSizeF CompactLayout::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
214
if (which == Qt::PreferredSize) {
215
QHash<QGraphicsLayoutItem*, QRectF> geometries =
216
d->calculateGeometries(geometry(), which, d->hackedConstraint(constraint));
217
return d->boundingRect(geometries.values()).size();
218
} else if (which == Qt::MinimumSize) {
221
foreach (QGraphicsLayoutItem *item, d->items) {
222
QSizeF itemMin = item->minimumSize();
224
if (itemMin.height() > min.height()) {
225
min.setHeight(itemMin.height());
228
if (itemMin.width() > min.width()) {
229
min.setWidth(itemMin.width());
233
//kDebug() << "porporting" << min;
241
QRectF CompactLayout::Private::boundingRect(const QList<QRectF> &rects) const
245
foreach (const QRectF &rect, rects) {
246
if (boundingRect.isNull()) {
249
boundingRect = boundingRect.united(rect);
257
QHash<QGraphicsLayoutItem*, QRectF> CompactLayout::Private::calculateGeometries(const QRectF &geom, Qt::SizeHint which, const QSizeF &constraint) const
259
QSizePolicy sizePolicy;
260
//FIXME: not really pretty: try to fetch it from the grandparent (assumption on how taskarea is done) otherwise from the parent
261
if (q->parentLayoutItem()->parentLayoutItem()) {
262
sizePolicy = q->parentLayoutItem()->parentLayoutItem()->sizePolicy();
263
} else if (q->parentLayoutItem()) {
264
sizePolicy = q->parentLayoutItem()->sizePolicy();
266
sizePolicy = q->sizePolicy();
269
QHash<QGraphicsLayoutItem*, QRectF> geometries;
270
QList<qreal> xPositions;
271
QList<qreal> yPositions;
273
xPositions << geom.left();
274
yPositions << geom.top();
276
foreach (QGraphicsLayoutItem *item, items) {
277
QGraphicsWidget *widget = dynamic_cast<QGraphicsWidget *>(item);
278
if (widget && !widget->isVisible()) {
283
rect.setSize(item->effectiveSizeHint(which));
285
rect.setWidth(qBound(item->minimumWidth(), rect.width(), constraint.width()));
286
rect.setHeight(qBound(item->minimumHeight(), rect.height(), constraint.height()));
288
// Try to find an empty space for the item within the bounds
289
// of the already positioned out items
290
foreach (qreal x, xPositions) {
292
if (rect.right() >= xPositions.last()) {
296
foreach (qreal y, yPositions) {
298
if (rect.bottom() >= yPositions.last()) {
302
bool overlapping = false;
303
foreach (const QRectF &existingRect, geometries) {
304
if (existingRect.intersects(rect)) {
310
goto positioning_done;
315
// It didn't fit anywhere, so the current bounds will need to
317
Qt::Orientation direction;
320
const int yDelta = yPositions.last() + rect.height() - constraint.height();
321
const int xDelta = xPositions.last() + rect.width() - constraint.width();
322
//a layout without elements will have height==0 when vertical width == 0 when horizontal
323
if (int(constraint.height()) == 0 && constraint.width() > 0) {
324
direction = Qt::Vertical;
325
} else if (int(constraint.width()) == 0 && constraint.height() > 0) {
326
direction = Qt::Horizontal;
327
// Extend based on constraints and prevent expanding if possible
328
} else if ((sizePolicy.verticalPolicy() != QSizePolicy::Expanding) && xDelta < 0) {
329
direction = Qt::Horizontal;
330
} else if ((sizePolicy.horizontalPolicy() != QSizePolicy::Expanding) && yDelta < 0) {
331
direction = Qt::Vertical;
332
// Then extend based on expanding policy
333
} else if (sizePolicy.horizontalPolicy() != QSizePolicy::Expanding) {
334
direction = Qt::Horizontal;
335
} else if (sizePolicy.verticalPolicy() != QSizePolicy::Expanding) {
336
direction = Qt::Vertical;
337
// Otherwise try to keep the shape of a square
338
} else if (yPositions.last() >= xPositions.last()) {
339
direction = Qt::Horizontal;
341
direction = Qt::Vertical;
345
if (direction == Qt::Horizontal) {
346
rect.moveTop(yPositions.first());
347
rect.moveLeft(xPositions.last());
349
rect.moveLeft(xPositions.first());
350
rect.moveTop(yPositions.last());
354
if (!xPositions.contains(rect.right() + spacing)) {
355
xPositions.append(rect.right() + spacing);
359
if (!yPositions.contains(rect.bottom() + spacing)) {
360
yPositions.append(rect.bottom() + spacing);
364
geometries[item] = rect;
371
QSizeF CompactLayout::Private::hackedConstraint(const QSizeF &constraint) const
373
// Qt doesn't seem to ever specify constraints to sizeHint()
374
// but the layout needs to know what the constraints are.
375
// This function returns a new constraint with the size of
376
// the containing view when Qt hasn't passed a constraint.
378
if (constraint.width() != -1 || constraint.height() != -1) {
382
const QGraphicsWidget *widget = 0;
383
const QGraphicsLayoutItem *item = q;
385
while (item && !widget) {
386
item = item->parentLayoutItem();
387
if (!item->isLayout()) {
388
widget = static_cast<const QGraphicsWidget*>(item);
397
qreal xMargins = 0.0, yMargins = 0.0;
400
parentSize = widget->size();
402
qreal left, top, right, bottom;
404
if (widget->layout()) {
405
widget->layout()->getContentsMargins(&left, &top, &right, &bottom);
406
xMargins += left + right;
407
yMargins += top + bottom;
410
widget->getContentsMargins(&left, &top, &right, &bottom);
411
xMargins += left + right;
412
yMargins += top + bottom;
414
widget = widget->parentWidget();
417
return parentSize - QSizeF(xMargins, yMargins);