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 QSizeF
&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
)
148 QHash
<QGraphicsLayoutItem
*, QRectF
> geometries
;
149 geometries
= d
->calculateGeometries(rect
, Qt::PreferredSize
, rect
.size());
150 d
->addPadding(geometries
, rect
.size());
152 QHashIterator
<QGraphicsLayoutItem
*, QRectF
> i(geometries
);
153 while (i
.hasNext()) {
155 QGraphicsLayoutItem
*item
= i
.key();
156 item
->setGeometry(i
.value());
161 void CompactLayout::Private::addPadding(QHash
<QGraphicsLayoutItem
*, QRectF
> &geometries
, const QSizeF
&constraint
)
163 QSizeF size
= boundingRect(geometries
.values()).size();
165 qreal xAdjustment
= (constraint
.width() - size
.width()) / 2.0;
166 qreal yAdjustment
= (constraint
.height() - size
.height()) / 2.0;
168 if (xAdjustment
|| yAdjustment
) {
169 foreach (QGraphicsLayoutItem
*item
, items
) {
170 QGraphicsWidget
*widget
= dynamic_cast<QGraphicsWidget
*>(item
);
171 if (widget
&& !widget
->isVisible()) {
175 geometries
[item
].moveLeft(geometries
[item
].left() + xAdjustment
);
176 geometries
[item
].moveTop(geometries
[item
].top() + yAdjustment
);
182 QGraphicsLayoutItem
* CompactLayout::itemAt(int index
) const
184 return d
->items
.at(index
);
188 void CompactLayout::removeAt(int index
)
190 QGraphicsLayoutItem
* item
= itemAt(index
);
192 item
->setParentLayoutItem(0);
193 d
->items
.removeAt(index
);
198 QSizeF
CompactLayout::sizeHint(Qt::SizeHint which
, const QSizeF
&constraint
) const
200 if (which
!= Qt::PreferredSize
) {
204 QHash
<QGraphicsLayoutItem
*, QRectF
> geometries
=
205 d
->calculateGeometries(geometry(), which
, d
->hackedConstraint(constraint
));
207 return d
->boundingRect(geometries
.values()).size();
211 QRectF
CompactLayout::Private::boundingRect(const QList
<QRectF
> &rects
) const
215 foreach (const QRectF
&rect
, rects
) {
216 if (boundingRect
.isNull()) {
219 boundingRect
= boundingRect
.united(rect
);
227 QHash
<QGraphicsLayoutItem
*, QRectF
> CompactLayout::Private::calculateGeometries(const QRectF
&geom
, Qt::SizeHint which
, const QSizeF
&constraint
) const
229 QSizePolicy sizePolicy
;
230 //FIXME: not really pretty: try to fetch it from the grandparent (assumption on how taskarea is done) otherwise from the parent
231 if (q
->parentLayoutItem()->parentLayoutItem()) {
232 sizePolicy
= q
->parentLayoutItem()->parentLayoutItem()->sizePolicy();
233 } else if (q
->parentLayoutItem()) {
234 sizePolicy
= q
->parentLayoutItem()->sizePolicy();
236 sizePolicy
= q
->sizePolicy();
239 QHash
<QGraphicsLayoutItem
*, QRectF
> geometries
;
240 QList
<qreal
> xPositions
;
241 QList
<qreal
> yPositions
;
243 xPositions
<< geom
.left();
244 yPositions
<< geom
.top();
246 foreach (QGraphicsLayoutItem
*item
, items
) {
247 QGraphicsWidget
*widget
= dynamic_cast<QGraphicsWidget
*>(item
);
248 if (widget
&& !widget
->isVisible()) {
253 rect
.setSize(item
->effectiveSizeHint(which
));
255 rect
.setWidth(qBound(item
->minimumWidth(), rect
.width(), constraint
.width()));
256 rect
.setHeight(qBound(item
->minimumHeight(), rect
.height(), constraint
.height()));
258 // Try to find an empty space for the item within the bounds
259 // of the already positioned out items
260 foreach (qreal x
, xPositions
) {
262 if (rect
.right() >= xPositions
.last()) {
266 foreach (qreal y
, yPositions
) {
268 if (rect
.bottom() >= yPositions
.last()) {
272 bool overlapping
= false;
273 foreach (const QRectF
&existingRect
, geometries
) {
274 if (existingRect
.intersects(rect
)) {
280 goto positioning_done
;
285 // It didn't fit anywhere, so the current bounds will need to
287 Qt::Orientation direction
;
290 const int yDelta
= yPositions
.last() + rect
.height() - constraint
.height();
291 const int xDelta
= xPositions
.last() + rect
.width() - constraint
.width();
292 //a layout without elements will have height==0 when vertical width == 0 when horizontal
293 if (int(constraint
.height()) == 0 && constraint
.width() > 0) {
294 direction
= Qt::Vertical
;
295 } else if (int(constraint
.width()) == 0 && constraint
.height() > 0) {
296 direction
= Qt::Horizontal
;
297 // Extend based on constraints and prevent expanding if possible
298 } else if ((sizePolicy
.verticalPolicy() != QSizePolicy::Expanding
) && xDelta
< 0) {
299 direction
= Qt::Horizontal
;
300 } else if ((sizePolicy
.horizontalPolicy() != QSizePolicy::Expanding
) && yDelta
< 0) {
301 direction
= Qt::Vertical
;
302 // Then extend based on expanding policy
303 } else if (sizePolicy
.horizontalPolicy() != QSizePolicy::Expanding
) {
304 direction
= Qt::Horizontal
;
305 } else if (sizePolicy
.verticalPolicy() != QSizePolicy::Expanding
) {
306 direction
= Qt::Vertical
;
307 // Otherwise try to keep the shape of a square
308 } else if (yPositions
.last() >= xPositions
.last()) {
309 direction
= Qt::Horizontal
;
311 direction
= Qt::Vertical
;
315 if (direction
== Qt::Horizontal
) {
316 rect
.moveTop(yPositions
.first());
317 rect
.moveLeft(xPositions
.last());
319 rect
.moveLeft(xPositions
.first());
320 rect
.moveTop(yPositions
.last());
324 if (!xPositions
.contains(rect
.right() + spacing
)) {
325 xPositions
.append(rect
.right() + spacing
);
329 if (!yPositions
.contains(rect
.bottom() + spacing
)) {
330 yPositions
.append(rect
.bottom() + spacing
);
334 geometries
[item
] = rect
;
341 QSizeF
CompactLayout::Private::hackedConstraint(const QSizeF
&constraint
) const
343 // Qt doesn't seem to ever specify constraints to sizeHint()
344 // but the layout needs to know what the constraints are.
345 // This function returns a new constraint with the size of
346 // the containing view when Qt hasn't passed a constraint.
348 if (constraint
.width() != -1 || constraint
.height() != -1) {
352 const QGraphicsWidget
*widget
= 0;
353 const QGraphicsLayoutItem
*item
= q
;
355 while (item
&& !widget
) {
356 item
= item
->parentLayoutItem();
357 if (!item
->isLayout()) {
358 widget
= static_cast<const QGraphicsWidget
*>(item
);
367 qreal xMargins
= 0.0, yMargins
= 0.0;
369 while (widget
->parentWidget()) {
370 widget
= widget
->parentWidget();
371 parentSize
= widget
->size();
373 qreal left
, top
, right
, bottom
;
375 if (widget
->layout()) {
376 widget
->layout()->getContentsMargins(&left
, &top
, &right
, &bottom
);
378 widget
->getContentsMargins(&left
, &top
, &right
, &bottom
);
381 xMargins
+= left
+ right
;
382 yMargins
+= top
+ bottom
;
385 return parentSize
- QSizeF(xMargins
, yMargins
);