Lomiri
Loading...
Searching...
No Matches
DecoratedWindow.qml
1/*
2 * Copyright (C) 2014-2017 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17import QtQuick 2.15
18import Lomiri.Components 1.3
19import QtMir.Application 0.1
20import "Spread/MathUtils.js" as MathUtils
21import Lomiri.ApplicationMenu 0.1
22import Lomiri.Indicators 0.1 as Indicators
23import "../Components/PanelState"
24
25FocusScope {
26 id: root
27
28 // The DecoratedWindow takes requestedWidth/requestedHeight and asks its surface to be resized to that
29 // (minus the window decoration size in case hasDecoration and showDecoration are true)
30 // The surface might not be able to resize to the requested values. It will return its actual size
31 // in implicitWidth/implicitHeight.
32
33 property alias application: applicationWindow.application
34 property alias surface: applicationWindow.surface
35 readonly property alias focusedSurface: applicationWindow.focusedSurface
36 readonly property alias supportsResize: applicationWindow.supportsResize
37 property alias active: decoration.active
38 readonly property alias title: applicationWindow.title
39 property alias maximizeButtonShown: decoration.maximizeButtonShown
40 property alias interactive: applicationWindow.interactive
41 readonly property alias orientationChangesEnabled: applicationWindow.orientationChangesEnabled
42 property alias windowControlButtonsVisible: decoration.windowControlButtonsVisible
43 property alias stage: applicationWindow.stage
44 property PanelState panelState
45
46 // Changing this will actually add/remove a decoration, meaning, requestedHeight will take the decoration into account.
47 property bool hasDecoration: true
48 // This will temporarily show/hide the decoration without actually changing the surface's dimensions
49 property real showDecoration: 1
50 property alias decorationHeight: decoration.height
51 property bool animateDecoration: false
52 property bool showHighlight: false
53 property int highlightSize: units.gu(1)
54 property real shadowOpacity: 0
55 property bool darkening: false
56 property bool lightMode: false
57
58 property real requestedWidth
59 property real requestedHeight
60 property real scaleToPreviewProgress: 0
61 property int scaleToPreviewSize: units.gu(30)
62
63 property alias surfaceOrientationAngle: applicationWindow.surfaceOrientationAngle
64
65 // Height of the decoration that's actually being displayed at this moment. Will match decorationHeight
66 // when the decoration is being fully displayed
67 readonly property real actualDecorationHeight: Math.min(d.visibleDecorationHeight, d.requestedDecorationHeight)
68
69 readonly property bool counterRotate: surfaceOrientationAngle != 0 && surfaceOrientationAngle != 180
70
71 readonly property int minimumWidth: !counterRotate ? applicationWindow.minimumWidth : applicationWindow.minimumHeight
72 readonly property int minimumHeight: actualDecorationHeight + (!counterRotate ? applicationWindow.minimumHeight : applicationWindow.minimumWidth)
73 readonly property int maximumWidth: !counterRotate ? applicationWindow.maximumWidth : applicationWindow.maximumHeight
74 readonly property int maximumHeight: (root.decorationShown && applicationWindow.maximumHeight > 0 ? decoration.height : 0)
75 + (!counterRotate ? applicationWindow.maximumHeight : applicationWindow.maximumWidth)
76 readonly property int widthIncrement: !counterRotate ? applicationWindow.widthIncrement : applicationWindow.heightIncrement
77 readonly property int heightIncrement: !counterRotate ? applicationWindow.heightIncrement : applicationWindow.widthIncrement
78
79 property alias overlayShown: decoration.overlayShown
80 property alias boundsItem: moveHandler.boundsItem
81 readonly property alias dragging: moveHandler.dragging
82
83 readonly property Item clientAreaItem: applicationWindow
84
85 property alias altDragEnabled: altDragHandler.enabled
86
87 property alias clipSurface: applicationWindow.clip
88
89 property Item windowMargins
90
91 signal closeClicked()
92 signal maximizeClicked()
93 signal maximizeHorizontallyClicked()
94 signal maximizeVerticallyClicked()
95 signal minimizeClicked()
96 signal decorationPressed()
97 signal decorationReleased()
98
99 function cancelDrag() {
100 moveHandler.cancelDrag();
101 }
102
103 QtObject {
104 id: d
105 property int requestedDecorationHeight: root.hasDecoration ? decoration.height : 0
106 Behavior on requestedDecorationHeight { enabled: root.animateDecoration; LomiriNumberAnimation { } }
107
108 property int visibleDecorationHeight: root.hasDecoration ? root.showDecoration * decoration.height : 0
109 Behavior on visibleDecorationHeight { enabled: root.animateDecoration; LomiriNumberAnimation { } }
110 }
111
112 StateGroup {
113 states: [
114 State {
115 name: "normal"; when: root.scaleToPreviewProgress <= 0 && root.application.state === ApplicationInfoInterface.Running
116 PropertyChanges {
117 target: root
118 implicitWidth: counterRotate ? applicationWindow.implicitHeight : applicationWindow.implicitWidth
119 implicitHeight: root.actualDecorationHeight + (counterRotate ? applicationWindow.implicitWidth: applicationWindow.implicitHeight)
120 }
121 },
122 State {
123 name: "normalSuspended"; when: root.scaleToPreviewProgress <= 0 && root.application.state !== ApplicationInfoInterface.Running
124 extend: "normal"
125 PropertyChanges {
126 target: root
127 implicitWidth: counterRotate ? applicationWindow.requestedHeight : applicationWindow.requestedWidth
128 implicitHeight: root.actualDecorationHeight + (counterRotate ? applicationWindow.requestedWidth: applicationWindow.requestedHeight)
129 }
130 },
131 State {
132 name: "preview"; when: root.scaleToPreviewProgress > 0
133 PropertyChanges {
134 target: root
135 implicitWidth: MathUtils.linearAnimation(0, 1, applicationWindow.requestedWidth, root.scaleToPreviewSize, root.scaleToPreviewProgress)
136 implicitHeight: MathUtils.linearAnimation(0, 1, applicationWindow.requestedHeight, root.scaleToPreviewSize, root.scaleToPreviewProgress)
137 }
138 PropertyChanges {
139 target: applicationWindow;
140// requestedWidth: applicationWindow.oldRequestedWidth
141// requestedHeight: applicationWindow.oldRequestedHeight
142 width: MathUtils.linearAnimation(0, 1, applicationWindow.requestedWidth, applicationWindow.minSize, root.scaleToPreviewProgress)
143 height: MathUtils.linearAnimation(0, 1, applicationWindow.requestedHeight, applicationWindow.minSize, root.scaleToPreviewProgress)
144 itemScale: root.implicitWidth / width
145 }
146 }
147 ]
148 }
149
150 Rectangle {
151 id: selectionHighlight
152 objectName: "selectionHighlight"
153 anchors.fill: parent
154 anchors.margins: -root.highlightSize
155 color: "white"
156 opacity: showHighlight ? 0.55 : 0
157 visible: opacity > 0
158 }
159
160 BorderImage {
161 id: dropShadow
162 anchors {
163 left: parent.left; top: parent.top; right: parent.right
164 margins: active ? -units.gu(2) : -units.gu(1.5)
165 }
166 height: Math.min(applicationWindow.implicitHeight, applicationWindow.height) * applicationWindow.itemScale
167 + root.actualDecorationHeight * Math.min(1, root.showDecoration) + (active ? units.gu(4) : units.gu(3))
168 source: "../graphics/dropshadow2gu.sci"
169 opacity: root.shadowOpacity
170 }
171
172 ApplicationWindow {
173 id: applicationWindow
174 objectName: "appWindow"
175 anchors.top: parent.top
176 anchors.topMargin: root.actualDecorationHeight * Math.min(1, root.showDecoration)
177 anchors.left: parent.left
178 width: implicitWidth
179 height: implicitHeight
180 requestedHeight: !counterRotate ? root.requestedHeight - d.requestedDecorationHeight : root.requestedWidth
181 requestedWidth: !counterRotate ? root.requestedWidth : root.requestedHeight - d.requestedDecorationHeight
182// property int oldRequestedWidth: requestedWidth
183// property int oldRequestedHeight: requestedHeight
184// onRequestedWidthChanged: oldRequestedWidth = requestedWidth
185// onRequestedHeightChanged: oldRequestedHeight = requestedHeight
186 focus: true
187
188 property real itemScale: 1
189 property real minSize: Math.min(root.scaleToPreviewSize, Math.min(requestedHeight, Math.min(requestedWidth, Math.min(implicitHeight, implicitWidth))))
190
191 transform: [
192 Rotation {
193 id: rotationTransform
194 readonly property int rotationAngle: applicationWindow.application &&
195 applicationWindow.application.rotatesWindowContents
196 ? ((360 - applicationWindow.surfaceOrientationAngle) % 360) : 0
197 origin.x: {
198 if (rotationAngle == 90) return applicationWindow.height / 2;
199 else if (rotationAngle == 270) return applicationWindow.width / 2;
200 else if (rotationAngle == 180) return applicationWindow.width / 2;
201 else return 0;
202 }
203 origin.y: {
204 if (rotationAngle == 90) return applicationWindow.height / 2;
205 else if (rotationAngle == 270) return applicationWindow.width / 2;
206 else if (rotationAngle == 180) return applicationWindow.height / 2;
207 else return 0;
208 }
209 angle: rotationAngle
210 },
211 Scale {
212 xScale: applicationWindow.itemScale
213 yScale: applicationWindow.itemScale
214 }
215 ]
216 }
217
218 WindowDecoration {
219 id: decoration
220 closeButtonVisible: true
221 objectName: "appWindowDecoration"
222
223 anchors { left: parent.left; top: parent.top; right: parent.right }
224 height: units.gu(3) // a default value. overwritten by root.decorationHeight
225
226 title: applicationWindow.title
227 windowMoving: moveHandler.moving && !altDragHandler.dragging
228 panelState: root.panelState
229 lightMode: root.lightMode
230
231 opacity: root.hasDecoration ? Math.min(1, root.showDecoration) : 0
232 Behavior on opacity { LomiriNumberAnimation { } }
233 visible: opacity > 0 // don't eat input when decoration is fully translucent
234
235 onPressed: root.decorationPressed();
236 onPressedChanged: moveHandler.handlePressedChanged(pressed, pressedButtons, mouseX, mouseY)
237 onPressedChangedEx: moveHandler.handlePressedChanged(pressed, pressedButtons, mouseX, mouseY)
238 onPositionChanged: moveHandler.handlePositionChanged(mouse)
239 onReleased: {
240 root.decorationReleased();
241 moveHandler.handleReleased();
242 }
243
244 onCloseClicked: root.closeClicked();
245 onMaximizeClicked: { root.decorationPressed(); root.maximizeClicked(); }
246 onMaximizeHorizontallyClicked: { root.decorationPressed(); root.maximizeHorizontallyClicked(); }
247 onMaximizeVerticallyClicked: { root.decorationPressed(); root.maximizeVerticallyClicked(); }
248 onMinimizeClicked: root.minimizeClicked();
249
250 enableMenus: {
251 return active &&
252 surface &&
253 (panelState.focusedPersistentSurfaceId === surface.persistentId && !panelState.decorationsVisible)
254 }
255 menu: sharedAppModel.model
256
257 Indicators.SharedLomiriMenuModel {
258 id: sharedAppModel
259 property var menus: surface ? ApplicationMenuRegistry.getMenusForSurface(surface.persistentId) : []
260 property var menuService: menus.length > 0 ? menus[0] : undefined
261
262 busName: menuService ? menuService.service : ""
263 menuObjectPath: menuService && menuService.menuPath ? menuService.menuPath : ""
264 actions: menuService && menuService.actionPath ? { "lomiri": menuService.actionPath } : {}
265 }
266
267 Connections {
268 target: ApplicationMenuRegistry
269 function onSurfaceMenuRegistered(surfaceId) {
270 if (surface && surfaceId === surface.persistentId) {
271 sharedAppModel.menus = Qt.binding(function() { return surface ? ApplicationMenuRegistry.getMenusForSurface(surface.persistentId) : [] });
272 }
273 }
274 function onSurfaceMenuUnregistered(surfaceId) {
275 if (surface && surfaceId === surface.persistentId) {
276 sharedAppModel.menus = Qt.binding(function() { return surface ? ApplicationMenuRegistry.getMenusForSurface(surface.persistentId) : [] });
277 }
278 }
279 }
280 }
281
282 MouseArea {
283 id: altDragHandler
284 anchors.fill: applicationWindow
285 acceptedButtons: Qt.LeftButton
286 property bool dragging: false
287 cursorShape: undefined // don't interfere with the cursor shape set by the underlying MirSurfaceItem
288 visible: enabled
289 onPressed: {
290 if (mouse.button == Qt.LeftButton && mouse.modifiers == Qt.AltModifier) {
291 root.decorationPressed(); // to raise it
292 moveHandler.handlePressedChanged(true, Qt.LeftButton, mouse.x, mouse.y);
293 dragging = true;
294 mouse.accepted = true;
295 } else {
296 mouse.accepted = false;
297 }
298 }
299 onPositionChanged: {
300 if (dragging) {
301 moveHandler.handlePositionChanged(mouse);
302 }
303 }
304 onReleased: {
305 if (dragging) {
306 moveHandler.handlePressedChanged(false, Qt.LeftButton);
307 root.decorationReleased(); // commits the fake preview max rectangle
308 moveHandler.handleReleased();
309 dragging = false;
310 }
311 }
312 }
313
314 MoveHandler {
315 id: moveHandler
316 objectName: "moveHandler"
317 target: root.parent
318 buttonsWidth: decoration.buttonsWidth
319 }
320
321 Rectangle {
322 anchors.fill: parent
323 color: "black"
324 opacity: root.darkening && !root.showHighlight ? 0.05 : 0
325 Behavior on opacity { LomiriNumberAnimation { duration: LomiriAnimation.SnapDuration } }
326 }
327}