00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "boxlayout.h"
00024
00025 #include <math.h>
00026
00027 #include <QtCore/QList>
00028 #include <QtCore/QTimeLine>
00029
00030 #include <KDebug>
00031
00032
00033 #include "layoutanimator.h"
00034
00035 namespace Plasma
00036 {
00037
00038 class BoxLayout::Private
00039 {
00040 public:
00041 BoxLayout *const q;
00042 Direction direction;
00043 QList<LayoutItem*> children;
00044 bool multiRow;
00045 int rowCount;
00046 int colCount() const {
00047 return ((children.count()-1)/rowCount)+1;
00048 }
00049
00050 Private(BoxLayout *parent)
00051 : q(parent)
00052 , direction(LeftToRight), multiRow(false) , rowCount(1)
00053 {
00054 }
00055
00056
00057
00058 qreal size(const QSizeF& size) const
00059 {
00060 switch (direction) {
00061 case LeftToRight:
00062 case RightToLeft:
00063 return size.width();
00064 case TopToBottom:
00065 case BottomToTop:
00066 return size.height();
00067 default:
00068 Q_ASSERT(false);
00069 return 0;
00070 }
00071 }
00072
00073
00074 qreal size_o(const QSizeF& size) const
00075 {
00076 switch (direction) {
00077 case LeftToRight:
00078 case RightToLeft:
00079 return size.height();
00080 case TopToBottom:
00081 case BottomToTop:
00082 return size.width();
00083 default:
00084 Q_ASSERT(false);
00085 return 0;
00086 }
00087 }
00088
00089
00090
00091
00092 Qt::Orientation expandingDirection() const
00093 {
00094 switch ( direction ) {
00095 case LeftToRight:
00096 case RightToLeft:
00097 return Qt::Horizontal;
00098 case TopToBottom:
00099 case BottomToTop:
00100 return Qt::Vertical;
00101 default:
00102 Q_ASSERT(false);
00103 return Qt::Horizontal;
00104 }
00105 }
00106
00107
00108
00109 qreal startPos(const QRectF& geometry) const
00110 {
00111 switch ( direction ) {
00112 case LeftToRight:
00113 return geometry.left() + q->margin(LeftMargin);
00114 case TopToBottom:
00115 return geometry.top() + q->margin(TopMargin);
00116 case RightToLeft:
00117 return geometry.right() - q->margin(RightMargin);
00118 case BottomToTop:
00119 return geometry.bottom() - q->margin(BottomMargin);
00120 default:
00121 Q_ASSERT(false);
00122 return 0;
00123 }
00124 }
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135 qreal layoutItem(const QRectF& geometry , LayoutItem *item , const qreal pos , qreal size, int row)
00136 {
00137
00138
00139 QRectF newGeometry;
00140 qreal newPos = 0;
00141
00142 qreal top = 0;
00143 qreal height = 0;
00144
00145 QSizeF minSize = item->minimumSize();
00146 QSizeF maxSize = item->maximumSize();
00147 switch ( direction ) {
00148 case LeftToRight:
00149 case RightToLeft:
00150 height = (geometry.height()-q->spacing()*(rowCount-1))/rowCount;
00151 top = geometry.top()+row*(height+q->spacing());
00152 height = qBound(minSize.height(), height, maxSize.height());
00153 break;
00154 case TopToBottom:
00155 case BottomToTop:
00156 height = (geometry.width()-q->spacing()*(rowCount-1))/rowCount;
00157 top = geometry.left()+row*(height+q->spacing());
00158 height = qBound(minSize.width(), height, maxSize.width());
00159 break;
00160 }
00161
00162 switch ( direction ) {
00163 case LeftToRight:
00164 newGeometry = QRectF(pos,top,size,height);
00165 newPos = pos+size+q->spacing();
00166 break;
00167 case RightToLeft:
00168 newGeometry = QRectF(geometry.width()-pos-size,top,
00169 size,height);
00170 newPos = pos-size-q->spacing();
00171 break;
00172 case TopToBottom:
00173 newGeometry = QRectF(top,pos,height,size);
00174 newPos = pos+size+q->spacing();
00175 break;
00176 case BottomToTop:
00177 newGeometry = QRectF(top,geometry.height()-pos-size,
00178 height,size);
00179 newPos = pos-size-q->spacing();
00180 break;
00181 }
00182
00183
00184
00185 if (q->animator()) {
00186 q->animator()->setGeometry(item, newGeometry);
00187 } else {
00188 item->setGeometry(newGeometry);
00189 }
00190
00191 return newPos;
00192 }
00193
00194 enum SizeType
00195 {
00196 MinSize,
00197 MaxSize,
00198 HintSize
00199 };
00200
00201
00202
00203 static qreal sum(const qreal a , const qreal b)
00204 {
00205 return a+b;
00206 }
00207
00208
00209
00210
00211
00212
00213 template <class T>
00214 qreal calculateSize(SizeType sizeType , Qt::Orientation dir , T (*op)(const T,const T)) const
00215 {
00216 qreal value = 0;
00217 for ( int i = 0 ; i < children.count() ; i++ ) {
00218
00219 QSizeF itemSize;
00220 switch ( sizeType ) {
00221 case MinSize:
00222 itemSize = children[i]->minimumSize();
00223 break;
00224 case MaxSize:
00225 itemSize = children[i]->maximumSize();
00226 break;
00227 case HintSize:
00228 itemSize = children[i]->sizeHint();
00229 break;
00230 }
00231
00232 if ( dir == Qt::Horizontal ) {
00233 value = op(value,itemSize.width());
00234 } else {
00235 value = op(value,itemSize.height());
00236 }
00237 }
00238
00239 return value;
00240 }
00241
00242
00243
00244 QSizeF calculateSize(SizeType calculateSizeType) const
00245 {
00246 QSizeF result;
00247
00248 const qreal totalSpacingC = q->spacing() * colCount()-1;
00249 const qreal totalSpacingR = q->spacing() * rowCount-1;
00250
00251 switch ( direction ) {
00252 case LeftToRight:
00253 case RightToLeft:
00254 result = QSizeF(calculateSize(calculateSizeType,Qt::Horizontal,sum)/rowCount,
00255 calculateSize(calculateSizeType,Qt::Vertical,qMax<qreal>)*rowCount);
00256
00257 result.rwidth() += q->margin(LeftMargin) + q->margin(RightMargin) + totalSpacingC;
00258 result.rheight() += q->margin(TopMargin) + q->margin(BottomMargin) + totalSpacingR;
00259
00260 break;
00261 case TopToBottom:
00262 case BottomToTop:
00263 result = QSizeF(calculateSize(calculateSizeType,Qt::Horizontal,qMax<qreal>)/rowCount,
00264 calculateSize(calculateSizeType,Qt::Vertical,sum)*rowCount);
00265
00266 result.rheight() += q->margin(TopMargin) + q->margin(BottomMargin) + totalSpacingC;
00267 result.rwidth() += q->margin(LeftMargin) + q->margin(RightMargin) + totalSpacingR;
00268
00269 break;
00270 }
00271
00272 return result;
00273 }
00274 };
00275
00276
00277 BoxLayout::BoxLayout(Direction direction , LayoutItem *parent)
00278 : Layout(parent),
00279 d(new Private(this))
00280 {
00281 d->direction = direction;
00282 }
00283
00284 void BoxLayout::setDirection(Direction direction)
00285 {
00286 d->direction = direction;
00287 updateGeometry();
00288 }
00289 BoxLayout::Direction BoxLayout::direction() const
00290 {
00291 return d->direction;
00292 }
00293
00294 BoxLayout::~BoxLayout()
00295 {
00296 foreach (LayoutItem* item, d->children) {
00297 item->unsetManagingLayout(this);
00298 }
00299 delete d;
00300 }
00301
00302 Qt::Orientations BoxLayout::expandingDirections() const
00303 {
00304 switch ( d->direction ) {
00305 case LeftToRight:
00306 case RightToLeft:
00307 return Qt::Horizontal;
00308 case TopToBottom:
00309 case BottomToTop:
00310 return Qt::Vertical;
00311 default:
00312 Q_ASSERT(false);
00313 return 0;
00314 }
00315 }
00316
00317 int BoxLayout::count() const
00318 {
00319 return d->children.count();
00320 }
00321
00322 void BoxLayout::setAnimator(LayoutAnimator *animator)
00323 {
00324 Layout::setAnimator(animator);
00325
00326 if (animator) {
00327 foreach (LayoutItem *item, d->children) {
00328 animator->setGeometry(item, item->geometry());
00329 animator->setCurrentState(item, LayoutAnimator::StandardState);
00330 }
00331 }
00332 }
00333
00334 void BoxLayout::insertItem(int index, LayoutItem *item)
00335 {
00336 if (!item || d->children.contains(item)) {
00337 return;
00338 }
00339
00340 item->setManagingLayout(this);
00341
00342 if (index == -1) {
00343 index = d->children.size();
00344 }
00345
00346 d->children.insert(index, item);
00347
00348 if (animator()) {
00349 animator()->setCurrentState(item, LayoutAnimator::InsertedState);
00350 }
00351
00352 updateGeometry();
00353 }
00354
00355 void BoxLayout::addItem(LayoutItem *item)
00356 {
00357 if (!item) {
00358 return;
00359 }
00360
00361 insertItem(-1, item);
00362 }
00363
00364 void BoxLayout::removeItem(LayoutItem *item)
00365 {
00366 if (!item) {
00367 return;
00368 }
00369
00370 item->unsetManagingLayout(this);
00371 d->children.removeAll(item);
00372
00373 if (animator()) {
00374 animator()->setCurrentState(item, LayoutAnimator::RemovedState);
00375 }
00376
00377 updateGeometry();
00378 }
00379
00380 int BoxLayout::indexOf(LayoutItem *l) const
00381 {
00382 return d->children.indexOf(l);
00383 }
00384
00385 LayoutItem *BoxLayout::itemAt(int i) const
00386 {
00387 if (i >= d->children.count()) {
00388 return 0;
00389 }
00390
00391 return d->children[i];
00392 }
00393
00394 LayoutItem *BoxLayout::takeAt(int i)
00395 {
00396 if (i >= d->children.count()) {
00397 return 0;
00398 }
00399
00400 return d->children.takeAt(i);
00401
00402 updateGeometry();
00403 }
00404
00405 void BoxLayout::relayout()
00406 {
00407 QRectF margined = geometry().adjusted(margin(LeftMargin), margin(TopMargin), -margin(RightMargin), -margin(BottomMargin));
00408
00409
00410
00411
00412
00413 d->rowCount = 1;
00414 if(d->multiRow) {
00415 qreal minRowSize=1;
00416 qreal minWidth=0;
00417 for(int i = 0; i < d->children.count(); i++) {
00418 minRowSize = qMax(minRowSize, d->size_o(d->children[i]->minimumSize()));
00419 minWidth += d->size(d->children[i]->minimumSize());
00420 }
00421
00422 const qreal ratio = 2.25;
00423
00424 int maxRow = (d->size_o(margined.size()) + spacing()) /
00425 (minRowSize + spacing());
00426
00427 int minRow = (minWidth + d->children.count() * spacing()) /
00428 (d->size(margined.size()) + spacing() + 0.1);
00429
00430
00431 d->rowCount = 1 + sqrt(ratio * count() * d->size_o(margined.size()) /
00432 (d->size(margined.size())+1));
00433
00434 d->rowCount = qMax(minRow,d->rowCount);
00435 d->rowCount = qMin(maxRow,d->rowCount);
00436 d->rowCount = qMax(1,d->rowCount);
00437 }
00438
00439 int colCount = d->colCount();
00440
00441 QVector<qreal> sizes(colCount,0);
00442 QVector<qreal> expansionSpace(colCount,0);
00443
00444 qreal available = d->size(margined.size()) - spacing() * colCount;
00445 qreal perItemSize = available / colCount;
00446
00447
00448 for ( int i = 0 ; i < colCount ; i++ ) {
00449 qreal minItemSize=0;
00450 qreal maxItemSize=65536;
00451 bool isExpanding=true;
00452 qreal hint = 0;
00453
00454 for(int f = i*d->rowCount; f < (i+1)*d->rowCount; f++) {
00455 if(f>=count()) {
00456 break;
00457 }
00458 const LayoutItem *item = d->children[f];
00459 const bool itemExp = (item->expandingDirections() & d->expandingDirection());
00460 isExpanding = isExpanding && itemExp;
00461 minItemSize = qMax(minItemSize,d->size(item->minimumSize()));
00462 maxItemSize = qMin(maxItemSize,d->size(item->maximumSize()));
00463 if(!itemExp)
00464 hint = qMax(hint,d->size(item->sizeHint()));
00465 }
00466
00467 if ( isExpanding ) {
00468 sizes[i] = perItemSize;
00469 } else {
00470 sizes[i] = hint;
00471 }
00472
00473
00474
00475 sizes[i] = qMin( sizes[i] , maxItemSize );
00476 sizes[i] = qMax( sizes[i] , minItemSize );
00477
00478
00479
00480
00481 if ( isExpanding ) {
00482 expansionSpace[i] = maxItemSize-sizes[i];
00483 } else {
00484 expansionSpace[i] = 0;
00485 }
00486
00487 available -= sizes[i];
00488
00489 if ( sizes[i] != perItemSize && i != sizes.count()-1 ) {
00490 perItemSize = available / (sizes.count()-i-1);
00491 }
00492 }
00493
00494
00495
00496
00497
00498 int expandable = sizes.count();
00499 const qreal threshold = 1.0;
00500 while ( available > threshold && expandable > 0 ) {
00501
00502 qreal extraSpace = available / expandable;
00503 for ( int i = 0 ; i < colCount ; i++ ) {
00504 if ( expansionSpace[i] > threshold ) {
00505 qreal oldSize = sizes[i];
00506
00507 sizes[i] += qMin(extraSpace,expansionSpace[i]);
00508
00509 expansionSpace[i] -= sizes[i]-oldSize;
00510 available -= sizes[i]-oldSize;
00511 } else {
00512 expandable--;
00513 }
00514 }
00515 }
00516
00517
00518 qreal pos = d->startPos(geometry());
00519 for ( int col = 0 ; col < colCount ; col++ ) {
00520 int newPos = pos;
00521 for ( int row = 0 ; row < d->rowCount ; row++ ) {
00522 int i = col*d->rowCount+row;
00523 if(i>=count())
00524 break;
00525
00526
00527
00528
00529
00530 int p = d->layoutItem(margined, d->children[i], pos , sizes[col], row);
00531 newPos = (row != 0 && p < pos) ? qMin(p,newPos) : qMax(p,newPos);
00532 }
00533 pos=newPos;
00534 }
00535
00536 startAnimation();
00537 }
00538
00539
00540 QSizeF BoxLayout::maximumSize() const
00541 {
00542 return Layout::maximumSize();
00543 }
00544 QSizeF BoxLayout::minimumSize() const
00545 {
00546 return d->calculateSize(Private::MinSize);
00547 }
00548 QSizeF BoxLayout::sizeHint() const
00549 {
00550 return d->calculateSize(Private::HintSize);
00551 }
00552
00553 void BoxLayout::setMultiRow(bool b)
00554 {
00555 d->multiRow = b;
00556 }
00557
00558 HBoxLayout::HBoxLayout(LayoutItem *parent)
00559 : BoxLayout(LeftToRight,parent)
00560 {
00561 }
00562
00563 VBoxLayout::VBoxLayout(LayoutItem *parent)
00564 : BoxLayout(TopToBottom,parent)
00565 {
00566 }
00567
00568 }
00569
00570