summaryrefslogtreecommitdiffstats
path: root/tests/manual
diff options
context:
space:
mode:
authorMorten Johan Sørvig <morten.sorvig@qt.io>2021-04-16 12:04:49 +0200
committerMorten Johan Sørvig <morten.sorvig@qt.io>2021-04-19 18:45:39 +0200
commitfea54241aaefcfce6c0d19a8f22f922b83318f81 (patch)
tree7e0efbf695380b081a27fb9a175796f4910a3154 /tests/manual
parent74cae2580f5d04913ff343a750823c3441b32b71 (diff)
Add ScreenGadget utility
ScreenGadget visualizes virtual desktop screen layout, in device independent and native pixels. This can be used to debug the (sometimes surprising) device independent screen geometry resulting from Qt applying a scale factor. Change-Id: I5b18e0fc9a54ba3e14d648794429b2eeadd25748 Reviewed-by: Morten Johan Sørvig <morten.sorvig@qt.io>
Diffstat (limited to 'tests/manual')
-rw-r--r--tests/manual/highdpi/screengadget/CMakeLists.txt20
-rw-r--r--tests/manual/highdpi/screengadget/main.cpp244
-rw-r--r--tests/manual/highdpi/screengadget/screengadget.pro2
3 files changed, 266 insertions, 0 deletions
diff --git a/tests/manual/highdpi/screengadget/CMakeLists.txt b/tests/manual/highdpi/screengadget/CMakeLists.txt
new file mode 100644
index 0000000000..cbccd5de69
--- /dev/null
+++ b/tests/manual/highdpi/screengadget/CMakeLists.txt
@@ -0,0 +1,20 @@
+# special case skip regeneration
+cmake_minimum_required(VERSION 3.14)
+project(screengadget LANGUAGES CXX)
+set(CMAKE_AUTOMOC ON)
+
+find_package(Qt6 COMPONENTS Core)
+find_package(Qt6 COMPONENTS Gui)
+find_package(Qt6 COMPONENTS Widgets)
+
+qt_add_executable(screengadget
+ main.cpp
+)
+
+target_link_libraries(screengadget PUBLIC
+ Qt::Core
+ Qt::Gui
+ Qt::GuiPrivate
+ Qt::Widgets
+)
+
diff --git a/tests/manual/highdpi/screengadget/main.cpp b/tests/manual/highdpi/screengadget/main.cpp
new file mode 100644
index 0000000000..762981c395
--- /dev/null
+++ b/tests/manual/highdpi/screengadget/main.cpp
@@ -0,0 +1,244 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2021 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 <QtGui>
+#include <QtWidgets>
+
+#include <QtGui/qpa/qplatformscreen.h>
+#include <QtGui/qpa/qplatformwindow.h>
+#include <QtGui/qpa/qplatformcursor.h>
+
+// This test app optionally doubles as a manual test for QScreen::mapToNative()
+// #define TEST_MAP_TO_NATIVE
+
+// ScreenDisplayer based on original impl from qtbase/tests/manual/highdpi
+class ScreenDisplayer : public QWidget
+{
+public:
+ ScreenDisplayer() = default;
+
+ void setTitle() {
+
+ QPoint deviceIndependentPos = pos();
+ QPlatformWindow *platformWindow = windowHandle()->handle();
+ QPoint nativePos = platformWindow->geometry().topLeft();
+
+ QString windowDebug = QString("pos ")
+ + QString("device independent %1 %2 ").arg(deviceIndependentPos.x()).arg(deviceIndependentPos.y())
+ + QString("native %1 %2 ").arg(nativePos.x()).arg(nativePos.y())
+ ;
+
+ setWindowTitle(windowDebug);
+ }
+
+ void updateMapToNative(QPointF pos)
+ {
+#ifdef TEST_MAP_TO_NATIVE
+ mappedToNative = QGuiApplication::mapToNative(QRectF(pos, QPointF(1,1))).topLeft(); // there
+ mappedFromNative = QGuiApplication::mapFromNative(QRectF(mappedToNative, QPointF(1,1))).topLeft(); // and back again
+#endif
+ }
+
+ void timerEvent(QTimerEvent *) override
+ {
+ update();
+ setTitle();
+ }
+
+ void mousePressEvent(QMouseEvent *) override
+ {
+ displayMappedToNativeCursor = true;
+ }
+
+ void mouseMoveEvent(QMouseEvent *e) override
+ {
+ setTitle();
+ updateMapToNative(e->globalPosition());
+ }
+
+ void mouseReleaseEvent(QMouseEvent *) override
+ {
+ }
+
+ void showEvent(QShowEvent *) override
+ {
+ refreshTimer.start(60, this);
+ }
+
+ void hideEvent(QHideEvent *) override
+ {
+ refreshTimer.stop();
+ }
+
+ void paintScreensInRect(QPainter &p, QRect rect, bool native) {
+ p.save();
+
+ QRectF total;
+ const auto screens = QGuiApplication::screens();
+ for (const QScreen *screen : screens)
+ total |= native ? screen->handle()->geometry() : screen->geometry();
+ if (total.isEmpty())
+ return;
+
+ qreal scaleMargin = 0.9;
+ scaleFactor = scaleMargin * qMin(rect.width()/total.width(), rect.height()/total.height());
+
+ // p.fillRect(rect, Qt::black);
+ int margin = 20;
+ p.translate(margin, margin + rect.y());
+ p.scale(scaleFactor, scaleFactor);
+ p.translate(-total.topLeft());
+ p.setPen(QPen(Qt::white, 10));
+ p.setBrush(Qt::gray);
+
+ for (const QScreen *screen : screens) {
+ QRect geometry = native ? screen->handle()->geometry() : screen->geometry();
+ p.drawRect(geometry);
+ QFont f("Courier New");
+ f.setPixelSize(geometry.height() / 16);
+ p.setFont(f);
+
+ if (displayInfo) {
+ QString text = "Name: " + screen->name() + "\n";
+ text += QString("\nGeometry: %1 %2 %3 %4 \n ").arg(geometry.x()).arg(geometry.y()).arg(geometry.width()).arg(geometry.height());
+ p.save();
+ p.translate(20, 0);
+ p.drawText(geometry, Qt::AlignLeft | Qt::AlignVCenter, text);
+ p.restore();
+ }
+ }
+ p.setBrush(QColor(200,220,255,127));
+
+ const auto topLevels = QApplication::topLevelWidgets();
+ for (QWidget *widget : topLevels) {
+ if (!widget->isHidden())
+ p.drawRect(native ? widget->windowHandle()->handle()->geometry() : 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);
+
+ p.save();
+ p.translate(native ? qApp->primaryScreen()->handle()->cursor()->pos() : QCursor::pos());
+ p.drawPolygon(cursorShape);
+ p.restore();
+
+#ifdef TEST_MAP_TO_NATIVE
+ // Draw red "mapped to native" cursor. We expect this
+ // cursor to track the normal blue cursor if everything
+ // works out ok.
+ if (displayMappedToNativeCursor) {
+ p.save();
+ p.setBrush(QColor(230,120,155,127));
+ p.translate(native ? mappedToNative: mappedFromNative);
+ p.drawPolygon(cursorShape);
+ p.restore();
+ }
+#endif
+
+ p.restore();
+ }
+
+ void paintEvent(QPaintEvent *) override
+ {
+ QPainter p(this);
+
+ QRect g = geometry();
+ int halfHeight = g.height() / 2;
+ QRect topHalf = QRect(0, 0, g.width(), halfHeight);
+ QRect bottomHalf = QRect(0, halfHeight, g.width(), halfHeight);
+
+ if (displayDeviceIndependentGeometry)
+ paintScreensInRect(p, topHalf, false);
+ if (displayNativeGeometry)
+ paintScreensInRect(p, bottomHalf, true);
+ }
+
+ bool displayInfo = false;
+ bool displayDeviceIndependentGeometry = true;
+ bool displayNativeGeometry = false;
+private:
+ bool displayMappedToNativeCursor = false;
+ QPointF mappedToNative;
+ QPointF mappedFromNative;
+ qreal scaleFactor = 1;
+ QBasicTimer refreshTimer;
+};
+
+class Controller : public QWidget {
+public:
+ Controller(ScreenDisplayer *displayer) {
+ setWindowTitle("Controller");
+
+ QVBoxLayout *layout = new QVBoxLayout();
+ setLayout(layout);
+
+ layout->addWidget(new QLabel("Coordinate System:"));
+
+ QCheckBox *deviceIndpendentGeometry = new QCheckBox("Device Independent");
+ deviceIndpendentGeometry->setChecked(true);
+ connect(deviceIndpendentGeometry, &QCheckBox::stateChanged, [displayer](int checked){ displayer->displayDeviceIndependentGeometry = checked > 0; });
+ layout->addWidget(deviceIndpendentGeometry);
+
+ QCheckBox *nativeGeometry = new QCheckBox("Native");
+ nativeGeometry->setChecked(false);
+ connect(nativeGeometry, &QCheckBox::stateChanged, [displayer](int checked){ displayer->displayNativeGeometry = checked > 0; });
+ layout->addWidget(nativeGeometry);
+
+ layout->addSpacing(40);
+
+ QCheckBox *debug = new QCheckBox("Debug!");
+ debug->setChecked(false);
+ connect(debug, &QCheckBox::stateChanged, [displayer](int checked){ displayer->displayInfo = checked > 0; });
+ layout->addWidget(debug);
+
+ layout->addStretch();
+ }
+};
+
+int main(int argc, char **argv) {
+
+ QApplication app(argc, argv);
+
+ ScreenDisplayer displayer;
+ displayer.resize(300, 200);
+ displayer.show();
+
+ Controller controller(&displayer);
+ controller.resize(100, 200);
+
+ QTimer::singleShot(300, [&controller, &displayer](){
+ controller.move(displayer.pos() + QPoint(displayer.width(), 0) + QPoint(50, 0));
+ controller.show();
+ });
+
+ return app.exec();
+}
diff --git a/tests/manual/highdpi/screengadget/screengadget.pro b/tests/manual/highdpi/screengadget/screengadget.pro
new file mode 100644
index 0000000000..cdbaa8c2a1
--- /dev/null
+++ b/tests/manual/highdpi/screengadget/screengadget.pro
@@ -0,0 +1,2 @@
+QT += widgets gui_private
+SOURCES += main.cpp