From d6dfbe8fd5d09388fc624d85c4aefa0269c20db9 Mon Sep 17 00:00:00 2001 From: Laszlo Agocs Date: Thu, 22 Sep 2016 14:22:26 +0200 Subject: Enable making window-screen associations from QML Qt Quick provides a Screen attached property to query information about the screen an item's window belongs to. This is a good start, but has two problems: it lacks some virtual desktop related info (e.g. the position in the virtual desktop) and it cannot be used in combination with the Window element in order to achieve a QML equivalent of QWindow::setScreen(). Therefore add the missing virtualX and virtualY properties and introduce Qt.application.screens. The latter is an equivalent to QGuiApplication::screens() and is a JS array the elements of which can be set as the value of the new Window.targetScreen property. This means that a call like window->setScreen(QGuiApplication::screens()[0]) translates to Window { targetScreen: Qt.application.screens[0]; ... } when using the Window type from QML. Screen addition or removal can be acted upon via onScreensChanged. QQuickScreenAttached has been split into two in order to allow reusing the QScreen wrapping queries for other purposes as well. Task-number: QTBUG-56115 Change-Id: I4b2fbd873315b40d0afe878da2fc50966c00e2e0 Reviewed-by: Shawn Rutledge Reviewed-by: Laszlo Agocs --- examples/quick/window/AllScreens.qml | 75 ++++++++ examples/quick/window/CurrentScreen.qml | 105 +++++++++++ examples/quick/window/ScreenInfo.qml | 102 ----------- examples/quick/window/doc/src/window.qdoc | 2 +- examples/quick/window/window.qml | 15 +- examples/quick/window/window.qrc | 3 +- src/qml/qml/qqmlengine.cpp | 14 ++ src/quick/items/qquickscreen.cpp | 195 +++++++++++++-------- src/quick/items/qquickscreen_p.h | 48 +++-- src/quick/items/qquickwindow.cpp | 22 +++ src/quick/items/qquickwindowmodule.cpp | 25 +++ src/quick/items/qquickwindowmodule_p.h | 5 + src/quick/util/qquickapplication.cpp | 34 +++- src/quick/util/qquickapplication_p.h | 9 +- tests/auto/quick/qquickscreen/data/screen.qml | 14 +- tests/auto/quick/qquickscreen/tst_qquickscreen.cpp | 40 ++++- .../quick/qquickwindow/data/windowWithScreen.qml | 10 ++ tests/auto/quick/qquickwindow/tst_qquickwindow.cpp | 19 ++ 18 files changed, 535 insertions(+), 202 deletions(-) create mode 100644 examples/quick/window/AllScreens.qml create mode 100644 examples/quick/window/CurrentScreen.qml delete mode 100644 examples/quick/window/ScreenInfo.qml create mode 100644 tests/auto/quick/qquickwindow/data/windowWithScreen.qml diff --git a/examples/quick/window/AllScreens.qml b/examples/quick/window/AllScreens.qml new file mode 100644 index 0000000000..83a6c5f958 --- /dev/null +++ b/examples/quick/window/AllScreens.qml @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.3 +import QtQuick.Window 2.3 +import "../shared" as Shared + +Column { + id: root + spacing: 8 + + Shared.Label { + text: "Total number of screens: " + screenInfo.count + font.bold: true + } + + Flow { + spacing: 12 + width: parent.width + + Repeater { + id: screenInfo + model: Qt.application.screens + Shared.Label { + lineHeight: 1.5 + text: name + "\n" + virtualX + ", " + virtualY + " " + modelData.width + "x" + modelData.height + } + } + } + + Component.onCompleted: { + var screens = Qt.application.screens; + for (var i = 0; i < screens.length; ++i) + console.log("screen " + screens[i].name + " has geometry " + + screens[i].virtualX + ", " + screens[i].virtualY + " " + + screens[i].width + "x" + screens[i].height) + } +} diff --git a/examples/quick/window/CurrentScreen.qml b/examples/quick/window/CurrentScreen.qml new file mode 100644 index 0000000000..c65baab1f4 --- /dev/null +++ b/examples/quick/window/CurrentScreen.qml @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.3 +import QtQuick.Window 2.1 +import "../shared" as Shared + +Item { + id: root + width: 400 + height: propertyGrid.implicitHeight + 16 + + function orientationToString(o) { + switch (o) { + case Qt.PrimaryOrientation: + return "primary"; + case Qt.PortraitOrientation: + return "portrait"; + case Qt.LandscapeOrientation: + return "landscape"; + case Qt.InvertedPortraitOrientation: + return "inverted portrait"; + case Qt.InvertedLandscapeOrientation: + return "inverted landscape"; + } + return "unknown"; + } + + Grid { + id: propertyGrid + columns: 2 + spacing: 8 + x: spacing + y: spacing + + //! [screen] + Shared.Label { + text: "Screen \"" + Screen.name + "\":" + font.bold: true + } + Item { width: 1; height: 1 } // spacer + + Shared.Label { text: "dimensions" } + Shared.Label { text: Screen.width + "x" + Screen.height } + + Shared.Label { text: "pixel density" } + Shared.Label { text: Screen.pixelDensity.toFixed(2) + " dots/mm (" + (Screen.pixelDensity * 25.4).toFixed(2) + " dots/inch)" } + + Shared.Label { text: "logical pixel density" } + Shared.Label { text: Screen.logicalPixelDensity.toFixed(2) + " dots/mm (" + (Screen.logicalPixelDensity * 25.4).toFixed(2) + " dots/inch)" } + + Shared.Label { text: "device pixel ratio" } + Shared.Label { text: Screen.devicePixelRatio.toFixed(2) } + + Shared.Label { text: "available virtual desktop" } + Shared.Label { text: Screen.desktopAvailableWidth + "x" + Screen.desktopAvailableHeight } + + Shared.Label { text: "position in virtual desktop" } + Shared.Label { text: Screen.virtualX + ", " + Screen.virtualY } + + Shared.Label { text: "orientation" } + Shared.Label { text: orientationToString(Screen.orientation) + " (" + Screen.orientation + ")" } + + Shared.Label { text: "primary orientation" } + Shared.Label { text: orientationToString(Screen.primaryOrientation) + " (" + Screen.primaryOrientation + ")" } + //! [screen] + } +} diff --git a/examples/quick/window/ScreenInfo.qml b/examples/quick/window/ScreenInfo.qml deleted file mode 100644 index 7d7cd946c5..0000000000 --- a/examples/quick/window/ScreenInfo.qml +++ /dev/null @@ -1,102 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of The Qt Company Ltd nor the names of its -** contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -import QtQuick 2.3 -import QtQuick.Window 2.1 -import "../shared" as Shared - -Item { - id: root - width: 400 - height: propertyGrid.implicitHeight + 16 - - function orientationToString(o) { - switch (o) { - case Qt.PrimaryOrientation: - return "primary"; - case Qt.PortraitOrientation: - return "portrait"; - case Qt.LandscapeOrientation: - return "landscape"; - case Qt.InvertedPortraitOrientation: - return "inverted portrait"; - case Qt.InvertedLandscapeOrientation: - return "inverted landscape"; - } - return "unknown"; - } - - Grid { - id: propertyGrid - columns: 2 - spacing: 8 - x: spacing - y: spacing - - //! [screen] - Shared.Label { - text: "Screen \"" + Screen.name + "\":" - font.bold: true - } - Item { width: 1; height: 1 } // spacer - - Shared.Label { text: "dimensions" } - Shared.Label { text: Screen.width + "x" + Screen.height } - - Shared.Label { text: "pixel density" } - Shared.Label { text: Screen.pixelDensity.toFixed(2) + " dots/mm (" + (Screen.pixelDensity * 25.4).toFixed(2) + " dots/inch)" } - - Shared.Label { text: "logical pixel density" } - Shared.Label { text: Screen.logicalPixelDensity.toFixed(2) + " dots/mm (" + (Screen.logicalPixelDensity * 25.4).toFixed(2) + " dots/inch)" } - - Shared.Label { text: "device pixel ratio" } - Shared.Label { text: Screen.devicePixelRatio.toFixed(2) } - - Shared.Label { text: "available virtual desktop" } - Shared.Label { text: Screen.desktopAvailableWidth + "x" + Screen.desktopAvailableHeight } - - Shared.Label { text: "orientation" } - Shared.Label { text: orientationToString(Screen.orientation) + " (" + Screen.orientation + ")" } - - Shared.Label { text: "primary orientation" } - Shared.Label { text: orientationToString(Screen.primaryOrientation) + " (" + Screen.primaryOrientation + ")" } - //! [screen] - } -} diff --git a/examples/quick/window/doc/src/window.qdoc b/examples/quick/window/doc/src/window.qdoc index 5cd51beceb..2028b31383 100644 --- a/examples/quick/window/doc/src/window.qdoc +++ b/examples/quick/window/doc/src/window.qdoc @@ -73,7 +73,7 @@ \l Screen has several properties which are generally useful to applications which need to rotate some content when the screen orientation changes, to position windows on the screen or to convert real units to - logical pixel units. ScreenInfo.qml (which is displayed inline in + logical pixel units. CurrentScreen.qml (which is displayed inline in window.qml, or can be run by itself with qmlscene) simply displays the property values, while the splash screen uses them to center the window on the screen. diff --git a/examples/quick/window/window.qml b/examples/quick/window/window.qml index f3052a7d62..d27ab3d0a3 100644 --- a/examples/quick/window/window.qml +++ b/examples/quick/window/window.qml @@ -39,7 +39,7 @@ ****************************************************************************/ import QtQuick 2.0 -import QtQuick.Window 2.1 +import QtQuick.Window 2.3 import "../shared" as Shared QtObject { @@ -47,7 +47,7 @@ QtObject { property SystemPalette palette: SystemPalette { } property var controlWindow: Window { - width: visibilityLabel.implicitWidth * 1.2 + width: col.implicitWidth + defaultSpacing * 2 height: col.implicitHeight + defaultSpacing * 2 color: palette.window title: "Control Window" @@ -127,12 +127,17 @@ QtObject { " and has visibility " + parent.visibilityToString(testWindow.visibility) } Rectangle { - id: horizontalRule - color: "black" + color: palette.text width: parent.width height: 1 } - ScreenInfo { } + CurrentScreen { } + Rectangle { + color: palette.text + width: parent.width + height: 1 + } + AllScreens { width: parent.width } } } diff --git a/examples/quick/window/window.qrc b/examples/quick/window/window.qrc index dc211bdaaf..89d1de1b1f 100644 --- a/examples/quick/window/window.qrc +++ b/examples/quick/window/window.qrc @@ -2,6 +2,7 @@ window.qml Splash.qml - ScreenInfo.qml + CurrentScreen.qml + AllScreens.qml diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 01f1f93b81..daa12fae26 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -554,6 +554,18 @@ The following functions are also on the Qt object. \li This read-only property can be used to determine whether or not the platform supports multiple windows. Some embedded platforms do not support multiple windows, for example. + + \row + \li \c application.screens + \li An array containing the descriptions of all connected screens. The + elements of the array are objects with the same properties as the + \l{Screen} attached object. In practice the array corresponds to the screen + list returned by QGuiApplication::screens(). In addition to examining + properties like name, width, height, etc., the array elements can also be + assigned to the targetScreen property of Window items, thus serving as an + alternative to the C++ side's QWindow::setScreen(). This property has been + added in Qt 5.9. + \endtable The object also has one signal, aboutToQuit(), which is the same as \l QCoreApplication::aboutToQuit(). @@ -570,6 +582,8 @@ The following functions are also on the Qt object. \li application.layoutDirection \li application.font \endlist + + \sa Screen, Window, Window.targetScreen */ /*! diff --git a/src/quick/items/qquickscreen.cpp b/src/quick/items/qquickscreen.cpp index 5d01a2af9d..9347b55c70 100644 --- a/src/quick/items/qquickscreen.cpp +++ b/src/quick/items/qquickscreen.cpp @@ -207,100 +207,181 @@ QT_BEGIN_NAMESPACE By default it is set to the value of the QScreen that the window uses. */ -QQuickScreenAttached::QQuickScreenAttached(QObject* attachee) - : QObject(attachee) - , m_screen(NULL) - , m_window(NULL) - , m_updateMask(0) - , m_updateMaskSet(false) +QQuickScreenInfo::QQuickScreenInfo(QObject *parent) + : QObject(parent), + m_screen(nullptr) { - m_attachee = qobject_cast(attachee); - - if (m_attachee) { - QQuickItemPrivate::get(m_attachee)->extra.value().screenAttached = this; - - if (m_attachee->window()) //It might not be assigned to a window yet - windowChanged(m_attachee->window()); - } else { - QQuickWindow *window = qobject_cast(attachee); - if (window) - windowChanged(window); - } - - if (!m_screen) - screenChanged(QGuiApplication::primaryScreen()); } -QString QQuickScreenAttached::name() const +QString QQuickScreenInfo::name() const { if (!m_screen) return QString(); return m_screen->name(); } -int QQuickScreenAttached::width() const +int QQuickScreenInfo::width() const { if (!m_screen) return 0; return m_screen->size().width(); } -int QQuickScreenAttached::height() const +int QQuickScreenInfo::height() const { if (!m_screen) return 0; return m_screen->size().height(); } -int QQuickScreenAttached::desktopAvailableWidth() const +int QQuickScreenInfo::desktopAvailableWidth() const { if (!m_screen) return 0; return m_screen->availableVirtualSize().width(); } -int QQuickScreenAttached::desktopAvailableHeight() const +int QQuickScreenInfo::desktopAvailableHeight() const { if (!m_screen) return 0; return m_screen->availableVirtualSize().height(); } -qreal QQuickScreenAttached::logicalPixelDensity() const +qreal QQuickScreenInfo::logicalPixelDensity() const { if (!m_screen) return 0.0; return m_screen->logicalDotsPerInch() / 25.4; } -qreal QQuickScreenAttached::pixelDensity() const +qreal QQuickScreenInfo::pixelDensity() const { if (!m_screen) return 0.0; return m_screen->physicalDotsPerInch() / 25.4; } -qreal QQuickScreenAttached::devicePixelRatio() const +qreal QQuickScreenInfo::devicePixelRatio() const { if (!m_screen) return 1.0; return m_screen->devicePixelRatio(); } -Qt::ScreenOrientation QQuickScreenAttached::primaryOrientation() const +Qt::ScreenOrientation QQuickScreenInfo::primaryOrientation() const { if (!m_screen) return Qt::PrimaryOrientation; return m_screen->primaryOrientation(); } -Qt::ScreenOrientation QQuickScreenAttached::orientation() const +Qt::ScreenOrientation QQuickScreenInfo::orientation() const { if (!m_screen) return Qt::PrimaryOrientation; return m_screen->orientation(); } +int QQuickScreenInfo::virtualX() const +{ + if (!m_screen) + return 0; + return m_screen->geometry().topLeft().x(); +} + +int QQuickScreenInfo::virtualY() const +{ + if (!m_screen) + return 0; + return m_screen->geometry().topLeft().y(); +} + +void QQuickScreenInfo::setWrappedScreen(QScreen *screen) +{ + if (screen == m_screen) + return; + + QScreen *oldScreen = m_screen; + m_screen = screen; + + if (oldScreen) + oldScreen->disconnect(this); + + if (!screen) //Don't bother emitting signals, because the new values are garbage anyways + return; + + if (!oldScreen || screen->geometry() != oldScreen->geometry()) { + emit virtualXChanged(); + emit virtualYChanged(); + } + if (!oldScreen || screen->size() != oldScreen->size()) { + emit widthChanged(); + emit heightChanged(); + } + if (!oldScreen || screen->name() != oldScreen->name()) + emit nameChanged(); + if (!oldScreen || screen->orientation() != oldScreen->orientation()) + emit orientationChanged(); + if (!oldScreen || screen->primaryOrientation() != oldScreen->primaryOrientation()) + emit primaryOrientationChanged(); + if (!oldScreen || screen->availableVirtualGeometry() != oldScreen->availableVirtualGeometry()) + emit desktopGeometryChanged(); + if (!oldScreen || screen->logicalDotsPerInch() != oldScreen->logicalDotsPerInch()) + emit logicalPixelDensityChanged(); + if (!oldScreen || screen->physicalDotsPerInch() != oldScreen->physicalDotsPerInch()) + emit pixelDensityChanged(); + if (!oldScreen || screen->devicePixelRatio() != oldScreen->devicePixelRatio()) + emit devicePixelRatioChanged(); + + connect(screen, SIGNAL(geometryChanged(QRect)), + this, SIGNAL(widthChanged())); + connect(screen, SIGNAL(geometryChanged(QRect)), + this, SIGNAL(heightChanged())); + connect(screen, SIGNAL(geometryChanged(QRect)), + this, SIGNAL(virtualXChanged())); + connect(screen, SIGNAL(geometryChanged(QRect)), + this, SIGNAL(virtualYChanged())); + connect(screen, SIGNAL(orientationChanged(Qt::ScreenOrientation)), + this, SIGNAL(orientationChanged())); + connect(screen, SIGNAL(primaryOrientationChanged(Qt::ScreenOrientation)), + this, SIGNAL(primaryOrientationChanged())); + connect(screen, SIGNAL(virtualGeometryChanged(QRect)), + this, SIGNAL(desktopGeometryChanged())); + connect(screen, SIGNAL(logicalDotsPerInchChanged(qreal)), + this, SIGNAL(logicalPixelDensityChanged())); + connect(screen, SIGNAL(physicalDotsPerInchChanged(qreal)), + this, SIGNAL(pixelDensityChanged())); +} + +QScreen *QQuickScreenInfo::wrappedScreen() const +{ + return m_screen; +} + +QQuickScreenAttached::QQuickScreenAttached(QObject* attachee) + : QQuickScreenInfo(attachee) + , m_window(NULL) + , m_updateMask(0) + , m_updateMaskSet(false) +{ + m_attachee = qobject_cast(attachee); + + if (m_attachee) { + QQuickItemPrivate::get(m_attachee)->extra.value().screenAttached = this; + + if (m_attachee->window()) //It might not be assigned to a window yet + windowChanged(m_attachee->window()); + } else { + QQuickWindow *window = qobject_cast(attachee); + if (window) + windowChanged(window); + } + + if (!m_screen) + screenChanged(QGuiApplication::primaryScreen()); +} + Qt::ScreenOrientations QQuickScreenAttached::orientationUpdateMask() const { return m_updateMask; @@ -341,55 +422,15 @@ void QQuickScreenAttached::screenChanged(QScreen *screen) { //qDebug() << "QQuickScreenAttached::screenChanged" << (screen ? screen->name() : QString::fromLatin1("null")); if (screen != m_screen) { - QScreen* oldScreen = m_screen; - m_screen = screen; - - if (oldScreen) - oldScreen->disconnect(this); - - if (!screen) - return; //Don't bother emitting signals, because the new values are garbage anyways - + setWrappedScreen(screen); + if (!m_screen) + return; if (m_updateMaskSet) { - screen->setOrientationUpdateMask(m_updateMask); - } else if (m_updateMask != screen->orientationUpdateMask()) { - m_updateMask = screen->orientationUpdateMask(); + m_screen->setOrientationUpdateMask(m_updateMask); + } else if (m_updateMask != m_screen->orientationUpdateMask()) { + m_updateMask = m_screen->orientationUpdateMask(); emit orientationUpdateMaskChanged(); } - - if (!oldScreen || screen->size() != oldScreen->size()) { - emit widthChanged(); - emit heightChanged(); - } - if (!oldScreen || screen->name() != oldScreen->name()) - emit nameChanged(); - if (!oldScreen || screen->orientation() != oldScreen->orientation()) - emit orientationChanged(); - if (!oldScreen || screen->primaryOrientation() != oldScreen->primaryOrientation()) - emit primaryOrientationChanged(); - if (!oldScreen || screen->availableVirtualGeometry() != oldScreen->availableVirtualGeometry()) - emit desktopGeometryChanged(); - if (!oldScreen || screen->logicalDotsPerInch() != oldScreen->logicalDotsPerInch()) - emit logicalPixelDensityChanged(); - if (!oldScreen || screen->physicalDotsPerInch() != oldScreen->physicalDotsPerInch()) - emit pixelDensityChanged(); - if (!oldScreen || screen->devicePixelRatio() != oldScreen->devicePixelRatio()) - emit devicePixelRatioChanged(); - - connect(screen, SIGNAL(geometryChanged(QRect)), - this, SIGNAL(widthChanged())); - connect(screen, SIGNAL(geometryChanged(QRect)), - this, SIGNAL(heightChanged())); - connect(screen, SIGNAL(orientationChanged(Qt::ScreenOrientation)), - this, SIGNAL(orientationChanged())); - connect(screen, SIGNAL(primaryOrientationChanged(Qt::ScreenOrientation)), - this, SIGNAL(primaryOrientationChanged())); - connect(screen, SIGNAL(virtualGeometryChanged(QRect)), - this, SIGNAL(desktopGeometryChanged())); - connect(screen, SIGNAL(logicalDotsPerInchChanged(qreal)), - this, SIGNAL(logicalPixelDensityChanged())); - connect(screen, SIGNAL(physicalDotsPerInchChanged(qreal)), - this, SIGNAL(pixelDensityChanged())); } } diff --git a/src/quick/items/qquickscreen_p.h b/src/quick/items/qquickscreen_p.h index 06d9a06070..06efb3ab45 100644 --- a/src/quick/items/qquickscreen_p.h +++ b/src/quick/items/qquickscreen_p.h @@ -63,10 +63,10 @@ class QQuickItem; class QQuickWindow; class QScreen; -class Q_AUTOTEST_EXPORT QQuickScreenAttached : public QObject + +class Q_AUTOTEST_EXPORT QQuickScreenInfo : public QObject { Q_OBJECT - Q_PROPERTY(QString name READ name NOTIFY nameChanged) Q_PROPERTY(int width READ width NOTIFY widthChanged) Q_PROPERTY(int height READ height NOTIFY heightChanged) @@ -79,11 +79,12 @@ class Q_AUTOTEST_EXPORT QQuickScreenAttached : public QObject Q_PROPERTY(Qt::ScreenOrientation primaryOrientation READ primaryOrientation NOTIFY primaryOrientationChanged) // TODO Qt 6 Remove this orientation -> incomplete device orientation -> better use OrientationSensor Q_PROPERTY(Qt::ScreenOrientation orientation READ orientation NOTIFY orientationChanged) - Q_PROPERTY(Qt::ScreenOrientations orientationUpdateMask READ orientationUpdateMask - WRITE setOrientationUpdateMask NOTIFY orientationUpdateMaskChanged) + + Q_PROPERTY(int virtualX READ virtualX NOTIFY virtualXChanged REVISION 1) + Q_PROPERTY(int virtualY READ virtualY NOTIFY virtualYChanged REVISION 1) public: - QQuickScreenAttached(QObject* attachee); + QQuickScreenInfo(QObject *parent = nullptr); QString name() const; int width() const; @@ -95,13 +96,11 @@ public: qreal devicePixelRatio() const; Qt::ScreenOrientation primaryOrientation() const; Qt::ScreenOrientation orientation() const; - Qt::ScreenOrientations orientationUpdateMask() const; - void setOrientationUpdateMask(Qt::ScreenOrientations mask); + int virtualX() const; + int virtualY() const; - //Treats int as Qt::ScreenOrientation, due to QTBUG-20639 - Q_INVOKABLE int angleBetween(int a, int b); - - void windowChanged(QQuickWindow*); + void setWrappedScreen(QScreen *screen); + QScreen *wrappedScreen() const; Q_SIGNALS: void nameChanged(); @@ -113,13 +112,37 @@ Q_SIGNALS: void devicePixelRatioChanged(); void primaryOrientationChanged(); void orientationChanged(); + Q_REVISION(1) void virtualXChanged(); + Q_REVISION(1) void virtualYChanged(); + +protected: + QPointer m_screen; +}; + +class Q_AUTOTEST_EXPORT QQuickScreenAttached : public QQuickScreenInfo +{ + Q_OBJECT + Q_PROPERTY(Qt::ScreenOrientations orientationUpdateMask READ orientationUpdateMask + WRITE setOrientationUpdateMask NOTIFY orientationUpdateMaskChanged) + +public: + QQuickScreenAttached(QObject* attachee); + + Qt::ScreenOrientations orientationUpdateMask() const; + void setOrientationUpdateMask(Qt::ScreenOrientations mask); + + //Treats int as Qt::ScreenOrientation, due to QTBUG-20639 + Q_INVOKABLE int angleBetween(int a, int b); + + void windowChanged(QQuickWindow*); + +Q_SIGNALS: void orientationUpdateMaskChanged(); protected Q_SLOTS: void screenChanged(QScreen*); private: - QPointer m_screen; QQuickWindow* m_window; QQuickItem* m_attachee; Qt::ScreenOrientations m_updateMask; @@ -136,5 +159,6 @@ public: QT_END_NAMESPACE QML_DECLARE_TYPEINFO(QQuickScreen, QML_HAS_ATTACHED_PROPERTIES) +QML_DECLARE_TYPE(QQuickScreenInfo) #endif diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 45341643be..dc0bd51a9a 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -4145,6 +4145,28 @@ void QQuickWindow::resetOpenGLState() \since 5.1 */ +/*! + \qmlproperty variant Window::targetScreen + + Specifies the screen the window should be placed on. Equivalent to + QWindow::setScreen(). + + The value must be an element from the Qt.application.screens array. + + By default the value is null which leads to using the primary screen. + + \note To ensure that the window is associated with the desired screen right + upon the underlying native window's initial creation, make sure this + property is set as early as possible and that the setting of its value is + not deferred. This can be particularly important on embedded platforms + without a windowing system, where only one window per screen is allowed at a + time. + + \since 5.9 + + \sa QWindow::setScreen(), QScreen, Qt.application + */ + /*! \qmlproperty Item Window::activeFocusItem \since 5.1 diff --git a/src/quick/items/qquickwindowmodule.cpp b/src/quick/items/qquickwindowmodule.cpp index c624d162a9..8ab2cee96f 100644 --- a/src/quick/items/qquickwindowmodule.cpp +++ b/src/quick/items/qquickwindowmodule.cpp @@ -58,6 +58,7 @@ public: : complete(false) , visible(false) , visibility(QQuickWindow::AutomaticVisibility) + , targetScreen(nullptr) { } @@ -65,6 +66,7 @@ public: bool visible; QQuickWindow::Visibility visibility; QV4::PersistentValue rootItemMarker; + QObject *targetScreen; }; QQuickWindowQmlImpl::QQuickWindowQmlImpl(QWindow *parent) @@ -170,6 +172,26 @@ void QQuickWindowQmlImpl::setWindowVisibility() } } +QObject *QQuickWindowQmlImpl::targetScreen() const +{ + Q_D(const QQuickWindowQmlImpl); + return d->targetScreen; +} + +void QQuickWindowQmlImpl::setTargetScreen(QObject *screen) +{ + Q_D(QQuickWindowQmlImpl); + if (d->targetScreen != screen) { + d->targetScreen = screen; + emit targetScreenChanged(); + QQuickScreenInfo *screenWrapper = qobject_cast(screen); + if (screenWrapper) + setScreen(screenWrapper->wrappedScreen()); + else + setScreen(nullptr); + } +} + void QQuickWindowModule::defineModule() { const char uri[] = "QtQuick.Window"; @@ -181,7 +203,10 @@ void QQuickWindowModule::defineModule() qmlRegisterRevision(uri, 2, 2); qmlRegisterType(uri, 2, 1, "Window"); qmlRegisterType(uri, 2, 2, "Window"); + qmlRegisterType(uri, 2, 3, "Window"); qmlRegisterUncreatableType(uri, 2, 0, "Screen", QStringLiteral("Screen can only be used via the attached property.")); + qmlRegisterUncreatableType(uri, 2, 3, "Screen", QStringLiteral("Screen can only be used via the attached property.")); + qmlRegisterUncreatableType(uri, 2, 3, "ScreenInfo", QStringLiteral("ScreenInfo can only be used via the attached property.")); } QT_END_NAMESPACE diff --git a/src/quick/items/qquickwindowmodule_p.h b/src/quick/items/qquickwindowmodule_p.h index 8a6bbac412..7ca29880ea 100644 --- a/src/quick/items/qquickwindowmodule_p.h +++ b/src/quick/items/qquickwindowmodule_p.h @@ -67,6 +67,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickWindowQmlImpl : public QQuickWindow, public Q Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged) Q_PROPERTY(Visibility visibility READ visibility WRITE setVisibility NOTIFY visibilityChanged) + Q_PROPERTY(QObject *targetScreen READ targetScreen WRITE setTargetScreen NOTIFY targetScreenChanged REVISION 2) public: QQuickWindowQmlImpl(QWindow *parent = Q_NULLPTR); @@ -74,11 +75,15 @@ public: void setVisible(bool visible); void setVisibility(Visibility visibility); + QObject *targetScreen() const; + void setTargetScreen(QObject *screen); + static QQuickWindowAttached *qmlAttachedProperties(QObject *object); Q_SIGNALS: void visibleChanged(bool arg); void visibilityChanged(QWindow::Visibility visibility); + Q_REVISION(2) void targetScreenChanged(); protected: void classBegin() Q_DECL_OVERRIDE; diff --git a/src/quick/util/qquickapplication.cpp b/src/quick/util/qquickapplication.cpp index 5c26b23ff7..ca4a5bfb56 100644 --- a/src/quick/util/qquickapplication.cpp +++ b/src/quick/util/qquickapplication.cpp @@ -38,7 +38,7 @@ ****************************************************************************/ #include "qquickapplication_p.h" - +#include #include #include #include @@ -63,6 +63,10 @@ QQuickApplication::QQuickApplication(QObject *parent) this, SIGNAL(stateChanged(Qt::ApplicationState))); connect(qApp, SIGNAL(applicationStateChanged(Qt::ApplicationState)), this, SIGNAL(activeChanged())); + + connect(qApp, &QGuiApplication::screenAdded, this, &QQuickApplication::updateScreens); + connect(qApp, &QGuiApplication::screenRemoved, this, &QQuickApplication::updateScreens); + updateScreens(); } } @@ -95,4 +99,32 @@ QFont QQuickApplication::font() const return QGuiApplication::font(); } +int screens_count(QQmlListProperty *prop) +{ + return static_cast *>(prop->data)->count(); +} + +QQuickScreenInfo *screens_at(QQmlListProperty *prop, int idx) +{ + return static_cast *>(prop->data)->at(idx); +} + +QQmlListProperty QQuickApplication::screens() +{ + return QQmlListProperty(this, + const_cast *>(&m_screens), &screens_count, &screens_at); +} + +void QQuickApplication::updateScreens() +{ + const QList screenList = QGuiApplication::screens(); + m_screens.resize(screenList.count()); + for (int i = 0; i < screenList.count(); ++i) { + if (!m_screens[i]) + m_screens[i] = new QQuickScreenInfo(this); + m_screens[i]->setWrappedScreen(screenList[i]); + } + emit screensChanged(); +} + QT_END_NAMESPACE diff --git a/src/quick/util/qquickapplication_p.h b/src/quick/util/qquickapplication_p.h index 091cb094ed..70381553b4 100644 --- a/src/quick/util/qquickapplication_p.h +++ b/src/quick/util/qquickapplication_p.h @@ -56,10 +56,10 @@ #include #include #include +#include "../items/qquickscreen_p.h" QT_BEGIN_NAMESPACE - class Q_AUTOTEST_EXPORT QQuickApplication : public QQmlApplication { Q_OBJECT @@ -68,6 +68,7 @@ class Q_AUTOTEST_EXPORT QQuickApplication : public QQmlApplication Q_PROPERTY(bool supportsMultipleWindows READ supportsMultipleWindows CONSTANT) Q_PROPERTY(Qt::ApplicationState state READ state NOTIFY stateChanged) Q_PROPERTY(QFont font READ font CONSTANT) + Q_PROPERTY(QQmlListProperty screens READ screens NOTIFY screensChanged) public: explicit QQuickApplication(QObject *parent = 0); @@ -77,14 +78,20 @@ public: bool supportsMultipleWindows() const; Qt::ApplicationState state() const; QFont font() const; + QQmlListProperty screens(); Q_SIGNALS: void activeChanged(); void layoutDirectionChanged(); void stateChanged(Qt::ApplicationState state); + void screensChanged(); + +private Q_SLOTS: + void updateScreens(); private: Q_DISABLE_COPY(QQuickApplication) + QVector m_screens; }; QT_END_NAMESPACE diff --git a/tests/auto/quick/qquickscreen/data/screen.qml b/tests/auto/quick/qquickscreen/data/screen.qml index c246b3cd83..cf60d0ae40 100644 --- a/tests/auto/quick/qquickscreen/data/screen.qml +++ b/tests/auto/quick/qquickscreen/data/screen.qml @@ -1,5 +1,5 @@ import QtQuick 2.0 -import QtQuick.Window 2.0 as Window +import QtQuick.Window 2.3 as Window Item { width: 100 @@ -10,6 +10,18 @@ Item { property int priOrientation: Window.Screen.primaryOrientation property int updateMask: Window.Screen.orientationUpdateMask property real devicePixelRatio: Window.Screen.devicePixelRatio + property int vx: Window.Screen.virtualX + property int vy: Window.Screen.virtualY Window.Screen.orientationUpdateMask: Qt.LandscapeOrientation | Qt.InvertedLandscapeOrientation + + property int screenCount: Qt.application.screens.length + + property variant allScreens + Component.onCompleted: { + allScreens = []; + var s = Qt.application.screens; + for (var i = 0; i < s.length; ++i) + allScreens.push(s[i]); + } } diff --git a/tests/auto/quick/qquickscreen/tst_qquickscreen.cpp b/tests/auto/quick/qquickscreen/tst_qquickscreen.cpp index 92afdf6864..26b687a4a6 100644 --- a/tests/auto/quick/qquickscreen/tst_qquickscreen.cpp +++ b/tests/auto/quick/qquickscreen/tst_qquickscreen.cpp @@ -33,13 +33,15 @@ #include #include #include "../../shared/util.h" - +#include +#include class tst_qquickscreen : public QQmlDataTest { Q_OBJECT private slots: void basicProperties(); void screenOnStartup(); + void fullScreenList(); }; void tst_qquickscreen::basicProperties() @@ -62,6 +64,10 @@ void tst_qquickscreen::basicProperties() QCOMPARE(int(screen->orientationUpdateMask()), root->property("updateMask").toInt()); QCOMPARE(screen->devicePixelRatio(), root->property("devicePixelRatio").toReal()); QVERIFY(screen->devicePixelRatio() >= 1.0); + QCOMPARE(screen->geometry().x(), root->property("vx").toInt()); + QCOMPARE(screen->geometry().y(), root->property("vy").toInt()); + + QVERIFY(root->property("screenCount").toInt() == QGuiApplication::screens().count()); } void tst_qquickscreen::screenOnStartup() @@ -83,6 +89,38 @@ void tst_qquickscreen::screenOnStartup() QCOMPARE(int(screen->orientationUpdateMask()), root->property("updateMask").toInt()); QCOMPARE(screen->devicePixelRatio(), root->property("devicePixelRatio").toReal()); QVERIFY(screen->devicePixelRatio() >= 1.0); + QCOMPARE(screen->geometry().x(), root->property("vx").toInt()); + QCOMPARE(screen->geometry().y(), root->property("vy").toInt()); +} + +void tst_qquickscreen::fullScreenList() +{ + QQuickView view; + view.setSource(testFileUrl("screen.qml")); + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + QQuickItem* root = view.rootObject(); + QVERIFY(root); + + QJSValue screensArray = root->property("allScreens").value(); + QVERIFY(screensArray.isArray()); + int length = screensArray.property("length").toInt(); + const QList screenList = QGuiApplication::screens(); + QVERIFY(length == screenList.count()); + + for (int i = 0; i < length; ++i) { + QQuickScreenInfo *info = qobject_cast(screensArray.property(i).toQObject()); + QVERIFY(info != nullptr); + QCOMPARE(screenList[i]->name(), info->name()); + QCOMPARE(screenList[i]->size().width(), info->width()); + QCOMPARE(screenList[i]->size().height(), info->height()); + QCOMPARE(screenList[i]->availableVirtualGeometry().width(), info->desktopAvailableWidth()); + QCOMPARE(screenList[i]->availableVirtualGeometry().height(), info->desktopAvailableHeight()); + QCOMPARE(screenList[i]->devicePixelRatio(), info->devicePixelRatio()); + QCOMPARE(screenList[i]->geometry().x(), info->virtualX()); + QCOMPARE(screenList[i]->geometry().y(), info->virtualY()); + } } QTEST_MAIN(tst_qquickscreen) diff --git a/tests/auto/quick/qquickwindow/data/windowWithScreen.qml b/tests/auto/quick/qquickwindow/data/windowWithScreen.qml new file mode 100644 index 0000000000..fdc0be3388 --- /dev/null +++ b/tests/auto/quick/qquickwindow/data/windowWithScreen.qml @@ -0,0 +1,10 @@ +import QtQuick 2.0 +import QtQuick.Window 2.3 as Window + +Window.Window { + color: "#00FF00" + targetScreen: Qt.application.screens[0] + Item { + objectName: "item" + } +} diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp index acccac8eca..bfffa6f7f5 100644 --- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp +++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp @@ -311,6 +311,7 @@ private slots: void clearWindow(); void qmlCreation(); + void qmlCreationWithScreen(); void clearColor(); void defaultState(); @@ -1122,6 +1123,24 @@ void tst_qquickwindow::qmlCreation() QCOMPARE(item->window(), window); } +void tst_qquickwindow::qmlCreationWithScreen() +{ + QQmlEngine engine; + QQmlComponent component(&engine); + component.loadUrl(testFileUrl("windowWithScreen.qml")); + QObject *created = component.create(); + QScopedPointer cleanup(created); + QVERIFY(created); + + QQuickWindow *window = qobject_cast(created); + QVERIFY(window); + QCOMPARE(window->color(), QColor(Qt::green)); + + QQuickItem *item = window->findChild("item"); + QVERIFY(item); + QCOMPARE(item->window(), window); +} + void tst_qquickwindow::clearColor() { //::grab examines rendering to make sure it works visually -- cgit v1.2.3