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