Revision control

++ // Copyright (C) 2021 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
++
++ 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 }
++ }
++ }
++ }
++ }