summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjoern Erik Nilsen <bjorn.nilsen@nokia.com>2009-04-29 17:51:02 (GMT)
committerBjoern Erik Nilsen <bjorn.nilsen@nokia.com>2009-04-29 17:51:02 (GMT)
commitd355e069ca1952504ef8cc6fb44350b53314d5d5 (patch)
treef2372d54b1bf988312ff4ab0b91b7d3e74629367
parentd20bf7c8b368cb56df1a965793edbd23ff0c3213 (diff)
parent073b7ce298b0e079f310ec22dee44a9fc0af9ee6 (diff)
downloadQt-d355e069ca1952504ef8cc6fb44350b53314d5d5.zip
Qt-d355e069ca1952504ef8cc6fb44350b53314d5d5.tar.gz
Qt-d355e069ca1952504ef8cc6fb44350b53314d5d5.tar.bz2
Merge commit 'origin/4.5'
-rw-r--r--doc/src/examples/flowlayout.qdoc117
-rw-r--r--doc/src/images/flowlayout-example.pngbin5054 -> 29350 bytes
-rw-r--r--doc/src/layout.qdoc2
-rw-r--r--examples/layouts/flowlayout/flowlayout.cpp27
-rw-r--r--examples/layouts/flowlayout/flowlayout.h3
-rw-r--r--examples/layouts/flowlayout/window.cpp3
-rw-r--r--examples/layouts/flowlayout/window.h3
-rw-r--r--src/gui/kernel/qlayout.cpp2
-rw-r--r--src/gui/kernel/qwidget.cpp9
-rw-r--r--src/gui/painting/qpainter.cpp31
-rw-r--r--src/gui/painting/qtransform.cpp4
-rw-r--r--tests/auto/qwidget/tst_qwidget.cpp108
12 files changed, 281 insertions, 28 deletions
diff --git a/doc/src/examples/flowlayout.qdoc b/doc/src/examples/flowlayout.qdoc
index 557ba39..5fdafe2 100644
--- a/doc/src/examples/flowlayout.qdoc
+++ b/doc/src/examples/flowlayout.qdoc
@@ -43,8 +43,117 @@
\example layouts/flowlayout
\title Flow Layout Example
- The Flow Layout example demonstrates a custom layout that arranges child widgets from
- left to right and top to bottom in a top-level widget.
+ The Flow Layout example demonstrates a custom layout that arranges child
+ widgets from left to right and top to bottom in a top-level widget.
- \image flowlayout-example.png
-*/
+ \image flowlayout-example.png Screenshot of the Flow Layout example
+
+ The items are first laid out horizontally and then vertically when each line
+ in the layout runs out of space.
+
+ The Flowlayout class mainly uses QLayout and QWidgetItem, while the
+ Window uses QWidget and QLabel. We will only document the definition
+ and implementation of \cFlowLayout below.
+
+ \section1 FlowLayout Class Definition
+
+ The \c FlowLayout class inherits QLayout. It is a custom layout class
+ that arranges its child widgets horizontally and vertically.
+
+ \snippet examples/layouts/flowlayout/flowlayout.h 0
+
+ We reimplement functions inherited from QLayout. These functions add items to
+ the layout and handle their orientation and geometry.
+
+ We also declare two private methods, \c doLayout() and \c smartSpacing().
+ \c doLayout() lays out the layout items, while the \c
+ smartSpacing() function calculates the spacing between them.
+
+ \section1 FlowLayout Class Implementation
+
+ We start off by looking at the constructor:
+
+ \snippet examples/layouts/flowlayout/flowlayout.cpp 1
+
+ In the constructor we call \c setContentsMargins() to set the left, top,
+ right and bottom margin. By default, QLayout uses values provided by
+ the current style (see QStyle::PixelMetric).
+
+ \snippet examples/layouts/flowlayout/flowlayout.cpp 2
+
+ In this example we reimplement \c addItem(), which is a pure virtual
+ function. When using \c addItem() the ownership of the layout items is
+ transferred to the layout, and it is therefore the layout's
+ responsibility to delete them.
+
+ \snippet examples/layouts/flowlayout/flowlayout.cpp 3
+
+ \c addItem() is implemented to add items to the layout.
+
+ \snippet examples/layouts/flowlayout/flowlayout.cpp 4
+
+ We implement \c horizontalSpacing() and \c verticalSpacing() to get
+ hold of the spacing between the widgets inside the layout. If the value
+ is less than or equal to 0, this value will be used. If not,
+ \c smartSpacing() will be called to calculate the spacing.
+
+ \snippet examples/layouts/flowlayout/flowlayout.cpp 5
+
+ We then implement \c count() to return the number of items in the
+ layout. To navigate the list of items we use \c itemAt() and
+ takeAt() to remove and return items from the list. If an item is
+ removed, the remaining items will be renumbered. All three
+ functions are pure virtual functions from QLayout.
+
+ \snippet examples/layouts/flowlayout/flowlayout.cpp 6
+
+ \c expandingDirections() returns the \l{Qt::Orientation}s in which the
+ layout can make use of more space than its \c sizeHint().
+
+ \snippet examples/layouts/flowlayout/flowlayout.cpp 7
+
+ To adjust to widgets of which height is dependent on width, we implement \c
+ heightForWidth(). The function \c hasHeightForWidth() is used to test for this
+ dependency, and \c heightForWidth() passes the width on to \c doLayout() which
+ in turn uses the width as an argument for the layout rect, i.e., the bounds in
+ which the items are laid out. This rect does not include the layout margin().
+
+ \snippet examples/layouts/flowlayout/flowlayout.cpp 8
+
+ \c setGeometry() is normally used to do the actual layout, i.e., calculate
+ the geometry of the layout's items. In this example, it calls \c doLayout()
+ and passes the layout rect.
+
+ \c sizeHint() returns the preferred size of the layout and \c minimumSize()
+ returns the minimum size of the layout.
+
+ \snippet examples/layouts/flowlayout/flowlayout.cpp 9
+
+ \c doLayout() handles the layout if \c horizontalSpacing() or \c
+ verticalSpacing() don't return the default value. It uses
+ \c getContentsMargins() to calculate the area available to the
+ layout items.
+
+ \snippet examples/layouts/flowlayout/flowlayout.cpp 10
+
+ It then sets the proper amount of spacing for each widget in the
+ layout, based on the current style.
+
+ \snippet examples/layouts/flowlayout/flowlayout.cpp 11
+
+ The position of each item in the layout is then calculated by
+ adding the items width and the line height to the initial x and y
+ coordinates. This in turn lets us find out whether the next item
+ will fit on the current line or if it must be moved down to the next.
+ We also find the height of the current line based on the widgets height.
+
+ \snippet examples/layouts/flowlayout/flowlayout.cpp 12
+
+ \c smartSpacing() is designed to get the default spacing for either
+ the top-level layouts or the sublayouts. The default spacing for
+ top-level layouts, when the parent is a QWidget, will be determined
+ by querying the style. The default spacing for sublayouts, when
+ the parent is a QLayout, will be determined by querying the spacing
+ of the parent layout.
+
+*/ \ No newline at end of file
diff --git a/doc/src/images/flowlayout-example.png b/doc/src/images/flowlayout-example.png
index 27660d6..61abe1f 100644
--- a/doc/src/images/flowlayout-example.png
+++ b/doc/src/images/flowlayout-example.png
Binary files differ
diff --git a/doc/src/layout.qdoc b/doc/src/layout.qdoc
index 38163c8..55dfd8b 100644
--- a/doc/src/layout.qdoc
+++ b/doc/src/layout.qdoc
@@ -371,7 +371,7 @@
should store the value in a local variable if you need it again
later within in the same function.
\o You should not call QLayoutItem::setGeometry() twice on the same
- item in the smae function. This call can be very expensive if the
+ item in the same function. This call can be very expensive if the
item has several child widgets, because the layout manager must do
a complete layout every time. Instead, calculate the geometry and
then set it. (This does not only apply to layouts, you should do
diff --git a/examples/layouts/flowlayout/flowlayout.cpp b/examples/layouts/flowlayout/flowlayout.cpp
index c4032d0..263911d 100644
--- a/examples/layouts/flowlayout/flowlayout.cpp
+++ b/examples/layouts/flowlayout/flowlayout.cpp
@@ -42,7 +42,7 @@
#include <QtGui>
#include "flowlayout.h"
-
+//! [1]
FlowLayout::FlowLayout(QWidget *parent, int margin, int hSpacing, int vSpacing)
: QLayout(parent), m_hSpace(hSpacing), m_vSpace(vSpacing)
{
@@ -54,19 +54,25 @@ FlowLayout::FlowLayout(int margin, int hSpacing, int vSpacing)
{
setContentsMargins(margin, margin, margin, margin);
}
+//! [1]
+//! [2]
FlowLayout::~FlowLayout()
{
QLayoutItem *item;
while ((item = takeAt(0)))
delete item;
}
+//! [2]
+//! [3]
void FlowLayout::addItem(QLayoutItem *item)
{
itemList.append(item);
}
+//! [3]
+//! [4]
int FlowLayout::horizontalSpacing() const
{
if (m_hSpace >= 0) {
@@ -84,7 +90,9 @@ int FlowLayout::verticalSpacing() const
return smartSpacing(QStyle::PM_LayoutVerticalSpacing);
}
}
+//! [4]
+//! [5]
int FlowLayout::count() const
{
return itemList.size();
@@ -102,12 +110,16 @@ QLayoutItem *FlowLayout::takeAt(int index)
else
return 0;
}
+//! [5]
+//! [6]
Qt::Orientations FlowLayout::expandingDirections() const
{
return 0;
}
+//! [6]
+//! [7]
bool FlowLayout::hasHeightForWidth() const
{
return true;
@@ -118,7 +130,9 @@ int FlowLayout::heightForWidth(int width) const
int height = doLayout(QRect(0, 0, width, 0), true);
return height;
}
+//! [7]
+//! [8]
void FlowLayout::setGeometry(const QRect &rect)
{
QLayout::setGeometry(rect);
@@ -140,7 +154,9 @@ QSize FlowLayout::minimumSize() const
size += QSize(2*margin(), 2*margin());
return size;
}
+//! [8]
+//! [9]
int FlowLayout::doLayout(const QRect &rect, bool testOnly) const
{
int left, top, right, bottom;
@@ -149,7 +165,9 @@ int FlowLayout::doLayout(const QRect &rect, bool testOnly) const
int x = effectiveRect.x();
int y = effectiveRect.y();
int lineHeight = 0;
+//! [9]
+//! [10]
QLayoutItem *item;
foreach (item, itemList) {
QWidget *wid = item->widget();
@@ -161,6 +179,8 @@ int FlowLayout::doLayout(const QRect &rect, bool testOnly) const
if (spaceY == -1)
spaceY = wid->style()->layoutSpacing(
QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical);
+//! [10]
+//! [11]
int nextX = x + item->sizeHint().width() + spaceX;
if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) {
x = effectiveRect.x();
@@ -177,7 +197,8 @@ int FlowLayout::doLayout(const QRect &rect, bool testOnly) const
}
return y + lineHeight - rect.y() + bottom;
}
-
+//! [11]
+//! [12]
int FlowLayout::smartSpacing(QStyle::PixelMetric pm) const
{
QObject *parent = this->parent();
@@ -190,4 +211,4 @@ int FlowLayout::smartSpacing(QStyle::PixelMetric pm) const
return static_cast<QLayout *>(parent)->spacing();
}
}
-
+//! [12]
diff --git a/examples/layouts/flowlayout/flowlayout.h b/examples/layouts/flowlayout/flowlayout.h
index 9940e55..bab7f36 100644
--- a/examples/layouts/flowlayout/flowlayout.h
+++ b/examples/layouts/flowlayout/flowlayout.h
@@ -45,7 +45,7 @@
#include <QLayout>
#include <QRect>
#include <QWidgetItem>
-
+//! [0]
class FlowLayout : public QLayout
{
public:
@@ -74,5 +74,6 @@ private:
int m_hSpace;
int m_vSpace;
};
+//! [0]
#endif
diff --git a/examples/layouts/flowlayout/window.cpp b/examples/layouts/flowlayout/window.cpp
index 51d9886..b7d9eae 100644
--- a/examples/layouts/flowlayout/window.cpp
+++ b/examples/layouts/flowlayout/window.cpp
@@ -43,7 +43,7 @@
#include "flowlayout.h"
#include "window.h"
-
+//! [1]
Window::Window()
{
FlowLayout *flowLayout = new FlowLayout;
@@ -57,3 +57,4 @@ Window::Window()
setWindowTitle(tr("Flow Layout"));
}
+//! [1] \ No newline at end of file
diff --git a/examples/layouts/flowlayout/window.h b/examples/layouts/flowlayout/window.h
index ffd60af..77f8b6f 100644
--- a/examples/layouts/flowlayout/window.h
+++ b/examples/layouts/flowlayout/window.h
@@ -47,7 +47,7 @@
QT_BEGIN_NAMESPACE
class QLabel;
QT_END_NAMESPACE
-
+//! [0]
class Window : public QWidget
{
Q_OBJECT
@@ -55,5 +55,6 @@ class Window : public QWidget
public:
Window();
};
+//! [0]
#endif
diff --git a/src/gui/kernel/qlayout.cpp b/src/gui/kernel/qlayout.cpp
index aa46249..1d5a70d 100644
--- a/src/gui/kernel/qlayout.cpp
+++ b/src/gui/kernel/qlayout.cpp
@@ -1239,7 +1239,7 @@ bool QLayout::activate()
Must be implemented in subclasses to remove the layout item at \a
index from the layout, and return the item. If there is no such
item, the function must do nothing and return 0. Items are numbered
- consecutively from 0. If an item is deleted, other items will be
+ consecutively from 0. If an item is removed, other items will be
renumbered.
The following code fragment shows a safe way to remove all items
diff --git a/src/gui/kernel/qwidget.cpp b/src/gui/kernel/qwidget.cpp
index dfc8365..dd95053 100644
--- a/src/gui/kernel/qwidget.cpp
+++ b/src/gui/kernel/qwidget.cpp
@@ -4829,8 +4829,13 @@ void QWidget::render(QPainter *painter, const QPoint &targetOffset,
const QRegion oldSystemClip = enginePriv->systemClip;
const QRegion oldSystemViewport = enginePriv->systemViewport;
- // This ensures that transformed system clips are inside the current system clip.
- enginePriv->setSystemViewport(oldSystemClip);
+ // This ensures that all painting triggered by render() is clipped to the current engine clip.
+ if (painter->hasClipping()) {
+ const QRegion painterClip = painter->deviceTransform().map(painter->clipRegion());
+ enginePriv->setSystemViewport(oldSystemClip.isEmpty() ? painterClip : oldSystemClip & painterClip);
+ } else {
+ enginePriv->setSystemViewport(oldSystemClip);
+ }
render(target, targetOffset, toBePainted, renderFlags);
diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp
index d9c7937..b158392 100644
--- a/src/gui/painting/qpainter.cpp
+++ b/src/gui/painting/qpainter.cpp
@@ -2398,7 +2398,6 @@ QRegion QPainter::clipRegion() const
// ### Falcon: Use QPainterPath
for (int i=0; i<d->state->clipInfo.size(); ++i) {
const QPainterClipInfo &info = d->state->clipInfo.at(i);
- QRegion other;
switch (info.clipType) {
case QPainterClipInfo::RegionClip: {
@@ -2451,15 +2450,20 @@ QRegion QPainter::clipRegion() const
lastWasNothing = false;
continue;
}
- if (info.operation == Qt::IntersectClip)
- region &= QRegion(info.rect) * matrix;
- else if (info.operation == Qt::UniteClip)
+ if (info.operation == Qt::IntersectClip) {
+ // Use rect intersection if possible.
+ if (matrix.type() <= QTransform::TxScale)
+ region &= matrix.mapRect(info.rect);
+ else
+ region &= matrix.map(QRegion(info.rect));
+ } else if (info.operation == Qt::UniteClip) {
region |= QRegion(info.rect) * matrix;
- else if (info.operation == Qt::NoClip) {
+ } else if (info.operation == Qt::NoClip) {
lastWasNothing = true;
region = QRegion();
- } else
+ } else {
region = QRegion(info.rect) * matrix;
+ }
break;
}
@@ -2470,15 +2474,20 @@ QRegion QPainter::clipRegion() const
lastWasNothing = false;
continue;
}
- if (info.operation == Qt::IntersectClip)
- region &= QRegion(info.rectf.toRect()) * matrix;
- else if (info.operation == Qt::UniteClip)
+ if (info.operation == Qt::IntersectClip) {
+ // Use rect intersection if possible.
+ if (matrix.type() <= QTransform::TxScale)
+ region &= matrix.mapRect(info.rectf.toRect());
+ else
+ region &= matrix.map(QRegion(info.rectf.toRect()));
+ } else if (info.operation == Qt::UniteClip) {
region |= QRegion(info.rectf.toRect()) * matrix;
- else if (info.operation == Qt::NoClip) {
+ } else if (info.operation == Qt::NoClip) {
lastWasNothing = true;
region = QRegion();
- } else
+ } else {
region = QRegion(info.rectf.toRect()) * matrix;
+ }
break;
}
}
diff --git a/src/gui/painting/qtransform.cpp b/src/gui/painting/qtransform.cpp
index d06107f..4a33b92 100644
--- a/src/gui/painting/qtransform.cpp
+++ b/src/gui/painting/qtransform.cpp
@@ -1387,12 +1387,16 @@ QRegion QTransform::map(const QRegion &r) const
TransformationType t = inline_type();
if (t == TxNone)
return r;
+
if (t == TxTranslate) {
QRegion copy(r);
copy.translate(qRound(affine._dx), qRound(affine._dy));
return copy;
}
+ if (t == TxScale && r.numRects() == 1)
+ return QRegion(mapRect(r.boundingRect()));
+
QPainterPath p = map(qt_regionToPath(r));
return p.toFillPolygon(QTransform()).toPolygon();
}
diff --git a/tests/auto/qwidget/tst_qwidget.cpp b/tests/auto/qwidget/tst_qwidget.cpp
index b32bc4d..ee61871 100644
--- a/tests/auto/qwidget/tst_qwidget.cpp
+++ b/tests/auto/qwidget/tst_qwidget.cpp
@@ -285,6 +285,8 @@ private slots:
void render_systemClip();
void render_systemClip2_data();
void render_systemClip2();
+ void render_systemClip3_data();
+ void render_systemClip3();
void setContentsMargins();
@@ -6995,7 +6997,7 @@ void tst_QWidget::render_systemClip2()
// Render entire widget directly onto device.
widget.render(&image);
-#ifndef RENDER_DEBUG
+#ifdef RENDER_DEBUG
image.save("systemclip_with_device.png");
#endif
// All pixels within the system clip should now be
@@ -7011,17 +7013,17 @@ void tst_QWidget::render_systemClip2()
// Refill image with red.
image.fill(QColor(Qt::red).rgb());
+ paintEngine->setSystemClip(systemClip);
// Do the same with an untransformed painter.
QPainter painter(&image);
//Make sure we're using the same paint engine and has the right clip set.
- paintEngine->setSystemClip(systemClip);
QCOMPARE(painter.paintEngine(), paintEngine);
QCOMPARE(paintEngine->systemClip(), systemClip);
widget.render(&painter);
-#ifndef RENDER_DEBUG
+#ifdef RENDER_DEBUG
image.save("systemclip_with_untransformed_painter.png");
#endif
// All pixels within the system clip should now be
@@ -7036,6 +7038,106 @@ void tst_QWidget::render_systemClip2()
}
}
+void tst_QWidget::render_systemClip3_data()
+{
+ QTest::addColumn<QSize>("size");
+ QTest::addColumn<bool>("useSystemClip");
+
+ // Reference: http://en.wikipedia.org/wiki/Flag_of_Norway
+ QTest::newRow("Norwegian Civil Flag") << QSize(220, 160) << false;
+ QTest::newRow("Norwegian War Flag") << QSize(270, 160) << true;
+}
+
+// This test ensures that the current engine clip (systemClip + painter clip)
+// is preserved after QPainter::setClipRegion(..., Qt::ReplaceClip);
+void tst_QWidget::render_systemClip3()
+{
+ QFETCH(QSize, size);
+ QFETCH(bool, useSystemClip);
+
+ // Calculate the inner/outer cross of the flag.
+ QRegion outerCross(0, 0, size.width(), size.height());
+ outerCross -= QRect(0, 0, 60, 60);
+ outerCross -= QRect(100, 0, size.width() - 100, 60);
+ outerCross -= QRect(0, 100, 60, 60);
+ outerCross -= QRect(100, 100, size.width() - 100, 60);
+
+ QRegion innerCross(0, 0, size.width(), size.height());
+ innerCross -= QRect(0, 0, 70, 70);
+ innerCross -= QRect(90, 0, size.width() - 90, 70);
+ innerCross -= QRect(0, 90, 70, 70);
+ innerCross -= QRect(90, 90, size.width() - 90, 70);
+
+ const QRegion redArea(QRegion(0, 0, size.width(), size.height()) - outerCross);
+ const QRegion whiteArea(outerCross - innerCross);
+ const QRegion blueArea(innerCross);
+ QRegion systemClip;
+
+ // Okay, here's the image that should look like a Norwegian civil/war flag in the end.
+ QImage flag(size, QImage::Format_ARGB32);
+ flag.fill(QColor(Qt::transparent).rgba());
+
+ if (useSystemClip) {
+ QPainterPath warClip(QPoint(size.width(), 0));
+ warClip.lineTo(size.width() - 110, 60);
+ warClip.lineTo(size.width(), 80);
+ warClip.lineTo(size.width() - 110, 100);
+ warClip.lineTo(size.width(), 160);
+ warClip.closeSubpath();
+ systemClip = QRegion(0, 0, size.width(), size.height()) - QRegion(warClip.toFillPolygon().toPolygon());
+ flag.paintEngine()->setSystemClip(systemClip);
+ }
+
+ QPainter painter(&flag);
+ painter.fillRect(QRect(QPoint(), size), Qt::red); // Fill image background with red.
+ painter.setClipRegion(outerCross); // Limit widget painting to inside the outer cross.
+
+ // Here's the widget that's supposed to draw the inner/outer cross of the flag.
+ // The outer cross (white) should be drawn when the background is auto-filled, and
+ // the inner cross (blue) should be drawn in the paintEvent.
+ class MyWidget : public QWidget
+ { public:
+ void paintEvent(QPaintEvent *)
+ {
+ QPainter painter(this);
+ // Be evil and try to paint outside the outer cross. This should not be
+ // possible since the shared painter is clipped to the outer cross.
+ painter.setClipRect(0, 0, 60, 60, Qt::ReplaceClip);
+ painter.fillRect(rect(), Qt::green);
+ painter.setClipRegion(clip, Qt::ReplaceClip);
+ painter.fillRect(rect(), Qt::blue);
+ }
+ QRegion clip;
+ };
+
+ MyWidget widget;
+ widget.clip = innerCross;
+ widget.setFixedSize(size);
+ widget.setPalette(Qt::white);
+ widget.setAutoFillBackground(true);
+ widget.render(&painter);
+
+#ifdef RENDER_DEBUG
+ flag.save("flag.png");
+#endif
+
+ // Let's make sure we got a Norwegian flag.
+ for (int i = 0; i < flag.height(); ++i) {
+ for (int j = 0; j < flag.width(); ++j) {
+ const QPoint pixel(j, i);
+ const QRgb pixelValue = flag.pixel(pixel);
+ if (useSystemClip && !systemClip.contains(pixel))
+ QCOMPARE(pixelValue, QColor(Qt::transparent).rgba());
+ else if (redArea.contains(pixel))
+ QCOMPARE(pixelValue, QColor(Qt::red).rgba());
+ else if (whiteArea.contains(pixel))
+ QCOMPARE(pixelValue, QColor(Qt::white).rgba());
+ else
+ QCOMPARE(pixelValue, QColor(Qt::blue).rgba());
+ }
+ }
+}
+
void tst_QWidget::setContentsMargins()
{
QLabel label("why does it always rain on me?");