diff --git a/CMakeLists.txt b/CMakeLists.txt index c0a0b5868..4a9c5f3ba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,8 +33,7 @@ set(plasma_LIB_SRCS karambamanager.cpp widgets/boxlayout.cpp widgets/checkbox.cpp - widgets/hboxlayout.cpp -# widgets/flowlayout.cpp + widgets/flowlayout.cpp widgets/flash.cpp widgets/icon.cpp widgets/label.cpp @@ -46,7 +45,6 @@ set(plasma_LIB_SRCS widgets/radiobutton.cpp # widgets/rectangle.cpp widgets/widget.cpp - widgets/vboxlayout.cpp widgets/signalplotter.cpp ) @@ -105,17 +103,18 @@ install(FILES install(FILES widgets/boxlayout.h widgets/hboxlayout.h + widgets/vboxlayout.h widgets/flash.h widgets/icon.h widgets/label.h widgets/layout.h + widgets/layoutanimator.h widgets/layoutitem.h widgets/lineedit.h widgets/pushbutton.h widgets/checkbox.h widgets/radiobutton.h # widgets/rectangle.h - widgets/vboxlayout.h widgets/widget.h widgets/signalplotter.h DESTINATION ${INCLUDE_INSTALL_DIR}/plasma/widgets) diff --git a/applet.cpp b/applet.cpp index 5c118de1b..181774202 100644 --- a/applet.cpp +++ b/applet.cpp @@ -54,7 +54,7 @@ #include "plasma/widgets/widget.h" #include "plasma/widgets/lineedit.h" #include "plasma/widgets/pushbutton.h" -#include "plasma/widgets/vboxlayout.h" +#include "plasma/widgets/boxlayout.h" //#define DYNAMIC_SHADOWS namespace Plasma @@ -300,7 +300,7 @@ public: { if (!background) { top = left = right = bottom = 0; - } else { + } else { top = background->elementSize("top").height(); left = background->elementSize("left").width(); right = background->elementSize("right").width(); @@ -568,7 +568,7 @@ void Applet::setFailedToLaunch(bool failed, const QString& reason) if (failed) { setDrawStandardBackground(true); - Layout* failureLayout = new VBoxLayout(this); + Layout* failureLayout = new BoxLayout(BoxLayout::TopToBottom,this); d->failureText = new LineEdit(this, scene()); d->failureText->setFlags(0); d->failureText->setHtml(visibleFailureText(reason)); @@ -598,7 +598,7 @@ void Applet::setNeedsConfiguring(bool needsConfig) if (needsConfig) { setDrawStandardBackground(true); - Layout* layout = new VBoxLayout(this); + Layout* layout = new BoxLayout(BoxLayout::TopToBottom,this); PushButton* button = new PushButton(this); button->setText(i18n("Configure...")); connect(button, SIGNAL(clicked()), this, SLOT(performSetupConfig())); @@ -628,6 +628,9 @@ QRectF Applet::boundingRect() const int bottom; d->getBorderSize(left,top,right,bottom); + + + //qDebug() << "Background , Border size" << d->background << left << top << right << bottom; return rect.adjusted(-left,-top,right,bottom); } @@ -641,7 +644,9 @@ QSizeF Applet::sizeHint() const d->getBorderSize(left,top,right,bottom); - return contentSize() + QSizeF(left+right,top+bottom); + //qDebug() << "Applet content size hint: " << contentSizeHint(); + + return contentSizeHint() + QSizeF(left+right,top+bottom); } QList Applet::contextActions() @@ -746,6 +751,17 @@ Location Applet::location() const } QSizeF Applet::contentSize() const +{ + int top , left , right , bottom; + d->getBorderSize(left,top,right,bottom); + + // qDebug() << "Geometry size: " << geometry().size(); + // qDebug() << "Borders: " << left << top << right << bottom; + + return geometry().size() - QSizeF(left+right,top+bottom); +} + +QSizeF Applet::contentSizeHint() const { if (layout()) { return layout()->sizeHint(); diff --git a/applet.h b/applet.h index 800f22560..b7452464f 100644 --- a/applet.h +++ b/applet.h @@ -197,39 +197,23 @@ class PLASMA_EXPORT Applet : public Widget Location location() const; /** - * Returns a maximum size hint based on the Corona's space availability. - * - * An applet may choose to violate this size hint, but should try and - * respect it as much as possible. - */ -// QRectF maxSizeHint() const; + * Returns the area within which contents can be painted. If there is no + * background, then this is equivalent to boundingRect().size() + **/ + QSizeF contentSize() const; /** - * Returns the area within which contents can be painted. If there is no - * background, then this is equivalent to boundingRect(). + * Returns an ideal size for the applet's content. + * Applets can re-implement this to provide a suitable size based + * on their contents. * - * Applets should implement contentSize() to tell Plasma::Applet how - * much space they need. + * Unlike sizeHint() , contentSizeHint() does not include the + * size of any borders surrounding the content area. * - * When drawing to the applet in the paintInterface() method, you can - * use the QRect passed to that function. Outside paintInterface(), for - * example when positioning a Plasma::Widget, you can assume your drawing - * area has (0,0) at the top left and is the size of contentSize(). - * - * If drawStandardBackground() == true, enough space will reserved - * within the borders for the content, and boundingRect() will the - * total size of the applet, including borders. - * - * If the applet has not asked Plasma::Applet to draw the default - * background, boundingRect().size() == contentSize(), and - * boundingRect().topLeft() == QPointF(0,0). Also, such applets will - * not break if they implement boundingRect() instead. - * - * Note that if the value returned by contentSize() (and hence - * boundingRect()) changes for any reason, you should call - * prepareGeometryChange() to notify the Corona. - **/ - virtual QSizeF contentSize() const; + * The default implementation returns the sizeHint() of the applet's + * layout if it has one, or a null size otherwise. + */ + virtual QSizeF contentSizeHint() const; /** * Returns a list of all known applets in a hash keyed by a unique @@ -470,6 +454,11 @@ class PLASMA_EXPORT Applet : public Widget */ bool isShadowShown() const; + // reimplemented from LayoutItem + // value is the same as contentSizeHint() if drawStandardBackground() is false + // or contentSizeHint() plus the size of the border otherwise. + virtual QSizeF sizeHint() const; + Q_SIGNALS: /** * Emitted when the applet needs to take (or lose) keyboard focus. @@ -550,11 +539,7 @@ class PLASMA_EXPORT Applet : public Widget */ QVariant itemChange(GraphicsItemChange change, const QVariant &value); - // reimplemented from LayoutItem - // value is the same as contentSize() if drawStandardBackground() is false - // or contentSize() plus the size of the border otherwise. - virtual QSizeF sizeHint() const; - + protected Q_SLOTS: /** * @internal used to show the configuration of an applet on first show diff --git a/corona.cpp b/corona.cpp index 9b27aa26d..61c327c05 100644 --- a/corona.cpp +++ b/corona.cpp @@ -37,8 +37,7 @@ #include "dataengine.h" #include "karambamanager.h" #include "phase.h" -#include "widgets/hboxlayout.h" -#include "widgets/vboxlayout.h" +#include "widgets/boxlayout.h" #include "widgets/icon.h" using namespace Plasma; @@ -136,10 +135,10 @@ void Corona::setFormFactor(FormFactor formFactor) case Planar: break; case Horizontal: - d->layout = new HBoxLayout; + d->layout = new BoxLayout(BoxLayout::LeftToRight); break; case Vertical: - d->layout = new VBoxLayout; + d->layout = new BoxLayout(BoxLayout::TopToBottom); break; case MediaCenter: break; @@ -224,17 +223,31 @@ Applet* Corona::addApplet(const QString& name, const QVariantList& args, uint id qreal appWidth = applet->boundingRect().width(); qreal appHeight = applet->boundingRect().height(); + + if (geometry.isValid()) { + applet->setGeometry(geometry); + } else { + applet->setGeometry( QRectF(geometry.topLeft(),applet->sizeHint()) ); + } + +#if 0 + if (!geometry.size().isValid()) { + + } + if (geometry.isValid()) { applet->setGeometry(geometry); } else if (geometry.x() != -1 && geometry.y() != -1) { // yes, this means we can't have items start -1, -1 - applet->setPos(geometry.topLeft() - QPoint(applet->boundingRect().width()/2, + applet->setPos(geometry.topLeft() - QPointF(applet->boundingRect().width()/2, applet->boundingRect().height()/2)); } else { //TODO: Make sure new applets don't overlap with existing ones // Center exactly: applet->setPos((width() / 2) - (appWidth / 2), (height() / 2) - (appHeight / 2)); } +#endif + addItem(applet); applet->updateConstraints(); diff --git a/widgets/boxlayout.cpp b/widgets/boxlayout.cpp index 60507c919..7e47035d3 100644 --- a/widgets/boxlayout.cpp +++ b/widgets/boxlayout.cpp @@ -1,5 +1,6 @@ /* * Copyright 2007 by Matias Valdenegro T. + * Copyright 2007 by Robert Knight * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License version 2 as @@ -19,34 +20,253 @@ #include "boxlayout.h" #include +#include #include +#include "layoutanimator.h" + namespace Plasma { class BoxLayout::Private { - public: - QRectF geometry; - QList childList; - Qt::Orientations orientation; +public: + BoxLayout *const q; + Direction direction; + QRectF geometry; + QList children; + + Private(BoxLayout *parent) + : q(parent) + , direction(LeftToRight) + { + } + + // returns the component of 'size' in the expanding direction + // of this layout + qreal size(const QSizeF& size) const + { + switch (direction) { + case LeftToRight: + case RightToLeft: + return size.width(); + case TopToBottom: + case BottomToTop: + return size.height(); + default: + Q_ASSERT(false); + return 0; + } + } + + // returns the direction in which this layout expands + // or shrinks + Qt::Orientation expandingDirection() const + { + switch ( direction ) { + case LeftToRight: + case RightToLeft: + return Qt::Horizontal; + case TopToBottom: + case BottomToTop: + return Qt::Vertical; + default: + Q_ASSERT(false); + return Qt::Horizontal; + } + } + + // returns the position from which layouting should + // begin depending on the direction of this layout + qreal startPos(const QRectF& geometry) const + { + switch ( direction ) { + case LeftToRight: + case TopToBottom: + return 0; + case RightToLeft: + return geometry.width(); + case BottomToTop: + return geometry.height(); + default: + Q_ASSERT(false); + return 0; + } + } + + // lays out an item + // + // 'geometry' the geometry of the layout + // 'item' the item whoose geometry should be altered + // 'pos' the position of the item (in the expanding direction of the layout) + // 'size' the size of the item (in the expanding direction of the layout) + // + // returns the position for the next item in the layout + // + qreal layoutItem(const QRectF& geometry , LayoutItem *item , const qreal pos , qreal size) + { + //qDebug() << "layoutItem: " << direction << "item size" << size; + + QRectF newGeometry; + qreal newPos = 0; + + qreal top = 0; + qreal height = 0; + + QSizeF minSize = item->minimumSize(); + QSizeF maxSize = item->maximumSize(); + switch ( direction ) { + case LeftToRight: + case RightToLeft: + height = qBound(minSize.height(),geometry.height(),maxSize.height()); + top = geometry.top(); + break; + case TopToBottom: + case BottomToTop: + height = qBound(minSize.width(),geometry.width(),maxSize.width()); + top = geometry.left(); + break; + } + + switch ( direction ) { + case LeftToRight: + newGeometry = QRectF(pos,top,size,height); + newPos = pos+size+q->spacing(); + break; + case RightToLeft: + newGeometry = QRectF(geometry.width()-pos-size,top, + size,height); + newPos = pos-size-q->spacing(); + break; + case TopToBottom: + newGeometry = QRectF(top,pos,height,size); + newPos = pos+size+q->spacing(); + break; + case BottomToTop: + newGeometry = QRectF(top,geometry.height()-pos-size, + height,size); + newPos = pos-size-q->spacing(); + break; + } + + // qDebug() << "Item geometry: " << newGeometry; + + if ( q->animator() ) + q->animator()->setGeometry(item,newGeometry); + else + item->setGeometry(newGeometry); + + return newPos; + } + + enum SizeType + { + MinSize, + MaxSize, + HintSize + }; + + // this provides a + function which can be passed as the 'op' + // argument to calculateSize + static qreal sum(const qreal a , const qreal b) + { + return a+b; + } + + // calcualtes a size hint or value for this layout + // 'sizeType' - The item size ( minimum , maximum , hint ) to use + // 'dir' - The direction component of the item size to use + // 'op' - A function to apply to the size of each item in the layout + // , usually qMax,qMin or sum + template + qreal calculateSize(SizeType sizeType , Qt::Orientation dir , T (*op)(T,T)) const + { + qreal value = 0; + for ( int i = 0 ; i < children.count() ; i++ ) { + + QSizeF itemSize; + switch ( sizeType ) { + case MinSize: + itemSize = children[i]->minimumSize(); + break; + case MaxSize: + itemSize = children[i]->maximumSize(); + break; + case HintSize: + itemSize = children[i]->sizeHint(); + break; + } + + if ( dir == Qt::Horizontal ) { + value = op(value,itemSize.width()); + } else { + value = op(value,itemSize.height()); + } + } + + return value; + } + + // calculates a size hint or value for this layout + // 'calculateSizeType' specifies the value to be calculated + QSizeF calculateSize(SizeType calculateSizeType) const + { + QSizeF result; + + const qreal totalMargin = q->margin() * 2; + const qreal totalSpacing = q->spacing() * (children.count()-1); + + switch ( direction ) { + case LeftToRight: + case RightToLeft: + result = QSizeF(calculateSize(calculateSizeType,Qt::Horizontal,sum), + calculateSize(calculateSizeType,Qt::Vertical,qMax)); + + result.rwidth() += totalMargin + totalSpacing; + result.rheight() += totalMargin; + + break; + case TopToBottom: + case BottomToTop: + result = QSizeF(calculateSize(calculateSizeType,Qt::Horizontal,qMax), + calculateSize(calculateSizeType,Qt::Vertical,sum)); + + result.rheight() += totalMargin + totalSpacing; + result.rwidth() += totalMargin; + + break; + } + + return result; + } }; -BoxLayout::BoxLayout(Qt::Orientations orientation, LayoutItem *parent) +BoxLayout::BoxLayout(Direction direction , LayoutItem *parent) : Layout(parent), - d(new Private) + d(new Private(this)) { if (parent) { parent->setLayout(this); } - d->orientation = orientation; + + d->direction = direction; +} + +void BoxLayout::setDirection(Direction direction) +{ + d->direction = direction; + update(); +} +BoxLayout::Direction BoxLayout::direction() const +{ + return d->direction; } BoxLayout::~BoxLayout() { - foreach (LayoutItem* item, children()) { + foreach (LayoutItem* item, d->children) { item->unsetManagingLayout(this); } delete d; @@ -54,17 +274,17 @@ BoxLayout::~BoxLayout() Qt::Orientations BoxLayout::expandingDirections() const { - return d->orientation; -} - -QSizeF BoxLayout::minimumSize() const -{ - return QSizeF(); -} - -QSizeF BoxLayout::maximumSize() const -{ - return QSizeF(); + switch ( d->direction ) { + case LeftToRight: + case RightToLeft: + return Qt::Horizontal; + case TopToBottom: + case BottomToTop: + return Qt::Vertical; + default: + Q_ASSERT(false); + return 0; + } } QRectF BoxLayout::geometry() const @@ -72,162 +292,180 @@ QRectF BoxLayout::geometry() const return d->geometry; } -void BoxLayout::setGeometry(const QRectF& geometry) -{ - if (!geometry.isValid() || geometry.isEmpty()) { - kDebug() << "Invalid Geometry " << geometry; - d->geometry = geometry; - return; - } - - kDebug() << this << " Geometry process " << geometry << " for " << children().count() << " children"; - - QList fixedChildren; - QList expandingChildren; - QList sizes; - QSizeF available = geometry.size() - QSizeF(2 * margin(), 2 * margin()); - - foreach (LayoutItem *l, children()) { - if (l->expandingDirections() & d->orientation) { - expandingChildren.append(l); - } else { - fixedChildren.append(l); - } - } - - foreach (LayoutItem *l, fixedChildren) { - QSizeF hint = l->sizeHint(); - if ( d->orientation == Qt::Vertical ) { - sizes.insert(indexOf(l), QSizeF(available.width(), hint.height())); - available -= QSizeF(0.0, hint.height() + spacing()); - } else { - sizes.insert(indexOf(l), QSizeF(hint.width(), available.height())); - available -= QSizeF(hint.width() + spacing(), 0.0f); - } - } - - if (expandingChildren.count() > 0) { - if ( d->orientation == Qt::Vertical ) { - qreal expandHeight = (available.height() - ((expandingChildren.count() - 1) * spacing())) / expandingChildren.count(); - foreach (LayoutItem *l, expandingChildren) { - sizes.insert(indexOf(l),QSizeF(available.width(), expandHeight)); - } - } else { - qreal expandWidth = (available.width() - ((expandingChildren.count() - 1) * spacing())) / expandingChildren.count(); - foreach (LayoutItem *l, expandingChildren) { - sizes.insert(indexOf(l), QSizeF(expandWidth, available.height())); - } - } - } - - QPointF start = geometry.topLeft(); - start += QPointF(margin(), spacing()); - - for (int i = 0; i < sizes.size(); i++) { - LayoutItem *l = itemAt(i); - kDebug() << "Setting Geometry for child " << l << " to " << QRectF(start, sizes[i]); - l->setGeometry(QRectF(start, sizes[i])); - if ( d->orientation == Qt::Vertical ) { - start += QPointF(0.0, sizes[i].height() + spacing()); - } else { - start += QPointF(sizes[i].width() + spacing(), 0.0); - } - } - - d->geometry = geometry; -} - -QSizeF BoxLayout::sizeHint() const -{ - qreal hintHeight = 0.0; - qreal hintWidth = 0.0; - - foreach(LayoutItem *l, children()) { - - QSizeF hint = l->sizeHint(); - - if ( d->orientation == Qt::Vertical ) { - hintWidth = qMax(hint.width(), hintWidth); - hintHeight += hint.height() + spacing(); - } else { - hintHeight = qMax(hint.height(), hintHeight); - hintWidth += hint.width() + spacing(); - } - } - hintWidth += 2 * margin(); - hintHeight += 2 * margin(); - - return QSizeF(hintWidth, hintHeight); -} - int BoxLayout::count() const { - return d->childList.count(); + return d->children.count(); } -bool BoxLayout::isEmpty() const +void BoxLayout::insertItem(int index, LayoutItem *item) { - return count() == 0; -} - -void BoxLayout::insertItem(int index, LayoutItem *l) -{ - if (!l) { + if (!item) { return; } - //l->setLayout(this); - d->childList.insert(index, l); - setGeometry(geometry()); + item->setManagingLayout(this); + + if ( index == -1 ) + index = d->children.size(); + + d->children.insert(index, item); + + if ( animator() ) + animator()->setCurrentState(item,LayoutAnimator::InsertedState); + + update(); } -void BoxLayout::addItem(LayoutItem *l) +void BoxLayout::addItem(LayoutItem *item) { - if (!l) { + insertItem(-1,item); +} + +void BoxLayout::removeItem(LayoutItem *item) +{ + if (!item) { return; } - l->setManagingLayout(this); - d->childList.append(l); - setGeometry(geometry()); -} + item->unsetManagingLayout(this); + d->children.removeAll(item); -void BoxLayout::removeItem(LayoutItem *l) -{ - if (!l) { - return; - } + if ( animator() ) + animator()->setCurrentState(item,LayoutAnimator::RemovedState); - l->unsetManagingLayout(this); - d->childList.removeAll(l); - setGeometry(geometry()); + update(); } int BoxLayout::indexOf(LayoutItem *l) const { - return d->childList.indexOf(l); + return d->children.indexOf(l); } LayoutItem *BoxLayout::itemAt(int i) const { - return d->childList[i]; + return d->children[i]; } LayoutItem *BoxLayout::takeAt(int i) { - return d->childList.takeAt(i); + return d->children.takeAt(i); - setGeometry(geometry()); + update(); } -QSizeF BoxLayout::size() const +void BoxLayout::setGeometry(const QRectF& geo) { - return d->geometry.size(); + QRectF geometry = geo.adjusted(margin(),margin(),-margin(),-margin()); + + //qDebug() << "geo before " << geo << "after" << geometry << "margin" << margin(); + //qDebug() << "Box layout beginning with geo" << geometry; + //qDebug() << "This box max size" << maximumSize(); + + QVector sizes(count()); + QVector expansionSpace(count()); + + qreal available = d->size(geometry.size()) - spacing()*(d->children.count()-1); + qreal perItemSize = available / count(); + + // initial distribution of space to items + for ( int i = 0 ; i < sizes.count() ; i++ ) { + const LayoutItem *item = d->children[i]; + + const bool isExpanding = item->expandingDirections() & d->expandingDirection(); + + if ( isExpanding ) + sizes[i] = perItemSize; + else + sizes[i] = d->size(item->sizeHint()); + + const qreal minItemSize = d->size(item->minimumSize()); + const qreal maxItemSize = d->size(item->maximumSize()); + + // qDebug() << "Layout max item " << i << "size: " << maxItemSize; + + sizes[i] = qMin( sizes[i] , maxItemSize ); + sizes[i] = qMax( sizes[i] , minItemSize ); + + // qDebug() << "Available: " << available << "per item:" << perItemSize << + // "Initial size: " << sizes[i]; + + if ( isExpanding ) + expansionSpace[i] = maxItemSize-sizes[i]; + else + expansionSpace[i] = 0; + + // adjust the per-item size if the space was over or under used + if ( sizes[i] != perItemSize && i != sizes.count()-1 ) { + perItemSize = available / (sizes.count()-i-1); + } + + available -= sizes[i]; + } + + // distribute out any remaining space to items which can still expand + // + // space is distributed equally amongst remaining items until we run + // out of space or items to expand + int expandable = sizes.count(); + const qreal threshold = 1.0; + while ( available > threshold && expandable > 0 ) { + + qreal extraSpace = available / expandable; + for ( int i = 0 ; i < sizes.count() ; i++ ) { + if ( expansionSpace[i] > threshold ) { + qreal oldSize = sizes[i]; + + sizes[i] += qMin(extraSpace,expansionSpace[i]); + + expansionSpace[i] -= sizes[i]-oldSize; + available -= sizes[i]-oldSize; + } else { + expandable--; + } + } + } + + // set items' geometry according to new sizes + qreal pos = d->startPos(geometry); + for ( int i = 0 ; i < sizes.count() ; i++ ) { + + //QObject *obj = dynamic_cast(d->children[i]); + //if ( obj ) + //qDebug() << "Item " << i << obj->metaObject()->className() << "size:" << sizes[i]; + + pos = d->layoutItem( geometry , d->children[i] , pos , sizes[i] ); + } + + d->geometry = geometry; + + if ( animator() && animator()->timeLine() ) + animator()->timeLine()->start(); } -QList BoxLayout::children() const + +QSizeF BoxLayout::maximumSize() const { - return d->childList; + return Layout::maximumSize(); } +QSizeF BoxLayout::minimumSize() const +{ + return d->calculateSize(Private::MinSize); +} +QSizeF BoxLayout::sizeHint() const +{ + return d->calculateSize(Private::HintSize); +} + +HBoxLayout::HBoxLayout(LayoutItem *parent) + : BoxLayout(LeftToRight,parent) +{ +} + +VBoxLayout::VBoxLayout(LayoutItem *parent) + : BoxLayout(TopToBottom,parent) +{ +} + } // Plasma namespace + diff --git a/widgets/boxlayout.h b/widgets/boxlayout.h index 6c443c505..a7f641815 100644 --- a/widgets/boxlayout.h +++ b/widgets/boxlayout.h @@ -1,5 +1,6 @@ /* * Copyright 2007 by Matias Valdenegro T. + * Copyright 2007 by Robert Knight * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License version 2 as @@ -27,61 +28,89 @@ namespace Plasma { - /** - * Vertical Box Layout - * - * @author Matias Valdenegro T. - * - * This class implements a generic box Layout used as a common API for VBox and HBox implementations. + * The BoxLayout class lays out items in a horizontal or vertical line. */ class PLASMA_EXPORT BoxLayout : public Layout { public: + /** + * This enum describes the directions in which items can be laid + * out. + */ + enum Direction + { + /** Lay items out horizontally, from left to right. */ + LeftToRight, + /** Lay items out horizontally, from right to left. */ + RightToLeft, + /** Lay items out vertically, from top to bottom. */ + TopToBottom, + /** Lay items out vertically, from bottom to top. */ + BottomToTop + }; /** - * Constructor. - */ - explicit BoxLayout(Qt::Orientations orientation, LayoutItem *parent = 0); - - /** - * Destructor. - */ + * Creates a new box layout which lays items out in the specified + * @p direction + */ + explicit BoxLayout(Direction direction , LayoutItem *parent = 0); ~BoxLayout(); - Qt::Orientations expandingDirections() const; - - QSizeF minimumSize() const; - QSizeF maximumSize() const; - - QRectF geometry() const; - void setGeometry(const QRectF& geometry); - - QSizeF sizeHint() const; - - int count() const; - - bool isEmpty() const; + /** Sets the direction in which items are laid out. */ + void setDirection(Direction direction); + /** Returns the direction in which items are laid out. */ + Direction direction() const; + /** Inserts a new item into the layout at the specified index. */ void insertItem(int index, LayoutItem *l); - void addItem(LayoutItem *l); + + // reimplemented from Layout + virtual void addItem(LayoutItem *l); + virtual void removeItem(LayoutItem *l); + virtual int indexOf(LayoutItem *l) const; + virtual LayoutItem *itemAt(int i) const; + virtual LayoutItem *takeAt(int i); + virtual Qt::Orientations expandingDirections() const; + virtual QRectF geometry() const; + virtual void setGeometry(const QRectF& geometry); + virtual int count() const; - void removeItem(LayoutItem *l); - - int indexOf(LayoutItem *l) const; - LayoutItem *itemAt(int i) const; - LayoutItem *takeAt(int i); - - QSizeF size() const; - - protected: - QList children() const; + virtual QSizeF minimumSize() const; + virtual QSizeF maximumSize() const; + virtual QSizeF sizeHint() const; private: class Private; Private *const d; }; +/** + * A BoxLayout which defaults to laying items out + * horizontally in a left-to-right order. + * + * Equivalent to creating a BoxLayout and passing LeftToRight + * in the constructor. + */ +class PLASMA_EXPORT HBoxLayout : public BoxLayout +{ +public: + explicit HBoxLayout(LayoutItem *parent = 0); +}; + +/** + * A BoxLayout which defaults to laying items out + * vertically in a top-to-bottom order. + * + * Equivalent to creating a BoxLayout and passing TopToBottom + * in the constructor. + */ +class PLASMA_EXPORT VBoxLayout : public BoxLayout +{ +public: + explicit VBoxLayout(LayoutItem *parent = 0); +}; + } -#endif /* _H_BOX_LAYOUT__ */ +#endif /* PLASMA_BOX_LAYOUT */ diff --git a/widgets/flowlayout.cpp b/widgets/flowlayout.cpp new file mode 100644 index 000000000..efe56788d --- /dev/null +++ b/widgets/flowlayout.cpp @@ -0,0 +1,204 @@ +/* +* Copyright 2007 by Robert Knight +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU Library General Public License version 2, +* or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details +* +* You should have received a copy of the GNU Library General Public +* License along with this program; if not, write to the +* Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "flowlayout.h" + +#include +#include + +#include +#include +#include + +#include + +#include "layoutanimator.h" + +using namespace Plasma; + +class FlowLayout::Private +{ +public: + QList items; + QRectF geometry; +}; + +FlowLayout::FlowLayout(LayoutItem* parent) + : Layout(parent) + , d(new Private) +{ +} +FlowLayout::~FlowLayout() +{ + delete d; +} + +int FlowLayout::count() const +{ + return d->items.count(); +} +void FlowLayout::addItem(LayoutItem* item) +{ + d->items << item; + + if ( animator() ) + animator()->setCurrentState(item,LayoutAnimator::InsertedState); + + item->setManagingLayout(this); +} +void FlowLayout::removeItem(LayoutItem* item) +{ + d->items.removeAll(item); + + if ( animator() ) + animator()->setCurrentState(item,LayoutAnimator::RemovedState); +} +int FlowLayout::indexOf(LayoutItem* item) const +{ + return d->items.indexOf(item); +} +LayoutItem* FlowLayout::itemAt(int i) const +{ + return d->items[i]; +} +QSizeF FlowLayout::sizeHint() const +{ + // TODO A proper algorithm here + // + // Idea: Return a size hint based on the golden ratio to + // make it aesthetically good + // eg. Longer side is 1.61x the length of the shorter side + // + + // testing + return QSizeF(500,500); +} +LayoutItem* FlowLayout::takeAt(int i) +{ + return d->items.takeAt(i); +} + +QRectF FlowLayout::geometry() const +{ + return d->geometry; +} + +template +T qSum(const QList& container) +{ + T total = 0; + foreach( const T& item , container ) { + total += item; + } + return total; +} + +void FlowLayout::setGeometry(const QRectF& geo) +{ + QRectF geometry(geo); + geometry.adjust(margin(),margin(),-margin(),-margin()); + + qDebug() << "Flow layout geometry set to " << geo; + + // calculate average size of items + qreal totalWidth = 0; + qreal totalHeight = 0; + + foreach( LayoutItem *item , d->items ) { + totalWidth += item->sizeHint().width(); + totalHeight += item->sizeHint().height(); + } + + // use the average item width as the column width. + // Also include the spacing either side of each item as part of the + // average width, this provides the spacing between the items and + // also allows some tolerance for small differences in item widths + const qreal averageWidth = totalWidth / d->items.count() + 2*spacing(); + + const int columnCount = (int)(geometry.width() / averageWidth); + + int insertColumn = 0; + qreal rowPos = 0; + qreal rowHeight = 0; + + // lay the items out in left-to-right , top-to-bottom order + foreach( LayoutItem *item , d->items ) { + + const QSizeF& itemSize = item->sizeHint(); + + int columnSpan = (int)ceil(itemSize.width() / averageWidth); + + if ( insertColumn + columnSpan > columnCount ) { + // start a new row + insertColumn = 0; + rowPos += rowHeight + spacing(); + } + + // qDebug() << "Inserting item at column" << insertColumn + // << "spanning" << columnSpan << "columns" + // << "with offset" << offset; + + + // try to expand the item to fill its allocated number of columns + qreal itemWidth = itemSize.width(); + const qreal idealWidth = columnSpan * averageWidth - spacing(); + if ( itemWidth < idealWidth && + idealWidth < item->maximumSize().width() ) { + itemWidth = idealWidth; + } + + // calculate offset to horizontally center item + qreal offset = (columnSpan * averageWidth) - itemWidth; + if ( insertColumn == 0 ) + offset -= spacing(); + offset /= 2; + + // try to restrict the item width to the available geometry's + // width + if ( itemWidth > geometry.width() ) { + itemWidth = qMax(geometry.width(),item->minimumSize().width()); + offset = 0; + } + + // position the item + const QRectF newGeometry( geometry.left() + + insertColumn * averageWidth + offset, + geometry.top() + rowPos , + itemWidth , + itemSize.height() ); + + rowHeight = qMax(rowHeight,itemSize.height()); + insertColumn += columnSpan; + + if ( animator() ) + animator()->setGeometry( item , newGeometry ); + else + item->setGeometry( newGeometry ); + } + + d->geometry = geo; + + if ( animator() && animator()->timeLine() ) + animator()->timeLine()->start(); +} + +Qt::Orientations FlowLayout::expandingDirections() const +{ + return Qt::Vertical | Qt::Horizontal; +} + diff --git a/widgets/flowlayout.h b/widgets/flowlayout.h new file mode 100644 index 000000000..dc70a0da7 --- /dev/null +++ b/widgets/flowlayout.h @@ -0,0 +1,64 @@ +/* +* Copyright 2007 by Robert Knight +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU Library General Public License version 2, +* or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details +* +* You should have received a copy of the GNU Library General Public +* License along with this program; if not, write to the +* Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef __FLOWLAYOUT__ +#define __FLOWLAYOUT__ + +#include + +#include "layout.h" + +namespace Plasma +{ + +class LayoutItem; + +/** + * A layout which lays items out left-to-right , top-to-bottom. + * + * This is similar to the layout of items in a QListView. + */ +class PLASMA_EXPORT FlowLayout : public Layout +{ +public: + /** Construct a new flow layout with the specified parent. */ + explicit FlowLayout(LayoutItem* parent); + virtual ~FlowLayout(); + + // reimplemented + virtual int count() const; + virtual void addItem(LayoutItem* item); + virtual void removeItem(LayoutItem* item); + virtual int indexOf(LayoutItem* item) const; + virtual LayoutItem* itemAt(int i) const; + virtual LayoutItem* takeAt(int i); + + virtual QSizeF sizeHint() const; + virtual QRectF geometry() const; + virtual void setGeometry(const QRectF& geometry); + virtual Qt::Orientations expandingDirections() const; + +private: + class Private; + Private *const d; +}; + +} + +#endif // __FLOWLAYOUT__ + diff --git a/widgets/hboxlayout.cpp b/widgets/hboxlayout.cpp deleted file mode 100644 index f74519c47..000000000 --- a/widgets/hboxlayout.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2007 by Matias Valdenegro T. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License version 2 as - * published by the Free Software Foundation - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include "hboxlayout.h" - -namespace Plasma -{ - -HBoxLayout::HBoxLayout(LayoutItem *parent) - : BoxLayout(Qt::Horizontal, parent), - d(0) -{ -} - -HBoxLayout::~HBoxLayout() -{ -} - -bool HBoxLayout::hasWidthForHeight() const -{ - return true; -} - -qreal HBoxLayout::widthForHeight(qreal w) const -{ - Q_UNUSED(w); - return qreal(); -} - -} diff --git a/widgets/hboxlayout.h b/widgets/hboxlayout.h index fce704a1a..ec3883155 100644 --- a/widgets/hboxlayout.h +++ b/widgets/hboxlayout.h @@ -1,5 +1,5 @@ /* - * Copyright 2007 by Matias Valdenegro T. + * Copyright 2007 by Robert Knight * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License version 2 as @@ -16,48 +16,9 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#ifndef __H_BOX_LAYOUT__ -#define __H_BOX_LAYOUT__ +#ifndef PLASMA_HBOXLAYOUT +#define PLASMA_HBOXLAYOUT -#include -#include - -#include #include -namespace Plasma -{ - - -/** - * Horizontal Box Layout - * - * @author Matias Valdenegro T. - * - * This class implements a Horizontal Box Layout, it just lays items horizontally, from left to right. - */ -class PLASMA_EXPORT HBoxLayout : public BoxLayout -{ - public: - - /** - * Constructor. - */ - HBoxLayout(LayoutItem *parent = 0); - - /** - * Virtual Destructor. - */ - virtual ~HBoxLayout(); - - bool hasWidthForHeight() const; - qreal widthForHeight(qreal w) const; - - private: - class Private; - Private *const d; -}; - -} - -#endif /* __H_BOX_LAYOUT__ */ +#endif /* PLASMA_HBOXLAYOUT */ diff --git a/widgets/tests/testLayouts.cpp b/widgets/tests/testLayouts.cpp index 590c9129d..07fe050c5 100644 --- a/widgets/tests/testLayouts.cpp +++ b/widgets/tests/testLayouts.cpp @@ -10,8 +10,7 @@ #include "../pushbutton.h" #include "../lineedit.h" -#include "../vboxlayout.h" -#include "../hboxlayout.h" +#include "../boxlayout.h" #include "../widget.h" #include "../label.h" @@ -54,14 +53,16 @@ int main(int argc, char **argv) h2->addItem(pushTwo); scene.addItem(pushTwo); - Plasma::LineEdit *editTwo = new Plasma::LineEdit; - h2->addItem(editTwo); - scene.addItem(editTwo); + Plasma::LineEdit *editTwo = new Plasma::LineEdit; + h2->addItem(editTwo); + scene.addItem(editTwo); - Plasma::Label *labelTwo = new Plasma::Label( 0 ); - labelTwo->setText( "hello world 2" ); - h2->addItem(labelTwo); - scene.addItem(labelTwo); + Plasma::Label *labelTwo = new Plasma::Label( 0 ); + labelTwo->setText( "hello world 2" ); + h2->addItem(labelTwo); + scene.addItem(labelTwo); + + widgetLayout->update(); view.show(); return app.exec(); diff --git a/widgets/vboxlayout.cpp b/widgets/vboxlayout.cpp deleted file mode 100644 index 7d09c12bd..000000000 --- a/widgets/vboxlayout.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2007 by Matias Valdenegro T. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License version 2 as - * published by the Free Software Foundation - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details - * - * You should have received a copy of the GNU Library General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include "vboxlayout.h" - -namespace Plasma -{ - -VBoxLayout::VBoxLayout(LayoutItem *parent) - : BoxLayout(Qt::Vertical, parent), - d(0) -{ -} - -VBoxLayout::~VBoxLayout() -{ -} - -bool VBoxLayout::hasHeightForWidth() const -{ - return true; -} - -qreal VBoxLayout::heightForWidth(qreal w) const -{ - Q_UNUSED(w); - return qreal(); -} - -} diff --git a/widgets/vboxlayout.h b/widgets/vboxlayout.h index a65bc9d8f..bde8dd264 100644 --- a/widgets/vboxlayout.h +++ b/widgets/vboxlayout.h @@ -1,5 +1,5 @@ /* - * Copyright 2007 by Matias Valdenegro T. + * Copyright 2007 by Robert Knight * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License version 2 as @@ -19,42 +19,6 @@ #ifndef PLASMA_VBOXLAYOUT #define PLASMA_VBOXLAYOUT -#include #include -namespace Plasma -{ - - -/** - * Vertical Box Layout - * - * @author Matias Valdenegro T. - * - * This class implements a Vertical Box Layout, it just lays items in vertical, from up to down. - */ -class PLASMA_EXPORT VBoxLayout : public BoxLayout -{ - public: - - /** - * Constructor. - */ - explicit VBoxLayout(LayoutItem *parent = 0); - - /** - * Virtual Destructor. - */ - ~VBoxLayout(); - - bool hasHeightForWidth() const; - qreal heightForWidth(qreal w) const; - - private: - class Private; - Private *const d; -}; - -} - #endif /* PLASMA_VBOXLAYOUT */