[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-panel-devel
Subject:    Re: [Panel-devel] [PATCH] Beginnings of a panel implementation for
From:       "Robert Knight" <robertknight () gmail ! com>
Date:       2007-08-31 11:41:26
Message-ID: 13ed09c00708310441v47e39bcdvd15f2459059b9d40 () mail ! gmail ! com
[Download RAW message or body]

Hi,

Percy, thanks for doing this, but the current BoxLayout class still
has some problems, mainly that it does not support the maximumSize()
or minimumSize() constraints of layout items.  expandingDirections()
turned out to be too crude to work in the task bar.

I also think it is better for the main layout algorithm to be
direction-agnostic with the direction-specific code elsewhere.  I had
a look at Qt's layout engine and the code is surprisingly complex, we
can probably get away with something less fancy though.

The attached patch is my work-in-progress rewrite of BoxLayout which
is 'good enough' for the Panel.  In addition to understanding
maximumSize() and minimumSize() it also has RightToLeft and
BottomToTop directions.  It currently ignores spacing() and margin() ,
but I will fix that shortly.

Regards,
Robert.

On 31/08/2007, Aaron J. Seigo <aseigo@kde.org> wrote:
> On Thursday 30 August 2007, Percy Leonhardt wrote:
> > On Thursday 23 August 2007, Matt Broadstone wrote:
> > > If you've got it in you (and the time) please just do this, it will
> > > make drop in replacement of QH/VBoxLayout when widgets on the canvas
> > > works trivial.
> >
> > Finally I had the time to do this. Attached is a patch for the 3 layout
> > classes.
> >
> > Can I commit this?
>
> sure thing.. very cool that you got around to this. less code! yay! =))
>
> --
> Aaron J. Seigo
> humru othro a kohnu se
> GPG Fingerprint: 8B8B 2209 0C6F 7C47 B1EA  EE75 D6B7 2EB1 A7F1 DB43
>
> KDE core developer sponsored by Trolltech
>

["rk_box_layout.patch" (text/x-diff)]

Index: CMakeLists.txt
===================================================================
--- CMakeLists.txt	(revision 706509)
+++ CMakeLists.txt	(working copy)
@@ -33,8 +33,7 @@
     karambamanager.cpp
     widgets/boxlayout.cpp
     widgets/checkbox.cpp
-    widgets/hboxlayout.cpp
     widgets/flash.cpp
     widgets/icon.cpp
     widgets/label.cpp
@@ -46,7 +45,6 @@
     widgets/radiobutton.cpp
 #    widgets/rectangle.cpp
     widgets/widget.cpp
-    widgets/vboxlayout.cpp
     widgets/signalplotter.cpp
 )
 
@@ -105,17 +103,18 @@
 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)
