diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/manual/qt-shell/CMakeLists.txt | 43 | ||||
-rw-r--r-- | tests/manual/qt-shell/images/background.jpg | bin | 0 -> 30730 bytes | |||
-rw-r--r-- | tests/manual/qt-shell/main.cpp | 68 | ||||
-rw-r--r-- | tests/manual/qt-shell/qml/Chrome.qml | 505 | ||||
-rw-r--r-- | tests/manual/qt-shell/qml/CompositorScreen.qml | 191 | ||||
-rw-r--r-- | tests/manual/qt-shell/qml/HandleHandler.qml | 133 | ||||
-rw-r--r-- | tests/manual/qt-shell/qml/Keyboard.qml | 59 | ||||
-rw-r--r-- | tests/manual/qt-shell/qml/main.qml | 69 | ||||
-rw-r--r-- | tests/manual/qt-shell/qt-shell.pro | 13 | ||||
-rw-r--r-- | tests/manual/qt-shell/qt-shell.qrc | 10 |
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 Binary files differnew file mode 100644 index 00000000..445567fb --- /dev/null +++ b/tests/manual/qt-shell/images/background.jpg 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> |