on xenial help sign in
Only show top tutorial after 3s of inactivity
1e10d9fMichael Terry
plugins/Utils/CMakeLists.txt+1−1
@@ -17,7 +17,7 @@ set(QMLPLUGIN_SRC
17 unitysortfilterproxymodelqml.cpp
17 unitysortfilterproxymodelqml.cpp
18 Timer.cpp
18 Timer.cpp
19 unitymenumodelpaths.cpp
19 unitymenumodelpaths.cpp
20− windowkeysfilter.cpp
20+ windowinputfilter.cpp
21 windowscreenshotprovider.cpp
21 windowscreenshotprovider.cpp
22 easingcurve.cpp
22 easingcurve.cpp
23 windowstatestorage.cpp
23 windowstatestorage.cpp
plugins/Utils/Utils.qmltypes+2−2
@@ -147,10 +147,10 @@ Module {
147 }
147 }
148 }
148 }
149 Component {
149 Component {
150− name: "WindowKeysFilter"
150+ name: "WindowInputFilter"
151 defaultProperty: "data"
151 defaultProperty: "data"
152 prototype: "QQuickItem"
152 prototype: "QQuickItem"
153− exports: ["Utils/WindowKeysFilter 0.1"]
153+ exports: ["Utils/WindowInputFilter 0.1"]
154 exportMetaObjectRevisions: [0]
154 exportMetaObjectRevisions: [0]
155 }
155 }
156 Component {
156 Component {
plugins/Utils/plugin.cpp+2−2
@@ -30,7 +30,7 @@
30#include "qlimitproxymodelqml.h"
30#include "qlimitproxymodelqml.h"
31#include "unitysortfilterproxymodelqml.h"
31#include "unitysortfilterproxymodelqml.h"
32#include "unitymenumodelpaths.h"
32#include "unitymenumodelpaths.h"
33−#include "windowkeysfilter.h"
33+#include "windowinputfilter.h"
34#include "windowscreenshotprovider.h"
34#include "windowscreenshotprovider.h"
35#include "windowstatestorage.h"
35#include "windowstatestorage.h"
36#include "constants.h"
36#include "constants.h"
@@ -58,7 +58,7 @@ void UtilsPlugin::registerTypes(const char *uri)
58 qmlRegisterType<QLimitProxyModelQML>(uri, 0, 1, "LimitProxyModel");
58 qmlRegisterType<QLimitProxyModelQML>(uri, 0, 1, "LimitProxyModel");
59 qmlRegisterType<UnitySortFilterProxyModelQML>(uri, 0, 1, "UnitySortFilterProxyModel");
59 qmlRegisterType<UnitySortFilterProxyModelQML>(uri, 0, 1, "UnitySortFilterProxyModel");
60 qmlRegisterType<UnityMenuModelPaths>(uri, 0, 1, "UnityMenuModelPaths");
60 qmlRegisterType<UnityMenuModelPaths>(uri, 0, 1, "UnityMenuModelPaths");
61− qmlRegisterType<WindowKeysFilter>(uri, 0, 1, "WindowKeysFilter");
61+ qmlRegisterType<WindowInputFilter>(uri, 0, 1, "WindowInputFilter");
62 qmlRegisterType<EasingCurve>(uri, 0, 1, "EasingCurve");
62 qmlRegisterType<EasingCurve>(uri, 0, 1, "EasingCurve");
63 qmlRegisterSingletonType<WindowStateStorage>(uri, 0, 1, "WindowStateStorage", createWindowStateStorage);
63 qmlRegisterSingletonType<WindowStateStorage>(uri, 0, 1, "WindowStateStorage", createWindowStateStorage);
64 qmlRegisterType<InputWatcher>(uri, 0, 1, "InputWatcher");
64 qmlRegisterType<InputWatcher>(uri, 0, 1, "InputWatcher");
plugins/Utils/windowkeysfilter.cpp → plugins/Utils/windowinputfilter.cpprenamed+14−16
@@ -16,36 +16,34 @@
16 * Author: Daniel d'Andrada <daniel.dandrada@canonical.com>
16 * Author: Daniel d'Andrada <daniel.dandrada@canonical.com>
17 */
17 */
18
18
19−#include "windowkeysfilter.h"
19+#include "windowinputfilter.h"
20
20
21#include <QQuickWindow>
21#include <QQuickWindow>
22
22
23−WindowKeysFilter::WindowKeysFilter(QQuickItem *parent)
23+WindowInputFilter::WindowInputFilter(QQuickItem *parent)
24 : QQuickItem(parent),
24 : QQuickItem(parent),
25− m_currentEventTimestamp(0)
25+ m_lastInputTimestamp(0)
26{
26{
27 connect(this, &QQuickItem::windowChanged,
27 connect(this, &QQuickItem::windowChanged,
28− this, &WindowKeysFilter::setupFilterOnWindow);
28+ this, &WindowInputFilter::setupFilterOnWindow);
29}
29}
30
30
31−bool WindowKeysFilter::eventFilter(QObject *watched, QEvent *event)
31+bool WindowInputFilter::eventFilter(QObject *watched, QEvent *event)
32{
32{
33 Q_ASSERT(!m_filteredWindow.isNull());
33 Q_ASSERT(!m_filteredWindow.isNull());
34 Q_ASSERT(watched == static_cast<QObject*>(m_filteredWindow.data()));
34 Q_ASSERT(watched == static_cast<QObject*>(m_filteredWindow.data()));
35 Q_UNUSED(watched);
35 Q_UNUSED(watched);
36
36
37+ QInputEvent *inputEvent = dynamic_cast<QInputEvent*>(event);
38+ if (inputEvent) {
39+ m_lastInputTimestamp = inputEvent->timestamp();
40+ Q_EMIT lastInputTimestampChanged();
41+ }
42+
37 if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) {
43 if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) {
38 // Let QML see this event and decide if it does not want it
44 // Let QML see this event and decide if it does not want it
39 event->accept();
45 event->accept();
40−
41− m_currentEventTimestamp = static_cast<QInputEvent*>(event)->timestamp();
42− Q_EMIT currentEventTimestampChanged();
43−
44 QCoreApplication::sendEvent(this, event);
46 QCoreApplication::sendEvent(this, event);
45−
46− m_currentEventTimestamp = 0;
47− Q_EMIT currentEventTimestampChanged();
48−
49 return event->isAccepted();
47 return event->isAccepted();
50 } else {
48 } else {
51 // Not interested
49 // Not interested
@@ -53,7 +51,7 @@ bool WindowKeysFilter::eventFilter(QObject *watched, QEvent *event)
53 }
51 }
54}
52}
55
53
56−void WindowKeysFilter::setupFilterOnWindow(QQuickWindow *window)
54+void WindowInputFilter::setupFilterOnWindow(QQuickWindow *window)
57{
55{
58 if (!m_filteredWindow.isNull()) {
56 if (!m_filteredWindow.isNull()) {
59 m_filteredWindow->removeEventFilter(this);
57 m_filteredWindow->removeEventFilter(this);
@@ -66,7 +64,7 @@ void WindowKeysFilter::setupFilterOnWindow(QQuickWindow *window)
66 }
64 }
67}
65}
68
66
69−ulong WindowKeysFilter::currentEventTimestamp() const
67+ulong WindowInputFilter::lastInputTimestamp() const
70{
68{
71− return m_currentEventTimestamp;
69+ return m_lastInputTimestamp;
72}
70}
plugins/Utils/windowkeysfilter.h → plugins/Utils/windowinputfilter.hrenamed+11−11
@@ -16,8 +16,8 @@
16 * Author: Daniel d'Andrada <daniel.dandrada@canonical.com>
16 * Author: Daniel d'Andrada <daniel.dandrada@canonical.com>
17 */
17 */
18
18
19−#ifndef UNITY_WINDOWKEYSFILTER_H
19+#ifndef UNITY_WINDOWINPUTFILTER_H
20−#define UNITY_WINDOWKEYSFILTER_H
20+#define UNITY_WINDOWINPUTFILTER_H
21
21
22#include <QQuickItem>
22#include <QQuickItem>
23#include <QPointer>
23#include <QPointer>
@@ -29,30 +29,30 @@
29 accepted ones will be filtered out. Events are accepted by default, so make sure you reject
29 accepted ones will be filtered out. Events are accepted by default, so make sure you reject
30 the keys you're not interested in.
30 the keys you're not interested in.
31
31
32− If more than one WindowKeysFilter exist in the same QML scene (and thus in the same QQuickWindow)
32+ If more than one WindowInputFilter exist in the same QML scene (and thus in the same QQuickWindow)
33 they will be called in the order of creation, which can be tricky to assess. So the best practice
33 they will be called in the order of creation, which can be tricky to assess. So the best practice
34− is to have at most one WindowKeysFilter per QML scene.
34+ is to have at most one WindowInputFilter per QML scene.
35 */
35 */
36−class WindowKeysFilter : public QQuickItem
36+class WindowInputFilter : public QQuickItem
37{
37{
38 Q_OBJECT
38 Q_OBJECT
39− Q_PROPERTY(ulong currentEventTimestamp READ currentEventTimestamp NOTIFY currentEventTimestampChanged)
39+ Q_PROPERTY(ulong lastInputTimestamp READ lastInputTimestamp NOTIFY lastInputTimestampChanged)
40public:
40public:
41− WindowKeysFilter(QQuickItem *parent = 0);
41+ WindowInputFilter(QQuickItem *parent = 0);
42
42
43 bool eventFilter(QObject *watched, QEvent *event) override;
43 bool eventFilter(QObject *watched, QEvent *event) override;
44
44
45− ulong currentEventTimestamp() const;
45+ ulong lastInputTimestamp() const;
46
46
47Q_SIGNALS:
47Q_SIGNALS:
48− void currentEventTimestampChanged();
48+ void lastInputTimestampChanged();
49
49
50private Q_SLOTS:
50private Q_SLOTS:
51 void setupFilterOnWindow(QQuickWindow *window);
51 void setupFilterOnWindow(QQuickWindow *window);
52
52
53private:
53private:
54 QPointer<QQuickWindow> m_filteredWindow;
54 QPointer<QQuickWindow> m_filteredWindow;
55− ulong m_currentEventTimestamp;
55+ ulong m_lastInputTimestamp;
56};
56};
57
57
58−#endif // UNITY_WINDOWKEYSFILTER_H
58+#endif // UNITY_WINDOWINPUTFILTER_H
qml/Shell.qml+4−4
@@ -24,7 +24,7 @@ import Ubuntu.Gestures 0.1
24import Ubuntu.Telephony 0.1 as Telephony
24import Ubuntu.Telephony 0.1 as Telephony
25import Unity.Connectivity 0.1
25import Unity.Connectivity 0.1
26import Unity.Launcher 0.1
26import Unity.Launcher 0.1
27−import GlobalShortcut 1.0 // has to be before Utils, because of WindowKeysFilter
27+import GlobalShortcut 1.0 // has to be before Utils, because of WindowInputFilter
28import Utils 0.1
28import Utils 0.1
29import Powerd 0.1
29import Powerd 0.1
30import SessionBroadcast 0.1
30import SessionBroadcast 0.1
@@ -171,9 +171,9 @@ Item {
171 // dummy shortcut to force creation of GlobalShortcutRegistry before WindowKeyFilter
171 // dummy shortcut to force creation of GlobalShortcutRegistry before WindowKeyFilter
172 }
172 }
173
173
174− WindowKeysFilter {
174+ WindowInputFilter {
175− Keys.onPressed: physicalKeysMapper.onKeyPressed(event, currentEventTimestamp);
175+ Keys.onPressed: physicalKeysMapper.onKeyPressed(event, lastInputTimestamp);
176− Keys.onReleased: physicalKeysMapper.onKeyReleased(event, currentEventTimestamp);
176+ Keys.onReleased: physicalKeysMapper.onKeyReleased(event, lastInputTimestamp);
177 }
177 }
178
178
179 HomeKeyWatcher {
179 HomeKeyWatcher {
qml/Tutorial/Tutorial.qml+7
@@ -34,6 +34,7 @@ Item {
34 property string usageScenario
34 property string usageScenario
35 property bool paused
35 property bool paused
36 property bool keyboardVisible
36 property bool keyboardVisible
37+ property int lastInputTimestamp
37
38
38 readonly property bool launcherEnabled: loader.item ? loader.item.launcherEnabled : true
39 readonly property bool launcherEnabled: loader.item ? loader.item.launcherEnabled : true
39 readonly property bool spreadEnabled: loader.item ? loader.item.spreadEnabled : true
40 readonly property bool spreadEnabled: loader.item ? loader.item.spreadEnabled : true
@@ -88,6 +89,12 @@ Item {
88 value: root.keyboardVisible
89 value: root.keyboardVisible
89 }
90 }
90
91
92+ Binding {
93+ target: loader.item
94+ property: "lastInputTimestamp"
95+ value: root.lastInputTimestamp
96+ }
97+
91 Connections {
98 Connections {
92 target: loader.item
99 target: loader.item
93 onFinished: AccountsService.demoEdges = false
100 onFinished: AccountsService.demoEdges = false
qml/Tutorial/TutorialContent.qml+37−8
@@ -28,6 +28,7 @@ Item {
28 property string usageScenario
28 property string usageScenario
29 property bool paused
29 property bool paused
30 property bool keyboardVisible
30 property bool keyboardVisible
31+ property var lastInputTimestamp
31
32
32 readonly property bool launcherEnabled: !running || tutorialLeft.shown
33 readonly property bool launcherEnabled: !running || tutorialLeft.shown
33 readonly property bool spreadEnabled: !running || tutorialRight.shown
34 readonly property bool spreadEnabled: !running || tutorialRight.shown
@@ -76,16 +77,36 @@ Item {
76 || AccountsService.demoEdgesCompleted.indexOf("top") != -1
77 || AccountsService.demoEdgesCompleted.indexOf("top") != -1
77 isReady: tutorialLeft.skipped && !skipped && !paused && !keyboardVisible && !tutorialBottom.shown
78 isReady: tutorialLeft.skipped && !skipped && !paused && !keyboardVisible && !tutorialBottom.shown
78
79
80+ // We fire 30s after left edge tutorial, with at least 3s of inactivity
81+
79 Timer {
82 Timer {
80− id: tutorialTopTimer
83+ id: tutorialTopInactivityTimer
81− objectName: "tutorialTopTimer"
84+ objectName: "tutorialTopInactivityTimer"
82− interval: 30000
85+ interval: 3000
86+
87+ Connections {
88+ target: root
89+ onLastInputTimestampChanged: {
90+ if (tutorialTopInactivityTimer.running) {
91+ tutorialTopInactivityTimer.restart();
92+ }
93+ }
94+ }
95+
96+ onTriggered: {
83− onTriggered: if (tutorialTop.isReady && !tutorialTop.shown) tutorialTop.show()
97+ if (tutorialTop.isReady) {
98+ tutorialTop.show();
99+ } else {
100+ restart();
101+ }
102+ }
84 }
103 }
85
104
86− Connections {
105+ Timer {
87− target: AccountsService
106+ id: tutorialTopTimer
88− onDemoEdgesCompletedChanged: if (tutorialTop.isReady && !tutorialTop.shown) tutorialTopTimer.start()
107+ objectName: "tutorialTopTimer"
108+ interval: 27000
109+ onTriggered: tutorialTopInactivityTimer.start()
89 }
110 }
90
111
91 onSkippedChanged: if (skipped && shown) hide()
112 onSkippedChanged: if (skipped && shown) hide()
@@ -110,7 +131,15 @@ Item {
110 id: tutorialRightTimer
131 id: tutorialRightTimer
111 objectName: "tutorialRightTimer"
132 objectName: "tutorialRightTimer"
112 interval: 3000
133 interval: 3000
134+ onTriggered: {
113− onTriggered: if (tutorialRight.isReady && !tutorialRight.shown) tutorialRight.show()
135+ if (tutorialRight.isReady) {
136+ if (!tutorialRight.shown) {
137+ tutorialRight.show();
138+ }
139+ } else {
140+ restart();
141+ }
142+ }
114 }
143 }
115
144
116 Connections {
145 Connections {
tests/mocks/Utils/CMakeLists.txt+1−1
@@ -14,7 +14,7 @@ set(QMLPLUGIN_SRC
14 ${CMAKE_SOURCE_DIR}/plugins/Utils/qlimitproxymodelqml.cpp
14 ${CMAKE_SOURCE_DIR}/plugins/Utils/qlimitproxymodelqml.cpp
15 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitysortfilterproxymodelqml.cpp
15 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitysortfilterproxymodelqml.cpp
16 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitymenumodelpaths.cpp
16 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitymenumodelpaths.cpp
17− ${CMAKE_SOURCE_DIR}/plugins/Utils/windowkeysfilter.cpp
17+ ${CMAKE_SOURCE_DIR}/plugins/Utils/windowinputfilter.cpp
18 ${CMAKE_SOURCE_DIR}/plugins/Utils/windowscreenshotprovider.cpp
18 ${CMAKE_SOURCE_DIR}/plugins/Utils/windowscreenshotprovider.cpp
19 ${CMAKE_SOURCE_DIR}/plugins/Utils/easingcurve.cpp
19 ${CMAKE_SOURCE_DIR}/plugins/Utils/easingcurve.cpp
20 ${CMAKE_SOURCE_DIR}/plugins/Utils/inputwatcher.cpp
20 ${CMAKE_SOURCE_DIR}/plugins/Utils/inputwatcher.cpp
tests/mocks/Utils/Utils.qmltypes+2−2
@@ -145,10 +145,10 @@ Module {
145 }
145 }
146 }
146 }
147 Component {
147 Component {
148− name: "WindowKeysFilter"
148+ name: "WindowInputFilter"
149 defaultProperty: "data"
149 defaultProperty: "data"
150 prototype: "QQuickItem"
150 prototype: "QQuickItem"
151− exports: ["Utils/WindowKeysFilter 0.1"]
151+ exports: ["Utils/WindowInputFilter 0.1"]
152 exportMetaObjectRevisions: [0]
152 exportMetaObjectRevisions: [0]
153 }
153 }
154 Component {
154 Component {
tests/mocks/Utils/plugin.cpp+2−2
@@ -32,7 +32,7 @@
32#include <qlimitproxymodelqml.h>
32#include <qlimitproxymodelqml.h>
33#include <unitysortfilterproxymodelqml.h>
33#include <unitysortfilterproxymodelqml.h>
34#include <unitymenumodelpaths.h>
34#include <unitymenumodelpaths.h>
35−#include <windowkeysfilter.h>
35+#include <windowinputfilter.h>
36#include <windowscreenshotprovider.h>
36#include <windowscreenshotprovider.h>
37#include <easingcurve.h>
37#include <easingcurve.h>
38
38
@@ -57,7 +57,7 @@ void FakeUtilsPlugin::registerTypes(const char *uri)
57 qmlRegisterType<QLimitProxyModelQML>(uri, 0, 1, "LimitProxyModel");
57 qmlRegisterType<QLimitProxyModelQML>(uri, 0, 1, "LimitProxyModel");
58 qmlRegisterType<UnitySortFilterProxyModelQML>(uri, 0, 1, "UnitySortFilterProxyModel");
58 qmlRegisterType<UnitySortFilterProxyModelQML>(uri, 0, 1, "UnitySortFilterProxyModel");
59 qmlRegisterType<UnityMenuModelPaths>(uri, 0, 1, "UnityMenuModelPaths");
59 qmlRegisterType<UnityMenuModelPaths>(uri, 0, 1, "UnityMenuModelPaths");
60− qmlRegisterType<WindowKeysFilter>(uri, 0, 1, "WindowKeysFilter");
60+ qmlRegisterType<WindowInputFilter>(uri, 0, 1, "WindowInputFilter");
61 qmlRegisterType<EasingCurve>(uri, 0, 1, "EasingCurve");
61 qmlRegisterType<EasingCurve>(uri, 0, 1, "EasingCurve");
62 qmlRegisterSingletonType<WindowStateStorage>(uri, 0, 1, "WindowStateStorage", createWindowStateStorage);
62 qmlRegisterSingletonType<WindowStateStorage>(uri, 0, 1, "WindowStateStorage", createWindowStateStorage);
63 qmlRegisterType<InputWatcher>(uri, 0, 1, "InputWatcher");
63 qmlRegisterType<InputWatcher>(uri, 0, 1, "InputWatcher");