not quite so much needs to be delayed to the init() function
[personal-kdebase.git] / workspace / plasma / applets / systemtray / ui / compactlayout.cpp
blobfcec5c587f8b5e44e0014abe937da77317fc1cb2
1 /***************************************************************************
2 * compactlayout.cpp *
3 * *
4 * Copyright (C) 2008 Jason Stubbs <jasonbstubbs@gmail.com> *
5 * *
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. *
10 * *
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. *
15 * *
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>
27 #include <kdebug.h>
29 namespace SystemTray
33 class CompactLayout::Private
35 public:
36 Private(CompactLayout *q)
37 : q(q),
38 spacing(4.0)
42 QHash<QGraphicsLayoutItem*, QRectF> calculateGeometries(const QRectF &rect,
43 Qt::SizeHint which,
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;
51 CompactLayout *q;
52 qreal spacing;
53 QList<QGraphicsLayoutItem*> items;
57 CompactLayout::CompactLayout(QGraphicsLayoutItem *parent)
58 : QGraphicsLayout(parent),
59 d(new Private(this))
64 CompactLayout::~CompactLayout()
66 foreach (QGraphicsLayoutItem* item, d->items) {
67 removeItem(item);
69 delete d;
73 qreal CompactLayout::spacing() const
75 return d->spacing;
79 void CompactLayout::setSpacing(qreal spacing)
81 d->spacing = 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);
92 if (widget) {
93 d->updateParentWidget(widget);
96 if (index == d->items.count()) {
97 d->items.append(item);
98 } else {
99 d->items.insert(index, item);
102 updateGeometry();
103 activate();
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();
118 if (parentItem) {
119 item->setParentItem(static_cast<QGraphicsWidget*>(parentItem));
124 void CompactLayout::removeItem(QGraphicsLayoutItem *item)
126 d->items.removeAll(item);
127 item->setParentLayoutItem(0);
128 updateGeometry();
129 activate();
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 //kDebug() << 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()) {
154 i.next();
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()) {
172 continue;
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);
191 if (item) {
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) {
201 return QSizeF();
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
213 QRectF boundingRect;
215 foreach (const QRectF &rect, rects) {
216 if (boundingRect.isNull()) {
217 boundingRect = rect;
218 } else {
219 boundingRect = boundingRect.united(rect);
223 return boundingRect;
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();
235 } else {
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()) {
249 continue;
252 QRectF rect;
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) {
261 rect.moveLeft(x);
262 if (rect.right() >= xPositions.last()) {
263 continue;
266 foreach (qreal y, yPositions) {
267 rect.moveTop(y);
268 if (rect.bottom() >= yPositions.last()) {
269 continue;
272 bool overlapping = false;
273 foreach (const QRectF &existingRect, geometries) {
274 if (existingRect.intersects(rect)) {
275 overlapping = true;
279 if (!overlapping) {
280 goto positioning_done;
285 // It didn't fit anywhere, so the current bounds will need to
286 // be extended.
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;
310 } else {
311 direction = Qt::Vertical;
315 if (direction == Qt::Horizontal) {
316 rect.moveTop(yPositions.first());
317 rect.moveLeft(xPositions.last());
318 } else {
319 rect.moveLeft(xPositions.first());
320 rect.moveTop(yPositions.last());
323 positioning_done:
324 if (!xPositions.contains(rect.right() + spacing)) {
325 xPositions.append(rect.right() + spacing);
326 qSort(xPositions);
329 if (!yPositions.contains(rect.bottom() + spacing)) {
330 yPositions.append(rect.bottom() + spacing);
331 qSort(yPositions);
334 geometries[item] = rect;
337 return geometries;
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) {
349 return constraint;
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);
362 if (!widget) {
363 return constraint;
366 QSizeF parentSize;
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);
377 } else {
378 widget->getContentsMargins(&left, &top, &right, &bottom);
381 xMargins += left + right;
382 yMargins += top + bottom;
385 return parentSize - QSizeF(xMargins, yMargins);