summaryrefslogtreecommitdiffstats
path: root/doc/layout.doc
diff options
context:
space:
mode:
Diffstat (limited to 'doc/layout.doc')
-rw-r--r--doc/layout.doc516
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().)
+
+*/