2 * Copyright 2007 by Robert Knight <robertknight@gmail.com>
3 * Copyright 2008 by William Egert <begert@gmail.com>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU Library General Public License
7 * as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This program 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
13 * GNU General Public License for more details
15 * You should have received a copy of the GNU Library General Public
16 * License along with this program; if not, write to the
17 * Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 #include "flowlayout.h"
26 #include <QtCore/QList>
27 #include <QtCore/QRectF>
28 #include <QtCore/QTimeLine>
32 // #include "layoutanimator.h"
34 using namespace Plasma
;
36 class FlowLayout::Private
39 Private() : columnWidth( -1 ), spacing(6.0) {}
40 QList
<QGraphicsLayoutItem
*> items
;
45 FlowLayout::FlowLayout(QGraphicsLayoutItem
* parent
)
46 : QGraphicsLayout(parent
)
49 setSizePolicy(QSizePolicy::Expanding
,QSizePolicy::Expanding
,QSizePolicy::DefaultType
);
52 FlowLayout::~FlowLayout()
57 int FlowLayout::count() const
59 return d
->items
.count();
62 void FlowLayout::addItem(QGraphicsLayoutItem
* item
)
64 if (!item
|| d
->items
.contains(item
)) {
71 // animator()->setCurrentState(item,LayoutAnimator::InsertedState);
76 void FlowLayout::removeItem(QGraphicsLayoutItem
* item
)
82 d
->items
.removeAll(item
);
85 // animator()->setCurrentState(item,LayoutAnimator::RemovedState);
90 int FlowLayout::indexOf(QGraphicsLayoutItem
* item
) const
96 return d
->items
.indexOf(item
);
98 QGraphicsLayoutItem
* FlowLayout::itemAt(int i
) const
100 if (i
>= d
->items
.count()) {
107 QSizeF
FlowLayout::sizeHint(Qt::SizeHint which
, const QSizeF
&constraint
) const
109 // TODO A proper algorithm here
111 // Idea: Return a size hint based on the golden ratio to
112 // make it aesthetically good
113 // eg. Longer side is 1.61x the length of the shorter side
117 return QSizeF(500,500);
120 QGraphicsLayoutItem
* FlowLayout::takeAt(int i
)
122 if (i
>= d
->items
.count()) {
126 return d
->items
.takeAt(i
);
127 // FIXME: Should updateGeometry() be called?
130 void FlowLayout::removeAt(int i
)
132 if (i
>= d
->items
.count()) {
136 d
->items
.removeAt(i
);
139 QRectF
FlowLayout::geometry() const
141 if (parentLayoutItem()) {
142 return parentLayoutItem()->geometry();
145 return QRectF(QPointF(0, 0), maximumSize());
148 void FlowLayout::setGeometry(const QRectF
&geom
)
150 if (!geom
.isValid() || geom
== geometry()) {
154 // QRectF newGeom = geom;
156 // if (d->parent && !dynamic_cast<QGraphicsLayout*>(d->parent)) {
157 // newGeom = d->parent->adjustToMargins(newGeom);
158 // //kDebug() << "parent rect is" << d->parent->topLeft() << d->parent->size()
159 // // << "and we are" << geometry() << "but aiming for"
160 // // << newGeom << "from" << geom;
163 // d->pos = newGeom.topLeft();
164 setPreferredSize(geom
.size());
165 // TODO: respect minimum and maximum sizes: is it possible?
166 //setSize(newGeom.size().expandedTo(minimumSize()).boundedTo(maximumSize()));
168 //kDebug() << "geometry is now" << geometry();
172 qreal
FlowLayout::spacing() const
177 void FlowLayout::setSpacing(qreal s
)
183 T
qSum(const QList
<T
>& container
)
186 foreach( const T
& item
, container
) {
192 void FlowLayout::relayout()
194 // const QRectF rect = adjustToMargins(geometry());
195 const QRectF rect
= geometry();
197 const qreal space
= spacing();
198 const qreal rectWidth
= rect
.width();
199 const qreal rectHeight
= rect
.height();
200 const int count
= d
->items
.count();
201 //kDebug() << "Flow layout geometry set to " << geometry();
203 // calculate average size of items
206 qreal maxItemWidth
= 0;
207 qreal minItemWidth
= 0;
208 //qreal maxItemHeight = 0;
209 qreal minItemHeight
= 0;
213 foreach(QGraphicsLayoutItem
*item
, d
->items
) {
214 maxItemWidth
= (maxItemWidth
< item
->maximumSize().width()) ?
215 item
->maximumSize().width() : maxItemWidth
;
216 minItemWidth
= (minItemWidth
< item
->minimumSize().width()) ?
217 item
->minimumSize().width() : minItemWidth
;
218 //maxItemHeight = (maxItemHeight < item->maximumSize().height()) ?
219 // item->maximumSize().height() : maxItemHeight;
220 minItemHeight
= (minItemHeight
< item
->minimumSize().height()) ?
221 item
->minimumSize().height() : minItemHeight
;
224 const int rowMax
= ((minItemHeight
!= 0) && (minItemHeight
!= rectHeight
)) ?
225 (int)(rectHeight
/ (minItemHeight
+ space
)) : 1;
227 if( maxItemWidth
== 0 && minItemWidth
!= 0 ) {
228 kDebug() << "******POSSIBLE DIVIDE BY ZERO: maxItemWidth = minItemWidth ********";
229 maxItemWidth
= minItemWidth
+ space
;
230 } else if( maxItemWidth
== 0 && minItemWidth
== 0 ) {
231 kDebug() << "******POSSIBLE DIVIDE BY ZERO: maxItemWidth = rectWidth ********";
232 maxItemWidth
= rectWidth
+ space
;
235 // try to use the maxwidth if there is room
236 // need usedSpace so we don't try to use the leftover
237 // area of our rect to make a item location.
238 maxItemWidth
+= space
;
239 qreal usedSpace
= floor( (rectWidth
/ maxItemWidth
) ) * maxItemWidth
;
240 for(int i
= 1; (i
<= rowMax
) && (colWidth
== 0); i
++) {
241 if( i
* (usedSpace
/ maxItemWidth
) >= count
) {
242 colWidth
= maxItemWidth
;
243 rowHeight
= (rectHeight
+ space
) / i
;
245 colCnt
= (int)(usedSpace
/ colWidth
);
249 //crazy algorithm to make items fit in available space
251 // These gave me the most trouble and should
252 // be taken into account if you try and change this:
253 // - maxRow = 3 with 9 items and 3 columns.
254 // - maxRow = 5 with 8 items and 3 colums.
255 // - maxRow = 1 with odd number columns.
257 const qreal tmp
= (qreal
)(count
+ (count
% 2)) / rowMax
;
258 if( (tmp
- floor(tmp
)) > 0.5) {
259 colCnt
= (int)ceil(tmp
) + 1;
261 colCnt
= (int)ceil(tmp
);
263 rowCnt
= (int)ceil((qreal
)count
/ colCnt
);
264 if( (rowCnt
== 1) && (colCnt
&2) ) {
267 colWidth
= rectWidth
/ colCnt
;
268 rowHeight
= (rectHeight
+ space
) / rowCnt
;
272 if( minItemHeight
> (rowHeight
- space
) ) {
273 rowHeight
= minItemHeight
+ space
;
276 // kDebug() << "colWidth: " << colWidth << "rowHeight: " << rowHeight
277 // << "rowCnt: " << rowCnt << "rowMax: " << rowMax << "colCnt: " << colCnt;
280 // lay the items out in left-to-right , top-to-bottom order
281 int insertColumn
= 0;
283 foreach(QGraphicsLayoutItem
*item
, d
->items
) {
285 if(insertColumn
>= colCnt
) {
291 const QRectF
newGeometry(rect
.left() + (insertColumn
* colWidth
),
296 //kDebug() << "newGeometry: " << newGeometry;
300 /* if ( animator() ){
301 animator()->setGeometry( item , newGeometry );
303 item
->setGeometry( newGeometry
);
310 qreal
FlowLayout::columnWidth() const
312 return d
->columnWidth
;
315 void FlowLayout::setColumnWidth( const qreal width
)
317 d
->columnWidth
= width
;