From 4fed50f4ab8fa6331e2f4d1d0567dcc961e7bb01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= Date: Fri, 18 Sep 2020 14:45:36 +0200 Subject: Prepare for having multiple highdpi manual tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the existing “kitchen sink” test out of the way, and rename it to “kitchensink” Change-Id: I121260e640bb2810a94f8112fcea212e97055fb3 Reviewed-by: Tor Arne Vestbø --- tests/manual/highdpi/CMakeLists.txt | 40 - tests/manual/highdpi/dragwidget.cpp | 221 ---- tests/manual/highdpi/dragwidget.h | 64 - tests/manual/highdpi/highdpi.pro | 18 - tests/manual/highdpi/highdpi.qrc | 10 - tests/manual/highdpi/kitchensink/CMakeLists.txt | 40 + tests/manual/highdpi/kitchensink/dragwidget.cpp | 221 ++++ tests/manual/highdpi/kitchensink/dragwidget.h | 64 + tests/manual/highdpi/kitchensink/kitchensink.pro | 18 + tests/manual/highdpi/kitchensink/kitchensink.qrc | 10 + tests/manual/highdpi/kitchensink/main.cpp | 1478 ++++++++++++++++++++++ tests/manual/highdpi/kitchensink/qticon16.png | Bin 0 -> 1884 bytes tests/manual/highdpi/kitchensink/qticon16@2x.png | Bin 0 -> 3187 bytes tests/manual/highdpi/kitchensink/qticon16@3x.png | Bin 0 -> 5307 bytes tests/manual/highdpi/kitchensink/qticon32.png | Bin 0 -> 3187 bytes tests/manual/highdpi/kitchensink/qticon32@2x.png | Bin 0 -> 6474 bytes tests/manual/highdpi/kitchensink/qticon64.png | Bin 0 -> 6474 bytes tests/manual/highdpi/main.cpp | 1478 ---------------------- tests/manual/highdpi/qticon16.png | Bin 1884 -> 0 bytes tests/manual/highdpi/qticon16@2x.png | Bin 3187 -> 0 bytes tests/manual/highdpi/qticon16@3x.png | Bin 5307 -> 0 bytes tests/manual/highdpi/qticon32.png | Bin 3187 -> 0 bytes tests/manual/highdpi/qticon32@2x.png | Bin 6474 -> 0 bytes tests/manual/highdpi/qticon64.png | Bin 6474 -> 0 bytes 24 files changed, 1831 insertions(+), 1831 deletions(-) delete mode 100644 tests/manual/highdpi/CMakeLists.txt delete mode 100644 tests/manual/highdpi/dragwidget.cpp delete mode 100644 tests/manual/highdpi/dragwidget.h delete mode 100644 tests/manual/highdpi/highdpi.pro delete mode 100644 tests/manual/highdpi/highdpi.qrc create mode 100644 tests/manual/highdpi/kitchensink/CMakeLists.txt create mode 100644 tests/manual/highdpi/kitchensink/dragwidget.cpp create mode 100644 tests/manual/highdpi/kitchensink/dragwidget.h create mode 100644 tests/manual/highdpi/kitchensink/kitchensink.pro create mode 100644 tests/manual/highdpi/kitchensink/kitchensink.qrc create mode 100644 tests/manual/highdpi/kitchensink/main.cpp create mode 100644 tests/manual/highdpi/kitchensink/qticon16.png create mode 100644 tests/manual/highdpi/kitchensink/qticon16@2x.png create mode 100644 tests/manual/highdpi/kitchensink/qticon16@3x.png create mode 100644 tests/manual/highdpi/kitchensink/qticon32.png create mode 100644 tests/manual/highdpi/kitchensink/qticon32@2x.png create mode 100644 tests/manual/highdpi/kitchensink/qticon64.png delete mode 100644 tests/manual/highdpi/main.cpp delete mode 100644 tests/manual/highdpi/qticon16.png delete mode 100644 tests/manual/highdpi/qticon16@2x.png delete mode 100644 tests/manual/highdpi/qticon16@3x.png delete mode 100644 tests/manual/highdpi/qticon32.png delete mode 100644 tests/manual/highdpi/qticon32@2x.png delete mode 100644 tests/manual/highdpi/qticon64.png (limited to 'tests/manual') diff --git a/tests/manual/highdpi/CMakeLists.txt b/tests/manual/highdpi/CMakeLists.txt deleted file mode 100644 index b163599185..0000000000 --- a/tests/manual/highdpi/CMakeLists.txt +++ /dev/null @@ -1,40 +0,0 @@ -# Generated from highdpi.pro. - -##################################################################### -## highdpi Binary: -##################################################################### - -qt_internal_add_manual_test(highdpi - SOURCES - dragwidget.cpp dragwidget.h - main.cpp - DEFINES - HAVE_SCREEN_BASE_DPI - INCLUDE_DIRECTORIES - . - PUBLIC_LIBRARIES - Qt::Gui - Qt::GuiPrivate - Qt::Widgets -) - -# Resources: -set(highdpi_resource_files - "qticon16.png" - "qticon16@2x.png" - "qticon16@3x.png" - "qticon32.png" - "qticon32@2x.png" - "qticon64.png" -) - -qt_internal_add_resource(highdpi "highdpi" - PREFIX - "/" - FILES - ${highdpi_resource_files} -) - - -#### Keys ignored in scope 1:.:.:highdpi.pro:: -# TEMPLATE = "app" diff --git a/tests/manual/highdpi/dragwidget.cpp b/tests/manual/highdpi/dragwidget.cpp deleted file mode 100644 index bd65fcf81b..0000000000 --- a/tests/manual/highdpi/dragwidget.cpp +++ /dev/null @@ -1,221 +0,0 @@ -/**************************************************************************** - ** - ** 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 -#include "dragwidget.h" - -class FramedLabel : public QLabel -{ -public: - FramedLabel(const QString &text, QWidget *parent) - : QLabel(text, parent) - { - setAutoFillBackground(true); - setFrameShape(QFrame::Panel); - setFrameShadow(QFrame::Raised); - } -}; - -DragWidget::DragWidget(QString text, QWidget *parent) - : QWidget(parent) -{ - int x = 5; - int y = 5; - - bool createChildWindow = text.isEmpty(); // OK, yes this is a hack... - if (text.isEmpty()) - text = "You can drag from this window and drop text here"; - - QStringList words = text.split(' '); - for (const QString &word : words) { - if (!word.isEmpty()) { - auto wordLabel = new FramedLabel(word, this); - wordLabel->move(x, y); - wordLabel->show(); - x += wordLabel->width() + 2; - if (x >= 245) { - x = 5; - y += wordLabel->height() + 2; - } - } - } - - /* - QPalette newPalette = palette(); - newPalette.setColor(QPalette::Window, Qt::white); - setPalette(newPalette); - */ - - setAcceptDrops(true); - setMinimumSize(400, qMax(200, y)); - setWindowTitle(tr("Draggable Text Window %1").arg(createChildWindow ? 1 : 2)); - if (createChildWindow) - otherWindow = new DragWidget("Here is a second window that accepts drops"); -} - -void DragWidget::dragEnterEvent(QDragEnterEvent *event) -{ - if (event->mimeData()->hasText()) { - if (event->source() == this) { - event->setDropAction(Qt::MoveAction); - event->accept(); - } else { - event->acceptProposedAction(); - } - } else { - event->ignore(); - } -} - -void DragWidget::dragMoveEvent(QDragMoveEvent * event) -{ - dragPos = event->pos(); - dragTimer.start(500, this); - update(); -} - -void DragWidget::dragLeaveEvent(QDragLeaveEvent *) -{ - dragTimer.stop(); - update(); -} - -void DragWidget::dropEvent(QDropEvent *event) -{ - if (event->mimeData()->hasText()) { - const QMimeData *mime = event->mimeData(); - QStringList pieces = mime->text().split(QRegularExpression("\\s+"), - Qt::SkipEmptyParts); - QPoint position = event->pos(); - QPoint hotSpot; - - QList hotSpotPos = mime->data("application/x-hotspot").split(' '); - if (hotSpotPos.size() == 2) { - hotSpot.setX(hotSpotPos.first().toInt()); - hotSpot.setY(hotSpotPos.last().toInt()); - } - dropPos = position - hotSpot; - dropTimer.start(500, this); - update(); - - foreach (QString piece, pieces) { - FramedLabel *newLabel = new FramedLabel(piece, this); - newLabel->move(position - hotSpot); - newLabel->show(); - - position += QPoint(newLabel->width(), 0); - } - - if (event->source() == this) { - event->setDropAction(Qt::MoveAction); - event->accept(); - } else { - event->acceptProposedAction(); - } - } else { - event->ignore(); - } - for (QObject *child : children()) { - if (child->isWidgetType()) { - auto widget = static_cast(child); - if (!widget->isVisible()) - widget->deleteLater(); - } - } -} - -void DragWidget::mousePressEvent(QMouseEvent *event) -{ - QLabel *child = static_cast(childAt(event->pos())); - if (!child) - return; - - QPoint hotSpot = event->pos() - child->pos(); - - QMimeData *mimeData = new QMimeData; - mimeData->setText(child->text()); - mimeData->setData("application/x-hotspot", - QByteArray::number(hotSpot.x()) + ' ' + QByteArray::number(hotSpot.y())); - - const qreal dpr = devicePixelRatio() > 1 && !(QGuiApplication::keyboardModifiers() & Qt::ShiftModifier) - ? devicePixelRatio() : 1; - - QPixmap pixmap(child->size() * dpr); - pixmap.setDevicePixelRatio(dpr); - child->render(&pixmap); - - auto drag = new QDrag(this); - drag->setMimeData(mimeData); - drag->setPixmap(pixmap); - drag->setHotSpot(hotSpot); - - Qt::DropAction dropAction = drag->exec(Qt::CopyAction | Qt::MoveAction, Qt::CopyAction); - - if (dropAction == Qt::MoveAction) - child->close(); -} - -void DragWidget::timerEvent(QTimerEvent *e) -{ - if (e->timerId() == dragTimer.timerId()) - dragTimer.stop(); - if (e->timerId() == dropTimer.timerId()) - dropTimer.stop(); - update(); -} - -void DragWidget::paintEvent(QPaintEvent *) -{ - QPainter p(this); - p.fillRect(rect(), Qt::white); - - if (dropTimer.isActive()) { - p.setBrush(Qt::red); - p.drawEllipse(dropPos, 50, 50); - } - - if (dragTimer.isActive()) { - p.setPen(QPen(Qt::blue, 5)); - QPoint p1 = (rect().topLeft()*3 + rect().bottomRight())/4; - QPoint p2 = (rect().topLeft() + rect().bottomRight()*3)/4; - p.drawLine(p1, dragPos); - p.drawLine(p2, dragPos); - } -} - -void DragWidget::showEvent(QShowEvent *) -{ - if (otherWindow) - otherWindow->show(); -} - -void DragWidget::hideEvent(QHideEvent *) -{ - if (otherWindow) - otherWindow->hide(); -} diff --git a/tests/manual/highdpi/dragwidget.h b/tests/manual/highdpi/dragwidget.h deleted file mode 100644 index 06663fc74a..0000000000 --- a/tests/manual/highdpi/dragwidget.h +++ /dev/null @@ -1,64 +0,0 @@ -/**************************************************************************** - ** - ** 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$ - ** - ****************************************************************************/ - -#ifndef DRAGWIDGET_H -#define DRAGWIDGET_H - -#include -#include - -QT_BEGIN_NAMESPACE -class QDragEnterEvent; -class QDropEvent; -QT_END_NAMESPACE - -class DragWidget : public QWidget -{ -public: - DragWidget(QString text = QString(), QWidget *parent = nullptr); - -protected: - void dragEnterEvent(QDragEnterEvent *event) override; - void dragLeaveEvent(QDragLeaveEvent *event) override; - void dropEvent(QDropEvent *event) override; - void mousePressEvent(QMouseEvent *event) override; - void dragMoveEvent(QDragMoveEvent * event) override; - void paintEvent(QPaintEvent *) override; - void timerEvent(QTimerEvent *event) override; - void showEvent(QShowEvent *event) override; - void hideEvent(QHideEvent *event) override; - -private: - QPoint dragPos; - QPoint dropPos; - QBasicTimer dragTimer; - QBasicTimer dropTimer; - QWidget *otherWindow = nullptr; -}; - -#endif // DRAGWIDGET_H diff --git a/tests/manual/highdpi/highdpi.pro b/tests/manual/highdpi/highdpi.pro deleted file mode 100644 index 2de8ed3bb5..0000000000 --- a/tests/manual/highdpi/highdpi.pro +++ /dev/null @@ -1,18 +0,0 @@ -TEMPLATE = app -TARGET = highdpi -INCLUDEPATH += . -QT += widgets gui-private -CONFIG += cmdline -CONFIG += c++11 -# Input -SOURCES += \ - dragwidget.cpp \ - main.cpp - -HEADERS += \ - dragwidget.h - -RESOURCES += \ - highdpi.qrc - -DEFINES += HAVE_SCREEN_BASE_DPI diff --git a/tests/manual/highdpi/highdpi.qrc b/tests/manual/highdpi/highdpi.qrc deleted file mode 100644 index 0e33ed33d7..0000000000 --- a/tests/manual/highdpi/highdpi.qrc +++ /dev/null @@ -1,10 +0,0 @@ - - - qticon16.png - qticon16@2x.png - qticon16@3x.png - qticon32.png - qticon32@2x.png - qticon64.png - - diff --git a/tests/manual/highdpi/kitchensink/CMakeLists.txt b/tests/manual/highdpi/kitchensink/CMakeLists.txt new file mode 100644 index 0000000000..b8dfa9be37 --- /dev/null +++ b/tests/manual/highdpi/kitchensink/CMakeLists.txt @@ -0,0 +1,40 @@ +# Generated from kitchensink.pro. + +##################################################################### +## kitchensink Binary: +##################################################################### + +qt_internal_add_manual_test(kitchensink + SOURCES + dragwidget.cpp dragwidget.h + main.cpp + DEFINES + HAVE_SCREEN_BASE_DPI + INCLUDE_DIRECTORIES + . + PUBLIC_LIBRARIES + Qt::Gui + Qt::GuiPrivate + Qt::Widgets +) + +# Resources: +set(kitchensink_resource_files + "qticon16.png" + "qticon16@2x.png" + "qticon16@3x.png" + "qticon32.png" + "qticon32@2x.png" + "qticon64.png" +) + +qt_internal_add_resource(kitchensink "kitchensink" + PREFIX + "/" + FILES + ${kitchensink_resource_files} +) + + +#### Keys ignored in scope 1:.:.:kitchensink.pro:: +# TEMPLATE = "app" diff --git a/tests/manual/highdpi/kitchensink/dragwidget.cpp b/tests/manual/highdpi/kitchensink/dragwidget.cpp new file mode 100644 index 0000000000..bd65fcf81b --- /dev/null +++ b/tests/manual/highdpi/kitchensink/dragwidget.cpp @@ -0,0 +1,221 @@ +/**************************************************************************** + ** + ** 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 +#include "dragwidget.h" + +class FramedLabel : public QLabel +{ +public: + FramedLabel(const QString &text, QWidget *parent) + : QLabel(text, parent) + { + setAutoFillBackground(true); + setFrameShape(QFrame::Panel); + setFrameShadow(QFrame::Raised); + } +}; + +DragWidget::DragWidget(QString text, QWidget *parent) + : QWidget(parent) +{ + int x = 5; + int y = 5; + + bool createChildWindow = text.isEmpty(); // OK, yes this is a hack... + if (text.isEmpty()) + text = "You can drag from this window and drop text here"; + + QStringList words = text.split(' '); + for (const QString &word : words) { + if (!word.isEmpty()) { + auto wordLabel = new FramedLabel(word, this); + wordLabel->move(x, y); + wordLabel->show(); + x += wordLabel->width() + 2; + if (x >= 245) { + x = 5; + y += wordLabel->height() + 2; + } + } + } + + /* + QPalette newPalette = palette(); + newPalette.setColor(QPalette::Window, Qt::white); + setPalette(newPalette); + */ + + setAcceptDrops(true); + setMinimumSize(400, qMax(200, y)); + setWindowTitle(tr("Draggable Text Window %1").arg(createChildWindow ? 1 : 2)); + if (createChildWindow) + otherWindow = new DragWidget("Here is a second window that accepts drops"); +} + +void DragWidget::dragEnterEvent(QDragEnterEvent *event) +{ + if (event->mimeData()->hasText()) { + if (event->source() == this) { + event->setDropAction(Qt::MoveAction); + event->accept(); + } else { + event->acceptProposedAction(); + } + } else { + event->ignore(); + } +} + +void DragWidget::dragMoveEvent(QDragMoveEvent * event) +{ + dragPos = event->pos(); + dragTimer.start(500, this); + update(); +} + +void DragWidget::dragLeaveEvent(QDragLeaveEvent *) +{ + dragTimer.stop(); + update(); +} + +void DragWidget::dropEvent(QDropEvent *event) +{ + if (event->mimeData()->hasText()) { + const QMimeData *mime = event->mimeData(); + QStringList pieces = mime->text().split(QRegularExpression("\\s+"), + Qt::SkipEmptyParts); + QPoint position = event->pos(); + QPoint hotSpot; + + QList hotSpotPos = mime->data("application/x-hotspot").split(' '); + if (hotSpotPos.size() == 2) { + hotSpot.setX(hotSpotPos.first().toInt()); + hotSpot.setY(hotSpotPos.last().toInt()); + } + dropPos = position - hotSpot; + dropTimer.start(500, this); + update(); + + foreach (QString piece, pieces) { + FramedLabel *newLabel = new FramedLabel(piece, this); + newLabel->move(position - hotSpot); + newLabel->show(); + + position += QPoint(newLabel->width(), 0); + } + + if (event->source() == this) { + event->setDropAction(Qt::MoveAction); + event->accept(); + } else { + event->acceptProposedAction(); + } + } else { + event->ignore(); + } + for (QObject *child : children()) { + if (child->isWidgetType()) { + auto widget = static_cast(child); + if (!widget->isVisible()) + widget->deleteLater(); + } + } +} + +void DragWidget::mousePressEvent(QMouseEvent *event) +{ + QLabel *child = static_cast(childAt(event->pos())); + if (!child) + return; + + QPoint hotSpot = event->pos() - child->pos(); + + QMimeData *mimeData = new QMimeData; + mimeData->setText(child->text()); + mimeData->setData("application/x-hotspot", + QByteArray::number(hotSpot.x()) + ' ' + QByteArray::number(hotSpot.y())); + + const qreal dpr = devicePixelRatio() > 1 && !(QGuiApplication::keyboardModifiers() & Qt::ShiftModifier) + ? devicePixelRatio() : 1; + + QPixmap pixmap(child->size() * dpr); + pixmap.setDevicePixelRatio(dpr); + child->render(&pixmap); + + auto drag = new QDrag(this); + drag->setMimeData(mimeData); + drag->setPixmap(pixmap); + drag->setHotSpot(hotSpot); + + Qt::DropAction dropAction = drag->exec(Qt::CopyAction | Qt::MoveAction, Qt::CopyAction); + + if (dropAction == Qt::MoveAction) + child->close(); +} + +void DragWidget::timerEvent(QTimerEvent *e) +{ + if (e->timerId() == dragTimer.timerId()) + dragTimer.stop(); + if (e->timerId() == dropTimer.timerId()) + dropTimer.stop(); + update(); +} + +void DragWidget::paintEvent(QPaintEvent *) +{ + QPainter p(this); + p.fillRect(rect(), Qt::white); + + if (dropTimer.isActive()) { + p.setBrush(Qt::red); + p.drawEllipse(dropPos, 50, 50); + } + + if (dragTimer.isActive()) { + p.setPen(QPen(Qt::blue, 5)); + QPoint p1 = (rect().topLeft()*3 + rect().bottomRight())/4; + QPoint p2 = (rect().topLeft() + rect().bottomRight()*3)/4; + p.drawLine(p1, dragPos); + p.drawLine(p2, dragPos); + } +} + +void DragWidget::showEvent(QShowEvent *) +{ + if (otherWindow) + otherWindow->show(); +} + +void DragWidget::hideEvent(QHideEvent *) +{ + if (otherWindow) + otherWindow->hide(); +} diff --git a/tests/manual/highdpi/kitchensink/dragwidget.h b/tests/manual/highdpi/kitchensink/dragwidget.h new file mode 100644 index 0000000000..06663fc74a --- /dev/null +++ b/tests/manual/highdpi/kitchensink/dragwidget.h @@ -0,0 +1,64 @@ +/**************************************************************************** + ** + ** 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$ + ** + ****************************************************************************/ + +#ifndef DRAGWIDGET_H +#define DRAGWIDGET_H + +#include +#include + +QT_BEGIN_NAMESPACE +class QDragEnterEvent; +class QDropEvent; +QT_END_NAMESPACE + +class DragWidget : public QWidget +{ +public: + DragWidget(QString text = QString(), QWidget *parent = nullptr); + +protected: + void dragEnterEvent(QDragEnterEvent *event) override; + void dragLeaveEvent(QDragLeaveEvent *event) override; + void dropEvent(QDropEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + void dragMoveEvent(QDragMoveEvent * event) override; + void paintEvent(QPaintEvent *) override; + void timerEvent(QTimerEvent *event) override; + void showEvent(QShowEvent *event) override; + void hideEvent(QHideEvent *event) override; + +private: + QPoint dragPos; + QPoint dropPos; + QBasicTimer dragTimer; + QBasicTimer dropTimer; + QWidget *otherWindow = nullptr; +}; + +#endif // DRAGWIDGET_H diff --git a/tests/manual/highdpi/kitchensink/kitchensink.pro b/tests/manual/highdpi/kitchensink/kitchensink.pro new file mode 100644 index 0000000000..29a40b1ffc --- /dev/null +++ b/tests/manual/highdpi/kitchensink/kitchensink.pro @@ -0,0 +1,18 @@ +TEMPLATE = app +TARGET = kitchensink +INCLUDEPATH += . +QT += widgets gui-private +CONFIG += cmdline +CONFIG += c++11 +# Input +SOURCES += \ + dragwidget.cpp \ + main.cpp + +HEADERS += \ + dragwidget.h + +RESOURCES += \ + kitchensink.qrc + +DEFINES += HAVE_SCREEN_BASE_DPI diff --git a/tests/manual/highdpi/kitchensink/kitchensink.qrc b/tests/manual/highdpi/kitchensink/kitchensink.qrc new file mode 100644 index 0000000000..0e33ed33d7 --- /dev/null +++ b/tests/manual/highdpi/kitchensink/kitchensink.qrc @@ -0,0 +1,10 @@ + + + qticon16.png + qticon16@2x.png + qticon16@3x.png + qticon32.png + qticon32@2x.png + qticon64.png + + diff --git a/tests/manual/highdpi/kitchensink/main.cpp b/tests/manual/highdpi/kitchensink/main.cpp new file mode 100644 index 0000000000..bdb4c3d571 --- /dev/null +++ b/tests/manual/highdpi/kitchensink/main.cpp @@ -0,0 +1,1478 @@ +/**************************************************************************** + ** + ** 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dragwidget.h" + +#include + +static QTextStream &operator<<(QTextStream &str, const QSizeF &s) +{ + str << s.width() << 'x' << s.height(); + return str; +} + +static QTextStream &operator<<(QTextStream &str, const QRect &r) +{ + str << r.width() << 'x' << r.height() << Qt::forcesign << r.x() << r.y() << Qt::noforcesign; + return str; +} + +static QString formatWindowTitle(const QString &title) +{ + QString result; + QTextStream(&result) << title << ' ' << QT_VERSION_STR << " (" + << QGuiApplication::platformName() + << '/' << QApplication::style()->objectName() << ')'; + return result; +} + +class DemoContainerBase +{ +public: + DemoContainerBase() = default; + virtual ~DemoContainerBase() = default; + QString name() { return option().names().constFirst(); } + virtual QCommandLineOption &option() = 0; + virtual void makeVisible(bool visible, QWidget *parent) = 0; + QWidget *widget() const { return m_widget; } + +protected: + QWidget *m_widget = nullptr; +}; + +using DemoContainerList = QList; + +template +class DemoContainer : public DemoContainerBase +{ +public: + DemoContainer(const QString &optionName, const QString &description) + : m_option(optionName, description) + { + } + ~DemoContainer() { delete m_widget; } + + QCommandLineOption &option() override { return m_option; } + + void makeVisible(bool visible, QWidget *parent) override + { + if (visible && !m_widget) { + m_widget = new T; + if (m_widget->windowTitle().isEmpty()) { + QString title = m_option.description(); + if (title.startsWith("Test ", Qt::CaseInsensitive)) + title.remove(0, 5); + title[0] = title.at(0).toUpper(); + m_widget->setWindowTitle(formatWindowTitle(title)); + } + m_widget->installEventFilter(parent); + } + if (m_widget) + m_widget->setVisible(visible); + } + +private: + QCommandLineOption m_option; +}; + +class LabelSlider : public QObject +{ +Q_OBJECT +public: + LabelSlider(QObject *parent, const QString &text, QGridLayout *layout, int row) + : QObject(parent) + { + QLabel *textLabel = new QLabel(text); + m_slider = new QSlider(); + m_slider->setOrientation(Qt::Horizontal); + m_slider->setMinimum(1); + m_slider->setMaximum(40); + m_slider->setValue(10); + m_slider->setTracking(false); + m_slider->setTickInterval(5); + m_slider->setTickPosition(QSlider::TicksBelow); + m_label = new QLabel("1.0"); + + // set up layouts + layout->addWidget(textLabel, row, 0); + layout->addWidget(m_slider, row, 1); + layout->addWidget(m_label, row, 2); + + // handle slider position change + connect(m_slider, &QSlider::sliderMoved, this, &LabelSlider::updateLabel); + connect(m_slider, &QSlider::valueChanged, this, &LabelSlider::valueChanged); + } + void setValue(int scaleFactor) + { + m_slider->setValue(scaleFactor); + updateLabel(scaleFactor); + } + +private slots: + void updateLabel(int scaleFactor) + { + // slider value is scale factor times ten; + qreal scalefactorF = qreal(scaleFactor) / 10.0; + + // update label, add ".0" if needed. + QString number = QString::number(scalefactorF); + if (!number.contains(QLatin1Char('.'))) + number.append(".0"); + m_label->setText(number); + } + +signals: + void valueChanged(int scaleFactor); + +private: + QSlider *m_slider; + QLabel *m_label; +}; + +static qreal getScreenFactorWithoutPixelDensity(const QScreen *screen) +{ + // this is a hack that relies on knowing the internals of QHighDpiScaling + static const char *scaleFactorProperty = "_q_scaleFactor"; + QVariant screenFactor = screen->property(scaleFactorProperty); + return screenFactor.isValid() ? screenFactor.toReal() : 1.0; +} + +static inline qreal getGlobalScaleFactor() +{ + QScreen *noScreen = nullptr; + return QHighDpiScaling::factor(noScreen); +} + +class DemoController : public QWidget +{ + Q_OBJECT +public: + DemoController(DemoContainerList demos, QCommandLineParser *parser); + ~DemoController(); + +protected: + bool eventFilter(QObject *object, QEvent *event) override; + void closeEvent(QCloseEvent *) override { QCoreApplication::quit(); } + +private slots: + void handleButton(int id, bool toggled); + +private: + DemoContainerList m_demos; + QButtonGroup *m_group; +}; + +DemoController::DemoController(DemoContainerList demos, QCommandLineParser *parser) + : m_demos(std::move(demos)) +{ + setWindowTitle(formatWindowTitle("Screen Scale Factors")); + setObjectName("controller"); // make WindowScaleFactorSetter skip this window + + auto mainLayout = new QVBoxLayout(this); + auto scaleLayout = new QGridLayout; + mainLayout->addLayout(scaleLayout); + + int layoutRow = 0; + LabelSlider *globalScaleSlider = new LabelSlider(this, "Global scale factor", scaleLayout, layoutRow++); + globalScaleSlider->setValue(int(getGlobalScaleFactor() * 10)); + connect(globalScaleSlider, &LabelSlider::valueChanged, [](int scaleFactor){ + // slider value is scale factor times ten; + qreal scalefactorF = qreal(scaleFactor) / 10.0; + QHighDpiScaling::setGlobalFactor(scalefactorF); + }); + + // set up one scale control line per screen + const auto screens = QGuiApplication::screens(); + for (QScreen *screen : screens) { + // create scale control line + QSize screenSize = screen->geometry().size(); + QString screenId = screen->name() + QLatin1Char(' ') + QString::number(screenSize.width()) + + QLatin1Char(' ') + QString::number(screenSize.height()); + LabelSlider *slider = new LabelSlider(this, screenId, scaleLayout, layoutRow++); + slider->setValue(getScreenFactorWithoutPixelDensity(screen) * 10); + + // handle slider value change + connect(slider, &LabelSlider::valueChanged, [screen](int scaleFactor){ + // slider value is scale factor times ten; + qreal scalefactorF = qreal(scaleFactor) / 10.0; + + // set scale factor for screen + qreal oldFactor = QHighDpiScaling::factor(screen); + QHighDpiScaling::setScreenFactor(screen, scalefactorF); + qreal newFactor = QHighDpiScaling::factor(screen); + + qDebug() << "factor was / is" << oldFactor << newFactor; + }); + } + + auto demoLayout = new QFormLayout; + mainLayout->addLayout(demoLayout); + m_group = new QButtonGroup(this); + m_group->setExclusive(false); + + for (int i = 0; i < m_demos.size(); ++i) { + DemoContainerBase *demo = m_demos.at(i); + QString name = demo->name(); + name[0] = name.at(0).toUpper(); + auto button = new QPushButton(name); + button->setCheckable(true); + demoLayout->addRow(demo->option().description(), button); + m_group->addButton(button, i); + + if (parser->isSet(demo->option())) { + demo->makeVisible(true, this); + button->setChecked(true); + } + } + connect(m_group, &QButtonGroup::idToggled, + this, &DemoController::handleButton); +} + +DemoController::~DemoController() +{ + qDeleteAll(m_demos); +} + +bool DemoController::eventFilter(QObject *object, QEvent *event) +{ + if (event->type() == QEvent::Close) { + for (int i = 0; i < m_demos.size(); ++i) { + DemoContainerBase *demo = m_demos.at(i); + if (demo->widget() == object) { + m_group->button(i)->setChecked(false); + break; + } + } + } + return false; +} + +void DemoController::handleButton(int id, bool toggled) +{ + m_demos.at(id)->makeVisible(toggled, this); +} + +class PixmapPainter : public QWidget +{ +public: + PixmapPainter(); + + void paintEvent(QPaintEvent *event) override; + +private: + QPixmap pixmap1X; + QPixmap pixmap2X; + QPixmap pixmapLarge; + QImage image1X; + QImage image2X; + QImage imageLarge; + QIcon qtIcon; +}; + +PixmapPainter::PixmapPainter() +{ + pixmap1X = QPixmap(":/qticon32.png"); + pixmap2X = QPixmap(":/qticon32@2x.png"); + pixmapLarge = QPixmap(":/qticon64.png"); + + image1X = QImage(":/qticon32.png"); + image2X = QImage(":/qticon32@2x.png"); + imageLarge = QImage(":/qticon64.png"); + + qtIcon.addFile(":/qticon32.png"); + qtIcon.addFile(":/qticon32@2x.png"); +} + +void PixmapPainter::paintEvent(QPaintEvent *) +{ + QPainter p(this); + p.fillRect(QRect(QPoint(0, 0), size()), QBrush(Qt::gray)); + + int pixmapPointSize = 32; + int y = 30; + int dy = 90; + + int x = 10; + int dx = 40; + // draw at point +// qDebug() << "paint pixmap" << pixmap1X.devicePixelRatio(); + p.drawPixmap(x, y, pixmap1X); + x+=dx;p.drawPixmap(x, y, pixmap2X); + x+=dx;p.drawPixmap(x, y, pixmapLarge); + x+=dx*2;p.drawPixmap(x, y, qtIcon.pixmap(QSize(pixmapPointSize, pixmapPointSize))); + x+=dx;p.drawImage(x, y, image1X); + x+=dx;p.drawImage(x, y, image2X); + x+=dx;p.drawImage(x, y, imageLarge); + + // draw at 32x32 rect + y+=dy; + x = 10; + p.drawPixmap(QRect(x, y, pixmapPointSize, pixmapPointSize), pixmap1X); + x+=dx;p.drawPixmap(QRect(x, y, pixmapPointSize, pixmapPointSize), pixmap2X); + x+=dx;p.drawPixmap(QRect(x, y, pixmapPointSize, pixmapPointSize), pixmapLarge); + x+=dx;p.drawPixmap(QRect(x, y, pixmapPointSize, pixmapPointSize), qtIcon.pixmap(QSize(pixmapPointSize, pixmapPointSize))); + x+=dx;p.drawImage(QRect(x, y, pixmapPointSize, pixmapPointSize), image1X); + x+=dx;p.drawImage(QRect(x, y, pixmapPointSize, pixmapPointSize), image2X); + x+=dx;p.drawImage(QRect(x, y, pixmapPointSize, pixmapPointSize), imageLarge); + + + // draw at 64x64 rect + y+=dy - 50; + x = 10; + p.drawPixmap(QRect(x, y, pixmapPointSize * 2, pixmapPointSize * 2), pixmap1X); + x+=dx * 2; p.drawPixmap(QRect(x, y, pixmapPointSize * 2, pixmapPointSize * 2), pixmap2X); + x+=dx * 2; p.drawPixmap(QRect(x, y, pixmapPointSize * 2, pixmapPointSize * 2), pixmapLarge); + x+=dx * 2; p.drawPixmap(QRect(x, y, pixmapPointSize * 2, pixmapPointSize * 2), qtIcon.pixmap(QSize(pixmapPointSize, pixmapPointSize))); + x+=dx * 2; p.drawImage(QRect(x, y, pixmapPointSize * 2, pixmapPointSize * 2), image1X); + x+=dx * 2; p.drawImage(QRect(x, y, pixmapPointSize * 2, pixmapPointSize * 2), image2X); + x+=dx * 2; p.drawImage(QRect(x, y, pixmapPointSize * 2, pixmapPointSize * 2), imageLarge); + } + +class TiledPixmapPainter : public QWidget +{ +public: + TiledPixmapPainter(); + + void paintEvent(QPaintEvent *event) override; + +private: + QPixmap pixmap1X; + QPixmap pixmap2X; + QPixmap pixmapLarge; +}; + +TiledPixmapPainter::TiledPixmapPainter() +{ + pixmap1X = QPixmap(":/qticon32.png"); + pixmap2X = QPixmap(":/qticon32@2x.png"); + pixmapLarge = QPixmap(":/qticon64.png"); +} + +void TiledPixmapPainter::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event); + QPainter p(this); + + int xoff = 10; + int yoff = 10; + int tiles = 4; + int pixmapEdge = 32; + int tileAreaEdge = pixmapEdge * tiles; + + // Expected behavior for both 1x and 2x dislays: + // 1x pixmap : 4 x 4 tiles + // large pixmap: 2 x 2 tiles + // 2x pixmap : 4 x 4 tiles + // + // On a 2x display the 2x pixmap tiles + // will be drawn in high resolution. + p.drawTiledPixmap(QRect(xoff, yoff, tileAreaEdge, tileAreaEdge), pixmap1X); + yoff += tiles * pixmapEdge + 10; + p.drawTiledPixmap(QRect(xoff, yoff, tileAreaEdge, tileAreaEdge), pixmapLarge); + yoff += tiles * pixmapEdge + 10; + p.drawTiledPixmap(QRect(xoff, yoff, tileAreaEdge, tileAreaEdge), pixmap2X); + + // Again, with an offset. The offset is in + // device-independent pixels. + QPoint offset(40, 40); // larger than the pixmap edge size to exercise that code path + yoff = 10; + xoff = 20 + tiles * pixmapEdge ; + p.drawTiledPixmap(QRect(xoff, yoff, tileAreaEdge, tileAreaEdge), pixmap1X, offset); + yoff += tiles * pixmapEdge + 10; + p.drawTiledPixmap(QRect(xoff, yoff, tileAreaEdge, tileAreaEdge), pixmapLarge, offset); + yoff += tiles * pixmapEdge + 10; + p.drawTiledPixmap(QRect(xoff, yoff, tileAreaEdge, tileAreaEdge), pixmap2X, offset); +} + +class Labels : public QWidget +{ +public: + Labels(); + +private: + QPixmap pixmap1X; + QPixmap pixmap2X; + QPixmap pixmapLarge; + QIcon qtIcon; +}; + +Labels::Labels() +{ + pixmap1X = QPixmap(":/qticon32.png"); + pixmap2X = QPixmap(":/qticon32@2x.png"); + pixmapLarge = QPixmap(":/qticon64.png"); + + qtIcon.addFile(":/qticon32.png"); + qtIcon.addFile(":/qticon32@2x.png"); + setWindowIcon(qtIcon); + setWindowTitle(formatWindowTitle("Labels")); + + QLabel *label1x = new QLabel(); + label1x->setPixmap(pixmap1X); + QLabel *label2x = new QLabel(); + label2x->setPixmap(pixmap2X); + QLabel *labelIcon = new QLabel(); + labelIcon->setPixmap(qtIcon.pixmap(QSize(32,32))); + QLabel *labelLarge = new QLabel(); + labelLarge->setPixmap(pixmapLarge); + + QHBoxLayout *layout = new QHBoxLayout(this); + layout->addWidget(label1x); //expected low-res on high-dpi displays + layout->addWidget(label2x); //expected high-res on high-dpi displays + layout->addWidget(labelIcon); //expected high-res on high-dpi displays + layout->addWidget(labelLarge); // expected large size and low-res + setLayout(layout); +} + +class MainWindow : public QMainWindow +{ + Q_OBJECT +public: + MainWindow(); + QMenu *addNewMenu(const QString &title, int itemCount = 5); + +private slots: + void maskActionToggled(bool t); + +private: + QIcon qtIcon; + QIcon qtIcon1x; + QIcon qtIcon2x; + + QToolBar *fileToolBar; + QAction *m_maskAction; + int menuCount = 0; +}; + +MainWindow::MainWindow() +{ + // beware that QIcon auto-loads the @2x versions. + qtIcon1x.addFile(":/qticon16.png"); + qtIcon2x.addFile(":/qticon32.png"); + setWindowIcon(qtIcon); + setWindowTitle(formatWindowTitle("MainWindow")); + + fileToolBar = addToolBar(tr("File")); +// fileToolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); + fileToolBar->addAction(new QAction(qtIcon1x, QString("1x"), this)); + fileToolBar->addAction(new QAction(qtIcon2x, QString("2x"), this)); + addNewMenu("&Edit"); + addNewMenu("&Build"); + addNewMenu("&Debug", 4); + QMenu *menu = addNewMenu("&Transmogrify", 7); + menu->addSeparator(); + m_maskAction = menu->addAction("Mask"); + m_maskAction->setCheckable(true); + connect(m_maskAction, &QAction::toggled, this, &MainWindow::maskActionToggled); + fileToolBar->addAction(m_maskAction); + addNewMenu("T&ools"); + addNewMenu("&Help", 2); +} + +QMenu *MainWindow::addNewMenu(const QString &title, int itemCount) +{ + QMenu *menu = menuBar()->addMenu(title); + for (int i = 0; i < itemCount; i++) { + menuCount++; + QString s = "Menu item " + QString::number(menuCount); + if (i == 3) { + QMenu *subMenu = menu->addMenu(s); + for (int j = 1; j < 4; j++) + subMenu->addAction(QString::fromLatin1("SubMenu item %1.%2").arg(menuCount).arg(j)); + } else { + menu->addAction(s); + } + } + return menu; +} + +void MainWindow::maskActionToggled(bool t) +{ + if (t) { + QList upperLeftTriangle; + upperLeftTriangle << QPoint(0, 0) << QPoint(width(), 0) << QPoint(0, height()); + setMask(QRegion(QPolygon(upperLeftTriangle))); + } else { + clearMask(); + } +} + +class StandardIcons : public QWidget +{ +public: + void paintEvent(QPaintEvent *) override + { + int x = 10; + int y = 10; + int dx = 50; + int dy = 50; + int maxX = 500; + + for (uint iconIndex = QStyle::SP_TitleBarMenuButton; iconIndex < QStyle::SP_MediaVolumeMuted; ++iconIndex) { + QIcon icon = qApp->style()->standardIcon(QStyle::StandardPixmap(iconIndex)); + QPainter p(this); + p.drawPixmap(x, y, icon.pixmap(dx - 5, dy - 5)); + if (x + dx > maxX) + y+=dy; + x = ((x + dx) % maxX); + } + } +}; + +class Caching : public QWidget +{ +public: + void paintEvent(QPaintEvent *) override + { + QSize layoutSize(75, 75); + + QPainter widgetPainter(this); + widgetPainter.fillRect(QRect(QPoint(0, 0), this->size()), Qt::gray); + + { + const qreal devicePixelRatio = this->windowHandle()->devicePixelRatio(); + QPixmap cache(layoutSize * devicePixelRatio); + cache.setDevicePixelRatio(devicePixelRatio); + + QPainter cachedPainter(&cache); + cachedPainter.fillRect(QRect(0,0, 75, 75), Qt::blue); + cachedPainter.fillRect(QRect(10,10, 55, 55), Qt::red); + cachedPainter.drawEllipse(QRect(10,10, 55, 55)); + + QPainter widgetPainter(this); + widgetPainter.drawPixmap(QPoint(10, 10), cache); + } + + { + const qreal devicePixelRatio = this->windowHandle()->devicePixelRatio(); + QImage cache = QImage(layoutSize * devicePixelRatio, QImage::Format_ARGB32_Premultiplied); + cache.setDevicePixelRatio(devicePixelRatio); + + QPainter cachedPainter(&cache); + cachedPainter.fillRect(QRect(0,0, 75, 75), Qt::blue); + cachedPainter.fillRect(QRect(10,10, 55, 55), Qt::red); + cachedPainter.drawEllipse(QRect(10,10, 55, 55)); + + QPainter widgetPainter(this); + widgetPainter.drawImage(QPoint(95, 10), cache); + } + + } +}; + +class Style : public QWidget +{ +public: + Style() + { + row1 = new QHBoxLayout(this); + + button = new QPushButton(); + button->setText("Test Button"); + row1->addWidget(button); + + lineEdit = new QLineEdit(); + lineEdit->setText("Test Lineedit"); + row1->addWidget(lineEdit); + + slider = new QSlider(); + row1->addWidget(slider); + + row1->addWidget(new QSpinBox); + row1->addWidget(new QScrollBar); + + auto tab = new QTabBar(); + tab->addTab("Foo"); + tab->addTab("Bar"); + row1->addWidget(tab); + } + +private: + QPushButton *button; + QLineEdit *lineEdit; + QSlider *slider; + QHBoxLayout *row1; +}; + +class Fonts : public QWidget +{ +public: + void paintEvent(QPaintEvent *) override + { + QPainter painter(this); + + // Points + int y = 10; + for (int fontSize = 6; fontSize < 18; fontSize += 2) { + QFont font; + font.setPointSize(fontSize); + QString string = QString(QStringLiteral("This text is in point size %1")).arg(fontSize); + painter.setFont(font); + y += (painter.fontMetrics().lineSpacing()); + painter.drawText(10, y, string); + } + + // Pixels + y += painter.fontMetrics().lineSpacing(); + for (int fontSize = 6; fontSize < 18; fontSize += 2) { + QFont font; + font.setPixelSize(fontSize); + QString string = QString(QStringLiteral("This text is in pixel size %1")).arg(fontSize); + painter.setFont(font); + y += (painter.fontMetrics().lineSpacing()); + painter.drawText(10, y, string); + } + } +}; + + +template +void apiTestdevicePixelRatioGetter() +{ + if (0) { + T *t = nullptr; + t->devicePixelRatio(); + } +} + +template +void apiTestdevicePixelRatioSetter() +{ + if (0) { + T *t = nullptr; + t->setDevicePixelRatio(2.0); + } +} + +void apiTest() +{ + // compile call to devicePixelRatio getter and setter (verify spelling) + apiTestdevicePixelRatioGetter(); + apiTestdevicePixelRatioGetter(); + apiTestdevicePixelRatioGetter(); + + apiTestdevicePixelRatioGetter(); + apiTestdevicePixelRatioSetter(); + apiTestdevicePixelRatioGetter(); + apiTestdevicePixelRatioSetter(); +} + +// Request and draw an icon at different sizes +class IconDrawing : public QWidget +{ +public: + IconDrawing() + { + const QString tempPath = m_temporaryDir.path(); + const QString path32 = tempPath + "/qticon32.png"; + const QString path32_2 = tempPath + "/qticon32-2.png"; + const QString path32_2x = tempPath + "/qticon32@2x.png"; + + QFile::copy(":/qticon32.png", path32_2); + QFile::copy(":/qticon32.png", path32); + QFile::copy(":/qticon32@2x.png", path32_2x); + + iconHighDPI.reset(new QIcon(path32)); // will auto-load @2x version. + iconNormalDpi.reset(new QIcon(path32_2)); // does not have a 2x version. + } + + void paintEvent(QPaintEvent *) override + { + int x = 10; + int y = 10; + int dx = 50; + int dy = 50; + int maxX = 600; + int minSize = 5; + int maxSize = 64; + int sizeIncrement = 5; + + // normal icon + for (int size = minSize; size < maxSize; size += sizeIncrement) { + QPainter p(this); + p.drawPixmap(x, y, iconNormalDpi->pixmap(size, size)); + if (x + dx > maxX) + y+=dy; + x = ((x + dx) % maxX); + } + x = 10; + y+=dy; + + // high-dpi icon + for (int size = minSize; size < maxSize; size += sizeIncrement) { + QPainter p(this); + p.drawPixmap(x, y, iconHighDPI->pixmap(size, size)); + if (x + dx > maxX) + y+=dy; + x = ((x + dx) % maxX); + } + } + +private: + QTemporaryDir m_temporaryDir; + QScopedPointer iconHighDPI; + QScopedPointer iconNormalDpi; +}; + +// Icons on buttons +class Buttons : public QWidget +{ +public: + Buttons() + { + QIcon icon; + icon.addFile(":/qticon16@2x.png"); + + QPushButton *button = new QPushButton(this); + button->setIcon(icon); + button->setText("16@2x"); + + QTabBar *tab = new QTabBar(this); + tab->addTab(QIcon(":/qticon16.png"), "16@1x"); + tab->addTab(QIcon(":/qticon16@2x.png"), "16@2x"); + tab->addTab(QIcon(":/qticon16.png"), ""); + tab->addTab(QIcon(":/qticon16@2x.png"), ""); + tab->move(10, 100); + tab->show(); + + auto toolBar = new QToolBar(this); + toolBar->addAction(QIcon(":/qticon16.png"), "16"); + toolBar->addAction(QIcon(":/qticon16@2x.png"), "16@2x"); + toolBar->addAction(QIcon(":/qticon32.png"), "32"); + toolBar->addAction(QIcon(":/qticon32@2x.png"), "32@2x"); + + toolBar->move(10, 200); + toolBar->show(); + } +}; + +class LinePainter : public QWidget +{ +public: + void paintEvent(QPaintEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + +private: + QPoint lastMousePoint; + QList linePoints; +}; + +void LinePainter::paintEvent(QPaintEvent *) +{ + QPainter p(this); + p.fillRect(QRect(QPoint(0, 0), size()), QBrush(Qt::gray)); + + // Default antialiased line + p.setRenderHint(QPainter::Antialiasing); + p.drawLines(linePoints); + + // Cosmetic 1 antialiased line + QPen pen; + pen.setCosmetic(true); + pen.setWidth(1); + p.setPen(pen); + p.translate(3, 3); + p.drawLines(linePoints); + + // Aliased cosmetic 1 line + p.setRenderHint(QPainter::Antialiasing, false); + p.translate(3, 3); + p.drawLines(linePoints); +} + +void LinePainter::mousePressEvent(QMouseEvent *event) +{ + lastMousePoint = event->pos(); +} + +void LinePainter::mouseReleaseEvent(QMouseEvent *) +{ + lastMousePoint = QPoint(); +} + +void LinePainter::mouseMoveEvent(QMouseEvent *event) +{ + if (lastMousePoint.isNull()) + return; + + QPoint newMousePoint = event->pos(); + if (lastMousePoint == newMousePoint) + return; + linePoints.append(lastMousePoint); + linePoints.append(newMousePoint); + lastMousePoint = newMousePoint; + update(); +} + +class CursorTester : public QWidget +{ +public: + CursorTester() = default; + + inline QRect getRect(int idx) const + { + int h = height() / 2; + return QRect(10, 10 + h * (idx - 1), width() - 20, h - 20); + } + + void paintEvent(QPaintEvent *) override + { + QPainter p(this); + QRect r1 = getRect(1); + QRect r2 = getRect(2); + p.fillRect(r1, QColor(200, 200, 250)); + p.drawText(r1, "Drag from here to move a window based on QCursor::pos()"); + p.fillRect(r2, QColor(250, 200, 200)); + p.drawText(r2, "Drag from here to move a window based on mouse event position"); + + if (moving) { + p.setPen(Qt::darkGray); + QFont f = font(); + f.setPointSize(8); + p.setFont(f); + p.drawEllipse(mousePos, 30,60); + QPoint pt = mousePos - QPoint(0, 60); + QPoint pt2 = pt - QPoint(30,10); + QPoint offs(30, 0); + p.drawLine(pt, pt2); + p.drawLine(pt2 - offs, pt2 + offs); + p.drawText(pt2 - offs, "mouse pos"); + + p.setPen(QColor(50,130,70)); + QPoint cursorPos = mapFromGlobal(QCursor::pos()); + pt = cursorPos - QPoint(0, 30); + pt2 = pt + QPoint(60, -20); + p.drawEllipse(cursorPos, 60, 30); + p.drawLine(pt, pt2); + p.drawLine(pt2 - offs, pt2 + offs); + p.drawText(pt2 - offs, "cursor pos"); + } + } + + void mousePressEvent(QMouseEvent *e) override + { + if (moving) + return; + QRect r1 = getRect(1); + QRect r2 = getRect(2); + + moving = r1.contains(e->pos()) || r2.contains(e->pos()); + if (!moving) + return; + useCursorPos = r1.contains(e->pos()); + + if (!moveLabel) + moveLabel = new QLabel(this,Qt::BypassWindowManagerHint|Qt::FramelessWindowHint|Qt::Window ); + + if (useCursorPos) + moveLabel->setText("I'm following QCursor::pos()"); + else + moveLabel->setText("I'm following QMouseEvent::globalPos()"); + moveLabel->adjustSize(); + mouseMoveEvent(e); + moveLabel->show(); + } + + void mouseReleaseEvent(QMouseEvent *) override + { + if (moveLabel) + moveLabel->hide(); + update(); + moving = false; + } + + void mouseMoveEvent(QMouseEvent *e) override + { + if (!moving) + return; + QPoint pos = useCursorPos ? QCursor::pos() : e->globalPosition().toPoint(); + pos -= moveLabel->rect().center(); + moveLabel->move(pos); + mousePos = e->pos(); + update(); + } + +private: + QLabel *moveLabel = nullptr; + QPoint mousePos; + bool useCursorPos = false; + bool moving = false; +}; + +class ScreenDisplayer : public QWidget +{ +public: + ScreenDisplayer() = default; + + void timerEvent(QTimerEvent *) override + { + update(); + } + + void mousePressEvent(QMouseEvent *) override + { + if (!moveLabel) + moveLabel = new QLabel(this,Qt::BypassWindowManagerHint|Qt::FramelessWindowHint|Qt::Window ); + moveLabel->setText("Hello, Qt this is a label\nwith some text"); + moveLabel->show(); + } + + void mouseMoveEvent(QMouseEvent *e) override + { + if (!moveLabel) + return; + moveLabel->move(e->pos() / scaleFactor); + QString str; + QDebug dbg(&str); + dbg.setAutoInsertSpaces(false); + dbg << moveLabel->geometry(); + moveLabel->setText(str); + } + + void mouseReleaseEvent(QMouseEvent *) override + { + if (moveLabel) + moveLabel->hide(); + } + + void showEvent(QShowEvent *) override + { + refreshTimer.start(300, this); + } + + void hideEvent(QHideEvent *) override + { + refreshTimer.stop(); + } + + void paintEvent(QPaintEvent *) override + { + QPainter p(this); + QRectF total; + const auto screens = QGuiApplication::screens(); + for (const QScreen *screen : screens) + total |= screen->geometry(); + if (total.isEmpty()) + return; + + scaleFactor = qMin(width()/total.width(), height()/total.height()); + + p.fillRect(rect(), Qt::black); + p.scale(scaleFactor, scaleFactor); + p.translate(-total.topLeft()); + p.setPen(QPen(Qt::white, 10)); + p.setBrush(Qt::gray); + + for (const QScreen *screen : screens) { + p.drawRect(screen->geometry()); + QFont f = font(); + f.setPixelSize(screen->geometry().height() / 8); + p.setFont(f); + p.drawText(screen->geometry(), Qt::AlignCenter, screen->name()); + } + p.setBrush(QColor(200,220,255,127)); + + const auto topLevels = QApplication::topLevelWidgets(); + for (QWidget *widget : topLevels) { + if (!widget->isHidden()) + p.drawRect(widget->geometry()); + } + + QPolygon cursorShape; + cursorShape << QPoint(0,0) << QPoint(20, 60) + << QPoint(30, 50) << QPoint(60, 80) + << QPoint(80, 60) << QPoint(50, 30) + << QPoint(60, 20); + cursorShape.translate(QCursor::pos()); + p.drawPolygon(cursorShape); + } + +private: + QLabel *moveLabel = nullptr; + qreal scaleFactor = 1; + QBasicTimer refreshTimer; +}; + +class PhysicalSizeTest : public QWidget +{ + Q_OBJECT +public: + PhysicalSizeTest() = default; + + void paintEvent(QPaintEvent *event) override; + + void resizeEvent(QResizeEvent *) override + { + qreal ppi = window()->windowHandle()->screen()->physicalDotsPerInchX(); + QSizeF s = size(); + if (!m_ignoreResize) + m_physicalSize = s / ppi; + } + + bool event(QEvent *event) override + { + if (event->type() == QEvent::ScreenChangeInternal) { + // we will get resize events when the scale factor changes + m_ignoreResize = true; + QTimer::singleShot(100, this, &PhysicalSizeTest::handleScreenChange); + } + return QWidget::event(event); + } + +public slots: + void handleScreenChange() + { + qreal ppi = window()->windowHandle()->screen()->physicalDotsPerInchX(); + QSizeF newSize = m_physicalSize * ppi; + resize(newSize.toSize()); + m_ignoreResize = false; + } + +private: + QSizeF m_physicalSize; + bool m_ignoreResize = false; +}; + +void PhysicalSizeTest::paintEvent(QPaintEvent *) +{ + QPainter p(this); + p.setRenderHint(QPainter::Antialiasing); + + qreal ppi = window()->windowHandle()->screen()->physicalDotsPerInchX(); + qreal ppmm = ppi / 25.4; + qreal h = 15 * ppmm; + QRectF rulerRect(0,0, width(), h); + rulerRect.moveCenter(rect().center()); + + QFont f = font(); + f.setPixelSize(18); + p.setFont(f); + + // draw a rectangle in (Qt) pixel coordinates, for comparison + QRect pixelRect(0, 0, 300, 50); + pixelRect.moveTopLeft(QPoint(5 * ppmm, rulerRect.bottom() + 5 * ppmm)); + p.fillRect(pixelRect, QColor(199,222,255)); + p.drawText(pixelRect, "This rectangle is 300x50 pixels"); + + f.setPixelSize(4 * ppmm); + p.setFont(f); + + QRectF topRect(0, 0, width(), rulerRect.top()); + p.drawText(topRect, Qt::AlignCenter, "The ruler is drawn in physical units.\nThis window tries to keep its physical size\nwhen moved between screens."); + + // draw a ruler in real physical coordinates + + p.fillRect(rulerRect, QColor(255, 222, 111)); + + QPen linePen(Qt::black, 0.3 * ppmm); + p.setPen(linePen); + f.setBold(true); + p.setFont(f); + + qreal vCenter = rulerRect.center().y(); + p.drawLine(0, vCenter, width(), vCenter); + + // cm + for (int i = 0;;) { + i++; + qreal x = i * ppmm; + if (x > width()) + break; + qreal y = rulerRect.bottom(); + qreal len; + if (i % 5) + len = 2 * ppmm; + else if (i % 10) + len = 3 * ppmm; + else + len = h / 2; + + p.drawLine(QPointF(x, y), QPointF(x, y - len)); + if (i % 10 == 5) { + QRectF textR(0, 0, 5 * ppmm, h / 2 - 2 * ppmm); + textR.moveTopLeft(QPointF(x, vCenter)); + int n = i / 10 + 1; + if (n % 10 == 0) + p.setPen(Qt::red); + p.drawText(textR, Qt::AlignCenter, QString::number(n)); + p.setPen(linePen); + } + } + + //inches + for (int i = 0;;) { + i++; + qreal x = i * ppi / 16; + if (x > width()) + break; + qreal y = rulerRect.top(); + + qreal d = h / 10; + qreal len; + if (i % 2) + len = 1 * d; + else if (i % 4) + len = 2 * d; + else if (i % 8) + len = 3 * d; + else if (i % 16) + len = 4 * d; + else + len = h / 2; + + p.drawLine(QPointF(x, y), QPointF(x, y + len)); + if (i % 16 == 12) { + QRectF textR(0, 0, 0.25 * ppi, h / 2 - 2 * d); + textR.moveBottomLeft(QPointF(x, vCenter)); + p.drawText(textR, Qt::AlignCenter, QString::number(1 + i/16)); + } + } + +} + +class GraphicsViewCaching : public QGraphicsView +{ +public: + GraphicsViewCaching() + { + auto scene = new QGraphicsScene(0, 0, 400, 400); + + QGraphicsTextItem *item = scene->addText("NoCache"); + item->setCacheMode(QGraphicsItem::NoCache); + item->setPos(10, 10); + + item = scene->addText("ItemCoordinateCache"); + item->setCacheMode(QGraphicsItem::ItemCoordinateCache); + item->setPos(10, 30); + + item = scene->addText("DeviceCoordinateCache"); + item->setCacheMode(QGraphicsItem::DeviceCoordinateCache); + item->setPos(10, 50); + + setScene(scene); + } +}; + +class MetricsTest : public QTabWidget +{ + Q_OBJECT +public: + MetricsTest() + { + qDebug().noquote().nospace() << "MetricsTest " << QT_VERSION_STR + << ' ' << QGuiApplication::platformName() << '\n' +<< R"(Relevant environment variables are: +QT_FONT_DPI=N +QT_SCALE_FACTOR=n +QT_ENABLE_HIGHDPI_SCALING=0|1 +QT_USE_PHYSICAL_DPI=0|1 +QT_SCREEN_SCALE_FACTORS=N;N;N or QT_SCREEN_SCALE_FACTORS=name:N +QT_SCALE_FACTOR_ROUNDING_POLICY=Round|Ceil|Floor|RoundPreferFloor|PassThrough +QT_DPI_ADJUSTMENT_POLICY=AdjustDpi|DontAdjustDpi|AdjustUpOnly)"; + + m_textEdit = addTextPage("Parameters"); + m_logEdit = addTextPage("Screen Change Log"); + + const auto screens = QGuiApplication::screens(); + for (auto screen : screens) + connectScreenChangeSignals(screen); + connect(qApp, &QGuiApplication::screenAdded, this, &MetricsTest::slotScreenAdded); + connect(qApp, &QGuiApplication::primaryScreenChanged, this, &MetricsTest::slotPrimaryScreenChanged); + connect(qApp, &QGuiApplication::screenRemoved, this, &MetricsTest::slotScreenRemoved); + + setWindowTitle(formatWindowTitle("Screens")); + m_logTimer.start(); + logMessage(briefFormatScreens()); + + // Resize to roughly match the metrics text. + const auto metrics = QFontMetrics(m_textEdit->font(), m_textEdit); + const int width = 10 + metrics.horizontalAdvance(QStringLiteral("X")) * 50; + const int height = 40 + metrics.height() * (10 + 8 * screens.size()); + resize(width, height); + } + + void setVisible(bool visible) override + { + QWidget::setVisible(visible); + if (visible && !m_screenChangedConnected) { + m_screenChangedConnected = true; + QObject::connect(windowHandle(), &QWindow::screenChanged, + this, &MetricsTest::screenChanged); + updateMetrics(); + } + } + + void updateMetrics() + { + QString text; + QTextStream str(&text); + + auto currentScreen = windowHandle()->screen(); + const auto screens = QGuiApplication::screens(); + for (int i = 0, size = screens.size(); i < size; ++i) { + auto screen = screens.at(i); + auto platformScreen = screen->handle(); + str << "Screen #" << i << " \"" << screen->name() << '"'; + if (screen == currentScreen) + str << " [current]"; + if (screen == QGuiApplication::primaryScreen()) + str << " [primary]"; + str << "\n screen geometry: " << screen->geometry() + << "\n platform screen geometry: " << platformScreen->geometry() + << "\n platform screen logicalDpi: " << platformScreen->logicalDpi().first; + +#ifdef HAVE_SCREEN_BASE_DPI + str << "\n platform screen logicalBaseDpi: " << platformScreen->logicalBaseDpi().first; +#endif + str << "\n platform screen devicePixelRatio: " <devicePixelRatio() + << "\n platform screen physicalDpi: " << screen->physicalDotsPerInch() + << "\n\n"; + } + + str << "widget devicePixelRatio: " << this->devicePixelRatio() + << "\nwidget logicalDpi: " << this->logicalDpiX() + << "\n\nQT_FONT_DPI: " << qgetenv("QT_FONT_DPI") + << "\nQT_SCALE_FACTOR: " << qgetenv("QT_SCALE_FACTOR") + << "\nQT_ENABLE_HIGHDPI_SCALING: " << qgetenv("QT_ENABLE_HIGHDPI_SCALING") + << "\nQT_SCREEN_SCALE_FACTORS: " << qgetenv("QT_SCREEN_SCALE_FACTORS") + << "\nQT_USE_PHYSICAL_DPI: " << qgetenv("QT_USE_PHYSICAL_DPI") + << "\nQT_SCALE_FACTOR_ROUNDING_POLICY: " << qgetenv("QT_SCALE_FACTOR_ROUNDING_POLICY") + << "\nQT_DPI_ADJUSTMENT_POLICY: " << qgetenv("QT_DPI_ADJUSTMENT_POLICY") + << '\n'; + + m_textEdit->setPlainText(text); + } + +private slots: + void screenChanged() + { + const QString message = QLatin1String("screenChanged ") + windowHandle()->screen()->name(); + qInfo("%s", qPrintable(message)); + logMessage(message); + updateMetrics(); + } + + void slotScreenAdded(QScreen *); + void slotScreenRemoved(QScreen *); + void slotPrimaryScreenChanged(QScreen *); + void slotScreenGeometryChanged(const QRect &geometry) + { logScreenChangeSignal(sender(), "geometry", geometry); } + void slotScreenAvailableGeometryChanged(const QRect &geometry) + { logScreenChangeSignal(sender(), "availableGeometry", geometry); } + void slotScreenPhysicalSizeChanged(const QSizeF &size) + { logScreenChangeSignal(sender(), "physicalSize", size); } + void slotScreenPhysicalDotsPerInchChanged(qreal dpi) + { logScreenChangeSignal(sender(), "physicalDotsPerInch", dpi); } + void slotScreenLogicalDotsPerInchChanged(qreal dpi) + { logScreenChangeSignal(sender(), "logicalDotsPerInch", dpi); } + void slotScreenVirtualGeometryChanged(const QRect &rect) + { logScreenChangeSignal(sender(), "virtualGeometry", rect); } + void slotScreenPrimaryOrientationChanged(Qt::ScreenOrientation orientation) + { logScreenChangeSignal(sender(), "primaryOrientation", orientation); } + void slotScreenOrientationChanged(Qt::ScreenOrientation orientation) + { logScreenChangeSignal(sender(), "orientation", orientation); } + void slotScreenRefreshRateChanged(qreal refreshRate) + { logScreenChangeSignal(sender(), "refreshRate", refreshRate); } + +private: + QPlainTextEdit *addTextPage(const QString &title); + void logMessage(const QString &); + void connectScreenChangeSignals(QScreen *s); + static QString briefFormatScreens(); + template + void logScreenChangeSignal(const QObject *o, const char *name, const T &value); + + QPlainTextEdit *m_textEdit; + QPlainTextEdit *m_logEdit; + QElapsedTimer m_logTimer; + bool m_screenChangedConnected = false; +}; + +void MetricsTest::slotScreenAdded(QScreen *screen) +{ + logMessage(QLatin1String("Added ") + screen->name() + QLatin1Char(' ') + + briefFormatScreens()); + connectScreenChangeSignals(screen); +} + +void MetricsTest::slotScreenRemoved(QScreen *screen) +{ + logMessage(QLatin1String("Removed ") + screen->name() + QLatin1Char(' ') + + briefFormatScreens()); +} + +void MetricsTest::slotPrimaryScreenChanged(QScreen *screen) +{ + logMessage(QLatin1String("PrimaryScreenChanged ") + screen->name() + QLatin1Char(' ') + + briefFormatScreens()); +} + +QPlainTextEdit *MetricsTest::addTextPage(const QString &title) +{ + auto result = new QPlainTextEdit(this); + result->setReadOnly(true); + result->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); + addTab(result, title); + return result; +} + +void MetricsTest::logMessage(const QString &m) +{ + const QString timeStamp = + QStringLiteral("%1ms: %2").arg(m_logTimer.elapsed(), 6, 10, QLatin1Char('0')).arg(m); + m_logEdit->appendPlainText(timeStamp); +} + +void MetricsTest::connectScreenChangeSignals(QScreen *s) +{ + connect(s, &QScreen::geometryChanged, this, &MetricsTest::slotScreenGeometryChanged); + connect(s, &QScreen::availableGeometryChanged, this, &MetricsTest::slotScreenAvailableGeometryChanged); + connect(s, &QScreen::physicalSizeChanged, this, &MetricsTest::slotScreenPhysicalSizeChanged); + connect(s, &QScreen::physicalDotsPerInchChanged, this, &MetricsTest::slotScreenPhysicalDotsPerInchChanged); + connect(s, &QScreen::logicalDotsPerInchChanged, this, &MetricsTest::slotScreenLogicalDotsPerInchChanged); + connect(s, &QScreen::virtualGeometryChanged, this, &MetricsTest::slotScreenVirtualGeometryChanged); + connect(s, &QScreen::primaryOrientationChanged, this, &MetricsTest::slotScreenPrimaryOrientationChanged); + connect(s, &QScreen::orientationChanged, this, &MetricsTest::slotScreenOrientationChanged); + connect(s, &QScreen::refreshRateChanged, this, &MetricsTest::slotScreenRefreshRateChanged); +} + +QString MetricsTest::briefFormatScreens() +{ + QString message; + QTextStream str(&message); + const auto screens = QGuiApplication::screens(); + for (int i = 0, size = screens.size(); i < size; ++i) { + str << (i ? ", " : "("); + str << screens.at(i)->name() << " " << screens.at(i)->geometry(); + } + str << ')'; + return message; +} + +template +void MetricsTest::logScreenChangeSignal(const QObject *o, const char *name, const T &value) +{ + auto screen = qobject_cast(o); + QString message; + QTextStream(&message) << (screen ? screen->name() : QString()) << ' ' << name << " changed: " << value; + logMessage(message); +} + +int main(int argc, char **argv) +{ + qInfo("High DPI tester %s", QT_VERSION_STR); + + QApplication app(argc, argv); + QCoreApplication::setApplicationVersion(QT_VERSION_STR); + + QCommandLineParser parser; + parser.setApplicationDescription("High DPI tester. Pass one or more of the options to\n" + "test various high-dpi aspects. \n" + "--interactive is a special option and opens a configuration" + " window."); + parser.addHelpOption(); + parser.addVersionOption(); + QCommandLineOption controllerOption("interactive", "Show configuration window."); + parser.addOption(controllerOption); + + DemoContainerList demoList; + demoList << new DemoContainer("pixmap", "Test pixmap painter"); + demoList << new DemoContainer("tiledpixmap", "Test tiled pixmap painter"); + demoList << new DemoContainer("label", "Test Labels"); + demoList << new DemoContainer("mainwindow", "Test QMainWindow"); + demoList << new DemoContainer("standard-icons", "Test standard icons"); + demoList << new DemoContainer("caching", "Test caching"); + demoList << new DemoContainer