on a6cff49 help sign in
unity8/qml/Components/VirtualTouchPad.qml
400 lines 17 kB history raw
1/*
2 * Copyright (C) 2015 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 QtQuick.Layouts 1.1
19import Ubuntu.Components 1.3
20import Qt.labs.settings 1.0
21import Unity.Screens 0.1
22import UInput 0.1
23import "../Components"
25Item {
26 id: root
28 Component.onCompleted: {
29 UInput.createMouse();
30 if (!settings.touchpadTutorialHasRun) {
31 root.runTutorial()
32 }
33 }
34 Component.onDestruction: UInput.removeMouse()
36 function runTutorial() {
37 // If the tutorial animation is started too early, e.g. in Component.onCompleted,
38 // root width & height might be reported as 0x0 still. As animations read their
39 // values at startup and won't update them, lets make sure to only start once
40 // we have some actual size.
41 if (root.width > 0 && root.height > 0) {
42 tutorial.start();
43 } else {
44 tutorialTimer.start();
45 }
46 }
48 Timer {
49 id: tutorialTimer
50 interval: 50
51 repeat: false
52 running: false
53 onTriggered: root.runTutorial();
54 }
56 readonly property bool pressed: point1.pressed || point2.pressed || leftButton.pressed || rightButton.pressed
58 property var settings: Settings {
59 objectName: "virtualTouchPadSettings"
60 property bool touchpadTutorialHasRun: false
61 property bool oskEnabled: true
62 }
64 MultiPointTouchArea {
65 objectName: "touchPadArea"
66 anchors.fill: parent
67 enabled: !tutorial.running || tutorial.paused
69 // FIXME: Once we have Qt DPR support, this should be Qt.styleHints.startDragDistance
70 readonly property int clickThreshold: internalGu * 1.5
71 property bool isClick: false
72 property bool isDoubleClick: false
73 property bool isDrag: false
75 onPressed: {
76 if (tutorial.paused) {
77 tutorial.resume();
78 return;
79 }
81 // If double-tapping *really* fast, it could happen that we end up having only point2 pressed
82 // Make sure we check for both combos, only point1 or only point2
83 if (((point1.pressed && !point2.pressed) || (!point1.pressed && point2.pressed))
84 && clickTimer.running) {
85 clickTimer.stop();
86 UInput.pressMouse(UInput.ButtonLeft)
87 isDoubleClick = true;
88 }
89 isClick = true;
90 }
92 onUpdated: {
93 switch (touchPoints.length) {
94 case 1:
95 moveMouse(touchPoints);
96 return;
97 case 2:
98 scroll(touchPoints);
99 return;
100 }
101 }
103 onReleased: {
104 if (isDoubleClick || isDrag) {
105 UInput.releaseMouse(UInput.ButtonLeft)
106 isDoubleClick = false;
107 }
108 if (isClick) {
109 clickTimer.scheduleClick(point1.pressed ? UInput.ButtonRight : UInput.ButtonLeft)
110 }
111 isClick = false;
112 isDrag = false;
113 }
115 Timer {
116 id: clickTimer
117 repeat: false
118 interval: 200
119 property int button: UInput.ButtonLeft
120 onTriggered: {
121 UInput.pressMouse(button);
122 UInput.releaseMouse(button);
123 }
124 function scheduleClick(button) {
125 clickTimer.button = button;
126 clickTimer.start();
127 }
128 }
130 function moveMouse(touchPoints) {
131 var tp = touchPoints[0];
132 if (isClick &&
133 (Math.abs(tp.x - tp.startX) > clickThreshold ||
134 Math.abs(tp.y - tp.startY) > clickThreshold)) {
135 isClick = false;
136 isDrag = true;
137 }
139 UInput.moveMouse(tp.x - tp.previousX, tp.y - tp.previousY);
140 }
142 function scroll(touchPoints) {
143 var dh = 0;
144 var dv = 0;
145 var tp = touchPoints[0];
146 if (isClick &&
147 (Math.abs(tp.x - tp.startX) > clickThreshold ||
148 Math.abs(tp.y - tp.startY) > clickThreshold)) {
149 isClick = false;
150 }
151 dh += tp.x - tp.previousX;
152 dv += tp.y - tp.previousY;
154 tp = touchPoints[1];
155 if (isClick &&
156 (Math.abs(tp.x - tp.startX) > clickThreshold ||
157 Math.abs(tp.y - tp.startY) > clickThreshold)) {
158 isClick = false;
159 }
160 dh += tp.x - tp.previousX;
161 dv += tp.y - tp.previousY;
163 // As we added up the movement of the two fingers, let's divide it again by 2
164 dh /= 2;
165 dv /= 2;
167 UInput.scrollMouse(dh, dv);
168 }
170 touchPoints: [
171 TouchPoint {
172 id: point1
173 },
174 TouchPoint {
175 id: point2
176 }
177 ]
178 }
180 RowLayout {
181 anchors { left: parent.left; right: parent.right; bottom: parent.bottom; margins: -internalGu * 1 }
182 height: internalGu * 10
183 spacing: internalGu * 1
185 MouseArea {
186 id: leftButton
187 objectName: "leftButton"
188 Layout.fillWidth: true
189 Layout.fillHeight: true
190 onPressed: UInput.pressMouse(UInput.ButtonLeft);
191 onReleased: UInput.releaseMouse(UInput.ButtonLeft);
192 property bool highlight: false
193 UbuntuShape {
194 anchors.fill: parent
195 backgroundColor: leftButton.highlight || leftButton.pressed ? UbuntuColors.ash : UbuntuColors.inkstone
196 Behavior on backgroundColor { ColorAnimation { duration: UbuntuAnimation.FastDuration } }
197 }
198 }
200 MouseArea {
201 id: rightButton
202 objectName: "rightButton"
203 Layout.fillWidth: true
204 Layout.fillHeight: true
205 onPressed: UInput.pressMouse(UInput.ButtonRight);
206 onReleased: UInput.releaseMouse(UInput.ButtonRight);
207 property bool highlight: false
208 UbuntuShape {
209 anchors.fill: parent
210 backgroundColor: rightButton.highlight || rightButton.pressed ? UbuntuColors.ash : UbuntuColors.inkstone
211 Behavior on backgroundColor { ColorAnimation { duration: UbuntuAnimation.FastDuration } }
212 }
213 }
214 }
216 AbstractButton {
217 id: oskButton
218 objectName: "oskButton"
219 anchors { right: parent.right; top: parent.top; margins: internalGu * 2 }
220 height: internalGu * 6
221 width: height
223 onClicked: {
224 settings.oskEnabled = !settings.oskEnabled
225 }
227 Rectangle {
228 anchors.fill: parent
229 radius: width / 2
230 color: UbuntuColors.inkstone
231 }
233 Icon {
234 anchors.fill: parent
235 anchors.margins: internalGu * 1.5
236 name: "input-keyboard-symbolic"
237 }
238 }
240 InputMethod {
241 id: inputMethod
242 // Don't resize when there is only one screen to avoid resize clashing with the InputMethod in the Shell.
243 enabled: Screens.count > 1 && settings.oskEnabled && !tutorial.running
244 objectName: "inputMethod"
245 anchors.fill: parent
246 }
248 Label {
249 id: tutorialLabel
250 objectName: "tutorialLabel"
251 anchors { left: parent.left; top: parent.top; right: parent.right; margins: internalGu * 4; topMargin: internalGu * 10 }
252 opacity: 0
253 visible: opacity > 0
254 font.pixelSize: 2 * internalGu
255 color: "white"
256 wrapMode: Text.WordWrap
257 }
259 Icon {
260 id: tutorialImage
261 objectName: "tutorialImage"
262 height: internalGu * 8
263 width: height
264 name: "input-touchpad-symbolic"
265 color: "white"
266 opacity: 0
267 visible: opacity > 0
268 anchors { top: tutorialLabel.bottom; horizontalCenter: parent.horizontalCenter; margins: internalGu * 2 }
269 }
271 Item {
272 id: tutorialFinger1
273 objectName: "tutorialFinger1"
274 width: internalGu * 5
275 height: width
276 property real scale: 1
277 opacity: 0
278 visible: opacity > 0
279 Rectangle {
280 width: parent.width * parent.scale
281 height: width
282 anchors.centerIn: parent
283 radius: width / 2
284 color: UbuntuColors.inkstone
285 }
286 }
288 Item {
289 id: tutorialFinger2
290 objectName: "tutorialFinger2"
291 width: internalGu * 5
292 height: width
293 property real scale: 1
294 opacity: 0
295 visible: opacity > 0
296 Rectangle {
297 width: parent.width * parent.scale
298 height: width
299 anchors.centerIn: parent
300 radius: width / 2
301 color: UbuntuColors.inkstone
302 }
303 }
305 SequentialAnimation {
306 id: tutorial
307 objectName: "tutorialAnimation"
309 PropertyAction { targets: [leftButton, rightButton, oskButton]; property: "enabled"; value: false }
310 PropertyAction { targets: [leftButton, rightButton, oskButton]; property: "opacity"; value: 0 }
311 PropertyAction { target: tutorialLabel; property: "text"; value: i18n.tr("Your device is now connected to an external display. Use this screen as a touch pad to interact with the pointer.") }
312 UbuntuNumberAnimation { targets: [tutorialLabel, tutorialImage]; property: "opacity"; to: 1; duration: UbuntuAnimation.FastDuration }
313 PropertyAction { target: tutorial; property: "paused"; value: true }
314 PauseAnimation { duration: 500 } // it takes a bit until pausing actually takes effect
315 UbuntuNumberAnimation { targets: [tutorialLabel, tutorialImage]; property: "opacity"; to: 0; duration: UbuntuAnimation.FastDuration }
317 UbuntuNumberAnimation { target: leftButton; property: "opacity"; to: 1 }
318 UbuntuNumberAnimation { target: rightButton; property: "opacity"; to: 1 }
320 PauseAnimation { duration: UbuntuAnimation.SleepyDuration }
321 PropertyAction { target: tutorialLabel; property: "text"; value: i18n.tr("Tap left button to click.") }
322 UbuntuNumberAnimation { target: tutorialLabel; property: "opacity"; to: 1; duration: UbuntuAnimation.FastDuration }
323 SequentialAnimation {
324 loops: 2
325 PropertyAction { target: leftButton; property: "highlight"; value: true }
326 PauseAnimation { duration: UbuntuAnimation.FastDuration }
327 PropertyAction { target: leftButton; property: "highlight"; value: false }
328 PauseAnimation { duration: UbuntuAnimation.SleepyDuration }
329 }
330 UbuntuNumberAnimation { target: tutorialLabel; property: "opacity"; to: 0; duration: UbuntuAnimation.FastDuration }
332 PauseAnimation { duration: UbuntuAnimation.SleepyDuration }
333 PropertyAction { target: tutorialLabel; property: "text"; value: i18n.tr("Tap right button to right click.") }
334 UbuntuNumberAnimation { target: tutorialLabel; property: "opacity"; to: 1; duration: UbuntuAnimation.FastDuration }
335 SequentialAnimation {
336 loops: 2
337 PropertyAction { target: rightButton; property: "highlight"; value: true }
338 PauseAnimation { duration: UbuntuAnimation.FastDuration }
339 PropertyAction { target: rightButton; property: "highlight"; value: false }
340 PauseAnimation { duration: UbuntuAnimation.SleepyDuration }
341 }
342 UbuntuNumberAnimation { target: tutorialLabel; property: "opacity"; to: 0; duration: UbuntuAnimation.FastDuration }
344 PauseAnimation { duration: UbuntuAnimation.SleepyDuration }
345 PropertyAction { target: tutorialLabel; property: "text"; value: i18n.tr("Swipe with two fingers to scroll.") }
346 UbuntuNumberAnimation { target: tutorialLabel; property: "opacity"; to: 1; duration: UbuntuAnimation.FastDuration }
347 PropertyAction { target: tutorialFinger1; property: "x"; value: root.width / 2 - tutorialFinger1.width - internalGu * 1 }
348 PropertyAction { target: tutorialFinger2; property: "x"; value: root.width / 2 + tutorialFinger1.width + internalGu * 1 - tutorialFinger2.width }
349 PropertyAction { target: tutorialFinger1; property: "y"; value: root.height / 2 - internalGu * 10 }
350 PropertyAction { target: tutorialFinger2; property: "y"; value: root.height / 2 - internalGu * 10 }
351 SequentialAnimation {
352 ParallelAnimation {
353 UbuntuNumberAnimation { target: tutorialFinger1; property: "opacity"; to: 1; duration: UbuntuAnimation.FastDuration }
354 UbuntuNumberAnimation { target: tutorialFinger2; property: "opacity"; to: 1; duration: UbuntuAnimation.FastDuration }
355 UbuntuNumberAnimation { target: tutorialFinger1; property: "scale"; from: 0; to: 1; duration: UbuntuAnimation.FastDuration }
356 UbuntuNumberAnimation { target: tutorialFinger2; property: "scale"; from: 0; to: 1; duration: UbuntuAnimation.FastDuration }
357 }
358 ParallelAnimation {
359 UbuntuNumberAnimation { target: tutorialFinger1; property: "y"; to: root.height / 2 + internalGu * 10; duration: UbuntuAnimation.SleepyDuration }
360 UbuntuNumberAnimation { target: tutorialFinger2; property: "y"; to: root.height / 2 + internalGu * 10; duration: UbuntuAnimation.SleepyDuration }
361 }
362 ParallelAnimation {
363 UbuntuNumberAnimation { target: tutorialFinger1; property: "opacity"; to: 0; duration: UbuntuAnimation.FastDuration }
364 UbuntuNumberAnimation { target: tutorialFinger2; property: "opacity"; to: 0; duration: UbuntuAnimation.FastDuration }
365 UbuntuNumberAnimation { target: tutorialFinger1; property: "scale"; from: 1; to: 0; duration: UbuntuAnimation.FastDuration }
366 UbuntuNumberAnimation { target: tutorialFinger2; property: "scale"; from: 1; to: 0; duration: UbuntuAnimation.FastDuration }
367 }
368 PauseAnimation { duration: UbuntuAnimation.SlowDuration }
369 ParallelAnimation {
370 UbuntuNumberAnimation { target: tutorialFinger1; property: "opacity"; to: 1; duration: UbuntuAnimation.FastDuration }
371 UbuntuNumberAnimation { target: tutorialFinger2; property: "opacity"; to: 1; duration: UbuntuAnimation.FastDuration }
372 UbuntuNumberAnimation { target: tutorialFinger1; property: "scale"; from: 0; to: 1; duration: UbuntuAnimation.FastDuration }
373 UbuntuNumberAnimation { target: tutorialFinger2; property: "scale"; from: 0; to: 1; duration: UbuntuAnimation.FastDuration }
374 }
375 ParallelAnimation {
376 UbuntuNumberAnimation { target: tutorialFinger1; property: "y"; to: root.height / 2 - internalGu * 10; duration: UbuntuAnimation.SleepyDuration }
377 UbuntuNumberAnimation { target: tutorialFinger2; property: "y"; to: root.height / 2 - internalGu * 10; duration: UbuntuAnimation.SleepyDuration }
378 }
379 ParallelAnimation {
380 UbuntuNumberAnimation { target: tutorialFinger1; property: "opacity"; to: 0; duration: UbuntuAnimation.FastDuration }
381 UbuntuNumberAnimation { target: tutorialFinger2; property: "opacity"; to: 0; duration: UbuntuAnimation.FastDuration }
382 UbuntuNumberAnimation { target: tutorialFinger1; property: "scale"; from: 1; to: 0; duration: UbuntuAnimation.FastDuration }
383 UbuntuNumberAnimation { target: tutorialFinger2; property: "scale"; from: 1; to: 0; duration: UbuntuAnimation.FastDuration }
384 }
385 PauseAnimation { duration: UbuntuAnimation.SlowDuration }
386 }
387 UbuntuNumberAnimation { target: tutorialLabel; property: "opacity"; to: 0; duration: UbuntuAnimation.FastDuration }
389 PauseAnimation { duration: UbuntuAnimation.SleepyDuration }
390 PropertyAction { target: tutorialLabel; property: "text"; value: i18n.tr("Find more settings in the system settings.") }
391 UbuntuNumberAnimation { target: tutorialLabel; property: "opacity"; to: 1; duration: UbuntuAnimation.FastDuration }
392 PauseAnimation { duration: 2000 }
393 UbuntuNumberAnimation { target: tutorialLabel; property: "opacity"; to: 0; duration: UbuntuAnimation.FastDuration }
395 UbuntuNumberAnimation { target: oskButton; property: "opacity"; to: 1 }
396 PropertyAction { targets: [leftButton, rightButton, oskButton]; property: "enabled"; value: true }
398 PropertyAction { target: settings; property: "touchpadTutorialHasRun"; value: true }
399 }