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