Index: widgets/boxlayout.h
===================================================================
--- widgets/boxlayout.h	(revision 706509)
+++ widgets/boxlayout.h	(working copy)
@@ -1,5 +1,6 @@
 /*
  *   Copyright 2007 by Matias Valdenegro T. <mvaldenegro@informatica.utem.cl>
+ *   Copyright 2007 by Robert Knight <robertknight@gmail.com>
  *
  *   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,62 +28,89 @@
 namespace Plasma
 {
 
-
 /**
- * Vertical Box Layout
- *
- * @author Matias Valdenegro T. <mvaldenegro@informatica.utem.cl>
- *
- * 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(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();
 
-        virtual Qt::Orientations expandingDirections() 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;
 
-        QSizeF minimumSize() const;
-        QSizeF maximumSize() const;
-
-        QRectF geometry() const;
-        void setGeometry(const QRectF& geometry);
-
-        //TODO: if we turn this into an actually usable layout we need to implement this
-        //QSizeF sizeHint() const;
-
-        int count() const;
-
-        bool isEmpty() 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);
+        virtual QSizeF minimumSize() const;
+        virtual QSizeF maximumSize() const;
+        virtual QSizeF sizeHint() const;
 
-        int indexOf(LayoutItem *l) const;
-        LayoutItem *itemAt(int i) const;
-        LayoutItem *takeAt(int i);
-
-        QSizeF size() const;
-
-    protected:
-        QList<LayoutItem *> children() 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 */
Index: widgets/boxlayout.cpp
===================================================================
--- widgets/boxlayout.cpp	(revision 706509)
+++ widgets/boxlayout.cpp	(working copy)
@@ -1,5 +1,6 @@
 /*
  *   Copyright 2007 by Matias Valdenegro T. <mvaldenegro@informatica.utem.cl>
+ *   Copyright 2007 by Robert Knight <robertknight@gmail.com>
  *
  *   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,32 +20,222 @@
 #include "boxlayout.h"
 
 #include <QtCore/QList>
+#include <QtCore/QTimeLine>
 
 #include <KDebug>
 
+#include "layoutanimator.h"
+
 namespace Plasma
 {
 
 class BoxLayout::Private
 {
-    public:
-        QRectF geometry;
-        QList<LayoutItem *> childList;
+public:
+    BoxLayout *const q;
+    Direction direction;
+    QRectF geometry;
+    QList<LayoutItem*> children;
+
+    Private(BoxLayout *parent)
+        : q(parent)
+        , direction(LeftToRight)
+    {
+    }
+
+    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;
+        }
+    }
+
+    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;
+        }
+    } 
+
+    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;
+        }
+    }
+
+    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;
+                break;
+            case RightToLeft:
+                newGeometry = QRectF(geometry.width()-pos-size,top,
+                                     size,height);
+                newPos = pos-size;
+                break;
+            case TopToBottom:
+                newGeometry = QRectF(top,pos,height,size);
+                newPos = pos+size;
+                break;
+            case BottomToTop:
+                newGeometry = QRectF(top,geometry.height()-pos-size,
+                                     height,size);
+                newPos = pos-size;
+                break;
+        }
+
+        qDebug() << "Item geometry: " << newGeometry;
+
+        if ( q->animator() )
+            q->animator()->setGeometry(item,newGeometry);
+        else
+            item->setGeometry(newGeometry);
+
+        return newPos; 
+    }
+
+    enum SizeOp
+    {
+        MinSize,
+        MaxSize,
+        HintSize
+    };
+    enum DirOp
+    {
+        WidthDir,
+        HeightDir
+    };
+    static qreal sum(const qreal a , const qreal b) 
+    {
+        return a+b;
+    }
+
+    template <class T> 
+    qreal sizeOp(SizeOp sizeOp , DirOp dirOp , T (*op)(T,T)) const
+    {
+        qreal value = 0;
+        for ( int i = 0 ; i < children.count() ; i++ ) {
+
+            QSizeF itemSize;
+            switch ( sizeOp ) {
+                case MinSize:
+                    itemSize = children[i]->minimumSize();
+                    break;
+                case MaxSize:
+                    itemSize = children[i]->maximumSize();
+                    break;
+                case HintSize:
+                    itemSize = children[i]->sizeHint();
+                    break;
+            }
+
+            if ( dirOp == WidthDir ) {
+                value = op(value,itemSize.width());
+            } else {
+                value = op(value,itemSize.height());
+            }
+        }
+
+        return value;
+    }
+
+    QSizeF totalSize(SizeOp totalSizeOp) const
+    {
+        QSizeF result;
+
+        switch ( direction ) {
+            case LeftToRight:
+            case RightToLeft:
+                result = QSizeF(sizeOp(totalSizeOp,WidthDir,sum),
+                                sizeOp(totalSizeOp,HeightDir,qMax<qreal>));
+                break; 
+            case TopToBottom:
+            case BottomToTop:
+                result = QSizeF(sizeOp(totalSizeOp,WidthDir,qMax<qreal>),
+                                sizeOp(totalSizeOp,HeightDir,sum));
+                break;
+        }
+
+        return result;    
+    }
 };
 
 
-BoxLayout::BoxLayout(LayoutItem *parent)
+BoxLayout::BoxLayout(Direction direction , LayoutItem *parent)
     : Layout(parent),
-      d(new Private)
+      d(new Private(this))
 {
     if (parent) {
         parent->setLayout(this);
     }
+
+    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;
@@ -52,97 +243,195 @@
 
 Qt::Orientations BoxLayout::expandingDirections() const
 {
-    return Qt::Vertical | Qt::Horizontal;
+    switch ( d->direction ) {
+        case LeftToRight:
+        case RightToLeft:
+            return Qt::Horizontal;
+        case TopToBottom:
+        case BottomToTop:
+            return Qt::Vertical;
+        default:
+            Q_ASSERT(false);
+            return 0;
+    }
 }
 
-QSizeF BoxLayout::minimumSize() const
-{
-    return QSizeF();
-}
-
-QSizeF BoxLayout::maximumSize() const
-{
-    return QSizeF();
-}
-
 QRectF BoxLayout::geometry() const
 {
     return d->geometry;
 }
 
-void BoxLayout::setGeometry(const QRectF& geometry)
-{
-    d->geometry = geometry;
-}
-
 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) {
-        return;
-    }
-
-    l->setManagingLayout(this);
-    d->childList.append(l);
-    setGeometry(geometry());
+    insertItem(-1,item);
 }
 
-void BoxLayout::removeItem(LayoutItem *l)
+void BoxLayout::removeItem(LayoutItem *item)
 {
-    if (!l) {
+    if (!item) {
         return;
     }
 
-    l->unsetManagingLayout(this);
-    d->childList.removeAll(l);
-    setGeometry(geometry());
+    item->unsetManagingLayout(this);
+    d->children.removeAll(item);
+
+    if ( animator() )
+        animator()->setCurrentState(item,LayoutAnimator::RemovedState);
+
+    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& geometry)
 {
-    return d->geometry.size();
+    //qDebug() << "Box layout beginning with geo" << geometry;
+    //qDebug() << "This box max size" << maximumSize();
+
+    QVector<qreal> sizes(count());
+    QVector<qreal> expansionSpace(count());
+
+    qreal available = d->size(geometry.size());
+    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<QObject*>(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<LayoutItem *> BoxLayout::children() const
+
+QSizeF BoxLayout::maximumSize() const
 {
-    return d->childList;
+    return Layout::maximumSize();
 }
+QSizeF BoxLayout::minimumSize() const
+{
+    return d->totalSize(Private::MinSize);
+}
+QSizeF BoxLayout::sizeHint() const
+{
+    return d->totalSize(Private::HintSize);   
+}
 
+HBoxLayout::HBoxLayout(LayoutItem *parent)
+    : BoxLayout(LeftToRight,parent)
+{
+}
+
+VBoxLayout::VBoxLayout(LayoutItem *parent)
+    : BoxLayout(TopToBottom,parent)
+{
+}
+
+
 } // Plasma namespace
+
Index: widgets/hboxlayout.h
===================================================================
--- widgets/hboxlayout.h	(revision 706509)
+++ widgets/hboxlayout.h	(working copy)
@@ -1,5 +1,5 @@
 /*
- *   Copyright 2007 by Matias Valdenegro T. <mvaldenegro@informatica.utem.cl>
+ *   Copyright 2007 by Robert Knight <robertknight@gmail.com> 
  *
  *   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,54 +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 <QtCore/QRectF>
-#include <QtCore/QSizeF>
-
-#include <plasma/plasma_export.h>
 #include <plasma/widgets/boxlayout.h>
 
-namespace Plasma
-{
-
-
-/**
- * Vertical Box Layout
- *
- * @author Matias Valdenegro T. <mvaldenegro@informatica.utem.cl>
- *
- * 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();
-
-        Qt::Orientations expandingDirections() const;
-
-        bool hasWidthForHeight() const;
-        qreal widthForHeight(qreal w) const;
-
-        void setGeometry(const QRectF& geometry);
-
-        QSizeF sizeHint() const;
-
-    private:
-        class Private;
-        Private *const d;
-};
-
-}
-
-#endif /* __H_BOX_LAYOUT__ */
+#endif /* PLASMA_HBOXLAYOUT */
Index: widgets/vboxlayout.h
===================================================================
--- widgets/vboxlayout.h	(revision 706509)
+++ widgets/vboxlayout.h	(working copy)
@@ -1,5 +1,5 @@
 /*
- *   Copyright 2007 by Matias Valdenegro T. <mvaldenegro@informatica.utem.cl>
+ *   Copyright 2007 by Robert Knight <robertknight@gmail.com> 
  *
  *   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,48 +19,6 @@
 #ifndef PLASMA_VBOXLAYOUT
 #define PLASMA_VBOXLAYOUT
 
-#include <plasma/plasma_export.h>
 #include <plasma/widgets/boxlayout.h>
 
-namespace Plasma
-{
-
-
-/**
- * Vertical Box Layout
- *
- * @author Matias Valdenegro T. <mvaldenegro@informatica.utem.cl>
- *
- * 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();
-
-        Qt::Orientations expandingDirections() const;
-
-        bool hasHeightForWidth() const;
-        qreal heightForWidth(qreal w) const;
-
-        void setGeometry(const QRectF& geometry);
-
-        QSizeF sizeHint() const;
-
-    private:
-        class Private;
-        Private *const d;
-};
-
-}
-
 #endif /* PLASMA_VBOXLAYOUT */


_______________________________________________
Panel-devel mailing list
Panel-devel@kde.org
https://mail.kde.org/mailman/listinfo/panel-devel


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic