Unity 8
DecoratedWindow.qml
1 /*
2  * Copyright (C) 2014-2016 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 
17 import QtQuick 2.4
18 import Ubuntu.Components 1.3
19 import Unity.Application 0.1
20 import "Spread/MathUtils.js" as MathUtils
21 
22 FocusScope {
23  id: root
24 
25  // The DecoratedWindow takes requestedWidth/requestedHeight and asks its surface to be resized to that
26  // (minus the window decoration size in case hasDecoration and showDecoration are true)
27  // The surface might not be able to resize to the requested values. It will return its actual size
28  // in implicitWidth/implicitHeight.
29 
30  property alias application: applicationWindow.application
31  property alias surface: applicationWindow.surface
32  readonly property alias focusedSurface: applicationWindow.focusedSurface
33  property alias active: decoration.active
34  readonly property alias title: applicationWindow.title
35  property alias maximizeButtonShown: decoration.maximizeButtonShown
36  property alias interactive: applicationWindow.interactive
37  readonly property alias orientationChangesEnabled: applicationWindow.orientationChangesEnabled
38 
39  // Changing this will actually add/remove a decoration, meaning, requestedHeight will take the decoration into account.
40  property bool hasDecoration: true
41  // This will temporarily show/hide the decoration without actually changing the surface's dimensions
42  property real showDecoration: 1
43  property bool animateDecoration: false
44  property bool showHighlight: false
45  property int highlightSize: units.gu(1)
46  property real shadowOpacity: 0
47  property bool darkening: false
48 
49  property real requestedWidth
50  property real requestedHeight
51  property real scaleToPreviewProgress: 0
52  property int scaleToPreviewSize: units.gu(30)
53 
54  property alias surfaceOrientationAngle: applicationWindow.surfaceOrientationAngle
55  readonly property real decorationHeight: Math.min(d.visibleDecorationHeight, d.requestedDecorationHeight)
56  readonly property bool counterRotate: surfaceOrientationAngle != 0 && surfaceOrientationAngle != 180
57 
58  readonly property int minimumWidth: !counterRotate ? applicationWindow.minimumWidth : applicationWindow.minimumHeight
59  readonly property int minimumHeight: decorationHeight + (!counterRotate ? applicationWindow.minimumHeight : applicationWindow.minimumWidth)
60  readonly property int maximumWidth: !counterRotate ? applicationWindow.maximumWidth : applicationWindow.maximumHeight
61  readonly property int maximumHeight: (root.decorationShown && applicationWindow.maximumHeight > 0 ? decoration.height : 0)
62  + (!counterRotate ? applicationWindow.maximumHeight : applicationWindow.maximumWidth)
63  readonly property int widthIncrement: !counterRotate ? applicationWindow.widthIncrement : applicationWindow.heightIncrement
64  readonly property int heightIncrement: !counterRotate ? applicationWindow.heightIncrement : applicationWindow.widthIncrement
65 
66  property alias overlayShown: decoration.overlayShown
67  property alias stageWidth: moveHandler.stageWidth
68  property alias stageHeight: moveHandler.stageHeight
69  readonly property alias dragging: moveHandler.dragging
70 
71  readonly property Item clientAreaItem: applicationWindow
72 
73  signal closeClicked()
74  signal maximizeClicked()
75  signal maximizeHorizontallyClicked()
76  signal maximizeVerticallyClicked()
77  signal minimizeClicked()
78  signal decorationPressed()
79  signal decorationReleased()
80 
81  QtObject {
82  id: d
83  property int requestedDecorationHeight: root.hasDecoration ? decoration.height : 0
84  Behavior on requestedDecorationHeight { enabled: root.animateDecoration; UbuntuNumberAnimation { } }
85 
86  property int visibleDecorationHeight: root.hasDecoration ? root.showDecoration * decoration.height : 0
87  Behavior on visibleDecorationHeight { enabled: root.animateDecoration; UbuntuNumberAnimation { } }
88  }
89 
90  StateGroup {
91  states: [
92  State {
93  name: "normal"; when: root.scaleToPreviewProgress <= 0 && root.application.state === ApplicationInfoInterface.Running
94  PropertyChanges {
95  target: root
96  implicitWidth: counterRotate ? applicationWindow.implicitHeight : applicationWindow.implicitWidth
97  implicitHeight: root.decorationHeight + (counterRotate ? applicationWindow.implicitWidth: applicationWindow.implicitHeight)
98  }
99  },
100  State {
101  name: "normalSuspended"; when: root.scaleToPreviewProgress <= 0 && root.application.state !== ApplicationInfoInterface.Running
102  extend: "normal"
103  PropertyChanges {
104  target: root
105  implicitWidth: counterRotate ? applicationWindow.requestedHeight : applicationWindow.requestedWidth
106  implicitHeight: root.decorationHeight + (counterRotate ? applicationWindow.requestedWidth: applicationWindow.requestedHeight)
107  }
108  },
109  State {
110  name: "preview"; when: root.scaleToPreviewProgress > 0
111  PropertyChanges {
112  target: root
113  implicitWidth: MathUtils.linearAnimation(0, 1, applicationWindow.oldRequestedWidth, root.scaleToPreviewSize, root.scaleToPreviewProgress)
114  implicitHeight: MathUtils.linearAnimation(0, 1, applicationWindow.oldRequestedHeight, root.scaleToPreviewSize, root.scaleToPreviewProgress)
115  }
116  PropertyChanges {
117  target: applicationWindow;
118  requestedWidth: applicationWindow.oldRequestedWidth
119  requestedHeight: applicationWindow.oldRequestedHeight
120  width: MathUtils.linearAnimation(0, 1, applicationWindow.oldRequestedWidth, applicationWindow.minSize, root.scaleToPreviewProgress)
121  height: MathUtils.linearAnimation(0, 1, applicationWindow.oldRequestedHeight, applicationWindow.minSize, root.scaleToPreviewProgress)
122  itemScale: root.implicitWidth / width
123  }
124  }
125  ]
126  }
127 
128  Rectangle {
129  id: selectionHighlight
130  objectName: "selectionHighlight"
131  anchors.fill: parent
132  anchors.margins: -root.highlightSize
133  color: "white"
134  opacity: showHighlight ? 0.55 : 0
135  visible: opacity > 0
136  }
137 
138  BorderImage {
139  id: dropShadow
140  anchors {
141  left: parent.left; top: parent.top; right: parent.right
142  margins: active ? -units.gu(2) : -units.gu(1.5)
143  }
144  height: Math.min(applicationWindow.implicitHeight, applicationWindow.height) * applicationWindow.itemScale
145  + root.decorationHeight * Math.min(1, root.showDecoration) + (active ? units.gu(4) : units.gu(3))
146  source: "../graphics/dropshadow2gu.sci"
147  opacity: root.shadowOpacity
148  }
149 
150  WindowDecoration {
151  id: decoration
152  closeButtonVisible: root.application.appId !== "unity8-dash"
153  objectName: "appWindowDecoration"
154  anchors { left: parent.left; top: parent.top; right: parent.right }
155  height: units.gu(3)
156  width: root.width
157  title: applicationWindow.title
158  opacity: root.hasDecoration ? Math.min(1, root.showDecoration) : 0
159 
160  Behavior on opacity { UbuntuNumberAnimation { } }
161 
162  onCloseClicked: root.closeClicked();
163  onMaximizeClicked: { root.decorationPressed(); root.maximizeClicked(); }
164  onMaximizeHorizontallyClicked: { root.decorationPressed(); root.maximizeHorizontallyClicked(); }
165  onMaximizeVerticallyClicked: { root.decorationPressed(); root.maximizeVerticallyClicked(); }
166  onMinimizeClicked: root.minimizeClicked();
167  onPressed: root.decorationPressed();
168 
169  onPressedChanged: moveHandler.handlePressedChanged(pressed, pressedButtons, mouseX, mouseY)
170  onPositionChanged: moveHandler.handlePositionChanged(mouse)
171  onReleased: {
172  root.decorationReleased();
173  moveHandler.handleReleased();
174  }
175  }
176 
177  MoveHandler {
178  id: moveHandler
179  objectName: "moveHandler"
180  target: root.parent
181  buttonsWidth: decoration.buttonsWidth
182  }
183 
184  ApplicationWindow {
185  id: applicationWindow
186  objectName: "appWindow"
187  anchors.left: parent.left
188  anchors.top: parent.top
189  anchors.topMargin: root.decorationHeight * Math.min(1, root.showDecoration)
190  width: implicitWidth
191  height: implicitHeight
192  requestedHeight: !counterRotate ? root.requestedHeight - d.requestedDecorationHeight : root.requestedWidth
193  requestedWidth: !counterRotate ? root.requestedWidth : root.requestedHeight - d.requestedDecorationHeight
194  property int oldRequestedWidth: requestedWidth
195  property int oldRequestedHeight: requestedHeight
196  onRequestedWidthChanged: oldRequestedWidth = requestedWidth
197  onRequestedHeightChanged: oldRequestedHeight = requestedHeight
198  focus: true
199 
200  property real itemScale: 1
201  property real minSize: Math.min(root.scaleToPreviewSize, Math.min(requestedHeight, Math.min(requestedWidth, Math.min(implicitHeight, implicitWidth))))
202 
203  transform: [
204  Rotation {
205  id: rotationTransform
206  readonly property int rotationAngle: applicationWindow.application &&
207  applicationWindow.application.rotatesWindowContents
208  ? ((360 - applicationWindow.surfaceOrientationAngle) % 360) : 0
209  origin.x: {
210  if (rotationAngle == 90) return applicationWindow.height / 2;
211  else if (rotationAngle == 270) return applicationWindow.width / 2;
212  else if (rotationAngle == 180) return applicationWindow.width / 2;
213  else return 0;
214  }
215  origin.y: {
216  if (rotationAngle == 90) return applicationWindow.height / 2;
217  else if (rotationAngle == 270) return applicationWindow.width / 2;
218  else if (rotationAngle == 180) return applicationWindow.height / 2;
219  else return 0;
220  }
221  angle: rotationAngle
222  },
223  Scale {
224  xScale: applicationWindow.itemScale
225  yScale: applicationWindow.itemScale
226  }
227  ]
228  }
229 
230  MouseArea {
231  anchors.fill: applicationWindow
232  acceptedButtons: Qt.LeftButton
233  property bool dragging: false
234  cursorShape: undefined // don't interfere with the cursor shape set by the underlying MirSurfaceItem
235  onPressed: {
236  if (mouse.button == Qt.LeftButton && mouse.modifiers == Qt.AltModifier) {
237  root.decorationPressed(); // to raise it
238  moveHandler.handlePressedChanged(true, Qt.LeftButton, mouse.x, mouse.y);
239  dragging = true;
240  mouse.accepted = true;
241  } else {
242  mouse.accepted = false;
243  }
244  }
245  onPositionChanged: {
246  if (dragging) {
247  moveHandler.handlePositionChanged(mouse);
248  }
249  }
250  onReleased: {
251  if (dragging) {
252  moveHandler.handlePressedChanged(false, Qt.LeftButton);
253  root.decorationReleased(); // commits the fake preview max rectangle
254  moveHandler.handleReleased();
255  dragging = false;
256  }
257  }
258  }
259 
260  Rectangle {
261  anchors.fill: parent
262  color: "black"
263  opacity: root.darkening && !root.showHighlight ? 0.05 : 0
264  Behavior on opacity { UbuntuNumberAnimation { duration: UbuntuAnimation.SnapDuration } }
265  }
266 }