on xenial-before-2019-edge-merge help sign in
unity8/qml/Stage/WindowControlsOverlay.qml
257 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 alias stageWidth: moveHandler.stageWidth
31 property alias stageHeight: moveHandler.stageHeight
33 // to be read from outside
34 readonly property alias overlayShown: overlay.visible
35 readonly property alias dragging: priv.dragging
37 signal fakeMaximizeAnimationRequested(real amount)
38 signal fakeMaximizeLeftAnimationRequested(real amount)
39 signal fakeMaximizeRightAnimationRequested(real amount)
40 signal fakeMaximizeTopLeftAnimationRequested(real amount)
41 signal fakeMaximizeTopRightAnimationRequested(real amount)
42 signal fakeMaximizeBottomLeftAnimationRequested(real amount)
43 signal fakeMaximizeBottomRightAnimationRequested(real amount)
44 signal stopFakeAnimation()
45 signal dragReleased()
47 TouchGestureArea {
48 id: gestureArea
49 anchors.fill: parent
51 // NB: for testing set to 2, not to clash with unity7 touch overlay controls
52 minimumTouchPoints: 3
53 maximumTouchPoints: minimumTouchPoints
55 readonly property bool recognizedPress: status == TouchGestureArea.Recognized &&
56 touchPoints.length >= minimumTouchPoints &&
57 touchPoints.length <= maximumTouchPoints
58 onRecognizedPressChanged: {
59 if (recognizedPress) {
60 target.focus = true;
61 overlayTimer.start();
62 }
63 }
65 readonly property bool recognizedDrag: recognizedPress && dragging
66 onRecognizedDragChanged: {
67 if (recognizedDrag) {
68 moveHandler.handlePressedChanged(true, Qt.LeftButton, tp.x, tp.y);
69 } else if (!mouseArea.containsPress) { // prevent interfering with the central piece drag/move
70 moveHandler.handlePressedChanged(false, Qt.LeftButton);
71 root.dragReleased();
72 moveHandler.handleReleased(true);
73 }
74 }
76 readonly property point tp: recognizedPress ? Qt.point(touchPoints[0].x, touchPoints[0].y) : Qt.point(-1, -1)
77 onUpdated: {
78 if (recognizedDrag) {
79 moveHandler.handlePositionChanged(tp, priv.getSensingPoints());
80 }
81 }
82 }
84 // dismiss timer
85 Timer {
86 id: overlayTimer
87 interval: 2000
88 repeat: priv.dragging
89 }
91 QtObject {
92 id: priv
93 readonly property var resizeArea: root.target && root.target.resizeArea ? root.target.resizeArea : null
94 readonly property bool ensureWindow: root.target.state == "normal" || root.target.state == "restored"
95 readonly property bool dragging: moveHandler.dragging || (resizeArea && resizeArea.dragging)
97 function getSensingPoints() {
98 var xPoints = [];
99 var yPoints = [];
100 for (var i = 0; i < gestureArea.touchPoints.length; i++) {
101 var pt = gestureArea.touchPoints[i];
102 xPoints.push(pt.x);
103 yPoints.push(pt.y);
104 }
106 var leftmost = Math.min.apply(Math, xPoints);
107 var rightmost = Math.max.apply(Math, xPoints);
108 var topmost = Math.min.apply(Math, yPoints);
109 var bottommost = Math.max.apply(Math, yPoints);
111 return {
112 left: mapToItem(target.parent, leftmost, (topmost+bottommost)/2),
113 top: mapToItem(target.parent, (leftmost+rightmost)/2, topmost),
114 right: mapToItem(target.parent, rightmost, (topmost+bottommost)/2),
115 topLeft: mapToItem(target.parent, leftmost, topmost),
116 topRight: mapToItem(target.parent, rightmost, topmost),
117 bottomLeft: mapToItem(target.parent, leftmost, bottommost),
118 bottomRight: mapToItem(target.parent, rightmost, bottommost)
119 }
120 }
121 }
123 // the visual overlay
124 Item {
125 id: overlay
126 objectName: "windowControlsOverlay"
127 anchors.fill: parent
128 enabled: overlayTimer.running
129 visible: opacity > 0
130 opacity: enabled ? 0.95 : 0
132 Behavior on opacity {
133 UbuntuNumberAnimation {}
134 }
136 Image {
137 source: "graphics/arrows-centre.png"
138 width: units.gu(10)
139 height: width
140 sourceSize: Qt.size(width, height)
141 anchors.centerIn: parent
142 visible: target && target.width > units.gu(12) && target.height > units.gu(12)
144 // move handler
145 MouseArea {
146 id: mouseArea
147 anchors.fill: parent
148 visible: overlay.visible
149 enabled: visible
150 hoverEnabled: true
152 onPressedChanged: moveHandler.handlePressedChanged(pressed, pressedButtons, mouseX, mouseY)
153 onPositionChanged: moveHandler.handlePositionChanged(mouse)
154 onReleased: {
155 root.dragReleased();
156 moveHandler.handleReleased();
157 }
158 }
160 MoveHandler {
161 id: moveHandler
162 objectName: "moveHandler"
163 target: root.target
165 onFakeMaximizeAnimationRequested: root.fakeMaximizeAnimationRequested(amount)
166 onFakeMaximizeLeftAnimationRequested: root.fakeMaximizeLeftAnimationRequested(amount)
167 onFakeMaximizeRightAnimationRequested: root.fakeMaximizeRightAnimationRequested(amount)
168 onFakeMaximizeTopLeftAnimationRequested: root.fakeMaximizeTopLeftAnimationRequested(amount)
169 onFakeMaximizeTopRightAnimationRequested: root.fakeMaximizeTopRightAnimationRequested(amount)
170 onFakeMaximizeBottomLeftAnimationRequested: root.fakeMaximizeBottomLeftAnimationRequested(amount)
171 onFakeMaximizeBottomRightAnimationRequested: root.fakeMaximizeBottomRightAnimationRequested(amount)
172 onStopFakeAnimation: root.stopFakeAnimation()
173 }
175 // dismiss area
176 InverseMouseArea {
177 anchors.fill: parent
178 visible: overlay.visible
179 enabled: visible
180 onPressed: {
181 if (gestureArea.recognizedPress || gestureArea.recognizedDrag) {
182 mouse.accepted = false;
183 return;
184 }
186 overlayTimer.stop();
187 mouse.accepted = root.contains(mapToItem(root.target, mouse.x, mouse.y));
188 }
189 propagateComposedEvents: true
190 }
191 }
193 ResizeGrip { // top left
194 anchors.horizontalCenter: parent.left
195 anchors.verticalCenter: parent.top
196 visible: priv.ensureWindow || target.maximizedBottomRight
197 resizeTarget: priv.resizeArea
198 }
200 ResizeGrip { // top center
201 anchors.horizontalCenter: parent.horizontalCenter
202 anchors.verticalCenter: parent.top
203 rotation: 45
204 visible: priv.ensureWindow || target.maximizedHorizontally || target.maximizedBottomLeft || target.maximizedBottomRight
205 resizeTarget: priv.resizeArea
206 }
208 ResizeGrip { // top right
209 anchors.horizontalCenter: parent.right
210 anchors.verticalCenter: parent.top
211 rotation: 90
212 visible: priv.ensureWindow || target.maximizedBottomLeft
213 resizeTarget: priv.resizeArea
214 }
216 ResizeGrip { // right
217 anchors.horizontalCenter: parent.right
218 anchors.verticalCenter: parent.verticalCenter
219 rotation: 135
220 visible: priv.ensureWindow || target.maximizedVertically || target.maximizedLeft ||
221 target.maximizedTopLeft || target.maximizedBottomLeft
222 resizeTarget: priv.resizeArea
223 }
225 ResizeGrip { // bottom right
226 anchors.horizontalCenter: parent.right
227 anchors.verticalCenter: parent.bottom
228 visible: priv.ensureWindow || target.maximizedTopLeft
229 resizeTarget: priv.resizeArea
230 }
232 ResizeGrip { // bottom center
233 anchors.horizontalCenter: parent.horizontalCenter
234 anchors.verticalCenter: parent.bottom
235 rotation: 45
236 visible: priv.ensureWindow || target.maximizedHorizontally || target.maximizedTopLeft || target.maximizedTopRight
237 resizeTarget: priv.resizeArea
238 }
240 ResizeGrip { // bottom left
241 anchors.horizontalCenter: parent.left
242 anchors.verticalCenter: parent.bottom
243 rotation: 90
244 visible: priv.ensureWindow || target.maximizedTopRight
245 resizeTarget: priv.resizeArea
246 }
248 ResizeGrip { // left
249 anchors.horizontalCenter: parent.left
250 anchors.verticalCenter: parent.verticalCenter
251 rotation: 135
252 visible: priv.ensureWindow || target.maximizedVertically || target.maximizedRight ||
253 target.maximizedTopRight || target.maximizedBottomRight
254 resizeTarget: priv.resizeArea
255 }
256 }