Home | All Classes | Main Classes | Annotated | Grouped Classes | Functions |

# Customized Layoutmanager

This examples demonstrates how to write customized layout (geometry) managers like card layouts, border layout and flow layouts.

See also: Documentation of Geometry Management.

Header file of the flow layout:

    /****************************************************************************
    ** $Id: qt/flow.h   3.3.1   edited May 27 2003 $
    **
    ** Definition of simple flow layout for custom layout example
    **
    ** Created : 979899
    **
    ** Copyright (C) 1997 by Trolltech AS.  All rights reserved.
    **
    ** This file is part of an example program for Qt.  This example
    ** program may be used, distributed and modified without limitation.
    **
    *****************************************************************************/

    #ifndef FLOW_H
    #define FLOW_H

    #include <qlayout.h>
    #include <qptrlist.h>

    class SimpleFlow : public QLayout
    {
    public:
        SimpleFlow( QWidget *parent, int border=0, int space=-1,
                    const char *name=0 )
            : QLayout( parent, border, space, name ),
            cached_width(0) {}
        SimpleFlow( QLayout* parent, int space=-1, const char *name=0 )
            : QLayout( parent, space, name ),
            cached_width(0) {}
        SimpleFlow( int space=-1, const char *name=0 )
            : QLayout( space, name ),
            cached_width(0) {}

        ~SimpleFlow();

        void addItem( QLayoutItem *item);
        bool hasHeightForWidth() const;
        int heightForWidth( int ) const;
        QSize sizeHint() const;
        QSize minimumSize() const;
        QLayoutIterator iterator();
        QSizePolicy::ExpandData expanding() const;

    protected:
        void setGeometry( const QRect& );

    private:
        int doLayout( const QRect&, bool testonly = FALSE );
        QPtrList<QLayoutItem> list;
        int cached_width;
        int cached_hfw;

    };

    #endif

Implementation of the flow layout:

    /****************************************************************************
    ** $Id: qt/flow.cpp   3.3.1   edited May 27 2003 $
    **
    ** Implementing your own layout: flow example
    **
    ** Copyright (C) 1996 by Trolltech AS.  All rights reserved.
    **
    ** This file is part of an example program for Qt.  This example
    ** program may be used, distributed and modified without limitation.
    **
    *****************************************************************************/

    #include "flow.h"

    class SimpleFlowIterator :public QGLayoutIterator
    {
    public:
        SimpleFlowIterator( QPtrList<QLayoutItem> *l ) :idx(0), list(l)  {}
        uint count() const;
        QLayoutItem *current();
        QLayoutItem *next();
        QLayoutItem *takeCurrent();

    private:
        int idx;
        QPtrList<QLayoutItem> *list;

    };

    uint SimpleFlowIterator::count() const
    {
        return list->count();
    }

    QLayoutItem *SimpleFlowIterator::current()
    {
        return idx < int(count()) ? list->at(idx) : 0;
    }

    QLayoutItem *SimpleFlowIterator::next()
    {
        idx++; return current();
    }

    QLayoutItem *SimpleFlowIterator::takeCurrent()
    {
        return idx < int(count()) ? list->take( idx ) : 0;
    }

    SimpleFlow::~SimpleFlow()
    {
        deleteAllItems();
    }

    int SimpleFlow::heightForWidth( int w ) const
    {
        if ( cached_width != w ) {
            //Not all C++ compilers support "mutable" yet:
            SimpleFlow * mthis = (SimpleFlow*)this;
            int h = mthis->doLayout( QRect(0,0,w,0), TRUE );
            mthis->cached_hfw = h;
            mthis->cached_width = w;
            return h;
        }
        return cached_hfw;
    }

    void SimpleFlow::addItem( QLayoutItem *item)
    {
        list.append( item );
    }

    bool SimpleFlow::hasHeightForWidth() const
    {
        return TRUE;
    }

    QSize SimpleFlow::sizeHint() const
    {
        return minimumSize();
    }

    QSizePolicy::ExpandData SimpleFlow::expanding() const
    {
        return QSizePolicy::NoDirection;
    }

    QLayoutIterator SimpleFlow::iterator()
    {
        return QLayoutIterator( new SimpleFlowIterator( &list ) );
    }

    void SimpleFlow::setGeometry( const QRect &r )
    {
        QLayout::setGeometry( r );
        doLayout( r );
    }

    int SimpleFlow::doLayout( const QRect &r, bool testonly )
    {
        int x = r.x();
        int y = r.y();
        int h = 0;          //height of this line so far.
        QPtrListIterator<QLayoutItem> it(list);
        QLayoutItem *o;
        while ( (o=it.current()) != 0 ) {
            ++it;
            int nextX = x + o->sizeHint().width() + spacing();
            if ( nextX - spacing() > r.right() && h > 0 ) {
                x = r.x();
                y = y + h + spacing();
                nextX = x + o->sizeHint().width() + spacing();
                h = 0;
            }
            if ( !testonly )
                o->setGeometry( QRect( QPoint( x, y ), o->sizeHint() ) );
            x = nextX;
            h = QMAX( h,  o->sizeHint().height() );
        }
        return y + h - r.y();
    }

    QSize SimpleFlow::minimumSize() const
    {
        QSize s(0,0);
        QPtrListIterator<QLayoutItem> it(list);
        QLayoutItem *o;
        while ( (o=it.current()) != 0 ) {
            ++it;
            s = s.expandedTo( o->minimumSize() );
        }
        return s;
    }

