2 * Copyright (C) 2015 Canonical Ltd.
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.
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.
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/>.
18import QtQuick.Layouts 1.1
19import Lomiri.Components 1.3
20import Qt.labs.settings 1.0
28 property bool oskEnabled: false
31 id: aethercastDisplays
34 Component.onCompleted: {
36 if (!settings.touchpadTutorialHasRun) {
40 Component.onDestruction: UInput.removeMouse()
42 function runTutorial() {
43 // If the tutorial animation is started too early, e.g. in Component.onCompleted,
44 // root width & height might be reported as 0x0 still. As animations read their
45 // values at startup and won't update them, lets make sure to only start once
46 // we have some actual size.
47 if (root.width > 0 && root.height > 0) {
50 tutorialTimer.start();
59 onTriggered: root.runTutorial();
62 readonly property bool pressed: point1.pressed || point2.pressed || leftButton.pressed || rightButton.pressed
64 property var settings: Settings {
65 objectName: "virtualTouchPadSettings"
66 property bool touchpadTutorialHasRun: false
67 property bool oskEnabled: true
71 objectName: "touchPadArea"
73 enabled: !tutorial.running || tutorial.paused
75 // FIXME: Once we have Qt DPR support, this should be Qt.styleHints.startDragDistance
76 readonly property int clickThreshold: internalGu * 0.5
77 property bool isClick: false
78 property bool isDoubleClick: false
79 property bool isDrag: false
82 if (tutorial.paused) {
87 // If double-tapping *really* fast, it could happen that we end up having only point2 pressed
88 // Make sure we check for both combos, only point1 or only point2
89 if (((point1.pressed && !point2.pressed) || (!point1.pressed && point2.pressed))
90 && clickTimer.running) {
92 UInput.pressMouse(UInput.ButtonLeft)
99 switch (touchPoints.length) {
101 moveMouse(touchPoints);
110 if (isDoubleClick || isDrag) {
111 UInput.releaseMouse(UInput.ButtonLeft)
112 isDoubleClick = false;
115 clickTimer.scheduleClick(point1.pressed ? UInput.ButtonRight : UInput.ButtonLeft)
125 property int button: UInput.ButtonLeft
127 UInput.pressMouse(button);
128 UInput.releaseMouse(button);
130 function scheduleClick(button) {
131 clickTimer.button = button;
136 function moveMouse(touchPoints) {
137 var tp = touchPoints[0];
139 (Math.abs(tp.x - tp.startX) > clickThreshold ||
140 Math.abs(tp.y - tp.startY) > clickThreshold)) {
145 UInput.moveMouse(tp.x - tp.previousX, tp.y - tp.previousY);
148 function scroll(touchPoints) {
151 var tp = touchPoints[0];
153 (Math.abs(tp.x - tp.startX) > clickThreshold ||
154 Math.abs(tp.y - tp.startY) > clickThreshold)) {
157 dh += tp.x - tp.previousX;
158 dv += tp.y - tp.previousY;
162 (Math.abs(tp.x - tp.startX) > clickThreshold ||
163 Math.abs(tp.y - tp.startY) > clickThreshold)) {
166 dh += tp.x - tp.previousX;
167 dv += tp.y - tp.previousY;
169 // As we added up the movement of the two fingers, let's divide it again by 2
173 UInput.scrollMouse(dh, dv);
187 anchors { left: parent.left; right: parent.right; bottom: parent.bottom; margins: -internalGu * 1 }
188 height: internalGu * 10
189 spacing: internalGu * 1
193 objectName: "leftButton"
194 Layout.fillWidth: true
195 Layout.fillHeight: true
196 onPressed: UInput.pressMouse(UInput.ButtonLeft);
197 onReleased: UInput.releaseMouse(UInput.ButtonLeft);
198 property bool highlight: false
201 backgroundColor: leftButton.highlight || leftButton.pressed ? LomiriColors.ash : LomiriColors.inkstone
202 Behavior on backgroundColor { ColorAnimation { duration: LomiriAnimation.FastDuration } }
208 objectName: "rightButton"
209 Layout.fillWidth: true
210 Layout.fillHeight: true
211 onPressed: UInput.pressMouse(UInput.ButtonRight);
212 onReleased: UInput.releaseMouse(UInput.ButtonRight);
213 property bool highlight: false
216 backgroundColor: rightButton.highlight || rightButton.pressed ? LomiriColors.ash : LomiriColors.inkstone
217 Behavior on backgroundColor { ColorAnimation { duration: LomiriAnimation.FastDuration } }
224 objectName: "disconnectButton"
225 anchors { right: parent.right; top: parent.top; margins: internalGu * 2 }
226 height: internalGu * 6
227 width: visible ? height : 0
228 visible: aethercastDisplays.state === "connected"
231 aethercastDisplays.enabled = false
237 color: LomiriColors.inkstone
242 anchors.margins: internalGu * 1.5
250 objectName: "oskButton"
251 anchors { right: disconnectButton.left; top: parent.top; margins: internalGu * 2 }
252 height: internalGu * 6
256 settings.oskEnabled = !settings.oskEnabled
262 color: LomiriColors.inkstone
267 anchors.margins: internalGu * 1.5
268 name: "input-keyboard-symbolic"
269 color: settings.oskEnabled ? LomiriColors.porcelain : LomiriColors.red
275 // Don't resize when there is only one screen to avoid resize clashing with the InputMethod in the Shell.
276 enabled: root.oskEnabled && settings.oskEnabled && !tutorial.running
277 objectName: "inputMethod"
283 objectName: "tutorialLabel"
284 anchors { left: parent.left; top: parent.top; right: parent.right; margins: internalGu * 4; topMargin: internalGu * 10 }
287 font.pixelSize: 2 * internalGu
289 wrapMode: Text.WordWrap
294 objectName: "tutorialImage"
295 height: internalGu * 8
297 name: "input-touchpad-symbolic"
301 anchors { top: tutorialLabel.bottom; horizontalCenter: parent.horizontalCenter; margins: internalGu * 2 }
306 objectName: "tutorialFinger1"
307 width: internalGu * 5
309 property real scale: 1
313 width: parent.width * parent.scale
315 anchors.centerIn: parent
317 color: LomiriColors.inkstone
323 objectName: "tutorialFinger2"
324 width: internalGu * 5
326 property real scale: 1
330 width: parent.width * parent.scale
332 anchors.centerIn: parent
334 color: LomiriColors.inkstone
338 SequentialAnimation {
340 objectName: "tutorialAnimation"
342 PropertyAction { targets: [leftButton, rightButton, oskButton]; property: "enabled"; value: false }
343 PropertyAction { targets: [leftButton, rightButton, oskButton]; property: "opacity"; value: 0 }
344 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.") }
345 LomiriNumberAnimation { targets: [tutorialLabel, tutorialImage]; property: "opacity"; to: 1; duration: LomiriAnimation.FastDuration }
346 PropertyAction { target: tutorial; property: "paused"; value: true }
347 PauseAnimation { duration: 500 } // it takes a bit until pausing actually takes effect
348 LomiriNumberAnimation { targets: [tutorialLabel, tutorialImage]; property: "opacity"; to: 0; duration: LomiriAnimation.FastDuration }
350 LomiriNumberAnimation { target: leftButton; property: "opacity"; to: 1 }
351 LomiriNumberAnimation { target: rightButton; property: "opacity"; to: 1 }
353 PauseAnimation { duration: LomiriAnimation.SleepyDuration }
354 PropertyAction { target: tutorialLabel; property: "text"; value: i18n.tr("Tap left button to click.") }
355 LomiriNumberAnimation { target: tutorialLabel; property: "opacity"; to: 1; duration: LomiriAnimation.FastDuration }
356 SequentialAnimation {
358 PropertyAction { target: leftButton; property: "highlight"; value: true }
359 PauseAnimation { duration: LomiriAnimation.FastDuration }
360 PropertyAction { target: leftButton; property: "highlight"; value: false }
361 PauseAnimation { duration: LomiriAnimation.SleepyDuration }
363 LomiriNumberAnimation { target: tutorialLabel; property: "opacity"; to: 0; duration: LomiriAnimation.FastDuration }
365 PauseAnimation { duration: LomiriAnimation.SleepyDuration }
366 PropertyAction { target: tutorialLabel; property: "text"; value: i18n.tr("Tap right button to right click.") }
367 LomiriNumberAnimation { target: tutorialLabel; property: "opacity"; to: 1; duration: LomiriAnimation.FastDuration }
368 SequentialAnimation {
370 PropertyAction { target: rightButton; property: "highlight"; value: true }
371 PauseAnimation { duration: LomiriAnimation.FastDuration }
372 PropertyAction { target: rightButton; property: "highlight"; value: false }
373 PauseAnimation { duration: LomiriAnimation.SleepyDuration }
375 LomiriNumberAnimation { target: tutorialLabel; property: "opacity"; to: 0; duration: LomiriAnimation.FastDuration }
377 PauseAnimation { duration: LomiriAnimation.SleepyDuration }
378 PropertyAction { target: tutorialLabel; property: "text"; value: i18n.tr("Swipe with two fingers to scroll.") }
379 LomiriNumberAnimation { target: tutorialLabel; property: "opacity"; to: 1; duration: LomiriAnimation.FastDuration }
380 PropertyAction { target: tutorialFinger1; property: "x"; value: root.width / 2 - tutorialFinger1.width - internalGu * 1 }
381 PropertyAction { target: tutorialFinger2; property: "x"; value: root.width / 2 + tutorialFinger1.width + internalGu * 1 - tutorialFinger2.width }
382 PropertyAction { target: tutorialFinger1; property: "y"; value: root.height / 2 - internalGu * 10 }
383 PropertyAction { target: tutorialFinger2; property: "y"; value: root.height / 2 - internalGu * 10 }
384 SequentialAnimation {
386 LomiriNumberAnimation { target: tutorialFinger1; property: "opacity"; to: 1; duration: LomiriAnimation.FastDuration }
387 LomiriNumberAnimation { target: tutorialFinger2; property: "opacity"; to: 1; duration: LomiriAnimation.FastDuration }
388 LomiriNumberAnimation { target: tutorialFinger1; property: "scale"; from: 0; to: 1; duration: LomiriAnimation.FastDuration }
389 LomiriNumberAnimation { target: tutorialFinger2; property: "scale"; from: 0; to: 1; duration: LomiriAnimation.FastDuration }
392 LomiriNumberAnimation { target: tutorialFinger1; property: "y"; to: root.height / 2 + internalGu * 10; duration: LomiriAnimation.SleepyDuration }
393 LomiriNumberAnimation { target: tutorialFinger2; property: "y"; to: root.height / 2 + internalGu * 10; duration: LomiriAnimation.SleepyDuration }
396 LomiriNumberAnimation { target: tutorialFinger1; property: "opacity"; to: 0; duration: LomiriAnimation.FastDuration }
397 LomiriNumberAnimation { target: tutorialFinger2; property: "opacity"; to: 0; duration: LomiriAnimation.FastDuration }
398 LomiriNumberAnimation { target: tutorialFinger1; property: "scale"; from: 1; to: 0; duration: LomiriAnimation.FastDuration }
399 LomiriNumberAnimation { target: tutorialFinger2; property: "scale"; from: 1; to: 0; duration: LomiriAnimation.FastDuration }
401 PauseAnimation { duration: LomiriAnimation.SlowDuration }
403 LomiriNumberAnimation { target: tutorialFinger1; property: "opacity"; to: 1; duration: LomiriAnimation.FastDuration }
404 LomiriNumberAnimation { target: tutorialFinger2; property: "opacity"; to: 1; duration: LomiriAnimation.FastDuration }
405 LomiriNumberAnimation { target: tutorialFinger1; property: "scale"; from: 0; to: 1; duration: LomiriAnimation.FastDuration }
406 LomiriNumberAnimation { target: tutorialFinger2; property: "scale"; from: 0; to: 1; duration: LomiriAnimation.FastDuration }
409 LomiriNumberAnimation { target: tutorialFinger1; property: "y"; to: root.height / 2 - internalGu * 10; duration: LomiriAnimation.SleepyDuration }
410 LomiriNumberAnimation { target: tutorialFinger2; property: "y"; to: root.height / 2 - internalGu * 10; duration: LomiriAnimation.SleepyDuration }
413 LomiriNumberAnimation { target: tutorialFinger1; property: "opacity"; to: 0; duration: LomiriAnimation.FastDuration }
414 LomiriNumberAnimation { target: tutorialFinger2; property: "opacity"; to: 0; duration: LomiriAnimation.FastDuration }
415 LomiriNumberAnimation { target: tutorialFinger1; property: "scale"; from: 1; to: 0; duration: LomiriAnimation.FastDuration }
416 LomiriNumberAnimation { target: tutorialFinger2; property: "scale"; from: 1; to: 0; duration: LomiriAnimation.FastDuration }
418 PauseAnimation { duration: LomiriAnimation.SlowDuration }
420 LomiriNumberAnimation { target: tutorialLabel; property: "opacity"; to: 0; duration: LomiriAnimation.FastDuration }
422 PauseAnimation { duration: LomiriAnimation.SleepyDuration }
423 PropertyAction { target: tutorialLabel; property: "text"; value: i18n.tr("Find more settings in the system settings.") }
424 LomiriNumberAnimation { target: tutorialLabel; property: "opacity"; to: 1; duration: LomiriAnimation.FastDuration }
425 PauseAnimation { duration: 2000 }
426 LomiriNumberAnimation { target: tutorialLabel; property: "opacity"; to: 0; duration: LomiriAnimation.FastDuration }
428 LomiriNumberAnimation { target: oskButton; property: "opacity"; to: 1 }
429 PropertyAction { targets: [leftButton, rightButton, oskButton]; property: "enabled"; value: true }
431 PropertyAction { target: settings; property: "touchpadTutorialHasRun"; value: true }