on a6cff49 help sign in
unity8/qml/Stage/WindowControlsOverlay.qml
259 lines 10 kB history raw
1/*
2 * Copyright (C) 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 */
17import QtQuick 2.4
18import Ubuntu.Components 1.3
19import Ubuntu.Gestures 0.1
20import Unity.Application 0.1
21import "../Components/PanelState"
23Item {
24 id: root
25 enabled: target && !target.fullscreen
26 anchors.fill: target
28 // to be set from outside
29 property Item target // appDelegate
30 property PanelState panelState
31 property alias stageWidth: moveHandler.stageWidth
32 property alias stageHeight: moveHandler.stageHeight
34 // to be read from outside
35 readonly property alias overlayShown: overlay.visible
36 readonly property alias dragging: priv.dragging
38 signal fakeMaximizeAnimationRequested(real amount)
39 signal fakeMaximizeLeftAnimationRequested(real amount)
40 signal fakeMaximizeRightAnimationRequested(real amount)
41 signal fakeMaximizeTopLeftAnimationRequested(real amount)
42 signal fakeMaximizeTopRightAnimationRequested(real amount)
43 signal fakeMaximizeBottomLeftAnimationRequested(real amount)
44 signal fakeMaximizeBottomRightAnimationRequested(real amount)
45 signal stopFakeAnimation()
46 signal dragReleased()
48 TouchGestureArea {
49 id: gestureArea
50 anchors.fill: parent
52 // NB: for testing set to 2, not to clash with unity7 touch overlay controls
53 minimumTouchPoints: 3
54 maximumTouchPoints: minimumTouchPoints
56 readonly property bool recognizedPress: status == TouchGestureArea.Recognized &&
57 touchPoints.length >= minimumTouchPoints &&
58 touchPoints.length <= maximumTouchPoints
59 onRecognizedPressChanged: {
60 if (recognizedPress) {
61 target.activate();
62 overlayTimer.start();
63 }
64 }
66 readonly property bool recognizedDrag: recognizedPress && dragging
67 onRecognizedDragChanged: {
68 if (recognizedDrag) {
69 moveHandler.handlePressedChanged(true, Qt.LeftButton, tp.x, tp.y);
70 } else if (!mouseArea.containsPress) { // prevent interfering with the central piece drag/move
71 moveHandler.handlePressedChanged(false, Qt.LeftButton);
72 root.dragReleased();
73 moveHandler.handleReleased(true);
74 }
75 }
77 readonly property point tp: recognizedPress ? Qt.point(touchPoints[0].x, touchPoints[0].y) : Qt.point(-1, -1)
78 onUpdated: {
79 if (recognizedDrag) {
80 moveHandler.handlePositionChanged(tp, priv.getSensingPoints());
81 }
82 }
83 }
85 // dismiss timer
86 Timer {
87 id: overlayTimer
88 interval: 2000
89 repeat: priv.dragging
90 }
92 QtObject {
93 id: priv
94 readonly property var resizeArea: root.target && root.target.resizeArea ? root.target.resizeArea : null
95 readonly property bool ensureWindow: root.target.state == "normal" || root.target.state == "restored"
96 readonly property bool dragging: moveHandler.dragging || (resizeArea && resizeArea.dragging)
98 function getSensingPoints() {
99 var xPoints = [];
100 var yPoints = [];
101 for (var i = 0; i < gestureArea.touchPoints.length; i++) {
102 var pt = gestureArea.touchPoints[i];
103 xPoints.push(pt.x);
104 yPoints.push(pt.y);
105 }
107 var leftmost = Math.min.apply(Math, xPoints);
108 var rightmost = Math.max.apply(Math, xPoints);
109 var topmost = Math.min.apply(Math, yPoints);
110 var bottommost = Math.max.apply(Math, yPoints);
112 return {
113 left: mapToItem(target.parent, leftmost, (topmost+bottommost)/2),
114 top: mapToItem(target.parent, (leftmost+rightmost)/2, topmost),
115 right: mapToItem(target.parent, rightmost, (topmost+bottommost)/2),
116 topLeft: mapToItem(target.parent, leftmost, topmost),
117 topRight: mapToItem(target.parent, rightmost, topmost),
118 bottomLeft: mapToItem(target.parent, leftmost, bottommost),
119 bottomRight: mapToItem(target.parent, rightmost, bottommost)
120 }
121 }
122 }
124 // the visual overlay
125 Item {
126 id: overlay
127 objectName: "windowControlsOverlay"
128 anchors.fill: parent
129 enabled: overlayTimer.running
130 visible: opacity > 0
131 opacity: enabled ? 0.95 : 0
133 Behavior on opacity {
134 UbuntuNumberAnimation {}
135 }
137 Image {
138 source: "graphics/arrows-centre.png"
139 width: units.gu(10)
140 height: width
141 sourceSize: Qt.size(width, height)
142 anchors.centerIn: parent
143 visible: target && target.width > units.gu(12) && target.height > units.gu(12)
145 // move handler
146 MouseArea {
147 id: mouseArea
148 anchors.fill: parent
149 visible: overlay.visible
150 enabled: visible
151 hoverEnabled: true
153 onPressedChanged: moveHandler.handlePressedChanged(pressed, pressedButtons, mouseX, mouseY)
154 onPositionChanged: moveHandler.handlePositionChanged(mouse)
155 onReleased: {
156 root.dragReleased();
157 moveHandler.handleReleased();
158 }
159 }
161 MoveHandler {
162 id: moveHandler
163 objectName: "moveHandler"
164 target: root.target
165 panelState: root.panelState
167 onFakeMaximizeAnimationRequested: root.fakeMaximizeAnimationRequested(amount)
168 onFakeMaximizeLeftAnimationRequested: root.fakeMaximizeLeftAnimationRequested(amount)
169 onFakeMaximizeRightAnimationRequested: root.fakeMaximizeRightAnimationRequested(amount)
170 onFakeMaximizeTopLeftAnimationRequested: root.fakeMaximizeTopLeftAnimationRequested(amount)
171 onFakeMaximizeTopRightAnimationRequested: root.fakeMaximizeTopRightAnimationRequested(amount)
172 onFakeMaximizeBottomLeftAnimationRequested: root.fakeMaximizeBottomLeftAnimationRequested(amount)
173 onFakeMaximizeBottomRightAnimationRequested: root.fakeMaximizeBottomRightAnimationRequested(amount)
174 onStopFakeAnimation: root.stopFakeAnimation()
175 }
177 // dismiss area
178 InverseMouseArea {
179 anchors.fill: parent
180 visible: overlay.visible
181 enabled: visible
182 onPressed: {
183 if (gestureArea.recognizedPress || gestureArea.recognizedDrag) {
184 mouse.accepted = false;
185 return;
186 }
188 overlayTimer.stop();
189 mouse.accepted = root.contains(mapToItem(root.target.clientAreaItem, mouse.x, mouse.y));
190 }
191 propagateComposedEvents: true
192 }
193 }
195 ResizeGrip { // top left
196 anchors.horizontalCenter: parent.left
197 anchors.verticalCenter: parent.top
198 visible: priv.ensureWindow || target.maximizedBottomRight
199 resizeTarget: priv.resizeArea
200 }
202 ResizeGrip { // top center
203 anchors.horizontalCenter: parent.horizontalCenter
204 anchors.verticalCenter: parent.top
205 rotation: 45
206 visible: priv.ensureWindow || target.maximizedHorizontally || target.maximizedBottomLeft || target.maximizedBottomRight
207 resizeTarget: priv.resizeArea
208 }
210 ResizeGrip { // top right
211 anchors.horizontalCenter: parent.right
212 anchors.verticalCenter: parent.top
213 rotation: 90
214 visible: priv.ensureWindow || target.maximizedBottomLeft
215 resizeTarget: priv.resizeArea
216 }
218 ResizeGrip { // right
219 anchors.horizontalCenter: parent.right
220 anchors.verticalCenter: parent.verticalCenter
221 rotation: 135
222 visible: priv.ensureWindow || target.maximizedVertically || target.maximizedLeft ||
223 target.maximizedTopLeft || target.maximizedBottomLeft
224 resizeTarget: priv.resizeArea
225 }
227 ResizeGrip { // bottom right
228 anchors.horizontalCenter: parent.right
229 anchors.verticalCenter: parent.bottom
230 visible: priv.ensureWindow || target.maximizedTopLeft
231 resizeTarget: priv.resizeArea
232 }
234 ResizeGrip { // bottom center
235 anchors.horizontalCenter: parent.horizontalCenter
236 anchors.verticalCenter: parent.bottom
237 rotation: 45
238 visible: priv.ensureWindow || target.maximizedHorizontally || target.maximizedTopLeft || target.maximizedTopRight
239 resizeTarget: priv.resizeArea
240 }
242 ResizeGrip { // bottom left
243 anchors.horizontalCenter: parent.left
244 anchors.verticalCenter: parent.bottom
245 rotation: 90
246 visible: priv.ensureWindow || target.maximizedTopRight
247 resizeTarget: priv.resizeArea
248 }
250 ResizeGrip { // left
251 anchors.horizontalCenter: parent.left
252 anchors.verticalCenter: parent.verticalCenter
253 rotation: 135
254 visible: priv.ensureWindow || target.maximizedVertically || target.maximizedRight ||
255 target.maximizedTopRight || target.maximizedBottomRight
256 resizeTarget: priv.resizeArea
257 }
258 }