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