Header file of the border layout:

    /****************************************************************************
    ** $Id: qt/border.h   3.3.1   edited May 27 2003 $
    **
    ** Definition of simple flow layout for custom layout example
    **
    ** Created : 979899
    **
    ** Copyright (C) 1997 by Trolltech AS.  All rights reserved.
    **
    ** This file is part of an example program for Qt.  This example
    ** program may be used, distributed and modified without limitation.
    **
    *****************************************************************************/

    #ifndef BORDER_H
    #define BORDER_H

    #include <qlayout.h>
    #include <qptrlist.h>

    class BorderWidgetItem : public QWidgetItem
    {
    public:
        BorderWidgetItem( QWidget *w )
            : QWidgetItem( w )
        {}

        void setGeometry( const QRect &r )
        { widget()->setGeometry( r ); }

    };

    class BorderLayout : public QLayout
    {
    public:
        enum Position {
            West = 0,
            North,
            South,
            East,
            Center
        };

        struct BorderLayoutStruct
        {
            BorderLayoutStruct( QLayoutItem *i, Position p ) {
                item = i;
                pos = p;
            }

            QLayoutItem *item;
            Position pos;
        };

        enum SizeType {
            Minimum = 0,
            SizeHint
        };

        BorderLayout( QWidget *parent, int border = 0, int autoBorder = -1,
                      const char *name = 0 )
            : QLayout( parent, border, autoBorder, name ), cached( 0, 0 ), mcached( 0, 0 ),
              sizeDirty( TRUE ), msizeDirty( TRUE )
        {}

        BorderLayout( QLayout* parent, int autoBorder = -1, const char *name = 0 )
            : QLayout( parent, autoBorder, name  ), cached( 0, 0 ), mcached( 0, 0 ),
              sizeDirty( TRUE ), msizeDirty( TRUE )
        {}

        BorderLayout( int autoBorder = -1, const char *name = 0 )
            : QLayout( autoBorder, name ), cached( 0, 0 ), mcached( 0, 0 ),
              sizeDirty( TRUE ), msizeDirty( TRUE )
        {}

        ~BorderLayout();

        void addItem( QLayoutItem *item );

        void addWidget( QWidget *widget, Position pos );
        void add( QLayoutItem *item, Position pos );

        bool hasHeightForWidth() const;

        QSize sizeHint() const;
        QSize minimumSize() const;

        QLayoutIterator iterator();

        QSizePolicy::ExpandData expanding() const;

    protected:
        void setGeometry( const QRect &rect );

    private:
        void doLayout( const QRect &rect, bool testonly = FALSE );
        void calcSize( SizeType st );

        QPtrList<BorderLayoutStruct> list;
        QSize cached, mcached;
        bool sizeDirty, msizeDirty;

    };

    #endif

Implementation of the border layout:

    /****************************************************************************
    ** $Id: qt/border.cpp   3.3.1   edited May 27 2003 $
    **
    ** Implementing your own layout: flow example
    **
    ** Copyright (C) 1996 by Trolltech AS.  All rights reserved.
    **
    ** This file is part of an example program for Qt.  This example
    ** program may be used, distributed and modified without limitation.
    **
    *****************************************************************************/

    #include "border.h"

    class BorderLayoutIterator : public QGLayoutIterator
    {
    public:
        BorderLayoutIterator( const QPtrList<BorderLayout::BorderLayoutStruct> *l )
            : idx( 0 ) , list( (QPtrList<BorderLayout::BorderLayoutStruct>*)l )
        {}

        uint count() const;
        QLayoutItem *current();
        BorderLayout::BorderLayoutStruct *currentStruct();
        void toFirst();
        QLayoutItem *next();
        QLayoutItem *takeCurrent();
        BorderLayoutIterator &operator++();

    private:
        int idx;
        QPtrList<BorderLayout::BorderLayoutStruct> *list;

    };

    uint BorderLayoutIterator::count() const
    {
        return list->count();
    }

    QLayoutItem *BorderLayoutIterator::current()
    {
        return idx < (int)count() ? list->at( idx )->item : 0;
    }

    BorderLayout::BorderLayoutStruct *BorderLayoutIterator::currentStruct()
    {
        return idx < (int)count() ? list->at( idx ) : 0;
    }

    void BorderLayoutIterator::toFirst()
    {
        idx = 0;
    }

    QLayoutItem *BorderLayoutIterator::next()
    {
        idx++;
        return current();
    }

    QLayoutItem *BorderLayoutIterator::takeCurrent()
    {
        BorderLayout::BorderLayoutStruct *b
            = idx < int( list->count() ) ? list->take(  idx  ) : 0;
        QLayoutItem *item =  b ? b->item : 0;
        delete b;
        return item;
    }

    BorderLayoutIterator &BorderLayoutIterator::operator++()
    {
        next();
        return *this;
    }

    BorderLayout::~BorderLayout()
    {
        deleteAllItems();
    }

    void BorderLayout::addItem( QLayoutItem *item )
    {
        add( item, West );
    }

    void BorderLayout::addWidget( QWidget *widget, Position pos )
    {
        add( new BorderWidgetItem( widget ), pos );
    }

    void BorderLayout::add( QLayoutItem *item, Position pos )
    {
        list.append( new BorderLayoutStruct( item, pos ) );
        sizeDirty = TRUE; msizeDirty = TRUE;
        calcSize( SizeHint ); calcSize( Minimum );
    }

    bool BorderLayout::hasHeightForWidth() const
    {
        return FALSE;
    }

    QSize BorderLayout::sizeHint() const
    {
        return cached;
    }

    QSize BorderLayout::minimumSize() const
    {
        return cached;
    }

    QSizePolicy::ExpandData BorderLayout::expanding() const

    {
        return QSizePolicy::BothDirections;
    }

    QLayoutIterator BorderLayout::iterator()
    {
        return QLayoutIterator( new BorderLayoutIterator( &list ) );
    }

    void BorderLayout::setGeometry( const QRect &rct )
    {
        QLayout::setGeometry( rct );
        doLayout( rct );
    }

    void BorderLayout::doLayout( const QRect &rct, bool /*testonly*/ )
    {
        int ew = 0, ww = 0, nh = 0, sh = 0;
        int h = 0;

        BorderLayoutIterator it( &list );
        BorderLayoutStruct *o;
        BorderLayoutStruct *center = 0;
        while ( ( o = it.currentStruct() ) != 0 ) {
            ++it;

            if ( o->pos == North ) {
                o->item->setGeometry( QRect( rct.x(), nh, rct.width(), o->item->sizeHint().height() ) );
                nh += o->item->geometry().height() + spacing();
            }
            if ( o->pos == South ) {
                o->item->setGeometry( QRect( o->item->geometry().x(), o->item->geometry().y(),
                                             rct.width(), o->item->sizeHint().height() ) );
                sh += o->item->geometry().height() + spacing();
                o->item->setGeometry( QRect( rct.x(), rct.y() + rct.height() - sh + spacing(),
                                             o->item->geometry().width(), o->item->geometry().height() ) );
            }
            if ( o->pos == Center )
                center = o;
        }

        h = rct.height() - nh - sh;

        it.toFirst();
        while ( ( o = it.currentStruct() ) != 0 ) {
            ++it;

            if ( o->pos == West ) {
                o->item->setGeometry( QRect( rct.x() + ww, nh, o->item->sizeHint().width(), h ) );
                ww += o->item->geometry().width() + spacing();
            }
            if ( o->pos == East ) {
                o->item->setGeometry( QRect( o->item->geometry().x(), o->item->geometry().y(),
                                             o->item->sizeHint().width(), h ) );
                ew += o->item->geometry().width() + spacing();
                o->item->setGeometry( QRect( rct.x() + rct.width() - ew + spacing(), nh,
                                             o->item->geometry().width(), o->item->geometry().height() ) );
            }
        }

        if ( center )
            center->item->setGeometry( QRect( ww, nh, rct.width() - ew - ww, h ) );
    }

    void BorderLayout::calcSize( SizeType st )
    {
        if ( ( st == Minimum && !msizeDirty ) ||
             ( st == SizeHint && !sizeDirty ) )
            return;

        int w = 0, h = 0;

        BorderLayoutIterator it( &list );
        BorderLayoutStruct *o;
        while ( ( o = it.currentStruct() ) != 0 ) {
            ++it;
            if ( o->pos == North ||
                 o->pos == South ) {
                if ( st == Minimum )
                    h += o->item->minimumSize().height();
                else
                    h += o->item->sizeHint().height();
            }
            else if ( o->pos == West ||
                      o->pos == East ) {
                if ( st == Minimum )
                    w += o->item->minimumSize().width();
                else
                    w += o->item->sizeHint().width();
            } else {
                if ( st == Minimum ) {
                    h += o->item->minimumSize().height();
                    w += o->item->minimumSize().width();
                }
                else {
                    h += o->item->sizeHint().height();
                    w += o->item->sizeHint().width();
                }
            }
        }

        if ( st == Minimum ) {
            msizeDirty = FALSE;
            mcached = QSize( w, h );
        } else {
            sizeDirty = FALSE;
            cached = QSize( w, h );
        }

        return;
    }

Header file of the card layout:

    /****************************************************************************
    ** $Id: qt/card.h   3.3.1   edited May 27 2003 $
    **
    ** Definition of simple flow layout for custom layout example
    **
    ** Created : 979899
    **
    ** Copyright (C) 1997 by Trolltech AS.  All rights reserved.
    **
    ** This file is part of an example program for Qt.  This example
    ** program may be used, distributed and modified without limitation.
    **
    *****************************************************************************/

    #ifndef CARD_H
    #define CARD_H

    #include <qlayout.h>
    #include <qptrlist.h>

    class CardLayout : public QLayout
    {
    public:
        CardLayout( QWidget *parent, int dist )
            : QLayout( parent, 0, dist ) {}
        CardLayout( QLayout* parent, int dist)
            : QLayout( parent, dist ) {}
        CardLayout( int dist )
            : QLayout( dist ) {}
        ~CardLayout();

        void addItem( QLayoutItem *item );
        QSize sizeHint() const;
        QSize minimumSize() const;
        QLayoutIterator iterator();
        void setGeometry( const QRect &rect );

    private:
        QPtrList<QLayoutItem> list;

    };

    #endif

Implementation of the card layout:

    /****************************************************************************
    ** $Id: qt/card.cpp   3.3.1   edited May 27 2003 $
    **
    ** Implementing your own layout: flow example
    **
    ** Copyright (C) 1996 by Trolltech AS.  All rights reserved.
    **
    ** This file is part of an example program for Qt.  This example
    ** program may be used, distributed and modified without limitation.
    **
    *****************************************************************************/

    #include "card.h"

    class CardLayoutIterator :public QGLayoutIterator
    {
    public:
        CardLayoutIterator( QPtrList<QLayoutItem> *l )
            : idx( 0 ), list( l )  {}

        QLayoutItem *current();
        QLayoutItem *next();
        QLayoutItem *takeCurrent();

    private:
        int idx;
        QPtrList<QLayoutItem> *list;
    };

    QLayoutItem *CardLayoutIterator::current()
    {
        return idx < int( list->count() ) ? list->at( idx ) : 0;
    }

    QLayoutItem *CardLayoutIterator::next()
    {
        idx++; return current();
    }

    QLayoutItem *CardLayoutIterator::takeCurrent()
    {
        return idx < int( list->count() ) ?list->take( idx ) : 0;
    }

    QLayoutIterator CardLayout::iterator()
    {
        return QLayoutIterator(  new CardLayoutIterator( &list )  );
    }

    CardLayout::~CardLayout()
    {
        deleteAllItems();
    }

    void CardLayout::addItem(  QLayoutItem *item  )
    {
        list.append( item );
    }

    void CardLayout::setGeometry( const QRect &rct )
    {
        QLayout::setGeometry( rct );

        QPtrListIterator<QLayoutItem> it( list );
        if ( it.count() == 0 )
            return;

        QLayoutItem *o;

        int i = 0;

        int w = rct.width() - ( list.count() - 1 ) * spacing();
        int h = rct.height() - ( list.count() - 1 ) * spacing();

        while ( ( o=it.current() ) != 0 ) {
            ++it;
            QRect geom( rct.x() + i * spacing(), rct.y() + i * spacing(),
                        w, h  );
            o->setGeometry(  geom  );
            ++i;
        }
    }

    QSize CardLayout::sizeHint() const
    {
        QSize s(0,0);
        int n = list.count();
        if ( n > 0 )
            s = QSize(100,70); //start with a nice default size
        QPtrListIterator<QLayoutItem> it(list);
        QLayoutItem *o;
        while ( (o=it.current()) != 0 ) {
            ++it;
            s = s.expandedTo( o->minimumSize() );
        }
        return s + n*QSize(spacing(),spacing());
    }

    QSize CardLayout::minimumSize() const
    {
        QSize s(0,0);
        int n = list.count();
        QPtrListIterator<QLayoutItem> it(list);
        QLayoutItem *o;
        while ( (o=it.current()) != 0 ) {
            ++it;
            s = s.expandedTo( o->minimumSize() );
        }
        return s + n*QSize(spacing(),spacing());
    }

Main:

    /****************************************************************************
    ** $Id: qt/main.cpp   3.3.1   edited May 27 2003 $
    **
    ** Main for custom layout example
    **
    ** Copyright (C) 1996 by Trolltech AS.  All rights reserved.
    **
    ** This file is part of an example program for Qt.  This example
    ** program may be used, distributed and modified without limitation.
    **
    *****************************************************************************/

    #include "flow.h"
    #include "border.h"
    #include "card.h"

    #include <qapplication.h>
    #include <qlabel.h>
    #include <qcolor.h>
    #include <qgroupbox.h>
    #include <qpushbutton.h>
    #include <qmultilineedit.h>
    #include <qcolor.h>

    int main( int argc, char **argv )
    {
        QApplication a( argc, argv );

        QWidget *f = new QWidget;
        QBoxLayout *gm = new QVBoxLayout( f, 5 );

        SimpleFlow *b1 = new SimpleFlow( gm );

        b1->add( new QPushButton( "Short", f ) );
        b1->add( new QPushButton( "Longer", f ) );
        b1->add( new QPushButton( "Different text", f ) );
        b1->add( new QPushButton( "More text", f ) );
        b1->add( new QPushButton( "Even longer button text", f ) );
        QPushButton* qb = new QPushButton( "Quit", f );
        a.connect( qb, SIGNAL( clicked() ), SLOT( quit() ) );
        b1->add( qb );

        QWidget *wid = new QWidget( f );

        BorderLayout *large = new BorderLayout( wid );
        large->setSpacing( 5 );
        large->addWidget( new QPushButton( "North", wid ), BorderLayout::North );
        large->addWidget( new QPushButton( "West", wid ), BorderLayout::West );
        QMultiLineEdit* m = new QMultiLineEdit( wid );
        m->setText( "Central\nWidget" );
        large->addWidget( m, BorderLayout::Center );
        QWidget *east1 = new QPushButton( "East", wid );
        large->addWidget( east1, BorderLayout::East );
        QWidget *east2 = new QPushButton( "East 2", wid );
        large->addWidget( east2 , BorderLayout::East );
        large->addWidget( new QPushButton( "South", wid ), BorderLayout::South );
        //Left-to-right tab order looks better:
        QWidget::setTabOrder( east2, east1 );
        gm->addWidget( wid );

        wid = new QWidget( f );
        CardLayout *card = new CardLayout( wid, 10 );

        QWidget *crd = new QWidget( wid );
        crd->setBackgroundColor( Qt::red );
        card->add( crd );
        crd = new QWidget( wid );
        crd->setBackgroundColor( Qt::green );
        card->add( crd );
        crd = new QWidget( wid );
        crd->setBackgroundColor( Qt::blue );
        card->add( crd );
        crd = new QWidget( wid );
        crd->setBackgroundColor( Qt::white );
        card->add( crd );
        crd = new QWidget( wid );
        crd->setBackgroundColor( Qt::black );
        card->add( crd );
        crd = new QWidget( wid );
        crd->setBackgroundColor( Qt::yellow );
        card->add( crd );

        gm->addWidget( wid );

        QLabel* s = new QLabel( f );
        s->setText( "outermost box" );
        s->setFrameStyle( QFrame::Panel | QFrame::Sunken );
        s->setAlignment( Qt::AlignVCenter | Qt::AlignHCenter );
        gm->addWidget( s );
        a.setMainWidget( f );
        f->setCaption("Qt Example - Custom Layout");
        f->show();

        int result = a.exec();
        delete f;
        return result;
    }

See also Examples.