summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/manual/qt-shell/CMakeLists.txt43
-rw-r--r--tests/manual/qt-shell/images/background.jpgbin0 -> 30730 bytes
-rw-r--r--tests/manual/qt-shell/main.cpp68
-rw-r--r--tests/manual/qt-shell/qml/Chrome.qml505
-rw-r--r--tests/manual/qt-shell/qml/CompositorScreen.qml191
-rw-r--r--tests/manual/qt-shell/qml/HandleHandler.qml133
-rw-r--r--tests/manual/qt-shell/qml/Keyboard.qml59
-rw-r--r--tests/manual/qt-shell/qml/main.qml69
-rw-r--r--tests/manual/qt-shell/qt-shell.pro13
-rw-r--r--tests/manual/qt-shell/qt-shell.qrc10
10 files changed, 1091 insertions, 0 deletions
diff --git a/tests/manual/qt-shell/CMakeLists.txt b/tests/manual/qt-shell/CMakeLists.txt
new file mode 100644
index 00000000..5a38aae4
--- /dev/null
+++ b/tests/manual/qt-shell/CMakeLists.txt
@@ -0,0 +1,43 @@
+cmake_minimum_required(VERSION 3.14)
+project(qt-shell LANGUAGES CXX)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_AUTORCC ON)
+set(CMAKE_AUTOUIC ON)
+
+find_package(Qt6 COMPONENTS Core)
+find_package(Qt6 COMPONENTS Gui)
+find_package(Qt6 COMPONENTS Qml)
+
+qt_add_executable(qt-shell
+ main.cpp
+)
+set_target_properties(qt-shell PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+target_link_libraries(qt-shell PUBLIC
+ Qt::Core
+ Qt::Gui
+ Qt::Qml
+)
+
+
+# Resources:
+set(qt-shell_resource_files
+ "images/background.jpg"
+ "qml/Chrome.qml"
+ "qml/HandleHandler.qml"
+ "qml/CompositorScreen.qml"
+ "qml/Keyboard.qml"
+ "qml/main.qml"
+)
+
+qt6_add_resources(qt-shell "qt-shell"
+ PREFIX
+ "/"
+ FILES
+ ${qt-shell_resource_files}
+)
diff --git a/tests/manual/qt-shell/images/background.jpg b/tests/manual/qt-shell/images/background.jpg
new file mode 100644
index 00000000..445567fb
--- /dev/null
+++ b/tests/manual/qt-shell/images/background.jpg
Binary files differ
diff --git a/tests/manual/qt-shell/main.cpp b/tests/manual/qt-shell/main.cpp
new file mode 100644
index 00000000..0b2750d8
--- /dev/null
+++ b/tests/manual/qt-shell/main.cpp
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, 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$
+**
+****************************************************************************/
+
+#include <QtCore/QUrl>
+#include <QtCore/QDebug>
+
+#include <QtGui/QGuiApplication>
+
+#include <QtQml/QQmlApplicationEngine>
+
+int main(int argc, char *argv[])
+{
+ // ShareOpenGLContexts is needed for using the threaded renderer
+ // on Nvidia EGLStreams
+ QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts, true);
+ QGuiApplication app(argc, argv);
+
+ QQmlApplicationEngine appEngine(QUrl("qrc:///qml/main.qml"));
+
+ return app.exec();
+}
diff --git a/tests/manual/qt-shell/qml/Chrome.qml b/tests/manual/qt-shell/qml/Chrome.qml
new file mode 100644
index 00000000..433fc5a5
--- /dev/null
+++ b/tests/manual/qt-shell/qml/Chrome.qml
@@ -0,0 +1,505 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, 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
+import QtQuick.Controls
+import QtQuick.Layouts
+import QtWayland.Compositor
+
+Item {
+ id: chrome
+
+ property bool positionSet: false
+
+ x: shellSurface.windowGeometry.x - leftResizeHandle.width
+ y: shellSurface.windowGeometry.y - topResizeHandle.height - titleBar.height
+ width: shellSurface.windowGeometry.width + leftResizeHandle.width + rightResizeHandle.width
+ height: shellSurface.windowGeometry.height + topResizeHandle.height + titleBar.height + bottomResizeHandle.height
+
+ property rect oldGeometry: Qt.rect(0, 0, 100, 100)
+ property bool isChild: parent.shellSurface !== undefined
+ property alias shellSurface: shellSurfaceItem.shellSurface
+
+ property int windowState: Qt.WindowNoState
+
+ signal destroyAnimationFinished
+ signal activated
+ signal deactivated
+
+ property int windowFlags: shellSurface.windowFlags !== Qt.Window
+ ? shellSurface.windowFlags
+ : defaultFlags
+ onDecorationsShowingChanged:{
+ shellSurfaceItem.updateFrameMargins()
+ }
+
+ Component.onCompleted: {
+ shellSurface.active = true
+ }
+
+ property int defaultFlags: (Qt.Window
+ | Qt.WindowMaximizeButtonHint
+ | Qt.WindowMinimizeButtonHint
+ | Qt.WindowCloseButtonHint)
+
+ property bool frameless: (chrome.windowFlags & Qt.FramelessWindowHint) != 0
+ || (chrome.windowState & Qt.WindowFullScreen) != 0
+ || ((chrome.windowFlags & Qt.Popup) == Qt.Popup
+ && (chrome.windowFlags & Qt.Tool) != Qt.Tool)
+
+ property bool decorationsShowing: (chrome.windowFlags & Qt.Window) != 0 && !frameless
+
+ transform: [
+ Scale {
+ id: scaleTransform
+ origin.x: chrome.width / 2
+ origin.y: chrome.height / 2
+ }
+ ]
+
+ Rectangle {
+ id: leftResizeHandle
+ color: "gray"
+ width: visible ? 5 : 0
+ anchors.left: parent.left
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+ visible: decorationsShowing
+
+ HandleHandler {
+ enabled: (chrome.windowState & (Qt.WindowMinimized|Qt.WindowMaximized)) == 0
+ flags: westBound
+ }
+ }
+
+ Rectangle {
+ id: rightResizeHandle
+ color: "gray"
+ width: visible ? 5 : 0
+ anchors.right: parent.right
+ anchors.top: parent.top
+ anchors.bottom: parent.bottom
+ visible: decorationsShowing
+
+ HandleHandler {
+ enabled: (chrome.windowState & (Qt.WindowMinimized|Qt.WindowMaximized)) == 0
+ flags: eastBound
+ }
+ }
+
+ Rectangle {
+ id: topResizeHandle
+ color: "gray"
+ height: visible ? 5 : 0
+ anchors.left: parent.left
+ anchors.top: parent.top
+ anchors.right: parent.right
+ visible: decorationsShowing
+
+ HandleHandler {
+ enabled: (chrome.windowState & (Qt.WindowMinimized|Qt.WindowMaximized)) == 0
+ flags: northBound
+ }
+ }
+
+ Rectangle {
+ id: bottomResizeHandle
+ color: "gray"
+ height: visible ? 5 : 0
+ anchors.left: parent.left
+ anchors.bottom: parent.bottom
+ anchors.right: parent.right
+ visible: decorationsShowing
+
+ HandleHandler {
+ enabled: (chrome.windowState & (Qt.WindowMinimized|Qt.WindowMaximized)) == 0
+ flags: southBound
+ }
+ }
+
+ Rectangle {
+ id: topLeftResizeHandle
+ color: "gray"
+ height: 5
+ width: 5
+ anchors.left: parent.left
+ anchors.top: parent.top
+ visible: decorationsShowing
+
+ HandleHandler {
+ enabled: (chrome.windowState & (Qt.WindowMinimized|Qt.WindowMaximized)) == 0
+ flags: westBound | northBound
+ }
+ }
+
+ Rectangle {
+ id: topRightResizeHandle
+ color: "gray"
+ height: 5
+ width: 5
+ anchors.right: parent.right
+ anchors.top: parent.top
+ visible: decorationsShowing
+
+ HandleHandler {
+ enabled: (chrome.windowState & (Qt.WindowMinimized|Qt.WindowMaximized)) == 0
+ flags: eastBound | northBound
+ }
+ }
+
+ Rectangle {
+ id: bottomLeftResizeHandle
+ color: "gray"
+ height: 5
+ width: 5
+ anchors.left: parent.left
+ anchors.bottom: parent.bottom
+ visible: decorationsShowing
+
+ HandleHandler {
+ enabled: (chrome.windowState & (Qt.WindowMinimized|Qt.WindowMaximized)) == 0
+ flags: westBound | southBound
+ }
+ }
+
+ Rectangle {
+ id: bottomRightResizeHandle
+ color: "gray"
+ height: 5
+ width: 5
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ visible: decorationsShowing
+
+ HandleHandler {
+ enabled: (chrome.windowState & (Qt.WindowMinimized|Qt.WindowMaximized)) == 0
+ flags: eastBound | southBound
+ }
+ }
+
+ function constrainPoint(mousePos) {
+ var x0 = usableArea.x
+ var y0 = usableArea.y
+ var x1 = x0 + usableArea.width
+ var y1 = y0 + usableArea.height
+ return Qt.point(Math.min(Math.max(x0,mousePos.x), x1),
+ Math.min(Math.max(y0,mousePos.y), y1))
+ }
+
+ function maxContentRect() {
+ var x0 = usableArea.x + leftResizeHandle.width
+ var x1 = usableArea.x + usableArea.width - rightResizeHandle.width
+ var y0 = usableArea.y + topResizeHandle.height + titleBar.height
+ var y1 = usableArea.y + usableArea.height - bottomResizeHandle.height
+ return Qt.rect(x0, y0, x1 - x0, y1 - y0)
+ }
+
+ function randomPos(windowSize, screenSize) {
+ var res = (windowSize >= screenSize) ? 0 : Math.floor(Math.random() * (screenSize - windowSize))
+ return res
+ }
+
+ function activate()
+ {
+ shellSurface.active = true
+ shellSurfaceItem.raise()
+ activated()
+ }
+
+ function deactivate()
+ {
+ shellSurface.active = true
+ deactivated()
+ }
+
+ function setWindowState(nextState) {
+ var currentState = chrome.windowState
+ if (currentState === nextState)
+ return
+
+ console.log("setWindowState", nextState.toString(16))
+
+ if ((currentState & (Qt.WindowMinimized | Qt.WindowMaximized | Qt.WindowFullScreen)) == 0)
+ chrome.oldGeometry = chrome.shellSurface.windowGeometry
+
+ chrome.windowState = nextState
+
+ if ((nextState & Qt.WindowMinimized) != 0) {
+ console.log("MINIMIZE")
+ chrome.shellSurface.requestWindowGeometry(nextState, Qt.rect(0, 0, 1, 1))
+ shellSurfaceItem.visible = false
+ } else if ((nextState & Qt.WindowFullScreen) != 0) {
+ console.log("FULLSCREENIZE")
+ chrome.shellSurface.requestWindowGeometry(nextState, Qt.rect(0, 0, output.window.width, output.window.height))
+ shellSurfaceItem.visible = true
+ } else if ((nextState & Qt.WindowMaximized) != 0) {
+ console.log("MAXIMIZE")
+ chrome.shellSurface.requestWindowGeometry(nextState, maxContentRect())
+ shellSurfaceItem.visible = true
+ } else {
+ console.log("NORMALIZE", chrome.oldGeometry)
+ chrome.shellSurface.requestWindowGeometry(nextState, chrome.oldGeometry)
+ shellSurfaceItem.visible = true
+ }
+ }
+
+ Rectangle {
+ id: titleBar
+ anchors.top: topResizeHandle.bottom
+ anchors.left: leftResizeHandle.right
+ anchors.right: rightResizeHandle.left
+ height: visible ? xButton.height + 10 : 0
+ color: shellSurface.active ? "cornflowerblue" : "lightgray"
+ visible: !frameless
+
+ Text {
+ anchors.left: parent.left
+ anchors.right: rowLayout.left
+ anchors.verticalCenter: parent.verticalCenter
+
+ font.pixelSize: xButton.height
+ text: shellSurface.windowTitle
+ fontSizeMode: Text.Fit
+ }
+
+ RowLayout {
+ id: rowLayout
+ anchors.right: parent.right
+ anchors.rightMargin: 5
+
+ ToolButton {
+ text: "-"
+ Layout.margins: 5
+ visible: (chrome.windowFlags & Qt.WindowMinimizeButtonHint) != 0
+ onClicked: {
+ var newState
+ if ((shellSurface.windowState & Qt.WindowMinimized) != 0)
+ newState = chrome.windowState & ~Qt.WindowMinimized
+ else
+ newState = chrome.windowState | Qt.WindowMinimized
+
+ if ((newState & Qt.WindowMaximized) != 0)
+ newState &= ~Qt.WindowMaximized
+
+ setWindowState(newState)
+ }
+ }
+
+ ToolButton {
+ text: "+"
+ Layout.margins: 5
+ visible: (chrome.windowFlags & Qt.WindowMaximizeButtonHint) != 0
+ onClicked: {
+ var newState
+ if ((shellSurface.windowState & Qt.WindowMaximized) != 0)
+ newState = shellSurface.windowState & ~Qt.WindowMaximized
+ else
+ newState = shellSurface.windowState | Qt.WindowMaximized
+
+ if ((newState & Qt.WindowMinimized) != 0)
+ newState &= ~Qt.WindowMinimized
+
+ setWindowState(newState)
+ }
+ }
+
+ ToolButton {
+ id: xButton
+ text: "X"
+ Layout.margins: 5
+ visible: (chrome.windowFlags & Qt.WindowCloseButtonHint) != 0
+ onClicked: shellSurface.sendClose()
+ }
+ }
+
+ DragHandler {
+ target: null
+ property real xOffset: -1.0
+ property real yOffset: -1.0
+ property bool started: false
+ enabled: (shellSurface.windowState & (Qt.WindowMinimized|Qt.WindowMaximized)) == 0
+
+ onGrabChanged: {
+ started = false
+ activate()
+ }
+
+ onCentroidChanged: {
+ if (!active)
+ return
+
+ if (!started) {
+ xOffset = shellSurface.windowPosition.x - centroid.scenePressPosition.x
+ yOffset = shellSurface.windowPosition.y - centroid.scenePressPosition.y
+ started = true
+ chrome.positionAutomatic = false
+ }
+
+ var pos = chrome.constrainPoint(centroid.scenePosition)
+ shellSurface.windowPosition = Qt.point(pos.x + xOffset, pos.y + yOffset)
+ }
+ }
+ }
+
+ ShellSurfaceItem {
+ id: shellSurfaceItem
+ anchors.top: titleBar.bottom
+ anchors.bottom: bottomResizeHandle.top
+ anchors.left: leftResizeHandle.right
+ anchors.right: rightResizeHandle.left
+
+ moveItem: chrome
+
+ staysOnBottom: shellSurface.windowFlags & Qt.WindowStaysOnBottomHint
+ staysOnTop: !staysOnBottom && shellSurface.windowFlags & Qt.WindowStaysOnTopHint
+ function updateFrameMargins()
+ {
+ shellSurface.frameMarginLeft = (decorationsShowing ? leftResizeHandle.width : 0)
+ shellSurface.frameMarginRight = (decorationsShowing ? rightResizeHandle.width : 0)
+ shellSurface.frameMarginTop = (decorationsShowing ? topResizeHandle.height : 0)
+ + (!frameless ? titleBar.height : 0)
+ shellSurface.frameMarginBottom = (decorationsShowing ? bottomResizeHandle.height : 0)
+ }
+
+ Component.onCompleted: {
+ updateFrameMargins()
+ }
+
+ onSurfaceDestroyed: {
+ bufferLocked = true;
+ destroyAnimation.start();
+ }
+
+ Connections {
+ target: shellSurface
+ function onWindowFlagsChanged() {
+ console.log("FLAGS", shellSurface.windowFlags.toString(16))
+ shellSurfaceItem.updateFrameMargins()
+ }
+
+ function onWindowStateChanged() {
+ setWindowState(shellSurface.windowState)
+ }
+
+ function onActiveChanged() {
+ if (shellSurface.active) {
+ shellSurfaceItem.raise()
+ activated()
+ } else {
+ deactivated()
+ }
+ }
+
+ function onStartResize() {
+ console.log("START SYSTEM RESIZE")
+ }
+ function onStartMove() {
+ console.log("START SYSTEM MOVE")
+ }
+
+ function onRaiseRequested() {
+ console.log("RAISE")
+ shellSurfaceItem.raise()
+ }
+ function onLowerRequested() {
+ console.log("LOWER")
+ shellSurfaceItem.lower()
+ }
+
+ function onWindowGeometryChanged() {
+ console.log("GEOM CHANGE", shellSurface.windowGeometry)
+ }
+ }
+
+ Connections {
+ target: shellSurface.surface
+ function onHasContentChanged() {
+ if (!chrome.positionSet) {
+ var rect = shellSurface.windowGeometry
+ var w = rect.width
+ var h = rect.height
+
+ var space = maxContentRect()
+
+ var randomize = shellSurface.positionAutomatic
+ var xpos = randomize ? randomPos(w, space.width) + space.x : Math.max(rect.x, space.x)
+ var ypos = randomize ? randomPos(h, space.height) + space.y : Math.max(rect.y, space.y)
+ shellSurface.windowPosition = Qt.point(xpos, ypos)
+ }
+ chrome.positionSet = true
+ }
+ }
+
+ SequentialAnimation {
+ id: destroyAnimation
+
+ ParallelAnimation {
+ NumberAnimation { target: scaleTransform; property: "yScale"; to: 2/height; duration: 150 }
+ NumberAnimation { target: scaleTransform; property: "xScale"; to: 0.4; duration: 150 }
+ NumberAnimation { target: chrome; property: "opacity"; to: chrome.isChild ? 0 : 1; duration: 150 }
+ }
+ NumberAnimation { target: scaleTransform; property: "xScale"; to: 0; duration: 150 }
+ ScriptAction { script: chrome.destroyAnimationFinished() }
+ }
+
+ SequentialAnimation {
+ id: receivedFocusAnimation
+
+ ParallelAnimation {
+ NumberAnimation { target: scaleTransform; property: "yScale"; to: 1.02; duration: 100; easing.type: Easing.OutQuad }
+ NumberAnimation { target: scaleTransform; property: "xScale"; to: 1.02; duration: 100; easing.type: Easing.OutQuad }
+ }
+ ParallelAnimation {
+ NumberAnimation { target: scaleTransform; property: "yScale"; to: 1; duration: 100; easing.type: Easing.InOutQuad }
+ NumberAnimation { target: scaleTransform; property: "xScale"; to: 1; duration: 100; easing.type: Easing.InOutQuad }
+ }
+ }
+ }
+}
diff --git a/tests/manual/qt-shell/qml/CompositorScreen.qml b/tests/manual/qt-shell/qml/CompositorScreen.qml
new file mode 100644
index 00000000..c634e08c
--- /dev/null
+++ b/tests/manual/qt-shell/qml/CompositorScreen.qml
@@ -0,0 +1,191 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, 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
+import QtQuick.Window
+import QtQuick.Controls
+import QtWayland.Compositor
+
+WaylandOutput {
+ id: output
+
+ property ListModel shellSurfaces: ListModel {}
+ property bool isNestedCompositor: Qt.platform.pluginName.startsWith("wayland") || Qt.platform.pluginName === "xcb"
+ property int currentActiveWindow: -1
+
+ function handleShellSurface(shellSurface) {
+ shellSurfaces.append({shellSurface: shellSurface});
+ }
+
+ // During development, it can be useful to start the compositor inside X11 or
+ // another Wayland compositor. In such cases, set sizeFollowsWindow to true to
+ // enable resizing of the compositor window to be forwarded to the Wayland clients
+ // as the output (screen) changing resolution. Consider setting it to false if you
+ // are running the compositor using eglfs, linuxfb or similar QPA backends.
+ sizeFollowsWindow: output.isNestedCompositor
+
+ window: Window {
+ width: 1920
+ height: 1080
+ visible: true
+
+ WaylandMouseTracker {
+ id: mouseTracker
+
+ anchors.fill: parent
+
+ // Set this to false to disable the outer mouse cursor when running nested
+ // compositors. Otherwise you would see two mouse cursors, one for each compositor.
+ windowSystemCursorEnabled: output.isNestedCompositor
+
+ Image {
+ id: background
+
+ anchors.fill: parent
+ fillMode: Image.Tile
+ source: "qrc:/images/background.jpg"
+ smooth: true
+
+ Repeater {
+ id: chromeRepeater
+ model: output.shellSurfaces
+ // Chrome displays a shell surface on the screen (See Chrome.qml)
+ Chrome {
+ shellSurface: modelData
+ onDestroyAnimationFinished:
+ {
+ if (currentActiveWindow > index) {
+ --currentActiveWindow
+ } else if (currentActiveWindow === index) {
+ currentActiveWindow = index - 1
+ if (currentActiveWindow >= 0) {
+ var nextActiveSurface = output.shellSurfaces.get(currentActiveWindow).shellSurface
+ if (nextActiveSurface !== undefined) // More than one surface can get destroyed at the same time
+ nextActiveSurface.active = true
+ }
+ }
+ output.shellSurfaces.remove(index)
+ }
+
+ onDeactivated: {
+ if (index === currentActiveWindow)
+ currentActiveWindow = -1
+ }
+
+ onActivated: {
+ if (index !== currentActiveWindow && currentActiveWindow >= 0) {
+ // This may already have been destroyed
+ if (output.shellSurfaces.get(currentActiveWindow).shellSurface !== undefined)
+ output.shellSurfaces.get(currentActiveWindow).shellSurface.active = false
+ }
+
+ currentActiveWindow = index
+ }
+ }
+ }
+ }
+
+ Rectangle {
+ anchors.fill: taskbar
+ color: "lavenderblush"
+ }
+
+ Row {
+ id: taskbar
+ height: 40
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+
+ Repeater {
+ anchors.fill: parent
+ model: output.shellSurfaces
+
+ ToolButton {
+ anchors.verticalCenter: parent.verticalCenter
+ text: modelData.windowTitle
+ onClicked: {
+ modelData.requestWindowGeometry(modelData.windowState & ~Qt.WindowMinimized,
+ modelData.windowGeometry)
+ chromeRepeater.itemAt(index).activate()
+ }
+ }
+ }
+ }
+
+ Item {
+ id: usableArea
+ anchors.top: parent.top
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.bottom: taskbar.top
+ }
+
+ // Virtual Keyboard
+ Loader {
+ anchors.fill: parent
+ source: "Keyboard.qml"
+ }
+
+ // Draws the mouse cursor for a given Wayland seat
+ WaylandCursorItem {
+ inputEventsEnabled: false
+ x: mouseTracker.mouseX
+ y: mouseTracker.mouseY
+ seat: output.compositor.defaultSeat
+ }
+ }
+
+ Shortcut {
+ sequence: "Ctrl+Alt+Backspace"
+ onActivated: Qt.quit()
+ }
+ }
+}
diff --git a/tests/manual/qt-shell/qml/HandleHandler.qml b/tests/manual/qt-shell/qml/HandleHandler.qml
new file mode 100644
index 00000000..5615fba6
--- /dev/null
+++ b/tests/manual/qt-shell/qml/HandleHandler.qml
@@ -0,0 +1,133 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, 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
+
+DragHandler {
+ target: null
+
+ function selectCursor(f)
+ {
+ switch (f) {
+ case (southBound | eastBound):
+ case (northBound | westBound):
+ return Qt.SizeFDiagCursor
+ case (southBound | westBound):
+ case (northBound | eastBound):
+ return Qt.SizeBDiagCursor
+ case westBound:
+ case eastBound:
+ return Qt.SizeHorCursor
+ default:
+ return Qt.SizeVerCursor
+ }
+ }
+
+ property int flags: WestBound
+ readonly property int westBound: 1
+ readonly property int eastBound: 2
+ readonly property int northBound: 4
+ readonly property int southBound: 8
+
+ cursorShape: selectCursor(flags)
+
+ property rect geom
+ property real startX: -1.0
+ property real startY: -1.0
+ property bool started: false
+ onGrabChanged: {
+ started = false
+ }
+ onCentroidChanged: {
+ if (!active)
+ return
+ if (!started) {
+ geom = shellSurface.windowGeometry
+ startX = centroid.scenePressPosition.x
+ startY = centroid.scenePressPosition.y
+ started = true
+ }
+
+ var pos = chrome.constrainPoint(centroid.scenePosition)
+ var dx = pos.x - startX
+ var dy = pos.y - startY
+
+ var minWidth = Math.max(0, shellSurface.minimumSize.width)
+ var minHeight = Math.max(0, shellSurface.minimumSize.height)
+
+ var maxWidth = shellSurface.maximumSize.width > 0 ? shellSurface.maximumSize.width : Number.MAX_VALUE
+ var maxHeight = shellSurface.maximumSize.height > 0 ? shellSurface.maximumSize.height : Number.MAX_VALUE
+
+ var newLeft = geom.left
+ if (flags & westBound)
+ newLeft = Math.max(geom.right - maxWidth, Math.min(geom.right - minWidth, newLeft + dx));
+
+ var newTop = geom.top
+ if (flags & northBound)
+ newTop = Math.max(geom.bottom - maxHeight, Math.min(geom.bottom - minHeight, newTop + dy));
+
+ var newRight = geom.right
+ if (flags & eastBound)
+ newRight = Math.max(geom.left, newRight + dx);
+
+ var newBottom = geom.bottom
+ if (flags & southBound)
+ newBottom = Math.max(geom.top, newBottom + dy);
+
+ console.log("RESIZE HANDLER", shellSurface.windowGeometry, geom, dy, newTop)
+
+
+ shellSurface.requestWindowGeometry(shellSurface.windowState,
+ Qt.rect(newLeft,
+ newTop,
+ newRight - newLeft,
+ newBottom - newTop))
+ }
+}
diff --git a/tests/manual/qt-shell/qml/Keyboard.qml b/tests/manual/qt-shell/qml/Keyboard.qml
new file mode 100644
index 00000000..12eae9f1
--- /dev/null
+++ b/tests/manual/qt-shell/qml/Keyboard.qml
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, 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
+import QtQuick.VirtualKeyboard
+
+InputPanel {
+ visible: active
+ y: active ? parent.height - height : parent.height
+ anchors.left: parent.left
+ anchors.right: parent.right
+}
diff --git a/tests/manual/qt-shell/qml/main.qml b/tests/manual/qt-shell/qml/main.qml
new file mode 100644
index 00000000..e988ae1b
--- /dev/null
+++ b/tests/manual/qt-shell/qml/main.qml
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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.
+**
+** BSD License Usage
+** Alternatively, 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
+import QtWayland.Compositor
+import QtWayland.Compositor.QtShell
+
+WaylandCompositor {
+ id: waylandCompositor
+
+ CompositorScreen { id: screen; compositor: waylandCompositor }
+
+ // Shell surface extension. Needed to provide a window concept for Wayland clients.
+ // I.e. requests and events for maximization, minimization, resizing, closing etc.
+
+ QtShell {
+ onQtShellSurfaceCreated: screen.handleShellSurface(qtShellSurface)
+ }
+
+ // Extension for Input Method (QT_IM_MODULE) support at compositor-side
+ QtTextInputMethodManager {}
+}
diff --git a/tests/manual/qt-shell/qt-shell.pro b/tests/manual/qt-shell/qt-shell.pro
new file mode 100644
index 00000000..c7e2cd91
--- /dev/null
+++ b/tests/manual/qt-shell/qt-shell.pro
@@ -0,0 +1,13 @@
+QT += gui qml
+
+SOURCES += \
+ main.cpp
+
+OTHER_FILES = \
+ qml/main.qml \
+ qml/CompositorScreen.qml \
+ qml/Chrome.qml \
+ qml/Keyboard.qml \
+ images/background.jpg \
+
+RESOURCES += qt-shell.qrc
diff --git a/tests/manual/qt-shell/qt-shell.qrc b/tests/manual/qt-shell/qt-shell.qrc
new file mode 100644
index 00000000..3120382f
--- /dev/null
+++ b/tests/manual/qt-shell/qt-shell.qrc
@@ -0,0 +1,10 @@
+<RCC>
+ <qresource prefix="/">
+ <file>images/background.jpg</file>
+ <file>qml/main.qml</file>
+ <file>qml/CompositorScreen.qml</file>
+ <file>qml/Chrome.qml</file>
+ <file>qml/Keyboard.qml</file>
+ <file>qml/HandleHandler.qml</file>
+ </qresource>
+</RCC>