• Skip to content
  • Skip to link menu
KDE 4.0 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

libplasma

boxlayout.cpp

Go to the documentation of this file.
00001 /*
00002  *   Copyright 2007 by Matias Valdenegro T. <mvaldenegro@informatica.utem.cl>
00003  *   Copyright 2007 by Robert Knight <robertknight@gmail.com>
00004  *   Copyright 2008 by Olivier Goffart <ogoffart@kde.org>
00005  *
00006  *   This program is free software; you can redistribute it and/or modify
00007  *   it under the terms of the GNU Library General Public License as
00008  *   published by the Free Software Foundation; either version 2, or
00009  *   (at your option) any later version.
00010 
00011  *
00012  *   This program is distributed in the hope that it will be useful,
00013  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  *   GNU General Public License for more details
00016  *
00017  *   You should have received a copy of the GNU Library General Public
00018  *   License along with this program; if not, write to the
00019  *   Free Software Foundation, Inc.,
00020  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
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     // returns the component of 'size' in the expanding direction
00057     // of this layout
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     // returns the component of 'size' in the other direction
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     // returns the direction in which this layout expands
00091     // or shrinks
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     // returns the position from which layouting should
00108     // begin depending on the direction of this layout
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     // lays out an item
00127     //
00128     // 'geometry' the geometry of the layout
00129     // 'item' the item whoose geometry should be altered
00130     // 'pos' the position of the item (in the expanding direction of the layout)
00131     // 'size' the size of the item (in the expanding direction of the layout)
00132     //
00133     // returns the position for the next item in the layout
00134     //
00135     qreal layoutItem(const QRectF& geometry , LayoutItem *item , const qreal pos ,  qreal size, int row)
00136     {
00137         //qDebug() << "layoutItem: " << direction << "item size" << size;
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        // qDebug() << "Item geometry: " << newGeometry;
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     // this provides a + function which can be passed as the 'op'
00202     // argument to calculateSize
00203     static qreal sum(const qreal a , const qreal b)
00204     {
00205         return a+b;
00206     }
00207 
00208     // calcualtes a size hint or value for this layout
00209     // 'sizeType' - The item size ( minimum , maximum , hint ) to use
00210     // 'dir' - The direction component of the item size to use
00211     // 'op' - A function to apply to the size of each item in the layout
00212     //        , usually qMax,qMin or sum
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     // calculates a size hint or value for this layout
00243     // 'calculateSizeType' specifies the value to be calculated
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     // FIXME: This is never reached. Should it be called?
00402     updateGeometry();
00403 }
00404 
00405 void BoxLayout::relayout()
00406 {
00407     QRectF margined = geometry().adjusted(margin(LeftMargin), margin(TopMargin), -margin(RightMargin), -margin(BottomMargin));
00408 
00409     //qDebug() << "geo before " << geo << "and with margins" << margined << "margins" << margin(LeftMargin)
00410     //         << margin(TopMargin) <<  -margin(RightMargin) << -margin(BottomMargin);
00411     //qDebug() << "Box layout beginning with geo" << geometry;
00412     //qDebug() << "This box max size" << maximumSize();
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; //maybe this should not be hardcoded
00423         //we want the height of items be larger than the minimum size
00424         int maxRow = (d->size_o(margined.size()) + spacing()) / 
00425                 (minRowSize + spacing());
00426         //we want enough rows to be able to fit each items width.
00427         int minRow = (minWidth + d->children.count() * spacing()) / 
00428                 (d->size(margined.size()) + spacing() + 0.1);
00429         //FIXME:  this formula doesn't take the cellspacing in account
00430         //        it should also try to "fill" before adding a row
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     // initial distribution of space to items
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        // qDebug() << "Layout max item " << i << "size: " << maxItemSize;
00474 
00475         sizes[i] = qMin( sizes[i] , maxItemSize );
00476         sizes[i] = qMax( sizes[i] , minItemSize );
00477 
00478        // qDebug() << "Available: " << available << "per item:" << perItemSize <<
00479        //     "Initial size: " << sizes[i];
00480 
00481         if ( isExpanding ) {
00482             expansionSpace[i] = maxItemSize-sizes[i];
00483         } else {
00484             expansionSpace[i] = 0;
00485         }
00486 
00487         available -= sizes[i];
00488         // adjust the per-item size if the space was over or under used
00489         if ( sizes[i] != perItemSize && i != sizes.count()-1 ) {
00490             perItemSize = available / (sizes.count()-i-1);
00491         }
00492     }
00493 
00494     // distribute out any remaining space to items which can still expand
00495     //
00496     // space is distributed equally amongst remaining items until we run
00497     // out of space or items to expand
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     // set items' geometry according to new sizes
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             //QObject *obj = dynamic_cast<QObject*>(d->children[i]);
00527             //if ( obj )
00528             //qDebug() << "Item " << i << obj->metaObject()->className() << "size:" << sizes[i];
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 } // Plasma namespace
00569 
00570 

libplasma

Skip menu "libplasma"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

API Reference

Skip menu "API Reference"
  • KWin
  •   KWin Libraries
  • Libraries
  •   libkworkspace
  •   libplasma
  • Plasma
  •   Animators
  •   Applets
  •   Engines
  • Solid Modules
Generated for API Reference by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal