diff options
Diffstat (limited to 'doc/layout.doc')
-rw-r--r-- | doc/layout.doc | 516 |
1 files changed, 516 insertions, 0 deletions
diff --git a/doc/layout.doc b/doc/layout.doc new file mode 100644 index 000000000..bb51aebf3 --- /dev/null +++ b/doc/layout.doc @@ -0,0 +1,516 @@ +/**************************************************************************** +** +** Explanation of the layout subsystem +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the Qt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free Qt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at [email protected]. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.QPL +** included in the packaging of this file. Licensees holding valid Qt +** Commercial licenses may use this file in accordance with the Qt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +/*! +\page layout.html + +\title Layout Classes + +The Qt layout system provides a simple and powerful way of specifying +the layout of child widgets. + +By specifying the logical layout once, you get the following benefits: +\list +\i Positioning of child widgets. +\i Sensible default sizes for top-level widgets. +\i Sensible minimum sizes for top-level widgets. +\i Resize handling. +\i Automatic update when contents change: + \list + \i Font size, text or other contents of subwidgets. + \i Hiding or showing a subwidget. + \i Removal of subwidget. + \endlist +\endlist + +Qt's layout classes were designed for hand-written C++ code, so +they're easy to understand and use. + +The disadvantage of hand-written layout code is that it isn't very +convenient when you're experimenting with the design of a form and you +have to go through the compile, link and run cycle for each change. +Our solution is \link designer-manual.book Qt Designer\endlink, a GUI +visual design tool which makes it fast and easy to experiment with +layouts and which generates the C++ layout code for you. + +\section1 Layout Widgets + +The easiest way to give your widgets a good layout is to use the +layout widgets: \l QHBox, \l QVBox and \l QGrid. A layout widget +automatically lays out its child widgets in the order they are +constructed. To create more complex layouts, you can nest layout +widgets inside each other. (Note that \l QWidget does not have a +layout by default, you must add one if you want to lay out widgets +inside a \l QWidget.) + +\list +\i A \l QHBox lays out its child widgets in a horizontal row, left to right. + + \img qhbox-m.png Horizontal box with five child widgets + +\i A \l QVBox lays out its child widgets in a vertical column, top to bottom. + + \img qvbox-m.png Vertical box with five child widgets + +\i A \l QGrid lays out its child widgets in a two dimensional grid. +You can specify how many columns the grid has, and it is populated left to +right, beginning a new row when the previous row is full. The grid is +fixed; the child widgets will not flow to other rows as the widget is +resized. +\endlist + + \img qgrid-m.png Two-column grid with five child widgets + +The grid shown above can be produced by the following code: +\code + QGrid *mainGrid = new QGrid( 2 ); // a 2 x n grid + new QLabel( "One", mainGrid ); + new QLabel( "Two", mainGrid ); + new QLabel( "Three", mainGrid ); + new QLabel( "Four", mainGrid ); + new QLabel( "Five", mainGrid ); +\endcode + +You can adjust the layout to some extent by calling +QWidget::setMinimumSize() or QWidget::setFixedSize() on the child widgets. + +\section1 Adding Widgets to a Layout + +When you add widgets to a layout the layout process works as follows: +\list 1 +\i All the widgets will initially be allocated an amount of space in +accordance with their QWidget::sizePolicy(). +\i If any of the widgets have stretch factors set, with a value +greater than zero, then they are allocated space in proportion to +their \link #stretch stretch factor\endlink. +\i If any of the widgets have stretch factors set to zero they will +only get more space if no other widgets want the space. Of these, +space is allocated to widgets with an \c Expanding size policy first. +\i Any widgets that are allocated less space than their minimum size +(or minimum size hint if no minimum size is specified) are allocated +this minimum size they retquire. (Widgets don't have to have a minimum +size or minimum size hint in which case the strech factor is their +determining factor.) +\i Any widgets that are allocated more space than their maximum size +are allocated the maximum size space they retquire. (Widgets don't have +to have a maximum size in which case the strech factor is their +determining factor.) +\endlist + +\target stretch +\section2 Stretch Factors +\keyword stretch factor + +Widgets are normally created without any stretch factor set. When they +are laid out in a layout the widgets are given a share of space in +accordance with their QWidget::sizePolicy() or their minimum size hint +whichever is the greater. Stretch factors are used to change how much +space widgets are given in proportion to one another. + +If we have three widgets laid out using a QHBox with no stretch +factors set we will get a layout like this: + +\img layout1.png 3 widgets in a row + +If we apply stretch factors to each widget, they will be laid out in +proportion (but never less than their minimum size hint), e.g. + +\img layout2.png 3 stretch factored widgets in a row + + +\section1 QLayout subclassing + +If you need more control over the layout, use a \link QLayout +QLayout\endlink subclass. The layout classes included in Qt are \link +QGridLayout QGridLayout\endlink and \link QBoxLayout +QBoxLayout\endlink. (\link QHBoxLayout QHBoxLayout\endlink and \link +QVBoxLayout QVBoxLayout\endlink are trivial subclasses of QBoxLayout, +that are simpler to use and make the code easier to read.) + +When you use a layout, you must insert each child both into its parent +widget (done in the constructor) and into its layout (typically done +with a function called addWidget()). This way, you can give layout +parameters for each widget, specifying properties like alignment, +stretch, and placement. + +The following code makes a grid like the one above, with a couple of +improvements: +\code + QWidget *main = new QWidget; + + // make a 1x1 grid; it will auto-expand + QGridLayout *grid = new QGridLayout( main, 1, 1 ); + + // add the first four widgets with (row, column) addressing + grid->addWidget( new QLabel( "One", main ), 0, 0 ); + grid->addWidget( new QLabel( "Two", main ), 0, 1 ); + grid->addWidget( new QLabel( "Three", main ), 1, 0 ); + grid->addWidget( new QLabel( "Four", main ), 1, 1 ); + + // add the last widget on row 2, spanning from column 0 to + // column 1, and center aligned + grid->addMultiCellWidget( new QLabel( "Five", main ), 2, 2, 0, 1, + Qt::AlignCenter ); + + // let the ratio between the widths of columns 0 and 1 be 2:3 + grid->setColStretch( 0, 2 ); + grid->setColStretch( 1, 3 ); +\endcode + +You can insert layouts inside a layout by giving the parent layout as +a parameter in the constructor. +\code + QWidget *main = new QWidget; + QLineEdit *field = new QLineEdit( main ); + QPushButton *ok = new QPushButton( "OK", main ); + QPushButton *cancel = new QPushButton( "Cancel", main ); + QLabel *label = new QLabel( "Write once, compile everywhere.", main ); + + // a layout on a widget + QVBoxLayout *vbox = new QVBoxLayout( main ); + vbox->addWidget( label ); + vbox->addWidget( field ); + + // a layout inside a layout + QHBoxLayout *buttons = new QHBoxLayout( vbox ); + buttons->addWidget( ok ); + buttons->addWidget( cancel ); +\endcode +If you are not satisfied with the default placement, you can create +the layout without a parent and then insert it with addLayout(). +The inner layout then becomes a child of the layout it is inserted +into. + +\section1 Custom Layouts + +If the built-in layout classes are not sufficient, you can define your +own. You must make a subclass of \l QLayout that handles resizing and +size calculations, as well as a subclass of \l QGLayoutIterator to +iterate over your layout class. + +See the \link customlayout.html Custom Layout \endlink page for an +in-depth description. + +\section1 Custom Widgets In Layouts + +When you make your own widget class, you should also communicate its +layout properties. If the widget has a QLayout, this is already taken +care of. If the widget does not have any child widgets, or uses manual +layout, you should reimplement the following QWidget member functions: + +\list +\i QWidget::sizeHint() returns the preferred size of the widget. +\i QWidget::minimumSizeHint() returns the smallest size the widget can have. +\i QWidget::sizePolicy() returns a \l QSizePolicy; a value describing + the space retquirements of the widget. +\endlist + +Call QWidget::updateGeometry() whenever the size hint, minimum size +hint or size policy changes. This will cause a layout recalculation. +Multiple calls to updateGeometry() will only cause one recalculation. + +If the preferred height of your widget depends on its actual width +(e.g. a label with automatic word-breaking), set the \link +QSizePolicy::hasHeightForWidth() hasHeightForWidth\endlink() flag in +\link QWidget::sizePolicy() sizePolicy\endlink(), and reimplement \l +QWidget::heightForWidth(). + +Even if you implement heightForWidth(), it is still necessary to +provide a good sizeHint(). The sizeHint() provides the preferred width +of the widget, and it is used by QLayout subclasses that do not +support heightForWidth() (both QGridLayout and QBoxLayout support it). + +For further guidance when implementing these functions, see their +implementations in existing Qt classes that have similar layout +retquirements to your new widget. + +\section1 Manual Layout + +If you are making a one-of-a-kind special layout, you can also make a +custom widget as described above. Reimplement QWidget::resizeEvent() +to calculate the retquired distribution of sizes and call \link +QWidget::setGeometry() setGeometry\endlink() on each child. + +The widget will get an event with \link QEvent::type() type \endlink +\c LayoutHint when the layout needs to be recalculated. Reimplement +QWidget::event() to be notified of \c LayoutHint events. + +\section1 Layout Issues + +The use of rich text in a label widget can introduce some problems to +the layout of its parent widget. Problems occur due to the way rich text +is handled by Qt's layout managers when the label is word wrapped. +In certain cases the parent layout is put into QLayout::FreeResize mode, +meaning that it will not adapt the layout of its contents to fit inside +small sized windows, or even prevent the user from making the +window too small to be usable. This can be overcome by subclassing +the problematic widgets, and implementing suitable sizeHint() and +minimumSizeHint() functions. + +*/ + + +/*! \page customlayout.html + +\title Writing your own layout manager + +Here we present an example in detail. The class CardLayout is inspired +by the Java layout manager of the same name. It lays out the items +(widgets or nested layouts) on top of each other, each item offset by +QLayout::spacing(). + +To write your own layout class, you must define the following: +\list +\i A data structure to store the items handled by the layout. Each +item is a \link QLayoutItem QLayoutItem\endlink. We will use a +QPtrList in this example. +\i \link QLayout::addItem() addItem() \endlink, how to add items to +the layout. +\i \link QLayout::setGeometry() setGeometry() \endlink, how to perform +the layout. +\i \link QLayout::sizeHint() sizeHint() \endlink, the preferred size +of the layout. +\i \link QLayout::iterator() iterator() \endlink, how to iterate over +the layout. +\endlist + +In most cases, you will also implement \link QLayout::minimumSize() +minimumSize\endlink(). + +\section1 card.h + +\code +#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 +\endcode + +\section2 card.cpp + +\code +#include "card.h" +\endcode + +First we define an iterator over the layout. Layout iterators are used +internally by the layout system to handle deletion of widgets. They +are also available for application programmers. + +There are two different classes involved: QLayoutIterator is the class +that is visible to application programmers, it is explicitly shared. +The QLayoutIterator contains a QGLayoutIterator that does all the +work. We must create a subclass of QGLayoutIterator that knows how to +iterate over our layout class. + +In this case, we choose a simple implementation: we store an integer +index into the list and a pointer to the list. Every \l +QGLayoutIterator subclass must implement \link +QGLayoutIterator::current() current\endlink(), \link +QGLayoutIterator::next() next\endlink() and \link +QGLayoutIterator::takeCurrent() takeCurrent\endlink(), as well as a +constructor. In our example we do not need a destructor. + +\code +class CardLayoutIterator : public QGLayoutIterator +{ +public: + CardLayoutIterator( QPtrList<QLayoutItem> *l ) + : idx( 0 ), list( l ) {} + + QLayoutItem *current() + { return idx < int(list->count()) ? list->at(idx) : 0; } + + QLayoutItem *next() + { idx++; return current(); } + + QLayoutItem *takeCurrent() + { return list->take( idx ); } + +private: + int idx; + QPtrList<QLayoutItem> *list; +}; +\endcode + +We must implement QLayout:iterator() to return a QLayoutIterator over +this layout. + +\code +QLayoutIterator CardLayout::iterator() +{ + return QLayoutIterator( new CardLayoutIterator(&list) ); +} +\endcode + +addItem() implements the default placement strategy for layout items. +It must be implemented. It is used by QLayout::add(), by the QLayout +constructor that takes a layout as parent, and it is used to implement +the \link QLayout::autoAdd() auto-add\endlink feature. If your layout +has advanced placement options that retquire parameters, you must +provide extra access functions such as \l QGridLayout::addMultiCell(). + +\code +void CardLayout::addItem( QLayoutItem *item ) +{ + list.append( item ); +} +\endcode + +The layout takes over responsibility of the items added. Since +QLayoutItem does not inherit QObject, we must delete the items +manually. The function QLayout::deleteAllItems() uses the iterator we +defined above to delete all the items in the layout. + +\code +CardLayout::~CardLayout() +{ + deleteAllItems(); +} +\endcode + +The setGeometry() function actually performs the layout. The rectangle +supplied as an argument does not include margin(). If relevant, use +spacing() as the distance between items. + +\code +void CardLayout::setGeometry( const QRect &rect ) +{ + QLayout::setGeometry( rect ); + + QPtrListIterator<QLayoutItem> it( list ); + if (it.count() == 0) + return; + + QLayoutItem *item; + + int i = 0; + + int w = rect.width() - ( list.count() - 1 ) * spacing(); + int h = rect.height() - ( list.count() - 1 ) * spacing(); + + while ( (item = it.current()) != 0 ) { + ++it; + QRect geom( rect.x() + i * spacing(), rect.y() + i * spacing(), + w, h ); + item->setGeometry( geom ); + ++i; + } +} +\endcode + +sizeHint() and minimumSize() are normally very similar in +implementation. The sizes returned by both functions should include +spacing(), but not margin(). + +\code +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 *item; + while ( (item = it.current()) != 0 ) { + ++it; + s = s.expandedTo( item->minimumSize() ); + } + return s + n * QSize( spacing(), spacing() ); +} + +QSize CardLayout::minimumSize() const +{ + QSize s( 0, 0 ); + int n = list.count(); + QPtrListIterator<QLayoutItem> it( list ); + QLayoutItem *item; + while ( (item = it.current()) != 0 ) { + ++it; + s = s.expandedTo( item->minimumSize() ); + } + return s + n * QSize( spacing(), spacing() ); +} +\endcode + +\section1 Further Notes + +This layout does not implement heightForWidth(). + +We ignore QLayoutItem::isEmpty(), this means that the layout will +treat hidden widgets as visible. + +For complex layouts, speed can be greatly increased by caching +calculated values. In that case, implement QLayoutItem::invalidate() +to mark the cached data as dirty. + +Calling QLayoutItem::sizeHint(), etc. may be expensive, so you should +store the value in a local variable if you need it again later in the +same function. + +You should not call QLayoutItem::setGeometry() twice on the same item +in the same function. That can be very expensive if the item has +several child widgets, because it will have to do a complete layout +every time. Instead, calculate the geometry and then set it. (This +doesn't only apply to layouts, you should do the same if you implement +your own resizeEvent().) + +*/ |