You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
517 lines
18 KiB
517 lines
18 KiB
13 years ago
|
/****************************************************************************
|
||
|
**
|
||
|
** 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 sales@trolltech.com.
|
||
|
**
|
||
|
** 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().)
|
||
|
|
||
|
*/
|