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.12
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 PanelState panelState
44
45 // Changing this will actually add/remove a decoration, meaning, requestedHeight will take the decoration into account.
46 property bool hasDecoration: true
47 // This will temporarily show/hide the decoration without actually changing the surface's dimensions
48 property real showDecoration: 1
49 property alias decorationHeight: decoration.height
50 property bool animateDecoration: false
51 property bool showHighlight: false
52 property int highlightSize: units.gu(1)
53 property real shadowOpacity: 0
54 property bool darkening: false
55
56 property real requestedWidth
57 property real requestedHeight
58 property real scaleToPreviewProgress: 0
59 property int scaleToPreviewSize: units.gu(30)
60
61 property alias surfaceOrientationAngle: applicationWindow.surfaceOrientationAngle
62
63 // Height of the decoration that's actually being displayed at this moment. Will match decorationHeight
64 // when the decoration is being fully displayed
65 readonly property real actualDecorationHeight: Math.min(d.visibleDecorationHeight, d.requestedDecorationHeight)
66
67 readonly property bool counterRotate: surfaceOrientationAngle != 0 && surfaceOrientationAngle != 180
68
69 readonly property int minimumWidth: !counterRotate ? applicationWindow.minimumWidth : applicationWindow.minimumHeight
70 readonly property int minimumHeight: actualDecorationHeight + (!counterRotate ? applicationWindow.minimumHeight : applicationWindow.minimumWidth)
71 readonly property int maximumWidth: !counterRotate ? applicationWindow.maximumWidth : applicationWindow.maximumHeight
72 readonly property int maximumHeight: (root.decorationShown && applicationWindow.maximumHeight > 0 ? decoration.height : 0)
73 + (!counterRotate ? applicationWindow.maximumHeight : applicationWindow.maximumWidth)
74 readonly property int widthIncrement: !counterRotate ? applicationWindow.widthIncrement : applicationWindow.heightIncrement
75 readonly property int heightIncrement: !counterRotate ? applicationWindow.heightIncrement : applicationWindow.widthIncrement
76
77 property alias overlayShown: decoration.overlayShown
78 property alias boundsItem: moveHandler.boundsItem
79 readonly property alias dragging: moveHandler.dragging
80
81 readonly property Item clientAreaItem: applicationWindow
82
83 property alias altDragEnabled: altDragHandler.enabled
84
85 property alias clipSurface: applicationWindow.clip
86
87 property Item windowMargins
88
89 signal closeClicked()
90 signal maximizeClicked()
91 signal maximizeHorizontallyClicked()
92 signal maximizeVerticallyClicked()
93 signal minimizeClicked()
94 signal decorationPressed()
95 signal decorationReleased()
96
97 function cancelDrag() {
98 moveHandler.cancelDrag();
99 }
100
101 QtObject {
102 id: d
103 property int requestedDecorationHeight: root.hasDecoration ? decoration.height : 0
104 Behavior on requestedDecorationHeight { enabled: root.animateDecoration; LomiriNumberAnimation { } }
105
106 property int visibleDecorationHeight: root.hasDecoration ? root.showDecoration * decoration.height : 0
107 Behavior on visibleDecorationHeight { enabled: root.animateDecoration; LomiriNumberAnimation { } }
108 }
109
110 StateGroup {
111 states: [
112 State {
113 name: "normal"; when: root.scaleToPreviewProgress <= 0 && root.application.state === ApplicationInfoInterface.Running
114 PropertyChanges {
115 target: root
116 implicitWidth: counterRotate ? applicationWindow.implicitHeight : applicationWindow.implicitWidth
117 implicitHeight: root.actualDecorationHeight + (counterRotate ? applicationWindow.implicitWidth: applicationWindow.implicitHeight)
118 }
119 },
120 State {
121 name: "normalSuspended"; when: root.scaleToPreviewProgress <= 0 && root.application.state !== ApplicationInfoInterface.Running
122 extend: "normal"
123 PropertyChanges {
124 target: root
125 implicitWidth: counterRotate ? applicationWindow.requestedHeight : applicationWindow.requestedWidth
126 implicitHeight: root.actualDecorationHeight + (counterRotate ? applicationWindow.requestedWidth: applicationWindow.requestedHeight)
127 }
128 },
129 State {
130 name: "preview"; when: root.scaleToPreviewProgress > 0
131 PropertyChanges {
132 target: root
133 implicitWidth: MathUtils.linearAnimation(0, 1, applicationWindow.requestedWidth, root.scaleToPreviewSize, root.scaleToPreviewProgress)
134 implicitHeight: MathUtils.linearAnimation(0, 1, applicationWindow.requestedHeight, root.scaleToPreviewSize, root.scaleToPreviewProgress)
135 }
136 PropertyChanges {
137 target: applicationWindow;
138// requestedWidth: applicationWindow.oldRequestedWidth
139// requestedHeight: applicationWindow.oldRequestedHeight
140 width: MathUtils.linearAnimation(0, 1, applicationWindow.requestedWidth, applicationWindow.minSize, root.scaleToPreviewProgress)
141 height: MathUtils.linearAnimation(0, 1, applicationWindow.requestedHeight, applicationWindow.minSize, root.scaleToPreviewProgress)
142 itemScale: root.implicitWidth / width
143 }
144 }
145 ]
146 }
147
148 Rectangle {
149 id: selectionHighlight
150 objectName: "selectionHighlight"
151 anchors.fill: parent
152 anchors.margins: -root.highlightSize
153 color: "white"
154 opacity: showHighlight ? 0.55 : 0
155 visible: opacity > 0
156 }
157
158 BorderImage {
159 id: dropShadow
160 anchors {
161 left: parent.left; top: parent.top; right: parent.right
162 margins: active ? -units.gu(2) : -units.gu(1.5)
163 }
164 height: Math.min(applicationWindow.implicitHeight, applicationWindow.height) * applicationWindow.itemScale
165 + root.actualDecorationHeight * Math.min(1, root.showDecoration) + (active ? units.gu(4) : units.gu(3))
166 source: "../graphics/dropshadow2gu.sci"
167 opacity: root.shadowOpacity
168 }
169
170 ApplicationWindow {
171 id: applicationWindow
172 objectName: "appWindow"
173 anchors.top: parent.top
174 anchors.topMargin: root.actualDecorationHeight * Math.min(1, root.showDecoration)
175 anchors.left: parent.left
176 width: implicitWidth
177 height: implicitHeight
178 requestedHeight: !counterRotate ? root.requestedHeight - d.requestedDecorationHeight : root.requestedWidth
179 requestedWidth: !counterRotate ? root.requestedWidth : root.requestedHeight - d.requestedDecorationHeight
180// property int oldRequestedWidth: requestedWidth
181// property int oldRequestedHeight: requestedHeight
182// onRequestedWidthChanged: oldRequestedWidth = requestedWidth
183// onRequestedHeightChanged: oldRequestedHeight = requestedHeight
184 focus: true
185
186 property real itemScale: 1
187 property real minSize: Math.min(root.scaleToPreviewSize, Math.min(requestedHeight, Math.min(requestedWidth, Math.min(implicitHeight, implicitWidth))))
188
189 transform: [
190 Rotation {
191 id: rotationTransform
192 readonly property int rotationAngle: applicationWindow.application &&
193 applicationWindow.application.rotatesWindowContents
194 ? ((360 - applicationWindow.surfaceOrientationAngle) % 360) : 0
195 origin.x: {
196 if (rotationAngle == 90) return applicationWindow.height / 2;
197 else if (rotationAngle == 270) return applicationWindow.width / 2;
198 else if (rotationAngle == 180) return applicationWindow.width / 2;
199 else return 0;
200 }
201 origin.y: {
202 if (rotationAngle == 90) return applicationWindow.height / 2;
203 else if (rotationAngle == 270) return applicationWindow.width / 2;
204 else if (rotationAngle == 180) return applicationWindow.height / 2;
205 else return 0;
206 }
207 angle: rotationAngle
208 },
209 Scale {
210 xScale: applicationWindow.itemScale
211 yScale: applicationWindow.itemScale
212 }
213 ]
214 }
215
216 WindowDecoration {
217 id: decoration
218 closeButtonVisible: true
219 objectName: "appWindowDecoration"
220
221 anchors { left: parent.left; top: parent.top; right: parent.right }
222 height: units.gu(3) // a default value. overwritten by root.decorationHeight
223
224 title: applicationWindow.title
225 windowMoving: moveHandler.moving && !altDragHandler.dragging
226 panelState: root.panelState
227
228 opacity: root.hasDecoration ? Math.min(1, root.showDecoration) : 0
229 Behavior on opacity { LomiriNumberAnimation { } }
230 visible: opacity > 0 // don't eat input when decoration is fully translucent
231
232 onPressed: root.decorationPressed();
233 onPressedChanged: moveHandler.handlePressedChanged(pressed, pressedButtons, mouseX, mouseY)
234 onPressedChangedEx: moveHandler.handlePressedChanged(pressed, pressedButtons, mouseX, mouseY)
235 onPositionChanged: moveHandler.handlePositionChanged(mouse)
236 onReleased: {
237 root.decorationReleased();
238 moveHandler.handleReleased();
239 }
240
241 onCloseClicked: root.closeClicked();
242 onMaximizeClicked: { root.decorationPressed(); root.maximizeClicked(); }
243 onMaximizeHorizontallyClicked: { root.decorationPressed(); root.maximizeHorizontallyClicked(); }
244 onMaximizeVerticallyClicked: { root.decorationPressed(); root.maximizeVerticallyClicked(); }
245 onMinimizeClicked: root.minimizeClicked();
246
247 enableMenus: {
248 return active &&
249 surface &&
250 (panelState.focusedPersistentSurfaceId === surface.persistentId && !panelState.decorationsVisible)
251 }
252 menu: sharedAppModel.model
253
254 Indicators.SharedLomiriMenuModel {
255 id: sharedAppModel
256 property var menus: surface ? ApplicationMenuRegistry.getMenusForSurface(surface.persistentId) : []
257 property var menuService: menus.length > 0 ? menus[0] : undefined
258
259 busName: menuService ? menuService.service : ""
260 menuObjectPath: menuService && menuService.menuPath ? menuService.menuPath : ""
261 actions: menuService && menuService.actionPath ? { "lomiri": menuService.actionPath } : {}
262 }
263
264 Connections {
265 target: ApplicationMenuRegistry
266 onSurfaceMenuRegistered: {
267 if (surface && surfaceId === surface.persistentId) {
268 sharedAppModel.menus = Qt.binding(function() { return surface ? ApplicationMenuRegistry.getMenusForSurface(surface.persistentId) : [] });
269 }
270 }
271 onSurfaceMenuUnregistered: {
272 if (surface && surfaceId === surface.persistentId) {
273 sharedAppModel.menus = Qt.binding(function() { return surface ? ApplicationMenuRegistry.getMenusForSurface(surface.persistentId) : [] });
274 }
275 }
276 }
277 }
278
279 MouseArea {
280 id: altDragHandler
281 anchors.fill: applicationWindow
282 acceptedButtons: Qt.LeftButton
283 property bool dragging: false
284 cursorShape: undefined // don't interfere with the cursor shape set by the underlying MirSurfaceItem
285 visible: enabled
286 onPressed: {
287 if (mouse.button == Qt.LeftButton && mouse.modifiers == Qt.AltModifier) {
288 root.decorationPressed(); // to raise it
289 moveHandler.handlePressedChanged(true, Qt.LeftButton, mouse.x, mouse.y);
290 dragging = true;
291 mouse.accepted = true;
292 } else {
293 mouse.accepted = false;
294 }
295 }
296 onPositionChanged: {
297 if (dragging) {
298 moveHandler.handlePositionChanged(mouse);
299 }
300 }
301 onReleased: {
302 if (dragging) {
303 moveHandler.handlePressedChanged(false, Qt.LeftButton);
304 root.decorationReleased(); // commits the fake preview max rectangle
305 moveHandler.handleReleased();
306 dragging = false;
307 }
308 }
309 }
310
311 MoveHandler {
312 id: moveHandler
313 objectName: "moveHandler"
314 target: root.parent
315 buttonsWidth: decoration.buttonsWidth
316 }
317
318 Rectangle {
319 anchors.fill: parent
320 color: "black"
321 opacity: root.darkening && !root.showHighlight ? 0.05 : 0
322 Behavior on opacity { LomiriNumberAnimation { duration: LomiriAnimation.SnapDuration } }
323 }
324}