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