diff options
author | Paul Olav Tvete <paul.tvete@theqtcompany.com> | 2015-06-18 17:00:27 +0200 |
---|---|---|
committer | Paul Olav Tvete <paul.tvete@theqtcompany.com> | 2015-07-30 18:10:03 +0000 |
commit | 62bd4f5852137276e354746737a88c06168c4b8b (patch) | |
tree | 3115f045001d3eb238f3dd0911b5818313baabca /tests/manual/highdpi | |
parent | 63090627220a6209652d236cf991305fbeb188b8 (diff) | |
download | qtbase-62bd4f5852137276e354746737a88c06168c4b8b.tar.gz |
Extend high-DPI manual test
Add several new tests to verify that Qt behaves properly when
high-DPI scaling is enabled. Add a generic framework for
manual tests for good measure. This could be refactored and
used for other manual tests later.
Includes tests written by Morten and Friedemann.
Task-number: QTBUG-46615
Change-Id: Ib6762ec1454711e71f0c094b19015932b99e8d6d
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@theqtcompany.com>
Diffstat (limited to 'tests/manual/highdpi')
-rw-r--r-- | tests/manual/highdpi/dragwidget.cpp | 223 | ||||
-rw-r--r-- | tests/manual/highdpi/dragwidget.h | 68 | ||||
-rw-r--r-- | tests/manual/highdpi/highdpi.pro | 13 | ||||
-rw-r--r-- | tests/manual/highdpi/main.cpp | 725 |
4 files changed, 954 insertions, 75 deletions
diff --git a/tests/manual/highdpi/dragwidget.cpp b/tests/manual/highdpi/dragwidget.cpp new file mode 100644 index 0000000000..b203566696 --- /dev/null +++ b/tests/manual/highdpi/dragwidget.cpp @@ -0,0 +1,223 @@ +/**************************************************************************** + ** + ** Copyright (C) 2015 The Qt Company Ltd. + ** Contact: http://www.qt.io/licensing/ + ** + ** This file is part of the test suite of the Qt Toolkit. + ** + ** $QT_BEGIN_LICENSE:LGPL21$ + ** 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 http://www.qt.io/terms-conditions. For further + ** information use the contact form at http://www.qt.io/contact-us. + ** + ** GNU Lesser General Public License Usage + ** Alternatively, this file may be used under the terms of the GNU Lesser + ** General Public License version 2.1 or version 3 as published by the Free + ** Software Foundation and appearing in the file LICENSE.LGPLv21 and + ** LICENSE.LGPLv3 included in the packaging of this file. Please review the + ** following information to ensure the GNU Lesser General Public License + ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and + ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + ** + ** As a special exception, The Qt Company gives you certain additional + ** rights. These rights are described in The Qt Company LGPL Exception + ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. + ** + ** $QT_END_LICENSE$ + ** + ****************************************************************************/ + +#include <QtWidgets> +#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), otherWindow(0) +{ + 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(' '); + foreach (QString word, words) { + if (!word.isEmpty()) { + FramedLabel *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(QRegExp("\\s+"), + QString::SkipEmptyParts); + QPoint position = event->pos(); + QPoint hotSpot; + + QList<QByteArray> 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(); + } + foreach (QObject *child, children()) { + if (child->inherits("QWidget")) { + QWidget *widget = static_cast<QWidget *>(child); + if (!widget->isVisible()) + widget->deleteLater(); + } + } +} + +void DragWidget::mousePressEvent(QMouseEvent *event) +{ + QLabel *child = static_cast<QLabel*>(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())); + + QPixmap pixmap(child->size()); + child->render(&pixmap); + + QDrag *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 new file mode 100644 index 0000000000..0d9631e2f8 --- /dev/null +++ b/tests/manual/highdpi/dragwidget.h @@ -0,0 +1,68 @@ +/**************************************************************************** + ** + ** Copyright (C) 2015 The Qt Company Ltd. + ** Contact: http://www.qt.io/licensing/ + ** + ** This file is part of the test suite of the Qt Toolkit. + ** + ** $QT_BEGIN_LICENSE:LGPL21$ + ** 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 http://www.qt.io/terms-conditions. For further + ** information use the contact form at http://www.qt.io/contact-us. + ** + ** GNU Lesser General Public License Usage + ** Alternatively, this file may be used under the terms of the GNU Lesser + ** General Public License version 2.1 or version 3 as published by the Free + ** Software Foundation and appearing in the file LICENSE.LGPLv21 and + ** LICENSE.LGPLv3 included in the packaging of this file. Please review the + ** following information to ensure the GNU Lesser General Public License + ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and + ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + ** + ** As a special exception, The Qt Company gives you certain additional + ** rights. These rights are described in The Qt Company LGPL Exception + ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. + ** + ** $QT_END_LICENSE$ + ** + ****************************************************************************/ + +#ifndef DRAGWIDGET_H +#define DRAGWIDGET_H + +#include <QWidget> +#include <QBasicTimer> + +QT_BEGIN_NAMESPACE +class QDragEnterEvent; +class QDropEvent; +QT_END_NAMESPACE + +class DragWidget : public QWidget +{ +public: + DragWidget(QString text = QString(), QWidget *parent = 0); + +protected: + void dragEnterEvent(QDragEnterEvent *event) Q_DECL_OVERRIDE; + void dragLeaveEvent(QDragLeaveEvent *event) Q_DECL_OVERRIDE; + void dropEvent(QDropEvent *event) Q_DECL_OVERRIDE; + void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; + void dragMoveEvent(QDragMoveEvent * event) Q_DECL_OVERRIDE; + void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE; + void timerEvent(QTimerEvent *event) Q_DECL_OVERRIDE; + void showEvent(QShowEvent *event) Q_DECL_OVERRIDE; + void hideEvent(QHideEvent *event) Q_DECL_OVERRIDE; +private: + QPoint dragPos; + QPoint dropPos; + QBasicTimer dragTimer; + QBasicTimer dropTimer; + QWidget *otherWindow; +}; + +#endif // DRAGWIDGET_H diff --git a/tests/manual/highdpi/highdpi.pro b/tests/manual/highdpi/highdpi.pro index 7a2979c74c..7d6b42535e 100644 --- a/tests/manual/highdpi/highdpi.pro +++ b/tests/manual/highdpi/highdpi.pro @@ -1,10 +1,17 @@ TEMPLATE = app TARGET = highdpi INCLUDEPATH += . -QT += widgets -CONFIG+=console +QT += widgets gui-private +CONFIG +=console +CONFIG -= app_bundle +CONFIG += c++11 # Input -SOURCES += main.cpp +SOURCES += \ + dragwidget.cpp \ + main.cpp + +HEADERS += \ + dragwidget.h RESOURCES += \ highdpi.qrc diff --git a/tests/manual/highdpi/main.cpp b/tests/manual/highdpi/main.cpp index fd14523a97..692a60d511 100644 --- a/tests/manual/highdpi/main.cpp +++ b/tests/manual/highdpi/main.cpp @@ -32,6 +32,7 @@ ****************************************************************************/ #include <QMainWindow> +#include <QMenuBar> #include <QLabel> #include <QHBoxLayout> #include <QApplication> @@ -39,6 +40,7 @@ #include <QStyle> #include <QToolBar> #include <QPushButton> +#include <QButtonGroup> #include <QLineEdit> #include <QScrollBar> #include <QSlider> @@ -49,10 +51,219 @@ #include <QWindow> #include <QScreen> #include <QFile> +#include <QMouseEvent> #include <QTemporaryDir> +#include <QTimer> #include <QCommandLineParser> #include <QCommandLineOption> +#include <QDebug> +#include <private/qhighdpiscaling_p.h> +#include "dragwidget.h" + +class DemoContainerBase +{ +public: + DemoContainerBase() : m_widget(0) {} + virtual ~DemoContainerBase() {} + QString name() { return option().names().first(); } + virtual QCommandLineOption &option() = 0; + virtual void makeVisible(bool visible, QWidget *parent) = 0; + QWidget *widget() { return m_widget; } +protected: + QWidget *m_widget; +}; + +typedef QList<DemoContainerBase*> DemoContainerList ; + + +template <class T> +class DemoContainer : public DemoContainerBase +{ +public: + DemoContainer(const QString &optionName, const QString &description) + : m_option(optionName, description) + { + } + ~DemoContainer() { delete m_widget; } + + QCommandLineOption &option() { return m_option; } + + void makeVisible(bool visible, QWidget *parent) { + if (visible && !m_widget) { + m_widget = new T; + 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(".")) + 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 = 0; + return QHighDpiScaling::factor(noScreen); +} + +class DemoController : public QWidget +{ +Q_OBJECT +public: + DemoController(DemoContainerList *demos, QCommandLineParser *parser); + ~DemoController(); +protected: + bool eventFilter(QObject *object, QEvent *event); + void closeEvent(QCloseEvent *) { qApp->quit(); } +private slots: + void handleButton(int id, bool toggled); +private: + DemoContainerList *m_demos; + QButtonGroup *m_group; +}; + +DemoController::DemoController(DemoContainerList *demos, QCommandLineParser *parser) + : m_demos(demos) +{ + setWindowTitle("screen scale factors"); + setObjectName("controller"); // make WindowScaleFactorSetter skip this window + + QGridLayout *layout = new QGridLayout; + setLayout(layout); + + int layoutRow = 0; + LabelSlider *globalScaleSlider = new LabelSlider(this, "Global scale factor", layout, 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 + QList<QScreen *> screens = QGuiApplication::screens(); + foreach (QScreen *screen, screens) { + // create scale control line + QSize screenSize = screen->geometry().size(); + QString screenId = screen->name() + " " + QString::number(screenSize.width()) + + " " + QString::number(screenSize.height()); + LabelSlider *slider = new LabelSlider(this, screenId, layout, 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; + }); + } + + m_group = new QButtonGroup(this); + m_group->setExclusive(false); + + for (int i = 0; i < m_demos->size(); ++i) { + DemoContainerBase *demo = m_demos->at(i); + QPushButton *button = new QPushButton(demo->name()); + button->setToolTip(demo->option().description()); + button->setCheckable(true); + layout->addWidget(button, layoutRow++, 0, 1, -1); + m_group->addButton(button, i); + + if (parser->isSet(demo->option())) { + demo->makeVisible(true, this); + button->setChecked(true); + } + } + connect(m_group, SIGNAL(buttonToggled(int, bool)), this, SLOT(handleButton(int, bool))); +} + +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 { @@ -69,7 +280,6 @@ public: QIcon qtIcon; }; - PixmapPainter::PixmapPainter() { pixmap1X = QPixmap(":/qticon32.png"); @@ -172,15 +382,18 @@ class MainWindow : public QMainWindow { public: MainWindow(); + QMenu *addNewMenu(const QString &title, int itemCount = 5); QIcon qtIcon; QIcon qtIcon1x; QIcon qtIcon2x; QToolBar *fileToolBar; + int menuCount; }; MainWindow::MainWindow() + :menuCount(0) { // beware that QIcon auto-loads the @2x versions. qtIcon1x.addFile(":/qticon16.png"); @@ -192,8 +405,33 @@ MainWindow::MainWindow() // 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); + addNewMenu("&Transmogrify", 7); + 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; +} + + class StandardIcons : public QWidget { public: @@ -205,7 +443,7 @@ public: int dy = 50; int maxX = 500; - for (int iconIndex = QStyle::SP_TitleBarMenuButton; iconIndex < QStyle::SP_MediaVolumeMuted; ++iconIndex) { + 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)); @@ -295,14 +533,27 @@ public: void paintEvent(QPaintEvent *) { QPainter painter(this); - int y = 40; - for (int fontSize = 2; fontSize < 18; fontSize += 2) { + + // Points + int y = 10; + for (int fontSize = 6; fontSize < 18; fontSize += 2) { QFont font; font.setPointSize(fontSize); - QString string = QString(QStringLiteral("%1 The quick brown fox jumped over the lazy Doug.")).arg(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); - y += (fontSize * 2.5); } } }; @@ -461,97 +712,427 @@ public: } }; +class LinePainter : public QWidget +{ +public: + void paintEvent(QPaintEvent *event); + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + + QPoint lastMousePoint; + QVector<QPoint> linePoints; +}; -int main(int argc, char **argv) +void LinePainter::paintEvent(QPaintEvent *) { - QApplication app(argc, argv); - QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); - QCoreApplication::setApplicationVersion(QT_VERSION_STR); + QPainter p(this); + p.fillRect(QRect(QPoint(0, 0), size()), QBrush(Qt::gray)); - QCommandLineParser parser; - parser.setApplicationDescription("High DPI tester"); - parser.addHelpOption(); - parser.addVersionOption(); - QCommandLineOption pixmapPainterOption("pixmap", "Test pixmap painter"); - parser.addOption(pixmapPainterOption); - QCommandLineOption labelOption("label", "Test Labels"); - parser.addOption(labelOption); - QCommandLineOption mainWindowOption("mainwindow", "Test QMainWindow"); - parser.addOption(mainWindowOption); - QCommandLineOption standardIconsOption("standard-icons", "Test standard icons"); - parser.addOption(standardIconsOption); - QCommandLineOption cachingOption("caching", "Test caching"); - parser.addOption(cachingOption); - QCommandLineOption styleOption("styles", "Test style"); - parser.addOption(styleOption); - QCommandLineOption fontsOption("fonts", "Test fonts"); - parser.addOption(fontsOption); - QCommandLineOption iconDrawingOption("icondrawing", "Test icon drawing"); - parser.addOption(iconDrawingOption); - QCommandLineOption buttonsOption("buttons", "Test buttons"); - parser.addOption(buttonsOption); + // 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); +} - parser.process(app); +void LinePainter::mousePressEvent(QMouseEvent *event) +{ + lastMousePoint = event->pos(); +} - QScopedPointer<PixmapPainter> pixmapPainter; - if (parser.isSet(pixmapPainterOption)) { - pixmapPainter.reset(new PixmapPainter); - pixmapPainter->show(); +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() + :moveLabel(0), moving(false) + { } - QScopedPointer<Labels> label; - if (parser.isSet(labelOption)) { - label.reset(new Labels); - label->resize(200, 200); - label->show(); + inline QRect getRect(int idx) const + { + int h = height() / 2; + return QRect(10, 10 + h * (idx - 1), width() - 20, h - 20); + } + void paintEvent(QPaintEvent *) + { + 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"); + } } - QScopedPointer<MainWindow> mainWindow; - if (parser.isSet(mainWindowOption)) { - mainWindow.reset(new MainWindow); - mainWindow->show(); + void mousePressEvent(QMouseEvent *e) + { + 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(); } - QScopedPointer<StandardIcons> icons; - if (parser.isSet(standardIconsOption)) { - icons.reset(new StandardIcons); - icons->resize(510, 510); - icons->show(); + void mouseReleaseEvent(QMouseEvent *) + { + if (moveLabel) + moveLabel->hide(); + update(); + moving = false; } - QScopedPointer<Caching> caching; - if (parser.isSet(cachingOption)) { - caching.reset(new Caching); - caching->resize(300, 300); - caching->show(); + void mouseMoveEvent(QMouseEvent *e) + { + if (!moving) + return; + QPoint pos = useCursorPos ? QCursor::pos() : e->globalPos(); + pos -= moveLabel->rect().center(); + moveLabel->move(pos); + mousePos = e->pos(); + update(); } - QScopedPointer<Style> style; - if (parser.isSet(styleOption)) { - style.reset(new Style); - style->show(); +private: + QLabel *moveLabel; + bool useCursorPos; + bool moving; + QPoint mousePos; +}; + + +class ScreenDisplayer : public QWidget +{ +public: + ScreenDisplayer() + : QWidget(), moveLabel(0), scaleFactor(1.0) + { } - QScopedPointer<Fonts> fonts; - if (parser.isSet(fontsOption)) { - fonts.reset(new Fonts); - fonts->show(); + void timerEvent(QTimerEvent *) { + update(); } - QScopedPointer<IconDrawing> iconDrawing; - if (parser.isSet(iconDrawingOption)) { - iconDrawing.reset(new IconDrawing); - iconDrawing->show(); + void mousePressEvent(QMouseEvent *) { + 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) { + 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 *) { + if (moveLabel) + moveLabel->hide(); + } + void showEvent(QShowEvent *) { + refreshTimer.start(300, this); } + void hideEvent(QHideEvent *) { + refreshTimer.stop(); + } + void paintEvent(QPaintEvent *) { + QPainter p(this); + QRectF total; + QList<QScreen*> screens = qApp->screens(); + foreach (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); + - QScopedPointer<Buttons> buttons; - if (parser.isSet(buttonsOption)) { - buttons.reset(new Buttons); - buttons->show(); + foreach (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)); + foreach (QWidget *widget, QApplication::topLevelWidgets()) { + 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; + QBasicTimer refreshTimer; + qreal scaleFactor; +}; + +class PhysicalSizeTest : public QWidget +{ +Q_OBJECT +public: + PhysicalSizeTest() : QWidget(), m_ignoreResize(false) {} + void paintEvent(QPaintEvent *event); + void resizeEvent(QResizeEvent *) { + qreal ppi = window()->windowHandle()->screen()->physicalDotsPerInchX(); + QSizeF s = size(); + if (!m_ignoreResize) + m_physicalSize = s / ppi; + } + bool event(QEvent *event) { + if (event->type() == QEvent::ScreenChangeInternal) { + // we will get resize events when the scale factor changes + m_ignoreResize = true; + QTimer::singleShot(100, this, SLOT(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; +}; + +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)); + } + } + +} + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); + QCoreApplication::setApplicationVersion(QT_VERSION_STR); + + int argumentCount = QCoreApplication::arguments().count(); + + 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<PixmapPainter>("pixmap", "Test pixmap painter"); + demoList << new DemoContainer<Labels>("label", "Test Labels"); + demoList << new DemoContainer<MainWindow>("mainwindow", "Test QMainWindow"); + demoList << new DemoContainer<StandardIcons>("standard-icons", "Test standard icons"); + demoList << new DemoContainer<Caching>("caching", "Test caching"); + demoList << new DemoContainer<Style>("styles", "Test style"); + demoList << new DemoContainer<Fonts>("fonts", "Test fonts"); + demoList << new DemoContainer<IconDrawing>("icondrawing", "Test icon drawing"); + demoList << new DemoContainer<Buttons>("buttons", "Test buttons"); + demoList << new DemoContainer<LinePainter>("linepainter", "Test line painting"); + demoList << new DemoContainer<DragWidget>("draganddrop", "Test drag and drop"); + demoList << new DemoContainer<CursorTester>("cursorpos", "Test cursor and window positioning"); + demoList << new DemoContainer<ScreenDisplayer>("screens", "Test screen and window positioning"); + demoList << new DemoContainer<PhysicalSizeTest>("physicalsize", "Test manual highdpi support using physicalDotsPerInch"); + + + foreach (DemoContainerBase *demo, demoList) + parser.addOption(demo->option()); + + parser.process(app); + + //controller takes ownership of all demos + DemoController controller(&demoList, &parser); + + if (parser.isSet(controllerOption) || argumentCount <= 1) + controller.show(); if (QApplication::topLevelWidgets().isEmpty()) parser.showHelp(0); return app.exec(); } + +#include "main.moc" |