qt5base-lts/tests/auto/widgets/graphicsview/qgraphicslayout/tst_qgraphicslayout.cpp
Jarek Kobus 5accfa1e68 Use QList instead of QVector in widgets tests
Task-number: QTBUG-84469
Change-Id: I490fdb237afad2d8a15954fe34d6b549a83fa4aa
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
2020-06-25 10:13:43 +02:00

1002 lines
30 KiB
C++

/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtTest/QtTest>
#include <QtGui>
#include <QtWidgets>
#include <math.h>
class tst_QGraphicsLayout : public QObject
{
Q_OBJECT
public:
tst_QGraphicsLayout();
virtual ~tst_QGraphicsLayout();
private slots:
void sizeHints();
void compressLayoutRequest();
void automaticReparenting();
void verifyActivate();
void sizeHintOfHiddenLayout();
void invalidate();
void constructors();
void alternativeLayoutItems();
void ownership();
};
tst_QGraphicsLayout::tst_QGraphicsLayout()
{
}
tst_QGraphicsLayout::~tst_QGraphicsLayout()
{
}
void tst_QGraphicsLayout::sizeHints()
{
QGraphicsView view;
QGraphicsScene scene;
QGraphicsWidget *window = new QGraphicsWidget();
scene.addItem(window);
QGraphicsLinearLayout *lout = new QGraphicsLinearLayout(window);
lout->setContentsMargins(0,0,0,0);
QGraphicsWidget *gw = new QGraphicsWidget(window);
gw->setMinimumSize(QSizeF(10,10));
gw->setPreferredSize(QSizeF(100,100));
gw->setMaximumSize(QSizeF(500,500));
lout->addItem(gw);
QCOMPARE(lout->effectiveSizeHint(Qt::MinimumSize), gw->effectiveSizeHint(Qt::MinimumSize));
QCOMPARE(lout->effectiveSizeHint(Qt::PreferredSize), gw->effectiveSizeHint(Qt::PreferredSize));
QCOMPARE(lout->effectiveSizeHint(Qt::MaximumSize), gw->effectiveSizeHint(Qt::MaximumSize));
}
enum FunctionType {
SetGeometry = 0,
Invalidate,
NumFunctionTypes
};
class TestGraphicsWidget : public QGraphicsWidget {
public:
TestGraphicsWidget(QGraphicsWidget *parent = 0) : QGraphicsWidget(parent)
{ }
bool event(QEvent *e) {
++(m_eventCount[int(e->type())]);
return QGraphicsWidget::event(e);
}
int eventCount(QEvent::Type type) {
return m_eventCount.value(int(type));
}
void clearEventCount() {
m_eventCount.clear();
}
void clearCounters() {
m_eventCount.clear();
functionCount.clear();
}
void setGeometry(const QRectF &rect)
{
QGraphicsWidget::setGeometry(rect);
++(functionCount[SetGeometry]);
}
void callUpdateGeometry()
{
// updateGeometry() is protected
QGraphicsWidget::updateGeometry();
}
QMap<FunctionType, int> functionCount;
private:
QMap<int, int> m_eventCount;
};
void tst_QGraphicsLayout::compressLayoutRequest()
{
QGraphicsView view;
QGraphicsScene scene;
TestGraphicsWidget *tw = new TestGraphicsWidget();
scene.addItem(tw);
view.show();
QVERIFY(QTest::qWaitForWindowExposed(&view));
QGraphicsLinearLayout *lout = new QGraphicsLinearLayout(tw);
for (int i = 0; i < 4; ++i) {
QGraphicsWidget *gw = new QGraphicsWidget(tw);
gw->setPreferredSize(QSizeF(50, 50));
lout->addItem(gw);
}
QApplication::processEvents();
QCOMPARE(tw->eventCount(QEvent::LayoutRequest), 1);
}
void tst_QGraphicsLayout::automaticReparenting()
{
QGraphicsView view;
QGraphicsScene scene;
{
QGraphicsWidget *w = new QGraphicsWidget();
QGraphicsLinearLayout *l = new QGraphicsLinearLayout(w);
QGraphicsWidget *w1 = new QGraphicsWidget;
l->addItem(w1);
scene.addItem(w);
QCOMPARE(w1->parentWidget(), w);
delete w;
}
{
QGraphicsWidget *w = new QGraphicsWidget();
QGraphicsLinearLayout *l = new QGraphicsLinearLayout(w);
QGraphicsWidget *w1 = new QGraphicsWidget;
l->addItem(w1);
scene.addItem(w);
QCOMPARE(w1->parentWidget(), w);
QGraphicsWidget *ww = new QGraphicsWidget();
QGraphicsLinearLayout *l1 = new QGraphicsLinearLayout(ww);
#if !defined(Q_OS_MAC) && defined(QT_DEBUG)
QTest::ignoreMessage(QtWarningMsg, "QGraphicsLayout::addChildLayoutItem: QGraphicsWidget \"\""
" in wrong parent; moved to correct parent");
#endif
l1->addItem(w1);
QCOMPARE(w1->parentWidget(), ww);
delete w;
}
QGraphicsWidget *window = new QGraphicsWidget();
scene.addItem(window);
view.show();
QGraphicsLinearLayout *l1 = new QGraphicsLinearLayout();
QGraphicsWidget *w1 = new QGraphicsWidget();
l1->addItem(w1);
QGraphicsWidget *w2 = new QGraphicsWidget();
l1->addItem(w2);
QCOMPARE(w1->parentItem(), nullptr);
QCOMPARE(w2->parentItem(), nullptr);
scene.addItem(w1);
QCOMPARE(w1->parentItem(), nullptr);
window->setLayout(l1);
QCOMPARE(w1->parentItem(), static_cast<QGraphicsItem*>(window));
QCOMPARE(w2->parentItem(), static_cast<QGraphicsItem*>(window));
// Sublayouts
QGraphicsLinearLayout *l2 = new QGraphicsLinearLayout();
QGraphicsWidget *w3 = new QGraphicsWidget();
l2->addItem(w3);
QGraphicsWidget *w4 = new QGraphicsWidget();
l2->addItem(w4);
QGraphicsLinearLayout *l3 = new QGraphicsLinearLayout();
l2->addItem(l3);
QGraphicsWidget *window2 = new QGraphicsWidget();
scene.addItem(window2);
window2->setLayout(l2);
QCOMPARE(w3->parentItem(), static_cast<QGraphicsItem*>(window2));
QCOMPARE(w4->parentItem(), static_cast<QGraphicsItem*>(window2));
// graphics item with another parent
QGraphicsLinearLayout *l5 = new QGraphicsLinearLayout();
l5->addItem(w1);
l5->addItem(w2);
QCOMPARE(w1->parentItem(), static_cast<QGraphicsItem*>(window));
QCOMPARE(w2->parentItem(), static_cast<QGraphicsItem*>(window));
QGraphicsLinearLayout *l4 = new QGraphicsLinearLayout();
l4->addItem(l5);
QGraphicsWidget *window3 = new QGraphicsWidget();
scene.addItem(window3);
window3->setLayout(l4);
QCOMPARE(w1->parentItem(), static_cast<QGraphicsItem*>(window3));
QCOMPARE(w2->parentItem(), static_cast<QGraphicsItem*>(window3));
}
class TestLayout : public QGraphicsLinearLayout
{
public:
TestLayout(QGraphicsLayoutItem *parent = 0)
: QGraphicsLinearLayout(parent)
{
setContentsMargins(0,0,0,0);
setSpacing(0);
}
void setGeometry(const QRectF &rect)
{
++(functionCount[SetGeometry]);
QGraphicsLinearLayout::setGeometry(rect);
}
void invalidate()
{
++(functionCount[Invalidate]);
QGraphicsLinearLayout::invalidate();
}
void clearCounters() {
functionCount.clear();
}
QMap<FunctionType, int> functionCount;
};
void tst_QGraphicsLayout::verifyActivate()
{
QGraphicsScene scene;
QGraphicsView view(&scene);
QGraphicsWidget *window = new QGraphicsWidget();
scene.addItem(window);
TestLayout *lout = new TestLayout(window);
QGraphicsWidget *w = new QGraphicsWidget();
lout->addItem(w);
window->setLayout(lout);
QCOMPARE(lout->functionCount[SetGeometry], 0);
window->setVisible(false);
QCOMPARE(lout->functionCount[SetGeometry], 0);
window->setVisible(true);
// on polish or the first time a widget is shown, the widget is resized.
QCOMPARE(lout->functionCount[SetGeometry], 1);
}
void tst_QGraphicsLayout::sizeHintOfHiddenLayout()
{
QGraphicsScene scene;
QGraphicsView view(&scene);
QGraphicsWidget *window = new QGraphicsWidget(0, Qt::Window);
scene.addItem(window);
TestLayout *lout = new TestLayout(window);
lout->setContentsMargins(1,2,2,1);
QGraphicsWidget *w = new QGraphicsWidget;
w->setPreferredSize(20, 20);
w->setMaximumSize(50, 50);
lout->addItem(w);
window->setLayout(lout);
for (int pass = 0; pass < 3; ++pass) {
QCOMPARE(lout->sizeHint(Qt::MinimumSize), QSizeF(3,3));
QCOMPARE(lout->sizeHint(Qt::PreferredSize), QSizeF(23,23));
QCOMPARE(lout->sizeHint(Qt::MaximumSize), QSizeF(53,53));
window->setVisible(pass % 2);
}
}
static void clearAllCounters(TestGraphicsWidget *widget)
{
if (!widget)
return;
widget->clearCounters();
TestLayout *layout = static_cast<TestLayout *>(widget->layout());
if (layout) {
layout->clearCounters();
for (int i = layout->count() - 1; i >=0; --i) {
QGraphicsLayoutItem *item = layout->itemAt(i);
if (item->isLayout()) {
// ### Not used ATM
//TestLayout *lay = static_cast<TestLayout*>(static_cast<QGraphicsLayout*>(item));
//clearAllCounters(lay);
} else {
TestGraphicsWidget *wid = static_cast<TestGraphicsWidget *>(item);
clearAllCounters(wid);
}
}
}
}
static void activateAndReset(TestGraphicsWidget *widget)
{
QApplication::sendPostedEvents();
QApplication::processEvents();
if (widget->layout())
widget->layout()->activate();
clearAllCounters(widget);
}
void tst_QGraphicsLayout::invalidate()
{
QGraphicsLayout::setInstantInvalidatePropagation(true);
QGraphicsScene scene;
QGraphicsView view(&scene);
TestGraphicsWidget *a = new TestGraphicsWidget;
a->setData(0, QString("a"));
scene.addItem(a);
TestLayout *alay = new TestLayout(a);
TestGraphicsWidget *b = new TestGraphicsWidget;
b->setData(0, QString("b"));
alay->addItem(b);
TestLayout *blay = new TestLayout(b);
TestGraphicsWidget *e = new TestGraphicsWidget;
e->setData(0, QString("e"));
blay->addItem(e);
TestGraphicsWidget *c = new TestGraphicsWidget;
c->setData(0, QString("c"));
alay->addItem(c);
TestLayout *clay = new TestLayout(c);
TestGraphicsWidget *f = new TestGraphicsWidget;
f->setData(0, QString("f"));
clay->addItem(f);
TestGraphicsWidget *d = new TestGraphicsWidget;
d->setData(0, QString("d"));
alay->addItem(d);
TestLayout *dlay = new TestLayout(d);
TestGraphicsWidget *g = new TestGraphicsWidget;
g->setData(0, QString("g"));
dlay->addItem(g);
view.show();
{
clearAllCounters(a);
QCoreApplication::sendPostedEvents();
QCoreApplication::processEvents();
alay->activate();
QCOMPARE(alay->isActivated(), true);
QCOMPARE(blay->isActivated(), true);
QCOMPARE(clay->isActivated(), true);
QCOMPARE(dlay->isActivated(), true);
}
{
clearAllCounters(a);
e->callUpdateGeometry();
QCOMPARE(alay->isActivated(), false);
QCOMPARE(blay->isActivated(), false);
QCOMPARE(clay->isActivated(), true);
QCOMPARE(dlay->isActivated(), true);
QCOMPARE(a->eventCount(QEvent::LayoutRequest), 0);
QCOMPARE(b->eventCount(QEvent::LayoutRequest), 0);
QCOMPARE(c->eventCount(QEvent::LayoutRequest), 0);
QCOMPARE(d->eventCount(QEvent::LayoutRequest), 0);
// should only invalidate ascendants of e
QCOMPARE(blay->functionCount[Invalidate], 1);
QCOMPARE(alay->functionCount[Invalidate], 1);
// not siblings
QCOMPARE(clay->functionCount[Invalidate], 0);
QCOMPARE(dlay->functionCount[Invalidate], 0);
QApplication::sendPostedEvents();
QCOMPARE(a->eventCount(QEvent::LayoutRequest), 1);
QCOMPARE(b->eventCount(QEvent::LayoutRequest), 1);
QCOMPARE(c->eventCount(QEvent::LayoutRequest), 0);
QCOMPARE(d->eventCount(QEvent::LayoutRequest), 0);
}
{
activateAndReset(a);
f->callUpdateGeometry();
QCOMPARE(alay->isActivated(), false);
QCOMPARE(blay->isActivated(), true);
QCOMPARE(clay->isActivated(), false);
QCOMPARE(dlay->isActivated(), true);
QCoreApplication::sendPostedEvents();
QCOMPARE(a->eventCount(QEvent::LayoutRequest), 1);
QCOMPARE(b->eventCount(QEvent::LayoutRequest), 0);
QCOMPARE(c->eventCount(QEvent::LayoutRequest), 1);
QCOMPARE(d->eventCount(QEvent::LayoutRequest), 0);
QCOMPARE(a->functionCount[SetGeometry], 1);
QCOMPARE(alay->functionCount[SetGeometry], 1);
QCOMPARE(b->functionCount[SetGeometry], 1);
QCOMPARE(c->functionCount[SetGeometry], 1);
QCOMPARE(d->functionCount[SetGeometry], 1);
// Since nothing really changed, blay and dlay don't need
// to be resized.
QCOMPARE(blay->functionCount[SetGeometry], 0);
QCOMPARE(clay->functionCount[SetGeometry], 1);
QCOMPARE(dlay->functionCount[SetGeometry], 0);
QCOMPARE(f->functionCount[SetGeometry], 1);
QCOMPARE(a->size(), QSizeF(150, 50));
}
{
activateAndReset(a);
f->setPreferredSize(QSizeF(60,50));
QCOMPARE(alay->isActivated(), false);
QCOMPARE(blay->isActivated(), true);
QCOMPARE(clay->isActivated(), false);
QCOMPARE(dlay->isActivated(), true);
QCOMPARE(c->eventCount(QEvent::LayoutRequest), 0);
QCoreApplication::sendPostedEvents();
QCOMPARE(a->eventCount(QEvent::LayoutRequest), 1);
QCOMPARE(b->eventCount(QEvent::LayoutRequest), 0);
QCOMPARE(c->eventCount(QEvent::LayoutRequest), 1);
QCOMPARE(d->eventCount(QEvent::LayoutRequest), 0);
QCOMPARE(a->functionCount[SetGeometry], 1);
QCOMPARE(alay->functionCount[SetGeometry], 1);
QCOMPARE(b->functionCount[SetGeometry], 1);
QCOMPARE(c->functionCount[SetGeometry], 1);
QCOMPARE(d->functionCount[SetGeometry], 1);
// f actually got wider, need to rearrange its siblings
QCOMPARE(blay->functionCount[SetGeometry], 1);
QCOMPARE(clay->functionCount[SetGeometry], 1);
QCOMPARE(dlay->functionCount[SetGeometry], 1);
QCOMPARE(e->functionCount[SetGeometry], 1);
QCOMPARE(f->functionCount[SetGeometry], 1);
QCOMPARE(g->functionCount[SetGeometry], 1);
QVERIFY(e->size().width() < f->size().width());
QVERIFY(g->size().width() < f->size().width());
}
{
// resize f so much that it'll force a resize of the top widget
// this will currently generate two setGeometry() calls on the child layout
// of the top widget.
activateAndReset(a);
f->setPreferredSize(QSizeF());
f->setMinimumSize(QSizeF(200,50));
QCOMPARE(alay->isActivated(), false);
QCOMPARE(blay->isActivated(), true);
QCOMPARE(clay->isActivated(), false);
QCOMPARE(dlay->isActivated(), true);
QCOMPARE(c->eventCount(QEvent::LayoutRequest), 0);
QCoreApplication::sendPostedEvents();
QCOMPARE(a->eventCount(QEvent::LayoutRequest), 1);
QCOMPARE(b->eventCount(QEvent::LayoutRequest), 0);
QCOMPARE(c->eventCount(QEvent::LayoutRequest), 1);
QCOMPARE(d->eventCount(QEvent::LayoutRequest), 0);
QCOMPARE(a->functionCount[SetGeometry], 1);
/* well, ideally one call to setGeometry(), but it will currently
* get two calls to setGeometry():
* 1. The first LayoutRequest will call activate() - that will call
* setGeometry() on the layout. This geometry will be based on
* the widget geometry which is not correct at this moment.
* (it is still 150 wide)
* 2. Next, we check if the widget is top level, and then we call
* parentWidget->resize(parentWidget->size());
* This will be adjusted to be minimum 200 pixels wide.
* The new size will then be propagated down to the layout
*
*/
QCOMPARE(alay->functionCount[SetGeometry], 2);
QCOMPARE(b->functionCount[SetGeometry], 2);
QCOMPARE(c->functionCount[SetGeometry], 2);
QCOMPARE(d->functionCount[SetGeometry], 2);
// f actually got wider, need to rearrange its siblings
QCOMPARE(blay->functionCount[SetGeometry], 1);
QCOMPARE(clay->functionCount[SetGeometry], 1);
QCOMPARE(dlay->functionCount[SetGeometry], 1);
QCOMPARE(e->functionCount[SetGeometry], 1);
QCOMPARE(f->functionCount[SetGeometry], 1);
QCOMPARE(g->functionCount[SetGeometry], 1);
QVERIFY(e->size().width() < f->size().width());
QVERIFY(g->size().width() < f->size().width());
}
{
f->setPreferredSize(QSizeF());
f->setMinimumSize(QSizeF());
a->adjustSize();
activateAndReset(a);
// update two different leaf widgets,
// eventCount and functionCount should never be >= 2
e->callUpdateGeometry();
g->callUpdateGeometry();
QCOMPARE(alay->isActivated(), false);
QCOMPARE(blay->isActivated(), false);
QCOMPARE(clay->isActivated(), true);
QCOMPARE(dlay->isActivated(), false);
QCoreApplication::sendPostedEvents();
QCOMPARE(a->eventCount(QEvent::LayoutRequest), 1);
QCOMPARE(b->eventCount(QEvent::LayoutRequest), 1);
QCOMPARE(c->eventCount(QEvent::LayoutRequest), 0);
QCOMPARE(d->eventCount(QEvent::LayoutRequest), 1);
QCOMPARE(a->functionCount[SetGeometry], 1);
QCOMPARE(alay->functionCount[SetGeometry], 1);
QCOMPARE(b->functionCount[SetGeometry], 1);
QCOMPARE(c->functionCount[SetGeometry], 1);
QCOMPARE(d->functionCount[SetGeometry], 1);
// f actually got wider, need to rearrange its siblings
QCOMPARE(blay->functionCount[SetGeometry], 1);
QCOMPARE(clay->functionCount[SetGeometry], 0);
QCOMPARE(dlay->functionCount[SetGeometry], 1);
QCOMPARE(e->functionCount[SetGeometry], 1);
QCOMPARE(f->functionCount[SetGeometry], 0);
QCOMPARE(g->functionCount[SetGeometry], 1);
}
QGraphicsLayout::setInstantInvalidatePropagation(false);
}
class Layout : public QGraphicsLayout
{
public:
Layout(QGraphicsLayoutItem *parentItem = 0) : QGraphicsLayout(parentItem) {}
void setGeometry(const QRectF &rect)
{
QGraphicsLayout::setGeometry(rect);
}
int count() const {
return 0;
}
QGraphicsLayoutItem *itemAt(int index) const {
Q_UNUSED(index);
return 0;
}
void removeAt(int index)
{
Q_UNUSED(index);
}
protected:
QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const
{
Q_UNUSED(constraint);
Q_UNUSED(which);
return QSizeF(100,100);
}
};
void tst_QGraphicsLayout::constructors()
{
// Strange test, but see the fix that was with this submit
QList<Layout *> layouts;
for (int pass = 0; pass < 5; ++pass) {
Layout *lay = new Layout();
layouts << lay;
qreal left, top, right, bottom;
lay->getContentsMargins(&left, &top, &right, &bottom);
// Test if the style defaults are sane (should always be ints)
double intpart;
QVERIFY(modf(left, &intpart) == 0.0);
QVERIFY(modf(top, &intpart) == 0.0);
QVERIFY(modf(right, &intpart) == 0.0);
QVERIFY(modf(bottom, &intpart) == 0.0);
lay->setContentsMargins(1, 2, 4, 8);
lay->getContentsMargins(&left, &top, &right, &bottom);
QCOMPARE(int(left), 1);
QCOMPARE(int(top), 2);
QCOMPARE(int(right), 4);
QCOMPARE(int(bottom), 8);
}
qDeleteAll(layouts);
}
class AnimatedLayoutItem : public QGraphicsLayoutItem {
public:
AnimatedLayoutItem(QGraphicsRectItem *item)
: QGraphicsLayoutItem()
{
setGraphicsItem(item);
}
void setGeometry(const QRectF &geom);
QSizeF sizeHint(Qt::SizeHint which, const QSizeF & constraint = QSizeF() ) const;
inline QGraphicsRectItem *rectItem() {
return static_cast<QGraphicsRectItem *>(graphicsItem());
}
QRectF m_geom;
private:
AnimatedLayoutItem() {}
};
void AnimatedLayoutItem::setGeometry(const QRectF &geom)
{
QGraphicsLayoutItem::setGeometry(geom);
}
QSizeF AnimatedLayoutItem::sizeHint(Qt::SizeHint which, const QSizeF & /* constraint */) const
{
switch (which) {
case Qt::MinimumSize:
return QSizeF(32,32);
case Qt::PreferredSize:
return QSizeF(160,90);
case Qt::MaximumSize:
return QSizeF(1000,1000);
default:
return QSizeF(300, 300);
}
}
class AnimatedLayout : public QObject, public QGraphicsLinearLayout {
Q_OBJECT
public:
AnimatedLayout(QGraphicsWidget *widget) : QGraphicsLinearLayout(widget), m_timeline(500, this)
{
connect(&m_timeline, SIGNAL(valueChanged(qreal)), this, SLOT(valueChanged(qreal)));
}
void setGeometry(const QRectF &geom) {
fromGeoms.clear();
toGeoms.clear();
for (int i = 0; i < count(); ++i) {
fromGeoms << itemAt(i)->geometry();
}
QGraphicsLinearLayout::setGeometry(geom);
for (int i = 0; i < count(); ++i) {
toGeoms << itemAt(i)->geometry();
}
m_timeline.start();
}
private slots:
void valueChanged(qreal value) {
for (int i = 0; i < fromGeoms.count(); ++i) {
QGraphicsLayoutItem *li = itemAt(i);
QRectF from = fromGeoms.at(i);
QRectF to = toGeoms.at(i);
QRectF geom(from.topLeft() + (to.topLeft() - from.topLeft()) * value,
from.size() + (to.size() - from.size()) * value);
static_cast<QGraphicsRectItem*>(li->graphicsItem())->setRect(geom);
}
}
private:
QTimeLine m_timeline;
QList<QRectF> fromGeoms;
QList<QRectF> toGeoms;
};
void tst_QGraphicsLayout::alternativeLayoutItems()
{
QGraphicsScene scene;
QGraphicsView view(&scene);
QGraphicsWidget *window = new QGraphicsWidget;
scene.addItem(window);
AnimatedLayout *lout = new AnimatedLayout(window);
lout->setContentsMargins(0, 0, 0, 0);
lout->setSpacing(0);
QGraphicsRectItem *item1 = new QGraphicsRectItem;
AnimatedLayoutItem *li1 = new AnimatedLayoutItem(item1);
lout->addItem(li1);
QGraphicsRectItem *item2 = new QGraphicsRectItem;
AnimatedLayoutItem *li2 = new AnimatedLayoutItem(item2);
lout->addItem(li2);
QGraphicsRectItem *item3 = new QGraphicsRectItem;
AnimatedLayoutItem *li3 = new AnimatedLayoutItem(item3);
lout->addItem(li3);
window->setLayout(lout);
window->setGeometry(0, 0, 99, 99);
view.setSceneRect(QRectF(-10, -10, 110, 110));
view.resize(150, 150);
view.show();
QTRY_COMPARE(static_cast<QGraphicsRectItem*>(li1->graphicsItem())->rect(), QRectF( 0, 0, 33, 99));
QTRY_COMPARE(static_cast<QGraphicsRectItem*>(li2->graphicsItem())->rect(), QRectF(33, 0, 33, 99));
QTRY_COMPARE(static_cast<QGraphicsRectItem*>(li3->graphicsItem())->rect(), QRectF(66, 0, 33, 99));
lout->setOrientation(Qt::Vertical);
QTRY_COMPARE(static_cast<QGraphicsRectItem*>(li1->graphicsItem())->rect(), QRectF(0, 0, 99, 33));
QTRY_COMPARE(static_cast<QGraphicsRectItem*>(li2->graphicsItem())->rect(), QRectF(0, 33, 99, 33));
QTRY_COMPARE(static_cast<QGraphicsRectItem*>(li3->graphicsItem())->rect(), QRectF(0, 66, 99, 33));
}
class CustomLayoutItem : public QGraphicsLayoutItem {
public:
CustomLayoutItem(QSet<QGraphicsLayoutItem*> *destructedSet)
: QGraphicsLayoutItem()
{
m_destructedSet = destructedSet;
setOwnedByLayout(true);
}
QSizeF sizeHint(Qt::SizeHint which, const QSizeF & constraint = QSizeF() ) const;
~CustomLayoutItem() {
m_destructedSet->insert(this);
}
private:
QSet<QGraphicsLayoutItem*> *m_destructedSet;
};
QSizeF CustomLayoutItem::sizeHint(Qt::SizeHint which, const QSizeF & /* constraint */) const
{
switch (which) {
case Qt::MinimumSize:
return QSizeF(32,32);
case Qt::PreferredSize:
return QSizeF(160,90);
case Qt::MaximumSize:
return QSizeF(1000,1000);
default:
return QSizeF(300, 300);
}
}
class CustomGraphicsWidget : public QGraphicsWidget {
public:
CustomGraphicsWidget(QSet<QGraphicsLayoutItem*> *destructedSet = 0)
: QGraphicsWidget()
{
m_destructedSet = destructedSet;
}
QSizeF sizeHint(Qt::SizeHint which, const QSizeF & constraint = QSizeF() ) const;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget * = 0)
{
const QRect r = option->rect.adjusted(0, 0, -1, -1);
painter->drawLine(r.topLeft(), r.bottomRight());
painter->drawLine(r.bottomLeft(), r.topRight());
painter->drawRect(r);
}
~CustomGraphicsWidget() {
if (m_destructedSet)
m_destructedSet->insert(this);
}
private:
QSet<QGraphicsLayoutItem*> *m_destructedSet;
};
QSizeF CustomGraphicsWidget::sizeHint(Qt::SizeHint which, const QSizeF & /* constraint */) const
{
switch (which) {
case Qt::MinimumSize:
return QSizeF(32,32);
case Qt::PreferredSize:
return QSizeF(160,90);
case Qt::MaximumSize:
return QSizeF(1000,1000);
default:
return QSizeF(300, 300);
}
}
static bool compareSets(const QSet<QGraphicsLayoutItem*> &actual, const QSet<QGraphicsLayoutItem*> &expected)
{
if (actual != expected) {
qDebug() << "actual:" << actual << "expected:" << expected;
return false;
}
return true;
}
class CustomLayout : public QGraphicsLayout
{
public :
CustomLayout(QGraphicsLayoutItem *parent)
: QGraphicsLayout(parent)
{
}
~CustomLayout()
{
}
int count() const
{
return items.count();
}
QGraphicsLayoutItem* itemAt(int index) const
{
return items.at(index);
}
void removeAt(int index)
{
items.removeAt(index);
}
void addItem(QGraphicsLayoutItem *item)
{
insertItem(items.count(), item);
}
void insertItem(int index, QGraphicsLayoutItem *item)
{
index = qBound(0, index, items.count());
item->setParentLayoutItem(this);
QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(item);
updateParentWidget(widget);
if (index == items.count()) {
items.append(item);
} else {
items.insert(index, item);
}
updateGeometry();
activate();
}
void updateParentWidget(QGraphicsWidget *item)
{
QGraphicsLayoutItem *parentItem = parentLayoutItem();
while (parentItem && parentItem->isLayout()) {
parentItem = parentItem->parentLayoutItem();
}
if (parentItem) {
item->setParentItem(static_cast<QGraphicsWidget*>(parentItem));
}
}
QSizeF sizeHint(Qt::SizeHint /* which */, const QSizeF & /* constraint */) const
{
return QSizeF(50,50);
}
QList<QGraphicsLayoutItem*> items;
};
void tst_QGraphicsLayout::ownership()
{
QGraphicsScene scene;
QGraphicsView view(&scene);
{
QGraphicsLinearLayout *lay = new QGraphicsLinearLayout;
QSet<QGraphicsLayoutItem*> destructedSet;
CustomLayoutItem *li1 = new CustomLayoutItem(&destructedSet);
lay->addItem(li1);
CustomLayoutItem *li2 = new CustomLayoutItem(&destructedSet);
lay->addItem(li2);
CustomLayoutItem *li3 = new CustomLayoutItem(&destructedSet);
lay->addItem(li3);
destructedSet.clear();
delete lay;
QSet<QGraphicsLayoutItem*> expected;
expected << li1 << li2 << li3;
QVERIFY(compareSets(destructedSet, expected));
}
{
QGraphicsWidget *window = new QGraphicsWidget;
QGraphicsLinearLayout *lay = new QGraphicsLinearLayout;
QSet<QGraphicsLayoutItem*> destructedSet;
CustomGraphicsWidget *li1 = new CustomGraphicsWidget(&destructedSet);
lay->addItem(li1);
CustomGraphicsWidget *li2 = new CustomGraphicsWidget(&destructedSet);
lay->addItem(li2);
CustomGraphicsWidget *li3 = new CustomGraphicsWidget(&destructedSet);
lay->addItem(li3);
window->setLayout(lay);
scene.addItem(window);
destructedSet.clear();
window->setLayout(0);
QCOMPARE(destructedSet.count(), 0);
delete window;
}
{
QGraphicsWidget *window = new QGraphicsWidget(0, Qt::Window);
QGraphicsLinearLayout *lay = new QGraphicsLinearLayout;
CustomGraphicsWidget *li1 = new CustomGraphicsWidget;
lay->addItem(li1);
QGraphicsLinearLayout *li2 = new QGraphicsLinearLayout;
CustomGraphicsWidget *li2_1 = new CustomGraphicsWidget;
li2->addItem(li2_1);
CustomGraphicsWidget *li2_2 = new CustomGraphicsWidget;
li2->addItem(li2_2);
CustomGraphicsWidget *li2_3 = new CustomGraphicsWidget;
li2->addItem(li2_3);
lay->addItem(li2);
CustomGraphicsWidget *li3 = new CustomGraphicsWidget;
lay->addItem(li3);
window->setLayout(lay);
scene.addItem(window);
view.resize(500, 200);
view.show();
for (int i = li2->count(); i > 0; --i) {
QCOMPARE(li2->count(), i);
delete li2->itemAt(0);
}
for (int i = lay->count(); i > 0; --i) {
QCOMPARE(lay->count(), i);
delete lay->itemAt(0);
}
delete window;
}
{
QGraphicsWidget *top = new QGraphicsWidget;
QGraphicsWidget *w = new QGraphicsWidget;
QGraphicsWidget *w2 = new QGraphicsWidget;
CustomLayout *layout = new CustomLayout(top);
layout->addItem(w);
layout->addItem(w2);
top->setLayout(layout);
delete top;
//don't crash after that.
}
}
QTEST_MAIN(tst_QGraphicsLayout)
#include "tst_qgraphicslayout.moc"