on xenial help sign in
Merged with trunk.
a6f5093Leo Arias
data/unity8-dash.conf+5
@@ -8,6 +8,11 @@ stop on stopping unity8
8
8
9kill timeout 120
9kill timeout 120
10
10
11+# Focused app is 100 so we want to be less likely to get
12+# killed than the focused app but more than Unity8
13+
14+oom score 50
15+
11respawn
16respawn
12
17
13env APP_ID=unity8-dash
18env APP_ID=unity8-dash
debian/changelog+78
@@ -1,3 +1,81 @@
1+unity8 (8.01+15.04.20141030-0ubuntu1) vivid; urgency=medium
2+
3+ [ Michael Terry ]
4+ * Provide 'passphrase' as a field of Components.Lockscreen
5+ * Fix a race between Qml loading and DBus registration that caused
6+ problems when jenkins tried to unlock the phone.
7+ * Set domain explicitly for the Dialogs component because the welcome
8+ wizard wants to import it. (LP: #1381731)
9+ * When greeter or lockscreen has focus, show active call panel. (LP:
10+ #1378872)
11+
12+ [ Ted Gould ]
13+ * Set the default OOM score for the dash (LP: #1379786)
14+
15+ [ Michał Sawicz ]
16+ * Revert lp:~unity-team/unity8/flickables-speed-workaround to avoid
17+ risk in RTM.
18+ * Updated behaviour for zoomable image, workaround for sourcesize (LP:
19+ #1333187)
20+
21+ [ Michael Frey ]
22+ * Added a check for Proximity to determine if we show the lock screen.
23+ (LP: #1378012)
24+
25+ [ Ying-Chun Liu ]
26+ * Add non-interactive code into GenericScopeView. (LP: #1384441)
27+
28+ [ Mirco Müller ]
29+ * Also use modal nature of snap-decision notifications when
30+ greeter/lockscreen is shown. This fixes LP: #1378827. (LP: #1378827)
31+
32+ [ Michael Zanetti ]
33+ * Make the launcher update on dconf changes (LP: #1376707)
34+ * exit spread on background tap (LP: #1368261)
35+ * Use an index instead of a scope id in DashCommunicator (LP:
36+ #1376044)
37+
38+ [ Andrea Cimitan ]
39+ * Updated behaviour for zoomable image, workaround for sourcesize (LP:
40+ #1333187)
41+
42+ [ Daniel d'Andrada ]
43+ * Make TouchGate synthesize QMouseEvents for mouse-based target items
44+ * Don't specify a distanceThreshold as it conflicts with
45+ hintDisplacment
46+
47+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Thu, 30 Oct 2014 21:43:42 +0000
48+
49+unity8 (8.00+15.04.20141030-0ubuntu1) vivid; urgency=low
50+
51+ [ josharenson ]
52+ * Fix lp:1370240 by making stages interactive when a snap decision is
53+ dismissed. (LP: #1370240)
54+
55+ [ Nick Dedekind ]
56+ * remove qml ownership confusion for caching unitymenumodels (LP:
57+ #1368856)
58+ * Implementation of expandable panel design (LP: #1368856)
59+
60+ [ Albert Astals ]
61+ * CardAttributes: Specify column and row since the gridlayout gets
62+ confused sometimes (LP: #1381092)
63+ * Reset VerticalJournal until the cardTool settles (LP: #1381255)
64+
65+ [ Mirco Müller ]
66+ * Added synchronous/confirmation notification support to unity8. (LP:
67+ #1232633)
68+
69+ [ Michael Zanetti ]
70+ * Fix lp:1370240 by making stages interactive when a snap decision is
71+ dismissed. (LP: #1370240)
72+ * Drop all visual indication of "pinning" (LP: #1381054)
73+
74+ [ Antti Kaijanmäki ]
75+ * Unlock all modems on boot. (LP: #1333121)
76+
77+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Thu, 30 Oct 2014 10:51:17 +0000
78+
1unity8 (8.00+14.10.20141013.2-0ubuntu1) utopic; urgency=low
79unity8 (8.00+14.10.20141013.2-0ubuntu1) utopic; urgency=low
2
80
3 [ Michael Terry ]
81 [ Michael Terry ]
debian/control+1−1
@@ -97,7 +97,7 @@ Depends: gsettings-desktop-schemas,
97Recommends: unity-scope-click,
97Recommends: unity-scope-click,
98 unity-scope-mediascanner2,
98 unity-scope-mediascanner2,
99 unity-scope-scopes,
99 unity-scope-scopes,
100−Breaks: indicator-network (<< 0.5.1),
100+Breaks: indicator-network (<< 0.5.1+14.10.20141014),
101 ubuntu-touch-session (<< 0.107),
101 ubuntu-touch-session (<< 0.107),
102 unity8-greeter (<< 7.89),
102 unity8-greeter (<< 7.89),
103Replaces: ubuntu-touch-session (<< 0.82~),
103Replaces: ubuntu-touch-session (<< 0.82~),
libs/UbuntuGestures/DebugHelpers.cpp+27
@@ -16,6 +16,7 @@
16
16
17#include "DebugHelpers.h"
17#include "DebugHelpers.h"
18#include <QTouchEvent>
18#include <QTouchEvent>
19+#include <QMouseEvent>
19
20
20QString touchPointStateToString(Qt::TouchPointState state)
21QString touchPointStateToString(Qt::TouchPointState state)
21{
22{
@@ -66,3 +67,29 @@ QString touchEventToString(const QTouchEvent *ev)
66
67
67 return message;
68 return message;
68}
69}
70+
71+QString mouseEventToString(const QMouseEvent *ev)
72+{
73+ QString message;
74+
75+ switch (ev->type()) {
76+ case QEvent::MouseButtonPress:
77+ message.append("MouseButtonPress ");
78+ break;
79+ case QEvent::MouseButtonRelease:
80+ message.append("MouseButtonRelease ");
81+ break;
82+ case QEvent::MouseButtonDblClick:
83+ message.append("MouseButtonDblClick ");
84+ break;
85+ case QEvent::MouseMove:
86+ message.append("MouseButtonMove ");
87+ break;
88+ default:
89+ message.append("INVALID_MOUSE_EVENT_TYPE ");
90+ }
91+
92+ message.append(QString("pos(%1, %2)").arg(ev->x()).arg(ev->y()));
93+
94+ return message;
95+}
libs/UbuntuGestures/DebugHelpers.h+2
@@ -21,9 +21,11 @@
21
21
22#include "UbuntuGesturesGlobal.h"
22#include "UbuntuGesturesGlobal.h"
23
23
24+class QMouseEvent;
24class QTouchEvent;
25class QTouchEvent;
25
26
26UBUNTUGESTURES_EXPORT QString touchPointStateToString(Qt::TouchPointState state);
27UBUNTUGESTURES_EXPORT QString touchPointStateToString(Qt::TouchPointState state);
27UBUNTUGESTURES_EXPORT QString touchEventToString(const QTouchEvent *ev);
28UBUNTUGESTURES_EXPORT QString touchEventToString(const QTouchEvent *ev);
29+UBUNTUGESTURES_EXPORT QString mouseEventToString(const QMouseEvent *ev);
28
30
29#endif // UBUNTUGESTURES_DEBUG_HELPER_H
31#endif // UBUNTUGESTURES_DEBUG_HELPER_H
plugins/AccountsService/AccountsService.qmltypes+2
@@ -23,6 +23,8 @@ Module {
23 }
23 }
24 Property { name: "user"; type: "string" }
24 Property { name: "user"; type: "string" }
25 Property { name: "demoEdges"; type: "bool" }
25 Property { name: "demoEdges"; type: "bool" }
26+ Property { name: "enableLauncherWhileLocked"; type: "bool"; isReadonly: true }
27+ Property { name: "enableIndicatorsWhileLocked"; type: "bool"; isReadonly: true }
26 Property { name: "backgroundFile"; type: "string"; isReadonly: true }
28 Property { name: "backgroundFile"; type: "string"; isReadonly: true }
27 Property { name: "statsWelcomeScreen"; type: "bool"; isReadonly: true }
29 Property { name: "statsWelcomeScreen"; type: "bool"; isReadonly: true }
28 Property { name: "passwordDisplayHint"; type: "PasswordDisplayHint"; isReadonly: true }
30 Property { name: "passwordDisplayHint"; type: "PasswordDisplayHint"; isReadonly: true }
plugins/Dash/CardAttributes.qml+2
@@ -41,6 +41,8 @@ GridLayout {
41 if (column == grid.columns - 1 || index == repeater.count - 1) return Qt.AlignRight;
41 if (column == grid.columns - 1 || index == repeater.count - 1) return Qt.AlignRight;
42 if (column == 1) return Qt.AlignHCenter;
42 if (column == 1) return Qt.AlignHCenter;
43 }
43 }
44+ Layout.column: index % grid.columns
45+ Layout.row: index / grid.columns
44 Layout.columnSpan: index == repeater.count - 1 && grid.columns == 3 && column == 1 ? 2 : 1
46 Layout.columnSpan: index == repeater.count - 1 && grid.columns == 3 && column == 1 ? 2 : 1
45 Layout.maximumWidth: Math.max(icon.width, label.x + label.implicitWidth)
47 Layout.maximumWidth: Math.max(icon.width, label.x + label.implicitWidth)
46 Layout.fillWidth: true
48 Layout.fillWidth: true
plugins/Dash/CardCreator.js+3−1
@@ -313,7 +313,9 @@ function cardString(template, components) {
313 readonly property string title: cardData && cardData["title"] || ""; \n\
313 readonly property string title: cardData && cardData["title"] || ""; \n\
314 property bool asynchronous: true; \n\
314 property bool asynchronous: true; \n\
315 property bool showHeader: true; \n\
315 property bool showHeader: true; \n\
316− implicitWidth: childrenRect.width; \n';
316+ implicitWidth: childrenRect.width; \n\
317+ enabled: root.template == null ? true : (root.template["non-interactive"] !== undefined ? !root.template["non-interactive"] : true); \n\
318+ \n';
317
319
318 var hasArt = components["art"] && components["art"]["field"] || false;
320 var hasArt = components["art"] && components["art"]["field"] || false;
319 var hasSummary = components["summary"] || false;
321 var hasSummary = components["summary"] || false;
plugins/Dash/Dash.qmltypes+3
@@ -40,9 +40,12 @@ Module {
40 Property { name: "forceNoClip"; type: "bool" }
40 Property { name: "forceNoClip"; type: "bool" }
41 Property { name: "stickyHeaderHeight"; type: "int"; isReadonly: true }
41 Property { name: "stickyHeaderHeight"; type: "int"; isReadonly: true }
42 Property { name: "headerItemShownHeight"; type: "double"; isReadonly: true }
42 Property { name: "headerItemShownHeight"; type: "double"; isReadonly: true }
43+ Property { name: "cacheBuffer"; type: "double" }
43 Signal { name: "headerChanged" }
44 Signal { name: "headerChanged" }
44 Method { name: "positionAtBeginning" }
45 Method { name: "positionAtBeginning" }
45 Method { name: "showHeader" }
46 Method { name: "showHeader" }
47+ Method { name: "firstCreatedIndex"; type: "int" }
48+ Method { name: "createdItemCount"; type: "int" }
46 Method {
49 Method {
47 name: "item"
50 name: "item"
48 type: "QQuickItem*"
51 type: "QQuickItem*"
plugins/LightDM/CMakeLists.txt+2
@@ -10,6 +10,7 @@ include_directories(
10 ${CMAKE_SOURCE_DIR}/plugins/Utils
10 ${CMAKE_SOURCE_DIR}/plugins/Utils
11 ${CMAKE_SOURCE_DIR}/tests/mocks/LightDM
11 ${CMAKE_SOURCE_DIR}/tests/mocks/LightDM
12 #${LIBLIGHTDM_INCLUDE_DIRS}
12 #${LIBLIGHTDM_INCLUDE_DIRS}
13+ ${libunity8-private_SOURCE_DIR}
13 ${LIBUSERMETRICSOUTPUT_INCLUDE_DIRS}
14 ${LIBUSERMETRICSOUTPUT_INCLUDE_DIRS}
14)
15)
15
16
@@ -28,6 +29,7 @@ add_library(LightDM-qml MODULE
28
29
29target_link_libraries(LightDM-qml
30target_link_libraries(LightDM-qml
30 MockLightDM-demo
31 MockLightDM-demo
32+ unity8-private
31# TODO: Once we split out a separate greeter process, uncomment these lines
33# TODO: Once we split out a separate greeter process, uncomment these lines
32# ${LIBLIGHTDM_LDFLAGS}
34# ${LIBLIGHTDM_LDFLAGS}
33 ${LIBUSERMETRICSOUTPUT_LDFLAGS}
35 ${LIBUSERMETRICSOUTPUT_LDFLAGS}
plugins/LightDM/DBusGreeter.cpp+3−24
@@ -20,11 +20,9 @@
20#include <QDBusMessage>
20#include <QDBusMessage>
21#include <QStringList>
21#include <QStringList>
22
22
23−DBusGreeter::DBusGreeter(Greeter *greeter, const QDBusConnection &connection, const QString &path)
23+DBusGreeter::DBusGreeter(Greeter *greeter, const QString &path)
24− : QObject(greeter),
24+ : UnityDBusObject(path, "com.canonical.UnityGreeter", true, greeter),
25− m_greeter(greeter),
25+ m_greeter(greeter)
26− m_connection(connection),
27− m_path(path)
28{
26{
29 connect(m_greeter, SIGNAL(isActiveChanged()), this, SLOT(isActiveChangedHandler()));
27 connect(m_greeter, SIGNAL(isActiveChanged()), this, SLOT(isActiveChangedHandler()));
30}
28}
@@ -49,22 +47,3 @@ void DBusGreeter::isActiveChangedHandler()
49 notifyPropertyChanged("IsActive", isActive());
47 notifyPropertyChanged("IsActive", isActive());
50 Q_EMIT isActiveChanged();
48 Q_EMIT isActiveChanged();
51}
49}
52−
53−// Manually emit a PropertiesChanged signal over DBus, because QtDBus
54−// doesn't do it for us on Q_PROPERTIES, oddly enough.
55−void DBusGreeter::notifyPropertyChanged(const QString& propertyName, const QVariant &value)
56−{
57− QDBusMessage message;
58− QVariantMap changedProps;
59−
60− changedProps.insert(propertyName, value);
61−
62− message = QDBusMessage::createSignal(m_path,
63− "org.freedesktop.DBus.Properties",
64− "PropertiesChanged");
65− message << "com.canonical.UnityGreeter";
66− message << changedProps;
67− message << QStringList();
68−
69− m_connection.send(message);
70−}
plugins/LightDM/DBusGreeter.h+3−6
@@ -17,15 +17,15 @@
17#ifndef UNITY_DBUSGREETER_H
17#ifndef UNITY_DBUSGREETER_H
18#define UNITY_DBUSGREETER_H
18#define UNITY_DBUSGREETER_H
19
19
20+#include "unitydbusobject.h"
20#include <QDBusConnection>
21#include <QDBusConnection>
21−#include <QObject>
22
22
23class Greeter;
23class Greeter;
24
24
25/** This is an internal class used to talk with the indicators.
25/** This is an internal class used to talk with the indicators.
26 */
26 */
27
27
28−class DBusGreeter : public QObject
28+class DBusGreeter : public UnityDBusObject
29{
29{
30 Q_OBJECT
30 Q_OBJECT
31 Q_CLASSINFO("D-Bus Interface", "com.canonical.UnityGreeter")
31 Q_CLASSINFO("D-Bus Interface", "com.canonical.UnityGreeter")
@@ -33,7 +33,7 @@ class DBusGreeter : public QObject
33 Q_PROPERTY(bool IsActive READ isActive NOTIFY isActiveChanged) // since 14.10
33 Q_PROPERTY(bool IsActive READ isActive NOTIFY isActiveChanged) // since 14.10
34
34
35public:
35public:
36− explicit DBusGreeter(Greeter *greeter, const QDBusConnection &connection, const QString &path);
36+ explicit DBusGreeter(Greeter *greeter, const QString &path);
37
37
38 bool isActive() const;
38 bool isActive() const;
39 Q_SCRIPTABLE void ShowGreeter(); // temporary, until we split the greeter again
39 Q_SCRIPTABLE void ShowGreeter(); // temporary, until we split the greeter again
@@ -44,12 +44,9 @@ Q_SIGNALS:
44
44
45private Q_SLOTS:
45private Q_SLOTS:
46 void isActiveChangedHandler();
46 void isActiveChangedHandler();
47− void notifyPropertyChanged(const QString &propertyName, const QVariant &value);
48
47
49private:
48private:
50 Greeter *m_greeter;
49 Greeter *m_greeter;
51− QDBusConnection m_connection;
52− QString m_path;
53};
50};
54
51
55#endif
52#endif
plugins/LightDM/DBusGreeterList.cpp+3−24
@@ -20,11 +20,9 @@
20#include <QDBusMessage>
20#include <QDBusMessage>
21#include <QStringList>
21#include <QStringList>
22
22
23−DBusGreeterList::DBusGreeterList(Greeter *greeter, const QDBusConnection &connection, const QString &path)
23+DBusGreeterList::DBusGreeterList(Greeter *greeter, const QString &path)
24− : QObject(greeter),
24+ : UnityDBusObject(path, "com.canonical.UnityGreeter", true, greeter),
25− m_greeter(greeter),
25+ m_greeter(greeter)
26− m_connection(connection),
27− m_path(path)
28{
26{
29 connect(m_greeter, SIGNAL(authenticationUserChanged(const QString &)), this, SLOT(authenticationUserChangedHandler(const QString &)));
27 connect(m_greeter, SIGNAL(authenticationUserChanged(const QString &)), this, SLOT(authenticationUserChangedHandler(const QString &)));
30 connect(m_greeter, SIGNAL(promptlessChanged()), this, SLOT(promptlessChangedHandler()));
28 connect(m_greeter, SIGNAL(promptlessChanged()), this, SLOT(promptlessChangedHandler()));
@@ -56,22 +54,3 @@ void DBusGreeterList::promptlessChangedHandler()
56 notifyPropertyChanged("EntryIsLocked", entryIsLocked());
54 notifyPropertyChanged("EntryIsLocked", entryIsLocked());
57 Q_EMIT entryIsLockedChanged();
55 Q_EMIT entryIsLockedChanged();
58}
56}
59−
60−// Manually emit a PropertiesChanged signal over DBus, because QtDBus
61−// doesn't do it for us on Q_PROPERTIES, oddly enough.
62−void DBusGreeterList::notifyPropertyChanged(const QString& propertyName, const QVariant &value)
63−{
64− QDBusMessage message;
65− QVariantMap changedProps;
66−
67− changedProps.insert(propertyName, value);
68−
69− message = QDBusMessage::createSignal(m_path,
70− "org.freedesktop.DBus.Properties",
71− "PropertiesChanged");
72− message << "com.canonical.UnityGreeter.List";
73− message << changedProps;
74− message << QStringList();
75−
76− m_connection.send(message);
77−}
plugins/LightDM/DBusGreeterList.h+3−6
@@ -17,15 +17,15 @@
17#ifndef UNITY_DBUSGREETERLIST_H
17#ifndef UNITY_DBUSGREETERLIST_H
18#define UNITY_DBUSGREETERLIST_H
18#define UNITY_DBUSGREETERLIST_H
19
19
20+#include "unitydbusobject.h"
20#include <QDBusConnection>
21#include <QDBusConnection>
21−#include <QObject>
22
22
23class Greeter;
23class Greeter;
24
24
25/** This is an internal class used to talk with the indicators.
25/** This is an internal class used to talk with the indicators.
26 */
26 */
27
27
28−class DBusGreeterList : public QObject
28+class DBusGreeterList : public UnityDBusObject
29{
29{
30 Q_OBJECT
30 Q_OBJECT
31 Q_CLASSINFO("D-Bus Interface", "com.canonical.UnityGreeter.List")
31 Q_CLASSINFO("D-Bus Interface", "com.canonical.UnityGreeter.List")
@@ -34,7 +34,7 @@ class DBusGreeterList : public QObject
34 Q_PROPERTY(bool EntryIsLocked READ entryIsLocked NOTIFY entryIsLockedChanged) // since 14.04
34 Q_PROPERTY(bool EntryIsLocked READ entryIsLocked NOTIFY entryIsLockedChanged) // since 14.04
35
35
36public:
36public:
37− explicit DBusGreeterList(Greeter *greeter, const QDBusConnection &connection, const QString &path);
37+ explicit DBusGreeterList(Greeter *greeter, const QString &path);
38
38
39 Q_SCRIPTABLE void SetActiveEntry(const QString &entry); // since 13.04
39 Q_SCRIPTABLE void SetActiveEntry(const QString &entry); // since 13.04
40 Q_SCRIPTABLE QString GetActiveEntry() const; // since 13.10
40 Q_SCRIPTABLE QString GetActiveEntry() const; // since 13.10
@@ -49,12 +49,9 @@ Q_SIGNALS:
49private Q_SLOTS:
49private Q_SLOTS:
50 void authenticationUserChangedHandler(const QString &user);
50 void authenticationUserChangedHandler(const QString &user);
51 void promptlessChangedHandler();
51 void promptlessChangedHandler();
52− void notifyPropertyChanged(const QString &propertyName, const QVariant &value);
53
52
54private:
53private:
55 Greeter *m_greeter;
54 Greeter *m_greeter;
56− QDBusConnection m_connection;
57− QString m_path;
58};
55};
59
56
60#endif
57#endif
plugins/LightDM/LightDM.qmltypes+1
@@ -37,6 +37,7 @@ Module {
37 Signal { name: "isActiveChanged" }
37 Signal { name: "isActiveChanged" }
38 Signal { name: "isAuthenticatedChanged" }
38 Signal { name: "isAuthenticatedChanged" }
39 Signal { name: "showGreeter" }
39 Signal { name: "showGreeter" }
40+ Signal { name: "hideGreeter" }
40 Signal {
41 Signal {
41 name: "requestAuthenticationUser"
42 name: "requestAuthenticationUser"
42 Parameter { name: "user"; type: "string" }
43 Parameter { name: "user"; type: "string" }
plugins/LightDM/plugin.cpp+2−9
@@ -30,21 +30,14 @@
30#include <QDBusConnection>
30#include <QDBusConnection>
31#include <QtQml/qqml.h>
31#include <QtQml/qqml.h>
32
32
33−static const char* GREETER_LIST_DBUS_PATH = "/list";
34−static const char* GREETER_DBUS_SERVICE = "com.canonical.UnityGreeter";
35−
36static QObject *greeter_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
33static QObject *greeter_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
37{
34{
38 Q_UNUSED(engine)
35 Q_UNUSED(engine)
39 Q_UNUSED(scriptEngine)
36 Q_UNUSED(scriptEngine)
40
37
41 Greeter *greeter = new Greeter();
38 Greeter *greeter = new Greeter();
42− QDBusConnection connection = QDBusConnection::sessionBus();
43− DBusGreeter *root = new DBusGreeter(greeter, connection, "/");
39+ new DBusGreeter(greeter, "/");
44− connection.registerObject("/", root, QDBusConnection::ExportScriptableContents);
45− DBusGreeterList *list = new DBusGreeterList(greeter, connection, GREETER_LIST_DBUS_PATH);
40+ new DBusGreeterList(greeter, "/list");
46− connection.registerObject(GREETER_LIST_DBUS_PATH, list, QDBusConnection::ExportScriptableContents);
47− connection.registerService(GREETER_DBUS_SERVICE);
48
41
49 return greeter;
42 return greeter;
50}
43}
plugins/Ubuntu/Gestures/CMakeLists.txt+1
@@ -8,6 +8,7 @@ set(UbuntuGesturesQml_SOURCES
8 DirectionalDragArea.cpp
8 DirectionalDragArea.cpp
9 PressedOutsideNotifier.cpp
9 PressedOutsideNotifier.cpp
10 TimeSource.cpp
10 TimeSource.cpp
11+ TouchDispatcher.cpp
11 TouchGate.cpp
12 TouchGate.cpp
12)
13)
13
14
plugins/Ubuntu/Gestures/TouchDispatcher.cppnew file+324
@@ -0,0 +1,324 @@
1+/*
2+ * Copyright (C) 2014 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+
17+#include "TouchDispatcher.h"
18+
19+#include <QScopedPointer>
20+
21+#pragma GCC diagnostic push
22+#pragma GCC diagnostic ignored "-pedantic"
23+#include <private/qquickitem_p.h>
24+#pragma GCC diagnostic pop
25+
26+#define TOUCHDISPATCHER_DEBUG 0
27+
28+#if TOUCHDISPATCHER_DEBUG
29+#include <DebugHelpers.h>
30+#endif
31+
32+TouchDispatcher::TouchDispatcher()
33+ : m_status(NoActiveTouch)
34+ , m_touchMouseId(-1)
35+{
36+}
37+
38+void TouchDispatcher::setTargetItem(QQuickItem *target)
39+{
40+ if (target != m_targetItem) {
41+ m_targetItem = target;
42+ if (m_status != NoActiveTouch) {
43+ qWarning("[TouchDispatcher] Changing target item in the middle of a touch stream");
44+ m_status = TargetRejectedTouches;
45+ }
46+ }
47+}
48+
49+void TouchDispatcher::dispatch(QEvent::Type eventType,
50+ QTouchDevice *device,
51+ Qt::KeyboardModifiers modifiers,
52+ const QList<QTouchEvent::TouchPoint> &touchPoints,
53+ QWindow *window,
54+ ulong timestamp)
55+{
56+ if (m_targetItem.isNull()) {
57+ qWarning("[TouchDispatcher] Cannot dispatch touch event because target item is null");
58+ return;
59+ }
60+
61+ if (eventType == QEvent::TouchBegin) {
62+ dispatchTouchBegin(device, modifiers, touchPoints, window, timestamp);
63+
64+ } else if (eventType == QEvent::TouchUpdate || eventType == QEvent::TouchEnd) {
65+
66+ if (m_status == DeliveringTouchEvents) {
67+ dispatchAsTouch(eventType, device, modifiers, touchPoints, window, timestamp);
68+ } else if (m_status == DeliveringMouseEvents) {
69+ dispatchAsMouse(device, modifiers, touchPoints, timestamp);
70+ } else {
71+ Q_ASSERT(m_status == TargetRejectedTouches);
72+ #if TOUCHDISPATCHER_DEBUG
73+ qDebug() << "[TouchDispatcher] Not dispatching touch event to" << m_targetItem.data()
74+ << "because it already rejected the touch stream.";
75+ #endif
76+ // Do nothing
77+ }
78+
79+ if (eventType == QEvent::TouchEnd) {
80+ m_status = NoActiveTouch;
81+ m_touchMouseId = -1;
82+ }
83+
84+ } else {
85+ // Should never happen
86+ qCritical() << "[TouchDispatcher] Unexpected event type" << eventType;
87+ Q_ASSERT(false);
88+ return;
89+ }
90+}
91+
92+void TouchDispatcher::dispatchTouchBegin(
93+ QTouchDevice *device,
94+ Qt::KeyboardModifiers modifiers,
95+ const QList<QTouchEvent::TouchPoint> &touchPoints,
96+ QWindow *window,
97+ ulong timestamp)
98+{
99+ Q_ASSERT(m_status == NoActiveTouch);
100+ QQuickItem *targetItem = m_targetItem.data();
101+
102+ if (!targetItem->isEnabled() || !targetItem->isVisible()) {
103+ #if TOUCHDISPATCHER_DEBUG
104+ qDebug() << "[TouchDispatcher] Cannot dispatch touch event to" << targetItem
105+ << "because it's disabled or invisible.";
106+ #endif
107+ return;
108+ }
109+
110+ // Map touch points to targetItem coordinates
111+ QList<QTouchEvent::TouchPoint> targetTouchPoints = touchPoints;
112+ transformTouchPoints(targetTouchPoints, QQuickItemPrivate::get(targetItem)->windowToItemTransform());
113+
114+ QScopedPointer<QTouchEvent> touchEvent(
115+ createQTouchEvent(QEvent::TouchBegin, device, modifiers, targetTouchPoints, window, timestamp));
116+
117+
118+ #if TOUCHDISPATCHER_DEBUG
119+ qDebug() << "[TouchDispatcher] dispatching" << qPrintable(touchEventToString(touchEvent.data()))
120+ << "to" << targetItem;
121+ #endif
122+ QCoreApplication::sendEvent(targetItem, touchEvent.data());
123+
124+
125+ if (touchEvent->isAccepted()) {
126+ #if TOUCHDISPATCHER_DEBUG
127+ qDebug() << "[TouchDispatcher] Item accepted the touch event.";
128+ #endif
129+ m_status = DeliveringTouchEvents;
130+ } else if (targetItem->acceptedMouseButtons() & Qt::LeftButton) {
131+ #if TOUCHDISPATCHER_DEBUG
132+ qDebug() << "[TouchDispatcher] Item rejected the touch event. Trying a QMouseEvent";
133+ #endif
134+ // NB: Arbitrarily chose the first touch point to emulate the mouse pointer
135+ QScopedPointer<QMouseEvent> mouseEvent(
136+ touchToMouseEvent(QEvent::MouseButtonPress, targetTouchPoints.at(0), timestamp,
137+ modifiers, false /* transformNeeded */));
138+ Q_ASSERT(targetTouchPoints.at(0).state() == Qt::TouchPointPressed);
139+
140+ #if TOUCHDISPATCHER_DEBUG
141+ qDebug() << "[TouchDispatcher] dispatching" << qPrintable(mouseEventToString(mouseEvent.data()))
142+ << "to" << m_targetItem.data();
143+ #endif
144+ QCoreApplication::sendEvent(targetItem, mouseEvent.data());
145+ if (mouseEvent->isAccepted()) {
146+ #if TOUCHDISPATCHER_DEBUG
147+ qDebug() << "[TouchDispatcher] Item accepted the QMouseEvent.";
148+ #endif
149+ m_status = DeliveringMouseEvents;
150+ m_touchMouseId = targetTouchPoints.at(0).id();
151+ } else {
152+ #if TOUCHDISPATCHER_DEBUG
153+ qDebug() << "[TouchDispatcher] Item rejected the QMouseEvent.";
154+ #endif
155+ m_status = TargetRejectedTouches;
156+ }
157+ } else {
158+ #if TOUCHDISPATCHER_DEBUG
159+ qDebug() << "[TouchDispatcher] Item rejected the touch event and does not accept mouse buttons.";
160+ #endif
161+ m_status = TargetRejectedTouches;
162+ }
163+}
164+
165+void TouchDispatcher::dispatchAsTouch(QEvent::Type eventType,
166+ QTouchDevice *device,
167+ Qt::KeyboardModifiers modifiers,
168+ const QList<QTouchEvent::TouchPoint> &touchPoints,
169+ QWindow *window,
170+ ulong timestamp)
171+{
172+ QQuickItem *targetItem = m_targetItem.data();
173+
174+ // Map touch points to targetItem coordinates
175+ QList<QTouchEvent::TouchPoint> targetTouchPoints = touchPoints;
176+ transformTouchPoints(targetTouchPoints, QQuickItemPrivate::get(targetItem)->windowToItemTransform());
177+
178+ QScopedPointer<QTouchEvent> eventForTargetItem(
179+ createQTouchEvent(eventType, device, modifiers, targetTouchPoints, window, timestamp));
180+
181+
182+ #if TOUCHDISPATCHER_DEBUG
183+ qDebug() << "[TouchDispatcher] dispatching" << qPrintable(touchEventToString(eventForTargetItem.data()))
184+ << "to" << targetItem;
185+ #endif
186+ QCoreApplication::sendEvent(targetItem, eventForTargetItem.data());
187+}
188+
189+void TouchDispatcher::dispatchAsMouse(
190+ QTouchDevice * /*device*/,
191+ Qt::KeyboardModifiers modifiers,
192+ const QList<QTouchEvent::TouchPoint> &touchPoints,
193+ ulong timestamp)
194+{
195+ // TODO: Detect double clicks in order to synthesize QEvent::MouseButtonDblClick events accordingly
196+
197+ Q_ASSERT(!touchPoints.isEmpty());
198+
199+ const QTouchEvent::TouchPoint *touchMouse = nullptr;
200+
201+ if (m_touchMouseId != -1) {
202+ for (int i = 0; i < touchPoints.count() && !touchMouse; ++i) {
203+ const auto &touchPoint = touchPoints.at(i);
204+ if (touchPoint.id() == m_touchMouseId) {
205+ touchMouse = &touchPoint;
206+ }
207+ }
208+
209+ Q_ASSERT(touchMouse);
210+ if (!touchMouse) {
211+ // should not happen, but deal with it just in case.
212+ qWarning("[TouchDispatcher] Didn't find touch with id %d, used for mouse pointer emulation.",
213+ m_touchMouseId);
214+ m_touchMouseId = touchPoints.at(0).id();
215+ touchMouse = &touchPoints.at(0);
216+ }
217+ } else {
218+ // Try to find a new touch for mouse emulation
219+ for (int i = 0; i < touchPoints.count() && !touchMouse; ++i) {
220+ const auto &touchPoint = touchPoints.at(i);
221+ if (touchPoint.state() == Qt::TouchPointPressed) {
222+ touchMouse = &touchPoint;
223+ m_touchMouseId = touchMouse->id();
224+ }
225+ }
226+ }
227+
228+ if (touchMouse) {
229+ QEvent::Type eventType;
230+ if (touchMouse->state() == Qt::TouchPointPressed) {
231+ eventType = QEvent::MouseButtonPress;
232+ } if (touchMouse->state() == Qt::TouchPointReleased) {
233+ eventType = QEvent::MouseButtonRelease;
234+ m_touchMouseId = -1;
235+ } else {
236+ eventType = QEvent::MouseMove;
237+ }
238+
239+ QScopedPointer<QMouseEvent> mouseEvent(touchToMouseEvent(eventType, *touchMouse, timestamp, modifiers,
240+ true /* transformNeeded */));
241+
242+ #if TOUCHDISPATCHER_DEBUG
243+ qDebug() << "[TouchDispatcher] dispatching" << qPrintable(mouseEventToString(mouseEvent.data()))
244+ << "to" << m_targetItem.data();
245+ #endif
246+ QCoreApplication::sendEvent(m_targetItem.data(), mouseEvent.data());
247+ }
248+}
249+
250+QTouchEvent *TouchDispatcher::createQTouchEvent(QEvent::Type eventType,
251+ QTouchDevice *device,
252+ Qt::KeyboardModifiers modifiers,
253+ const QList<QTouchEvent::TouchPoint> &touchPoints,
254+ QWindow *window,
255+ ulong timestamp)
256+{
257+ Qt::TouchPointStates eventStates = 0;
258+ for (int i = 0; i < touchPoints.count(); i++)
259+ eventStates |= touchPoints[i].state();
260+ // if all points have the same state, set the event type accordingly
261+ switch (eventStates) {
262+ case Qt::TouchPointPressed:
263+ eventType = QEvent::TouchBegin;
264+ break;
265+ case Qt::TouchPointReleased:
266+ eventType = QEvent::TouchEnd;
267+ break;
268+ default:
269+ eventType = QEvent::TouchUpdate;
270+ break;
271+ }
272+
273+ QTouchEvent *touchEvent = new QTouchEvent(eventType);
274+ touchEvent->setWindow(window);
275+ touchEvent->setTarget(m_targetItem.data());
276+ touchEvent->setDevice(device);
277+ touchEvent->setModifiers(modifiers);
278+ touchEvent->setTouchPoints(touchPoints);
279+ touchEvent->setTouchPointStates(eventStates);
280+ touchEvent->setTimestamp(timestamp);
281+ touchEvent->accept();
282+ return touchEvent;
283+}
284+
285+// NB: From QQuickWindow
286+void TouchDispatcher::transformTouchPoints(QList<QTouchEvent::TouchPoint> &touchPoints, const QTransform &transform)
287+{
288+ QMatrix4x4 transformMatrix(transform);
289+ for (int i=0; i<touchPoints.count(); i++) {
290+ QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
291+ touchPoint.setRect(transform.mapRect(touchPoint.sceneRect()));
292+ touchPoint.setStartPos(transform.map(touchPoint.startScenePos()));
293+ touchPoint.setLastPos(transform.map(touchPoint.lastScenePos()));
294+ touchPoint.setVelocity(transformMatrix.mapVector(touchPoint.velocity()).toVector2D());
295+ }
296+}
297+
298+// Copied with minor modifications from qtdeclarative/src/quick/items/qquickwindow.cpp
299+QMouseEvent *TouchDispatcher::touchToMouseEvent(
300+ QEvent::Type type, const QTouchEvent::TouchPoint &p,
301+ ulong timestamp, Qt::KeyboardModifiers modifiers,
302+ bool transformNeeded)
303+{
304+ QQuickItem *item = m_targetItem.data();
305+
306+ // The touch point local position and velocity are not yet transformed.
307+ QMouseEvent *me = new QMouseEvent(type, transformNeeded ? item->mapFromScene(p.scenePos()) : p.pos(),
308+ p.scenePos(), p.screenPos(), Qt::LeftButton,
309+ (type == QEvent::MouseButtonRelease ? Qt::NoButton : Qt::LeftButton),
310+ modifiers);
311+ me->setAccepted(true);
312+ me->setTimestamp(timestamp);
313+ QVector2D transformedVelocity = p.velocity();
314+ if (transformNeeded) {
315+ QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
316+ QMatrix4x4 transformMatrix(itemPrivate->windowToItemTransform());
317+ transformedVelocity = transformMatrix.mapVector(p.velocity()).toVector2D();
318+ }
319+
320+ // Add these later if needed:
321+ //QGuiApplicationPrivate::setMouseEventCapsAndVelocity(me, event->device()->capabilities(), transformedVelocity);
322+ //QGuiApplicationPrivate::setMouseEventSource(me, Qt::MouseEventSynthesizedByQt);
323+ return me;
324+}
plugins/Ubuntu/Gestures/TouchDispatcher.hnew file+86
@@ -0,0 +1,86 @@
1+/*
2+ * Copyright (C) 2014 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+
17+#ifndef UBUNTU_TOUCH_DISPATCHER_H
18+#define UBUNTU_TOUCH_DISPATCHER_H
19+
20+#include "UbuntuGesturesQmlGlobal.h"
21+
22+#include <QPointer>
23+#include <QQuickItem>
24+
25+/*
26+ Dispatches touches to the given target, converting the touch point
27+ coordinates accordingly.
28+
29+ Also takes care of synthesizing mouse events in case the target
30+ doesn't work with touch events.
31+ */
32+class UBUNTUGESTURESQML_EXPORT TouchDispatcher {
33+public:
34+ TouchDispatcher();
35+
36+ void setTargetItem(QQuickItem *target);
37+ QQuickItem *targetItem() { return m_targetItem; }
38+
39+ void dispatch(QEvent::Type eventType,
40+ QTouchDevice *device,
41+ Qt::KeyboardModifiers modifiers,
42+ const QList<QTouchEvent::TouchPoint> &touchPoints,
43+ QWindow *window,
44+ ulong timestamp);
45+private:
46+ void dispatchTouchBegin(
47+ QTouchDevice *device,
48+ Qt::KeyboardModifiers modifiers,
49+ const QList<QTouchEvent::TouchPoint> &touchPoints,
50+ QWindow *window,
51+ ulong timestamp);
52+ void dispatchAsTouch(QEvent::Type eventType,
53+ QTouchDevice *device,
54+ Qt::KeyboardModifiers modifiers,
55+ const QList<QTouchEvent::TouchPoint> &touchPoints,
56+ QWindow *window,
57+ ulong timestamp);
58+ void dispatchAsMouse(
59+ QTouchDevice *device,
60+ Qt::KeyboardModifiers modifiers,
61+ const QList<QTouchEvent::TouchPoint> &touchPoints,
62+ ulong timestamp);
63+
64+ static void transformTouchPoints(QList<QTouchEvent::TouchPoint> &touchPoints, const QTransform &transform);
65+ QTouchEvent *createQTouchEvent(QEvent::Type eventType,
66+ QTouchDevice *device,
67+ Qt::KeyboardModifiers modifiers,
68+ const QList<QTouchEvent::TouchPoint> &touchPoints,
69+ QWindow *window,
70+ ulong timestamp);
71+ QMouseEvent *touchToMouseEvent(QEvent::Type type, const QTouchEvent::TouchPoint &p,
72+ ulong timestamp, Qt::KeyboardModifiers modifiers, bool transformNeeded = true);
73+
74+ QPointer<QQuickItem> m_targetItem;
75+
76+ enum {
77+ NoActiveTouch,
78+ DeliveringTouchEvents,
79+ DeliveringMouseEvents,
80+ TargetRejectedTouches
81+ } m_status;
82+
83+ int m_touchMouseId;
84+};
85+
86+#endif // UBUNTU_TOUCH_DISPATCHER_H
plugins/Ubuntu/Gestures/TouchGate.cpp+7−103
@@ -22,16 +22,10 @@
22#include <TouchOwnershipEvent.h>
22#include <TouchOwnershipEvent.h>
23#include <TouchRegistry.h>
23#include <TouchRegistry.h>
24
24
25−#pragma GCC diagnostic push
26−#pragma GCC diagnostic ignored "-pedantic"
27−#include <private/qquickitem_p.h>
28−#pragma GCC diagnostic pop
29−
30#if TOUCHGATE_DEBUG
25#if TOUCHGATE_DEBUG
31#include <DebugHelpers.h>
26#include <DebugHelpers.h>
32#endif
27#endif
33
28
34−
35bool TouchGate::event(QEvent *e)
29bool TouchGate::event(QEvent *e)
36{
30{
37 if (e->type() == TouchOwnershipEvent::touchOwnershipEventType()) {
31 if (e->type() == TouchOwnershipEvent::touchOwnershipEventType()) {
@@ -189,125 +183,35 @@ void TouchGate::setTargetItem(QQuickItem *item)
189 // TODO: changing the target item while dispatch of touch events is taking place will
183 // TODO: changing the target item while dispatch of touch events is taking place will
190 // create a mess
184 // create a mess
191
185
192− if (item == m_targetItem.data())
186+ if (item == m_dispatcher.targetItem())
193 return;
187 return;
194
188
195− m_targetItem = item;
189+ m_dispatcher.setTargetItem(item);
196 Q_EMIT targetItemChanged(item);
190 Q_EMIT targetItemChanged(item);
197}
191}
198
192
199void TouchGate::dispatchTouchEventToTarget(const TouchEvent &event)
193void TouchGate::dispatchTouchEventToTarget(const TouchEvent &event)
200{
194{
201− dispatchTouchEventToTarget(event.eventType,
195+ removeTouchInfoForEndedTouches(event.touchPoints);
196+ m_dispatcher.dispatch(event.eventType,
202 event.device,
197 event.device,
203 event.modifiers,
198 event.modifiers,
204 event.touchPoints,
199 event.touchPoints,
205− event.target,
206 event.window,
200 event.window,
207 event.timestamp);
201 event.timestamp);
208}
202}
209
203
210void TouchGate::dispatchTouchEventToTarget(QTouchEvent* event)
204void TouchGate::dispatchTouchEventToTarget(QTouchEvent* event)
211{
205{
212− dispatchTouchEventToTarget(event->type(),
206+ removeTouchInfoForEndedTouches(event->touchPoints());
207+ m_dispatcher.dispatch(event->type(),
213 event->device(),
208 event->device(),
214 event->modifiers(),
209 event->modifiers(),
215 event->touchPoints(),
210 event->touchPoints(),
216− event->target(),
217 event->window(),
211 event->window(),
218 event->timestamp());
212 event->timestamp());
219}
213}
220
214
221−void TouchGate::dispatchTouchEventToTarget(QEvent::Type eventType,
222− QTouchDevice *device,
223− Qt::KeyboardModifiers modifiers,
224− const QList<QTouchEvent::TouchPoint> &touchPoints,
225− QObject *target,
226− QWindow *window,
227− ulong timestamp)
228−{
229− removeTouchInfoForEndedTouches(touchPoints);
230−
231− if (m_targetItem.isNull()) {
232− qWarning("[TouchGate] Cannot dispatch touch event because target item is null");
233− return;
234− }
235−
236− QQuickItem *targetItem = m_targetItem.data();
237−
238− if (!targetItem->isEnabled() || !targetItem->isVisible()) {
239− #if TOUCHGATE_DEBUG
240− qDebug() << "[TouchGate] Cannot dispatch touch event to" << targetItem
241− << "because it's disabled or invisible.";
242− #endif
243− return;
244− }
245−
246− // Map touch points to targetItem coordinates
247− QList<QTouchEvent::TouchPoint> targetTouchPoints = touchPoints;
248− transformTouchPoints(targetTouchPoints, QQuickItemPrivate::get(targetItem)->windowToItemTransform());
249− QTouchEvent *eventForTargetItem = createQTouchEvent(eventType, device, modifiers, targetTouchPoints,
250− target, window, timestamp);
251−
252− #if TOUCHGATE_DEBUG
253− qDebug() << "[TouchGate] dispatching" << qPrintable(touchEventToString(eventForTargetItem))
254− << "to" << targetItem;
255− #endif
256−
257− QCoreApplication::sendEvent(targetItem, eventForTargetItem);
258−
259− delete eventForTargetItem;
260−}
261−
262−// NB: From QQuickWindow
263−void TouchGate::transformTouchPoints(QList<QTouchEvent::TouchPoint> &touchPoints, const QTransform &transform)
264−{
265− QMatrix4x4 transformMatrix(transform);
266− for (int i=0; i<touchPoints.count(); i++) {
267− QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
268− touchPoint.setRect(transform.mapRect(touchPoint.sceneRect()));
269− touchPoint.setStartPos(transform.map(touchPoint.startScenePos()));
270− touchPoint.setLastPos(transform.map(touchPoint.lastScenePos()));
271− touchPoint.setVelocity(transformMatrix.mapVector(touchPoint.velocity()).toVector2D());
272− }
273−}
274−
275−QTouchEvent *TouchGate::createQTouchEvent(QEvent::Type eventType,
276− QTouchDevice *device,
277− Qt::KeyboardModifiers modifiers,
278− const QList<QTouchEvent::TouchPoint> &touchPoints,
279− QObject *target,
280− QWindow *window,
281− ulong timestamp)
282−{
283− Qt::TouchPointStates eventStates = 0;
284− for (int i = 0; i < touchPoints.count(); i++)
285− eventStates |= touchPoints[i].state();
286− // if all points have the same state, set the event type accordingly
287− switch (eventStates) {
288− case Qt::TouchPointPressed:
289− eventType = QEvent::TouchBegin;
290− break;
291− case Qt::TouchPointReleased:
292− eventType = QEvent::TouchEnd;
293− break;
294− default:
295− eventType = QEvent::TouchUpdate;
296− break;
297− }
298−
299− QTouchEvent *touchEvent = new QTouchEvent(eventType);
300− touchEvent->setWindow(window);
301− touchEvent->setTarget(target);
302− touchEvent->setDevice(device);
303− touchEvent->setModifiers(modifiers);
304− touchEvent->setTouchPoints(touchPoints);
305− touchEvent->setTouchPointStates(eventStates);
306− touchEvent->setTimestamp(timestamp);
307− touchEvent->accept();
308− return touchEvent;
309−}
310−
311void TouchGate::removeTouchInfoForEndedTouches(const QList<QTouchEvent::TouchPoint> &touchPoints)
215void TouchGate::removeTouchInfoForEndedTouches(const QList<QTouchEvent::TouchPoint> &touchPoints)
312{
216{
313 for (int i = 0; i < touchPoints.size(); ++i) {\
217 for (int i = 0; i < touchPoints.size(); ++i) {\
@@ -327,7 +231,7 @@ TouchGate::TouchEvent::TouchEvent(const QTouchEvent *event)
327 , device(event->device())
231 , device(event->device())
328 , modifiers(event->modifiers())
232 , modifiers(event->modifiers())
329 , touchPoints(event->touchPoints())
233 , touchPoints(event->touchPoints())
330− , target(event->target())
234+ , target(qobject_cast<QQuickItem*>(event->target()))
331 , window(event->window())
235 , window(event->window())
332 , timestamp(event->timestamp())
236 , timestamp(event->timestamp())
333{
237{
plugins/Ubuntu/Gestures/TouchGate.h+5−21
@@ -18,10 +18,10 @@
18#define UBUNTU_TOUCH_GATE_H
18#define UBUNTU_TOUCH_GATE_H
19
19
20#include "UbuntuGesturesQmlGlobal.h"
20#include "UbuntuGesturesQmlGlobal.h"
21+#include "TouchDispatcher.h"
21
22
22#include <QQuickItem>
23#include <QQuickItem>
23#include <QList>
24#include <QList>
24−#include <QPointer>
25#include <QMap>
25#include <QMap>
26
26
27#define TOUCHGATE_DEBUG 0
27#define TOUCHGATE_DEBUG 0
@@ -47,7 +47,7 @@ class UBUNTUGESTURESQML_EXPORT TouchGate : public QQuickItem {
47public:
47public:
48 bool event(QEvent *e) override;
48 bool event(QEvent *e) override;
49
49
50− QQuickItem *targetItem() { return m_targetItem; }
50+ QQuickItem *targetItem() { return m_dispatcher.targetItem(); }
51 void setTargetItem(QQuickItem *item);
51 void setTargetItem(QQuickItem *item);
52
52
53Q_SIGNALS:
53Q_SIGNALS:
@@ -66,7 +66,7 @@ private:
66 QTouchDevice *device;
66 QTouchDevice *device;
67 Qt::KeyboardModifiers modifiers;
67 Qt::KeyboardModifiers modifiers;
68 QList<QTouchEvent::TouchPoint> touchPoints;
68 QList<QTouchEvent::TouchPoint> touchPoints;
69− QObject *target;
69+ QQuickItem *target;
70 QWindow *window;
70 QWindow *window;
71 ulong timestamp;
71 ulong timestamp;
72 };
72 };
@@ -80,23 +80,7 @@ private:
80
80
81 void dispatchTouchEventToTarget(const TouchEvent &event);
81 void dispatchTouchEventToTarget(const TouchEvent &event);
82 void dispatchTouchEventToTarget(QTouchEvent* event);
82 void dispatchTouchEventToTarget(QTouchEvent* event);
83− void dispatchTouchEventToTarget(
83+
84− QEvent::Type eventType,
85− QTouchDevice *device,
86− Qt::KeyboardModifiers modifiers,
87− const QList<QTouchEvent::TouchPoint> &touchPoints,
88− QObject *target,
89− QWindow *window,
90− ulong timestamp);
91−
92− void transformTouchPoints(QList<QTouchEvent::TouchPoint> &touchPoints, const QTransform &transform);
93− static QTouchEvent *createQTouchEvent(QEvent::Type eventType,
94− QTouchDevice *device,
95− Qt::KeyboardModifiers modifiers,
96− const QList<QTouchEvent::TouchPoint> &touchPoints,
97− QObject *target,
98− QWindow *window,
99− ulong timestamp);
100 void removeTouchInfoForEndedTouches(const QList<QTouchEvent::TouchPoint> &touchPoints);
84 void removeTouchInfoForEndedTouches(const QList<QTouchEvent::TouchPoint> &touchPoints);
101
85
102 #if TOUCHGATE_DEBUG
86 #if TOUCHGATE_DEBUG
@@ -118,7 +102,7 @@ private:
118 };
102 };
119 QMap<int, TouchInfo> m_touchInfoMap;
103 QMap<int, TouchInfo> m_touchInfoMap;
120
104
121− QPointer<QQuickItem> m_targetItem;
105+ TouchDispatcher m_dispatcher;
122
106
123 friend class tst_TouchGate;
107 friend class tst_TouchGate;
124};
108};
plugins/Ubuntu/Payments/Payments.qmltypes+1
@@ -37,6 +37,7 @@ Module {
37 Parameter { name: "error"; type: "string" }
37 Parameter { name: "error"; type: "string" }
38 }
38 }
39 Signal { name: "purchaseCompleted" }
39 Signal { name: "purchaseCompleted" }
40+ Signal { name: "purchaseCancelled" }
40 Method { name: "start" }
41 Method { name: "start" }
41 }
42 }
42}
43}
plugins/Unity/DashCommunicator/DashCommunicator.qmltypes+18−5
@@ -9,14 +9,14 @@ import QtQuick.tooling 1.1
9Module {
9Module {
10 Component {
10 Component {
11 name: "DashCommunicator"
11 name: "DashCommunicator"
12− prototype: "QObject"
12+ prototype: "QThread"
13 exports: ["Unity.DashCommunicator/DashCommunicator 0.1"]
13 exports: ["Unity.DashCommunicator/DashCommunicator 0.1"]
14 exportMetaObjectRevisions: [0]
14 exportMetaObjectRevisions: [0]
15 Method {
15 Method {
16 name: "setCurrentScope"
16 name: "setCurrentScope"
17− Parameter { name: "scopeId"; type: "string" }
17+ Parameter { name: "index"; type: "int" }
18 Parameter { name: "animate"; type: "bool" }
18 Parameter { name: "animate"; type: "bool" }
19− Parameter { name: "reset"; type: "bool" }
19+ Parameter { name: "isSwipe"; type: "bool" }
20 }
20 }
21 }
21 }
22 Component {
22 Component {
@@ -26,9 +26,22 @@ Module {
26 exportMetaObjectRevisions: [0]
26 exportMetaObjectRevisions: [0]
27 Signal {
27 Signal {
28 name: "setCurrentScopeRequested"
28 name: "setCurrentScopeRequested"
29− Parameter { name: "scopeId"; type: "string" }
29+ Parameter { name: "index"; type: "int" }
30 Parameter { name: "animate"; type: "bool" }
30 Parameter { name: "animate"; type: "bool" }
31− Parameter { name: "reset"; type: "bool" }
31+ Parameter { name: "isSwipe"; type: "bool" }
32+ }
33+ }
34+ Component {
35+ name: "QThread"
36+ prototype: "QObject"
37+ Signal { name: "started" }
38+ Signal { name: "finished" }
39+ Method {
40+ name: "start"
41+ Parameter { type: "Priority" }
32 }
42 }
43+ Method { name: "start" }
44+ Method { name: "terminate" }
45+ Method { name: "quit" }
33 }
46 }
34}
47}
plugins/Unity/DashCommunicator/dashcommunicator.cpp+2−2
@@ -27,12 +27,12 @@ DashCommunicator::DashCommunicator(QObject *parent):
27 start();
27 start();
28}
28}
29
29
30−void DashCommunicator::setCurrentScope(const QString &scopeId, bool animate, bool isSwipe)
30+void DashCommunicator::setCurrentScope(int index, bool animate, bool isSwipe)
31{
31{
32 m_mutex.lock();
32 m_mutex.lock();
33 if (m_created) {
33 if (m_created) {
34 QMetaObject::invokeMethod(m_dashConnection, "setCurrentScope",
34 QMetaObject::invokeMethod(m_dashConnection, "setCurrentScope",
35− Q_ARG(QString, scopeId),
35+ Q_ARG(int, index),
36 Q_ARG(bool, animate),
36 Q_ARG(bool, animate),
37 Q_ARG(bool, isSwipe));
37 Q_ARG(bool, isSwipe));
38 }
38 }
plugins/Unity/DashCommunicator/dashcommunicator.h+1−1
@@ -31,7 +31,7 @@ public:
31 ~DashCommunicator() = default;
31 ~DashCommunicator() = default;
32
32
33public Q_SLOTS:
33public Q_SLOTS:
34− void setCurrentScope(const QString &scopeId, bool animate, bool isSwipe);
34+ void setCurrentScope(int index, bool animate, bool isSwipe);
35
35
36protected:
36protected:
37 void run() override;
37 void run() override;
plugins/Unity/DashCommunicator/dashcommunicatorservice.h+1−1
@@ -29,7 +29,7 @@ public:
29 ~DashCommunicatorService();
29 ~DashCommunicatorService();
30
30
31Q_SIGNALS:
31Q_SIGNALS:
32− void setCurrentScopeRequested(const QString &scopeId, bool animate, bool isSwipe);
32+ void setCurrentScopeRequested(int index, bool animate, bool isSwipe);
33
33
34private:
34private:
35 DBusDashCommunicatorService *m_dbusService;
35 DBusDashCommunicatorService *m_dbusService;
plugins/Unity/DashCommunicator/dashconnection.cpp+2−2
@@ -25,9 +25,9 @@ DashConnection::DashConnection(const QString &service, const QString &path, cons
25
25
26}
26}
27
27
28−void DashConnection::setCurrentScope(const QString &scopeId, bool animate, bool isSwipe)
28+void DashConnection::setCurrentScope(int index, bool animate, bool isSwipe)
29{
29{
30 if (dbusInterface()) {
30 if (dbusInterface()) {
31− dbusInterface()->asyncCall("SetCurrentScope", scopeId, animate, isSwipe);
31+ dbusInterface()->asyncCall("SetCurrentScope", index, animate, isSwipe);
32 }
32 }
33}
33}
plugins/Unity/DashCommunicator/dashconnection.h+1−1
@@ -28,7 +28,7 @@ public:
28 DashConnection(const QString &service, const QString &path, const QString &interface, QObject *parent = 0);
28 DashConnection(const QString &service, const QString &path, const QString &interface, QObject *parent = 0);
29
29
30public Q_SLOTS:
30public Q_SLOTS:
31− void setCurrentScope(const QString &scopeId, bool animate, bool isSwipe);
31+ void setCurrentScope(int index, bool animate, bool isSwipe);
32};
32};
33
33
34#endif
34#endif
plugins/Unity/DashCommunicator/dbusdashcommunicatorservice.cpp+3−7
@@ -21,19 +21,15 @@
21#include <QDebug>
21#include <QDebug>
22
22
23DBusDashCommunicatorService::DBusDashCommunicatorService(QObject *parent):
23DBusDashCommunicatorService::DBusDashCommunicatorService(QObject *parent):
24− QObject(parent)
24+ UnityDBusObject("/com/canonical/UnityDash", "com.canonical.UnityDash", false, parent)
25{
25{
26− QDBusConnection connection = QDBusConnection::sessionBus();
27−
28− connection.registerService("com.canonical.UnityDash");
29− connection.registerObject("/com/canonical/UnityDash", this, QDBusConnection::ExportScriptableSlots);
30}
26}
31
27
32DBusDashCommunicatorService::~DBusDashCommunicatorService()
28DBusDashCommunicatorService::~DBusDashCommunicatorService()
33{
29{
34}
30}
35
31
36−void DBusDashCommunicatorService::SetCurrentScope(const QString &scopeId, bool animate, bool isSwipe)
32+void DBusDashCommunicatorService::SetCurrentScope(int index, bool animate, bool isSwipe)
37{
33{
38− Q_EMIT setCurrentScopeRequested(scopeId, animate, isSwipe);
34+ Q_EMIT setCurrentScopeRequested(index, animate, isSwipe);
39}
35}
plugins/Unity/DashCommunicator/dbusdashcommunicatorservice.h+4−4
@@ -17,9 +17,9 @@
17#ifndef DBUSDASHCOMMUNICATORSERVICE_H
17#ifndef DBUSDASHCOMMUNICATORSERVICE_H
18#define DBUSDASHCOMMUNICATORSERVICE_H
18#define DBUSDASHCOMMUNICATORSERVICE_H
19
19
20−#include <QObject>
20+#include "unitydbusobject.h"
21
21
22−class DBusDashCommunicatorService: public QObject
22+class DBusDashCommunicatorService: public UnityDBusObject
23{
23{
24 Q_OBJECT
24 Q_OBJECT
25 Q_CLASSINFO("D-Bus Interface", "com.canonical.Unity.DashCommunicator")
25 Q_CLASSINFO("D-Bus Interface", "com.canonical.Unity.DashCommunicator")
@@ -29,10 +29,10 @@ public:
29 ~DBusDashCommunicatorService();
29 ~DBusDashCommunicatorService();
30
30
31Q_SIGNALS:
31Q_SIGNALS:
32− void setCurrentScopeRequested(const QString &scopeId, bool animate, bool isSwipe);
32+ void setCurrentScopeRequested(int index, bool animate, bool isSwipe);
33
33
34public Q_SLOTS:
34public Q_SLOTS:
35− Q_SCRIPTABLE void SetCurrentScope(const QString &scopeId, bool animate, bool isSwipe);
35+ Q_SCRIPTABLE void SetCurrentScope(int index, bool animate, bool isSwipe);
36
36
37};
37};
38
38
plugins/Unity/Indicators/CMakeLists.txt+1
@@ -24,6 +24,7 @@ set(IndicatorsQML_SOURCES
24 modelprinter.cpp
24 modelprinter.cpp
25 plugin.cpp
25 plugin.cpp
26 rootactionstate.cpp
26 rootactionstate.cpp
27+ sharedunitymenumodel.cpp
27 unitymenumodelcache.cpp
28 unitymenumodelcache.cpp
28 unitymenumodelstack.cpp
29 unitymenumodelstack.cpp
29 visibleindicatorsmodel.cpp
30 visibleindicatorsmodel.cpp
plugins/Unity/Indicators/Indicators.qmltypes+12−11
@@ -99,10 +99,8 @@ Module {
99 values: {
99 values: {
100 "Identifier": 0,
100 "Identifier": 0,
101 "Position": 1,
101 "Position": 1,
102− "WidgetSource": 2,
102+ "IndicatorProperties": 2,
103− "PageSource": 3,
103+ "IsVisible": 3
104− "IndicatorProperties": 4,
105− "IsVisible": 5
106 }
104 }
107 }
105 }
108 }
106 }
@@ -209,6 +207,16 @@ Module {
209 Signal { name: "updated" }
207 Signal { name: "updated" }
210 Signal { name: "indexChanged" }
208 Signal { name: "indexChanged" }
211 }
209 }
210+ Component {
211+ name: "SharedUnityMenuModel"
212+ prototype: "QObject"
213+ exports: ["Unity.Indicators/SharedUnityMenuModel 0.1"]
214+ exportMetaObjectRevisions: [0]
215+ Property { name: "busName"; type: "QByteArray" }
216+ Property { name: "menuObjectPath"; type: "QByteArray" }
217+ Property { name: "actions"; type: "QVariantMap" }
218+ Property { name: "model"; type: "UnityMenuModel"; isReadonly: true; isPointer: true }
219+ }
212 Component {
220 Component {
213 name: "UnityMenuModelCache"
221 name: "UnityMenuModelCache"
214 prototype: "QObject"
222 prototype: "QObject"
@@ -216,13 +224,6 @@ Module {
216 isCreatable: false
224 isCreatable: false
217 isSingleton: true
225 isSingleton: true
218 exportMetaObjectRevisions: [0]
226 exportMetaObjectRevisions: [0]
219− Method {
220− name: "model"
221− type: "UnityMenuModel*"
222− Parameter { name: "bus"; type: "QByteArray" }
223− Parameter { name: "path"; type: "QByteArray" }
224− Parameter { name: "actions"; type: "QVariantMap" }
225− }
226 Method {
227 Method {
227 name: "contains"
228 name: "contains"
228 type: "bool"
229 type: "bool"
plugins/Unity/Indicators/indicators.h−2
@@ -74,8 +74,6 @@ public:
74 enum Roles {
74 enum Roles {
75 Identifier = 0,
75 Identifier = 0,
76 Position,
76 Position,
77− WidgetSource,
78− PageSource,
79 IndicatorProperties,
77 IndicatorProperties,
80 IsVisible
78 IsVisible
81 };
79 };
plugins/Unity/Indicators/indicatorsmodel.cpp−6
@@ -202,8 +202,6 @@ QHash<int, QByteArray> IndicatorsModel::roleNames() const
202 {
202 {
203 roles[IndicatorsModelRole::Identifier] = "identifier";
203 roles[IndicatorsModelRole::Identifier] = "identifier";
204 roles[IndicatorsModelRole::Position] = "position";
204 roles[IndicatorsModelRole::Position] = "position";
205− roles[IndicatorsModelRole::WidgetSource] = "widgetSource";
206− roles[IndicatorsModelRole::PageSource] = "pageSource";
207 roles[IndicatorsModelRole::IndicatorProperties] = "indicatorProperties";
205 roles[IndicatorsModelRole::IndicatorProperties] = "indicatorProperties";
208 }
206 }
209 return roles;
207 return roles;
@@ -248,10 +246,6 @@ QVariant IndicatorsModel::data(const QModelIndex &index, int role) const
248 return QVariant(indicator->indicatorProperties());
246 return QVariant(indicator->indicatorProperties());
249 }
247 }
250 break;
248 break;
251− case IndicatorsModelRole::WidgetSource:
252− return qmlDirectory()+"/Panel/Indicators/DefaultIndicatorWidget.qml";
253− case IndicatorsModelRole::PageSource:
254− return qmlDirectory()+"/Panel/Indicators/DefaultIndicatorPage.qml";
255 default:
249 default:
256 break;
250 break;
257 }
251 }
plugins/Unity/Indicators/plugin.cpp+8−2
@@ -30,18 +30,23 @@
30#include "menucontentactivator.h"
30#include "menucontentactivator.h"
31#include "modelprinter.h"
31#include "modelprinter.h"
32#include "rootactionstate.h"
32#include "rootactionstate.h"
33+#include "sharedunitymenumodel.h"
33#include "unitymenumodelcache.h"
34#include "unitymenumodelcache.h"
34#include "unitymenumodelstack.h"
35#include "unitymenumodelstack.h"
35#include "visibleindicatorsmodel.h"
36#include "visibleindicatorsmodel.h"
36
37
38+#include <unitymenumodel.h>
39+
37static QObject* menuModelCacheSingleton(QQmlEngine* engine, QJSEngine* scriptEngine) {
40static QObject* menuModelCacheSingleton(QQmlEngine* engine, QJSEngine* scriptEngine) {
38 Q_UNUSED(engine);
41 Q_UNUSED(engine);
39 Q_UNUSED(scriptEngine);
42 Q_UNUSED(scriptEngine);
40− return new UnityMenuModelCache;
43+ return UnityMenuModelCache::singleton();
41}
44}
42
45
43−void Indicators2Plugin::registerTypes(const char *uri)
46+void IndicatorsPlugin::registerTypes(const char *uri)
44{
47{
48+ qRegisterMetaType<UnityMenuModel*>("UnityMenuModel*");
49+
45 qmlRegisterType<IndicatorsManager>(uri, 0, 1, "IndicatorsManager");
50 qmlRegisterType<IndicatorsManager>(uri, 0, 1, "IndicatorsManager");
46 qmlRegisterType<IndicatorsModel>(uri, 0, 1, "IndicatorsModel");
51 qmlRegisterType<IndicatorsModel>(uri, 0, 1, "IndicatorsModel");
47 qmlRegisterType<MenuContentActivator>(uri, 0, 1, "MenuContentActivator");
52 qmlRegisterType<MenuContentActivator>(uri, 0, 1, "MenuContentActivator");
@@ -49,6 +54,7 @@ void Indicators2Plugin::registerTypes(const char *uri)
49 qmlRegisterType<RootActionState>(uri, 0, 1, "RootActionState");
54 qmlRegisterType<RootActionState>(uri, 0, 1, "RootActionState");
50 qmlRegisterType<ModelPrinter>(uri, 0, 1, "ModelPrinter");
55 qmlRegisterType<ModelPrinter>(uri, 0, 1, "ModelPrinter");
51 qmlRegisterType<VisibleIndicatorsModel>(uri, 0, 1, "VisibleIndicatorsModel");
56 qmlRegisterType<VisibleIndicatorsModel>(uri, 0, 1, "VisibleIndicatorsModel");
57+ qmlRegisterType<SharedUnityMenuModel>(uri, 0, 1, "SharedUnityMenuModel");
52
58
53 qmlRegisterSingletonType<UnityMenuModelCache>(uri, 0, 1, "UnityMenuModelCache", menuModelCacheSingleton);
59 qmlRegisterSingletonType<UnityMenuModelCache>(uri, 0, 1, "UnityMenuModelCache", menuModelCacheSingleton);
54
60
plugins/Unity/Indicators/plugin.h+1−1
@@ -22,7 +22,7 @@
22
22
23#include <QtQml/QQmlExtensionPlugin>
23#include <QtQml/QQmlExtensionPlugin>
24
24
25−class Indicators2Plugin : public QQmlExtensionPlugin
25+class IndicatorsPlugin : public QQmlExtensionPlugin
26{
26{
27 Q_OBJECT
27 Q_OBJECT
28 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
28 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
plugins/Unity/Indicators/rootactionstate.cpp+1−1
@@ -156,7 +156,7 @@ QString RootActionState::accessibleName() const
156 return m_cachedState.value("accessible-desc", QVariant::fromValue(QString())).toString();
156 return m_cachedState.value("accessible-desc", QVariant::fromValue(QString())).toString();
157}
157}
158
158
159−bool RootActionState::isVisible() const
159+bool RootActionState::indicatorVisible() const
160{
160{
161 if (!isValid()) return false;
161 if (!isValid()) return false;
162
162
plugins/Unity/Indicators/rootactionstate.h+4−4
@@ -37,7 +37,7 @@ class UNITYINDICATORS_EXPORT RootActionState : public ActionStateParser
37 Q_PROPERTY(QString rightLabel READ rightLabel NOTIFY rightLabelChanged)
37 Q_PROPERTY(QString rightLabel READ rightLabel NOTIFY rightLabelChanged)
38 Q_PROPERTY(QStringList icons READ icons NOTIFY iconsChanged)
38 Q_PROPERTY(QStringList icons READ icons NOTIFY iconsChanged)
39 Q_PROPERTY(QString accessibleName READ accessibleName NOTIFY accessibleNameChanged)
39 Q_PROPERTY(QString accessibleName READ accessibleName NOTIFY accessibleNameChanged)
40− Q_PROPERTY(bool visible READ isVisible NOTIFY visibleChanged)
40+ Q_PROPERTY(bool indicatorVisible READ indicatorVisible NOTIFY indicatorVisibleChanged)
41public:
41public:
42 RootActionState(QObject *parent = 0);
42 RootActionState(QObject *parent = 0);
43 virtual ~RootActionState();
43 virtual ~RootActionState();
@@ -54,10 +54,10 @@ public:
54 QString rightLabel() const;
54 QString rightLabel() const;
55 QStringList icons() const;
55 QStringList icons() const;
56 QString accessibleName() const;
56 QString accessibleName() const;
57− bool isVisible() const;
57+ bool indicatorVisible() const;
58
58
59 // from ActionStateParser
59 // from ActionStateParser
60− virtual QVariant toQVariant(GVariant* state) const;
60+ virtual QVariant toQVariant(GVariant* state) const override;
61
61
62Q_SIGNALS:
62Q_SIGNALS:
63 void updated();
63 void updated();
@@ -71,7 +71,7 @@ Q_SIGNALS:
71 void rightLabelChanged();
71 void rightLabelChanged();
72 void iconsChanged();
72 void iconsChanged();
73 void accessibleNameChanged();
73 void accessibleNameChanged();
74− void visibleChanged();
74+ void indicatorVisibleChanged();
75
75
76private Q_SLOTS:
76private Q_SLOTS:
77 void onModelRowsAdded(const QModelIndex& parent, int start, int end);
77 void onModelRowsAdded(const QModelIndex& parent, int start, int end);
plugins/Unity/Indicators/sharedunitymenumodel.cppnew file+92
@@ -0,0 +1,92 @@
1+/*
2+ * Copyright 2014 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ */
17+
18+#include "sharedunitymenumodel.h"
19+#include "unitymenumodelcache.h"
20+
21+#include <unitymenumodel.h>
22+
23+SharedUnityMenuModel::SharedUnityMenuModel(QObject* parent)
24+ : QObject(parent)
25+{
26+}
27+
28+QByteArray SharedUnityMenuModel::busName() const
29+{
30+ return m_busName;
31+}
32+
33+void SharedUnityMenuModel::setBusName(const QByteArray& busName)
34+{
35+ if (m_busName != busName) {
36+ m_busName = busName;
37+ Q_EMIT busNameChanged();
38+ initialize();
39+ }
40+}
41+
42+QByteArray SharedUnityMenuModel::menuObjectPath() const
43+{
44+ return m_menuObjectPath;
45+}
46+
47+void SharedUnityMenuModel::setMenuObjectPath(const QByteArray& menuObjectPath)
48+{
49+ if (m_menuObjectPath != menuObjectPath) {
50+ m_menuObjectPath = menuObjectPath;
51+ Q_EMIT menuObjectPathChanged();
52+ initialize();
53+ }
54+}
55+
56+QVariantMap SharedUnityMenuModel::actions() const
57+{
58+ return m_actions;
59+}
60+
61+void SharedUnityMenuModel::setActions(const QVariantMap& actions)
62+{
63+ if (m_actions != actions) {
64+ m_actions = actions;
65+ Q_EMIT actionsChanged();
66+ initialize();
67+ }
68+}
69+
70+UnityMenuModel* SharedUnityMenuModel::model() const
71+{
72+ return m_model ? m_model.data() : nullptr;
73+}
74+
75+void SharedUnityMenuModel::initialize()
76+{
77+ if (m_busName.isEmpty() || m_menuObjectPath.isEmpty() || m_actions.isEmpty()) {
78+ if (!m_model.isNull()) {
79+ m_model.clear();
80+ Q_EMIT modelChanged();
81+ }
82+ } else {
83+ QSharedPointer<UnityMenuModel> model = UnityMenuModelCache::singleton()->model(m_menuObjectPath);
84+ if (model != m_model) {
85+ if (model->busName() != m_busName) model->setBusName(m_busName);
86+ if (model->actions() != m_actions) model->setActions(m_actions);
87+
88+ m_model = model;
89+ Q_EMIT modelChanged();
90+ }
91+ }
92+}
plugins/Unity/Indicators/sharedunitymenumodel.hnew file+66
@@ -0,0 +1,66 @@
1+/*
2+ * Copyright 2014 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ */
17+
18+#ifndef SHAREDUNITYMENUMODEL_H
19+#define SHAREDUNITYMENUMODEL_H
20+
21+#include "unityindicatorsglobal.h"
22+
23+#include <QObject>
24+#include <QSharedPointer>
25+#include <QVariantMap>
26+
27+class UnityMenuModel;
28+
29+class UNITYINDICATORS_EXPORT SharedUnityMenuModel : public QObject
30+{
31+ Q_OBJECT
32+ Q_PROPERTY(QByteArray busName READ busName WRITE setBusName NOTIFY busNameChanged)
33+ Q_PROPERTY(QByteArray menuObjectPath READ menuObjectPath WRITE setMenuObjectPath NOTIFY menuObjectPathChanged)
34+ Q_PROPERTY(QVariantMap actions READ actions WRITE setActions NOTIFY actionsChanged)
35+ Q_PROPERTY(UnityMenuModel* model READ model NOTIFY modelChanged)
36+
37+public:
38+ SharedUnityMenuModel(QObject* parent = nullptr);
39+
40+ QByteArray busName() const;
41+ void setBusName(const QByteArray&);
42+
43+ QByteArray menuObjectPath() const;
44+ void setMenuObjectPath(const QByteArray&);
45+
46+ QVariantMap actions() const;
47+ void setActions(const QVariantMap&);
48+
49+ UnityMenuModel* model() const;
50+
51+Q_SIGNALS:
52+ void busNameChanged();
53+ void menuObjectPathChanged();
54+ void actionsChanged();
55+ void modelChanged();
56+
57+private:
58+ void initialize();
59+
60+ QByteArray m_busName;
61+ QByteArray m_menuObjectPath;
62+ QVariantMap m_actions;
63+ QSharedPointer<UnityMenuModel> m_model;
64+};
65+
66+#endif // SHAREDUNITYMENUMODEL_H
plugins/Unity/Indicators/unitymenumodelcache.cpp+25−14
@@ -20,34 +20,45 @@
20#include "unitymenumodelcache.h"
20#include "unitymenumodelcache.h"
21#include <unitymenumodel.h>
21#include <unitymenumodel.h>
22
22
23−UnityMenuModelCache::UnityMenuModelCache(QObject* parent)
23+#include <QQmlEngine>
24− : QObject(parent)
24+
25+QPointer<UnityMenuModelCache> UnityMenuModelCache::theCache = nullptr;
26+
27+UnityMenuModelCache* UnityMenuModelCache::singleton()
25{
28{
29+ if (theCache.isNull()) {
30+ theCache = new UnityMenuModelCache();
31+ }
32+ return theCache.data();
26}
33}
27
34
28−UnityMenuModelCache::~UnityMenuModelCache()
35+UnityMenuModelCache::UnityMenuModelCache(QObject* parent)
36+ : QObject(parent)
29{
37{
30}
38}
31
39
32−UnityMenuModel* UnityMenuModelCache::model(const QByteArray& bus,
40+QSharedPointer<UnityMenuModel> UnityMenuModelCache::model(const QByteArray& path)
33− const QByteArray& path,
34− const QVariantMap& actions)
35{
41{
36 if (m_registry.contains(path))
42 if (m_registry.contains(path))
37 return m_registry[path];
43 return m_registry[path];
38
44
39− UnityMenuModel* menuModel = new UnityMenuModel;
45+ UnityMenuModel* model = new UnityMenuModel;
46+ QQmlEngine::setObjectOwnership(model, QQmlEngine::CppOwnership);
47+
48+ QSharedPointer<UnityMenuModel> menuModel(model);
40− connect(menuModel, &QObject::destroyed, this, [menuModel, this](QObject*) {
49+ connect(model, &QObject::destroyed, this, [this] {
41− QList<QByteArray> keys = m_registry.keys(menuModel);
50+ QMutableHashIterator<QByteArray, QWeakPointer<UnityMenuModel>> iter(m_registry);
42− Q_FOREACH(const QByteArray& key, keys) {
51+ while(iter.hasNext()) {
52+ auto keyVal = iter.next();
53+ if (keyVal.value().isNull()) {
43− m_registry.remove(key);
54+ iter.remove();
55+ break;
56+ }
44 }
57 }
45 });
58 });
46− m_registry[path] = menuModel;
59+ m_registry[path] = menuModel.toWeakRef();
47
60
48− menuModel->setBusName(bus);
49 menuModel->setMenuObjectPath(path);
61 menuModel->setMenuObjectPath(path);
50− menuModel->setActions(actions);
51 return menuModel;
62 return menuModel;
52}
63}
53
64
plugins/Unity/Indicators/unitymenumodelcache.h+11−7
@@ -24,6 +24,8 @@
24
24
25#include <QObject>
25#include <QObject>
26#include <QHash>
26#include <QHash>
27+#include <QPointer>
28+#include <QWeakPointer>
27
29
28class UnityMenuModel;
30class UnityMenuModel;
29
31
@@ -32,15 +34,17 @@ class UNITYINDICATORS_EXPORT UnityMenuModelCache : public QObject
32 Q_OBJECT
34 Q_OBJECT
33public:
35public:
34 UnityMenuModelCache(QObject*parent=nullptr);
36 UnityMenuModelCache(QObject*parent=nullptr);
35− ~UnityMenuModelCache();
36
37
37− Q_INVOKABLE UnityMenuModel* model(const QByteArray& bus,
38+ static UnityMenuModelCache* singleton();
38− const QByteArray& path,
39− const QVariantMap& actions);
40− Q_INVOKABLE bool contains(const QByteArray& path);
41
39
42−private:
40+ virtual QSharedPointer<UnityMenuModel> model(const QByteArray& path);
41+
42+ // for tests use
43+ Q_INVOKABLE virtual bool contains(const QByteArray& path);
44+
45+protected:
43− QHash<QByteArray, UnityMenuModel*> m_registry;
46+ QHash<QByteArray, QWeakPointer<UnityMenuModel>> m_registry;
47+ static QPointer<UnityMenuModelCache> theCache;
44};
48};
45
49
46#endif // UNITYMENUMODELCACHE_H
50#endif // UNITYMENUMODELCACHE_H
plugins/Unity/Indicators/visibleindicatorsmodel.cpp−2
@@ -33,8 +33,6 @@ QHash<int, QByteArray> VisibleIndicatorsModel::roleNames() const
33 {
33 {
34 roles[IndicatorsModelRole::Identifier] = "identifier";
34 roles[IndicatorsModelRole::Identifier] = "identifier";
35 roles[IndicatorsModelRole::Position] = "position";
35 roles[IndicatorsModelRole::Position] = "position";
36− roles[IndicatorsModelRole::WidgetSource] = "widgetSource";
37− roles[IndicatorsModelRole::PageSource] = "pageSource";
38 roles[IndicatorsModelRole::IndicatorProperties] = "indicatorProperties";
36 roles[IndicatorsModelRole::IndicatorProperties] = "indicatorProperties";
39 roles[IndicatorsModelRole::IsVisible] = "isVisible";
37 roles[IndicatorsModelRole::IsVisible] = "isVisible";
40 }
38 }
plugins/Unity/Launcher/CMakeLists.txt+5−1
@@ -9,6 +9,7 @@ include_directories(
9 ${CMAKE_CURRENT_SOURCE_DIR}
9 ${CMAKE_CURRENT_SOURCE_DIR}
10 ${CMAKE_SOURCE_DIR}/plugins/AccountsService
10 ${CMAKE_SOURCE_DIR}/plugins/AccountsService
11 ${GSETTINGS_QT_INCLUDE_DIRS}
11 ${GSETTINGS_QT_INCLUDE_DIRS}
12+ ${libunity8-private_SOURCE_DIR}
12)
13)
13
14
14set(QMLLAUNCHERPLUGIN_SRC
15set(QMLLAUNCHERPLUGIN_SRC
@@ -30,7 +31,10 @@ add_library(UnityLauncher-qml MODULE
30 ${QMLLAUNCHERPLUGIN_SRC}
31 ${QMLLAUNCHERPLUGIN_SRC}
31 )
32 )
32
33
33−target_link_libraries(UnityLauncher-qml ${GSETTINGS_QT_LDFLAGS})
34+target_link_libraries(UnityLauncher-qml
35+ unity8-private
36+ ${GSETTINGS_QT_LDFLAGS}
37+ )
34
38
35qt5_use_modules(UnityLauncher-qml DBus Qml Gui)
39qt5_use_modules(UnityLauncher-qml DBus Qml Gui)
36
40
plugins/Unity/Launcher/Launcher.qmltypes+17−12
@@ -13,14 +13,6 @@ Module {
13 exports: ["Unity.Launcher/LauncherItem 0.1"]
13 exports: ["Unity.Launcher/LauncherItem 0.1"]
14 isCreatable: false
14 isCreatable: false
15 exportMetaObjectRevisions: [0]
15 exportMetaObjectRevisions: [0]
16− Signal {
17− name: "favoriteChanged"
18− Parameter { name: "favorite"; type: "bool" }
19− }
20− Signal {
21− name: "runningChanged"
22− Parameter { name: "running"; type: "bool" }
23− }
24 }
16 }
25 Component {
17 Component {
26 name: "LauncherModel"
18 name: "LauncherModel"
@@ -29,6 +21,10 @@ Module {
29 isCreatable: false
21 isCreatable: false
30 isSingleton: true
22 isSingleton: true
31 exportMetaObjectRevisions: [0]
23 exportMetaObjectRevisions: [0]
24+ Method {
25+ name: "requestRemove"
26+ Parameter { name: "appId"; type: "string" }
27+ }
32 Method {
28 Method {
33 name: "get"
29 name: "get"
34 type: "unity::shell::launcher::LauncherItemInterface*"
30 type: "unity::shell::launcher::LauncherItemInterface*"
@@ -48,10 +44,6 @@ Module {
48 name: "pin"
44 name: "pin"
49 Parameter { name: "appId"; type: "string" }
45 Parameter { name: "appId"; type: "string" }
50 }
46 }
51− Method {
52− name: "requestRemove"
53− Parameter { name: "appId"; type: "string" }
54− }
55 Method {
47 Method {
56 name: "quickListActionInvoked"
48 name: "quickListActionInvoked"
57 Parameter { name: "appId"; type: "string" }
49 Parameter { name: "appId"; type: "string" }
@@ -88,6 +80,7 @@ Module {
88 Property { name: "recent"; type: "bool"; isReadonly: true }
80 Property { name: "recent"; type: "bool"; isReadonly: true }
89 Property { name: "progress"; type: "int"; isReadonly: true }
81 Property { name: "progress"; type: "int"; isReadonly: true }
90 Property { name: "count"; type: "int"; isReadonly: true }
82 Property { name: "count"; type: "int"; isReadonly: true }
83+ Property { name: "countVisible"; type: "bool"; isReadonly: true }
91 Property { name: "focused"; type: "bool"; isReadonly: true }
84 Property { name: "focused"; type: "bool"; isReadonly: true }
92 Property {
85 Property {
93 name: "quickList"
86 name: "quickList"
@@ -95,6 +88,14 @@ Module {
95 isReadonly: true
88 isReadonly: true
96 isPointer: true
89 isPointer: true
97 }
90 }
91+ Signal {
92+ name: "nameChanged"
93+ Parameter { name: "name"; type: "string" }
94+ }
95+ Signal {
96+ name: "iconChanged"
97+ Parameter { name: "icon"; type: "string" }
98+ }
98 Signal {
99 Signal {
99 name: "pinnedChanged"
100 name: "pinnedChanged"
100 Parameter { name: "pinned"; type: "bool" }
101 Parameter { name: "pinned"; type: "bool" }
@@ -115,6 +116,10 @@ Module {
115 name: "countChanged"
116 name: "countChanged"
116 Parameter { name: "count"; type: "int" }
117 Parameter { name: "count"; type: "int" }
117 }
118 }
119+ Signal {
120+ name: "countVisibleChanged"
121+ Parameter { name: "countVisible"; type: "bool" }
122+ }
118 Signal {
123 Signal {
119 name: "focusedChanged"
124 name: "focusedChanged"
120 Parameter { name: "focused"; type: "bool" }
125 Parameter { name: "focused"; type: "bool" }
plugins/Unity/Launcher/dbusinterface.cpp+3−36
@@ -27,25 +27,13 @@
27#include <QDebug>
27#include <QDebug>
28
28
29DBusInterface::DBusInterface(LauncherModel *parent):
29DBusInterface::DBusInterface(LauncherModel *parent):
30− QDBusVirtualObject(parent),
30+ UnityDBusVirtualObject("/com/canonical/Unity/Launcher", "com.canonical.Unity.Launcher", true, parent),
31 m_launcherModel(parent)
31 m_launcherModel(parent)
32{
32{
33− /* Set up ourselves on DBus */
34− QDBusConnection con = QDBusConnection::sessionBus();
35− if (!con.registerService("com.canonical.Unity.Launcher")) {
36− qWarning() << "Unable to register launcher name";
37− }
38− if (!con.registerVirtualObject("/com/canonical/Unity/Launcher", this, QDBusConnection::VirtualObjectRegisterOption::SubPath)) {
39− qWarning() << "Unable to register launcher object";
40− }
41}
33}
42
34
43DBusInterface::~DBusInterface()
35DBusInterface::~DBusInterface()
44{
36{
45− /* Remove oursevles from DBus */
46− QDBusConnection con = QDBusConnection::sessionBus();
47− con.unregisterService("com.canonical.Unity.Launcher");
48− con.unregisterObject("/com/canonical/Unity/Launcher");
49}
37}
50
38
51QString DBusInterface::introspect(const QString &path) const
39QString DBusInterface::introspect(const QString &path) const
@@ -186,13 +174,13 @@ bool DBusInterface::handleMessage(const QDBusMessage& message, const QDBusConnec
186 int newCount = message.arguments()[2].toInt();
174 int newCount = message.arguments()[2].toInt();
187 if (!item || newCount != item->count()) {
175 if (!item || newCount != item->count()) {
188 Q_EMIT countChanged(appid, newCount);
176 Q_EMIT countChanged(appid, newCount);
189− emitPropChangedDbus(appid, "count", QVariant(newCount));
177+ notifyPropertyChanged("com.canonical.Unity.Launcher.Item", encodeAppId(appid), "count", QVariant(newCount));
190 }
178 }
191 } else if (message.arguments()[1].toString() == "countVisible") {
179 } else if (message.arguments()[1].toString() == "countVisible") {
192 bool newVisible = message.arguments()[2].toBool();
180 bool newVisible = message.arguments()[2].toBool();
193 if (!item || newVisible != item->countVisible()) {
181 if (!item || newVisible != item->countVisible()) {
194 Q_EMIT countVisibleChanged(appid, newVisible);
182 Q_EMIT countVisibleChanged(appid, newVisible);
195− emitPropChangedDbus(appid, "countVisible", newVisible);
183+ notifyPropertyChanged("com.canonical.Unity.Launcher.Item", encodeAppId(appid), "countVisible", newVisible);
196 }
184 }
197 }
185 }
198 } else if (message.member() == "GetAll") {
186 } else if (message.member() == "GetAll") {
@@ -209,24 +197,3 @@ bool DBusInterface::handleMessage(const QDBusMessage& message, const QDBusConnec
209 QDBusMessage reply = message.createReply(retval);
197 QDBusMessage reply = message.createReply(retval);
210 return connection.send(reply);
198 return connection.send(reply);
211}
199}
212−
213−void DBusInterface::emitPropChangedDbus(const QString& appId, const QString& property, const QVariant &value)
214−{
215− QString path("/com/canonical/Unity/Launcher/");
216− path.append(encodeAppId(appId));
217−
218− QDBusMessage message = QDBusMessage::createSignal(path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
219−
220− QList<QVariant> arguments;
221− QVariantHash changedprops;
222− changedprops[property] = QVariant::fromValue(QDBusVariant(value));
223− QVariantList deletedprops;
224−
225− arguments.append(changedprops);
226− arguments.append(deletedprops);
227−
228− message.setArguments(arguments);
229−
230− QDBusConnection con = QDBusConnection::sessionBus();
231− con.send(message);
232−}
plugins/Unity/Launcher/dbusinterface.h+2−5
@@ -18,12 +18,11 @@
18 */
18 */
19
19
20#include "launcheritem.h"
20#include "launcheritem.h"
21−
21+#include "unitydbusvirtualobject.h"
22−#include <QDBusVirtualObject>
23
22
24class LauncherModel;
23class LauncherModel;
25
24
26−class DBusInterface: public QDBusVirtualObject
25+class DBusInterface: public UnityDBusVirtualObject
27{
26{
28 Q_OBJECT
27 Q_OBJECT
29public:
28public:
@@ -43,8 +42,6 @@ private:
43 static QString decodeAppId(const QString& path);
42 static QString decodeAppId(const QString& path);
44 static QString encodeAppId(const QString& appId);
43 static QString encodeAppId(const QString& appId);
45
44
46− void emitPropChangedDbus(const QString& appId, const QString& property, const QVariant &value);
47−
48 LauncherModel *m_launcherModel;
45 LauncherModel *m_launcherModel;
49
46
50};
47};
plugins/Unity/Launcher/desktopfilehandler.cpp+1−1
@@ -70,7 +70,7 @@ void DesktopFileHandler::load()
70
70
71 QStringList searchDirs = QStandardPaths::standardLocations(QStandardPaths::ApplicationsLocation);
71 QStringList searchDirs = QStandardPaths::standardLocations(QStandardPaths::ApplicationsLocation);
72#ifdef LAUNCHER_TESTING
72#ifdef LAUNCHER_TESTING
73− searchDirs << "";
73+ searchDirs << ".";
74#endif
74#endif
75
75
76 QString path;
76 QString path;
plugins/Unity/Launcher/gsettings.cpp+11−7
@@ -19,22 +19,20 @@
19
19
20#include "gsettings.h"
20#include "gsettings.h"
21
21
22−#include <QGSettings>
23#include <QVariant>
22#include <QVariant>
24
23
25GSettings::GSettings(QObject *parent):
24GSettings::GSettings(QObject *parent):
26 QObject(parent)
25 QObject(parent)
27{
26{
28−
27+ m_gSettings = new QGSettings("com.canonical.Unity.Launcher", "/com/canonical/unity/launcher/", this);
28+ connect(m_gSettings, &QGSettings::changed, this, &GSettings::onSettingsChanged);
29}
29}
30
30
31QStringList GSettings::storedApplications() const
31QStringList GSettings::storedApplications() const
32{
32{
33 QStringList storedApps;
33 QStringList storedApps;
34
34
35− QGSettings gSettings("com.canonical.Unity.Launcher", "/com/canonical/unity/launcher/");
36−
37− Q_FOREACH(const QString &entry, gSettings.get("items").toStringList()) {
35+ Q_FOREACH(const QString &entry, m_gSettings->get("items").toStringList()) {
38 if (entry.startsWith("application:///")) {
36 if (entry.startsWith("application:///")) {
39 // convert legacy entries to new world appids
37 // convert legacy entries to new world appids
40 QString appId = entry;
38 QString appId = entry;
@@ -57,10 +55,16 @@ QStringList GSettings::storedApplications() const
57
55
58void GSettings::setStoredApplications(const QStringList &storedApplications)
56void GSettings::setStoredApplications(const QStringList &storedApplications)
59{
57{
60− QGSettings gSettings("com.canonical.Unity.Launcher", "/com/canonical/unity/launcher/");
61 QStringList gSettingsList;
58 QStringList gSettingsList;
62 Q_FOREACH(const QString &entry, storedApplications) {
59 Q_FOREACH(const QString &entry, storedApplications) {
63 gSettingsList << QString("appid://%1").arg(entry);
60 gSettingsList << QString("appid://%1").arg(entry);
64 }
61 }
65− gSettings.set("items", gSettingsList);
62+ m_gSettings->set("items", gSettingsList);
63+}
64+
65+void GSettings::onSettingsChanged(const QString &key)
66+{
67+ if (key == "items") {
68+ Q_EMIT changed();
69+ }
66}
70}
plugins/Unity/Launcher/gsettings.h+10−1
@@ -22,7 +22,7 @@
22
22
23#include <QObject>
23#include <QObject>
24#include <QStringList>
24#include <QStringList>
25−
25+#include <QGSettings>
26
26
27class GSettings: public QObject
27class GSettings: public QObject
28{
28{
@@ -32,6 +32,15 @@ public:
32
32
33 QStringList storedApplications() const;
33 QStringList storedApplications() const;
34 void setStoredApplications(const QStringList &storedApplications);
34 void setStoredApplications(const QStringList &storedApplications);
35+
36+Q_SIGNALS:
37+ void changed();
38+
39+private Q_SLOTS:
40+ void onSettingsChanged(const QString &key);
41+
42+private:
43+ QGSettings *m_gSettings;
35};
44};
36
45
37#endif
46#endif
plugins/Unity/Launcher/launchermodel.cpp+75−32
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2013 Canonical Ltd.
2+ * Copyright 2013-2014 Canonical Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
5 * it under the terms of the GNU Lesser General Public License as published by
@@ -40,20 +40,9 @@ LauncherModel::LauncherModel(QObject *parent):
40 connect(m_dbusIface, &DBusInterface::countVisibleChanged, this, &LauncherModel::countVisibleChanged);
40 connect(m_dbusIface, &DBusInterface::countVisibleChanged, this, &LauncherModel::countVisibleChanged);
41 connect(m_dbusIface, &DBusInterface::refreshCalled, this, &LauncherModel::refresh);
41 connect(m_dbusIface, &DBusInterface::refreshCalled, this, &LauncherModel::refresh);
42
42
43− Q_FOREACH (const QString &entry, m_settings->storedApplications()) {
43+ connect(m_settings, &GSettings::changed, this, &LauncherModel::refresh);
44− DesktopFileHandler desktopFile(entry);
45− if (!desktopFile.isValid()) {
46− qWarning() << "Couldn't find a .desktop file for" << entry << ". Skipping...";
47− continue;
48− }
49
44
50− LauncherItem *item = new LauncherItem(entry,
45+ refresh();
51− desktopFile.displayName(),
52− desktopFile.icon(),
53− this);
54− item->setPinned(true);
55− m_list.append(item);
56− }
57}
46}
58
47
59LauncherModel::~LauncherModel()
48LauncherModel::~LauncherModel()
@@ -173,22 +162,7 @@ void LauncherModel::pin(const QString &appId, int index)
173
162
174void LauncherModel::requestRemove(const QString &appId)
163void LauncherModel::requestRemove(const QString &appId)
175{
164{
176− int index = findApplication(appId);
165+ unpin(appId);
177− if (index < 0) {
178− return;
179− }
180−
181− if (m_appManager->findApplication(appId)) {
182− m_list.at(index)->setPinned(false);
183− QModelIndex modelIndex = this->index(index);
184− Q_EMIT dataChanged(modelIndex, modelIndex, QVector<int>() << RolePinned);
185− return;
186− }
187−
188− beginRemoveRows(QModelIndex(), index, index);
189− m_list.takeAt(index)->deleteLater();
190− endRemoveRows();
191−
192 storeAppList();
166 storeAppList();
193}
167}
194
168
@@ -299,6 +273,24 @@ void LauncherModel::storeAppList()
299 m_settings->setStoredApplications(appIds);
273 m_settings->setStoredApplications(appIds);
300}
274}
301
275
276+void LauncherModel::unpin(const QString &appId)
277+{
278+ int index = findApplication(appId);
279+ if (index < 0) {
280+ return;
281+ }
282+
283+ if (m_appManager->findApplication(appId)) {
284+ m_list.at(index)->setPinned(false);
285+ QModelIndex modelIndex = this->index(index);
286+ Q_EMIT dataChanged(modelIndex, modelIndex, QVector<int>() << RolePinned);
287+ } else {
288+ beginRemoveRows(QModelIndex(), index, index);
289+ m_list.takeAt(index)->deleteLater();
290+ endRemoveRows();
291+ }
292+}
293+
302int LauncherModel::findApplication(const QString &appId)
294int LauncherModel::findApplication(const QString &appId)
303{
295{
304 for (int i = 0; i < m_list.count(); ++i) {
296 for (int i = 0; i < m_list.count(); ++i) {
@@ -320,7 +312,6 @@ void LauncherModel::progressChanged(const QString &appId, int progress)
320 }
312 }
321}
313}
322
314
323−
324void LauncherModel::countChanged(const QString &appId, int count)
315void LauncherModel::countChanged(const QString &appId, int count)
325{
316{
326 int idx = findApplication(appId);
317 int idx = findApplication(appId);
@@ -362,10 +353,15 @@ void LauncherModel::countVisibleChanged(const QString &appId, int countVisible)
362
353
363void LauncherModel::refresh()
354void LauncherModel::refresh()
364{
355{
356+ // First walk through all the existing items and see if we need to remove something
365 QList<LauncherItem*> toBeRemoved;
357 QList<LauncherItem*> toBeRemoved;
366 Q_FOREACH (LauncherItem* item, m_list) {
358 Q_FOREACH (LauncherItem* item, m_list) {
367 DesktopFileHandler desktopFile(item->appId());
359 DesktopFileHandler desktopFile(item->appId());
368 if (!desktopFile.isValid()) {
360 if (!desktopFile.isValid()) {
361+ // Desktop file not available for this app => drop it!
362+ toBeRemoved << item;
363+ } else if (!m_settings->storedApplications().contains(item->appId())) {
364+ // Item not in settings any more => drop it!
369 toBeRemoved << item;
365 toBeRemoved << item;
370 } else {
366 } else {
371 int idx = m_list.indexOf(item);
367 int idx = m_list.indexOf(item);
@@ -376,7 +372,54 @@ void LauncherModel::refresh()
376 }
372 }
377
373
378 Q_FOREACH (LauncherItem* item, toBeRemoved) {
374 Q_FOREACH (LauncherItem* item, toBeRemoved) {
379− requestRemove(item->appId());
375+ unpin(item->appId());
376+ }
377+
378+ // This brings the Launcher into sync with the settings backend again. There's an issue though:
379+ // If we can't find a .desktop file for an entry we need to skip it. That makes our settingsIndex
380+ // go out of sync with the actual index of items. So let's also use an addedIndex which reflects
381+ // the settingsIndex minus the skipped items.
382+ int addedIndex = 0;
383+
384+ // Now walk through settings and see if we need to add something
385+ for (int settingsIndex = 0; settingsIndex < m_settings->storedApplications().count(); ++settingsIndex) {
386+ QString entry = m_settings->storedApplications().at(settingsIndex);
387+ int itemIndex = -1;
388+ for (int i = 0; i < m_list.count(); ++i) {
389+ if (m_list.at(i)->appId() == entry) {
390+ itemIndex = i;
391+ break;
392+ }
393+ }
394+
395+ if (itemIndex == -1) {
396+ // Need to add it. Just add it into the addedIndex to keep same ordering as the list
397+ // in the settings.
398+ DesktopFileHandler desktopFile(entry);
399+ if (!desktopFile.isValid()) {
400+ qWarning() << "Couldn't find a .desktop file for" << entry << ". Skipping...";
401+ continue;
402+ }
403+
404+ LauncherItem *item = new LauncherItem(entry,
405+ desktopFile.displayName(),
406+ desktopFile.icon(),
407+ this);
408+ item->setPinned(true);
409+ beginInsertRows(QModelIndex(), addedIndex, addedIndex);
410+ m_list.insert(addedIndex, item);
411+ endInsertRows();
412+ } else if (itemIndex != addedIndex) {
413+ // The item is already there, but it is in a different place then in the settings.
414+ // Move it to the addedIndex
415+ beginMoveRows(QModelIndex(), itemIndex, itemIndex, QModelIndex(), addedIndex);
416+ m_list.move(itemIndex, addedIndex);
417+ endMoveRows();
418+ }
419+
420+ // Just like settingsIndex, this will increase with every item, except the ones we
421+ // skipped with the "continue" call above.
422+ addedIndex++;
380 }
423 }
381}
424}
382
425
plugins/Unity/Launcher/launchermodel.h+5−1
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2013 Canonical Ltd.
2+ * Copyright 2013-2014 Canonical Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
5 * it under the terms of the GNU Lesser General Public License as published by
@@ -62,6 +62,8 @@ public Q_SLOTS:
62private:
62private:
63 void storeAppList();
63 void storeAppList();
64
64
65+ void unpin(const QString &appId);
66+
65private Q_SLOTS:
67private Q_SLOTS:
66 void countChanged(const QString &appId, int count);
68 void countChanged(const QString &appId, int count);
67 void countVisibleChanged(const QString &appId, int count);
69 void countVisibleChanged(const QString &appId, int count);
@@ -79,6 +81,8 @@ private:
79 DBusInterface *m_dbusIface;
81 DBusInterface *m_dbusIface;
80
82
81 ApplicationManagerInterface *m_appManager;
83 ApplicationManagerInterface *m_appManager;
84+
85+ friend class LauncherModelTest;
82};
86};
83
87
84#endif // LAUNCHERMODEL_H
88#endif // LAUNCHERMODEL_H
plugins/Unity/Session/CMakeLists.txt+5−1
@@ -5,6 +5,7 @@ pkg_search_module(GIO REQUIRED gio-2.0)
5include_directories(
5include_directories(
6 ${CMAKE_CURRENT_SOURCE_DIR}
6 ${CMAKE_CURRENT_SOURCE_DIR}
7 ${GIO_INCLUDE_DIRS}
7 ${GIO_INCLUDE_DIRS}
8+ ${libunity8-private_SOURCE_DIR}
8)
9)
9
10
10set(QMLSESSIONPLUGIN_SRC
11set(QMLSESSIONPLUGIN_SRC
@@ -18,7 +19,10 @@ add_library(UnitySession-qml MODULE
18 )
19 )
19
20
20qt5_use_modules(UnitySession-qml DBus Qml)
21qt5_use_modules(UnitySession-qml DBus Qml)
21−target_link_libraries(UnitySession-qml ${GIO_LDFLAGS})
22+target_link_libraries(UnitySession-qml
23+ unity8-private
24+ ${GIO_LDFLAGS}
25+ )
22
26
23# export the qmldir and qmltypes files
27# export the qmldir and qmltypes files
24add_unity8_plugin(Unity.Session 0.1 Unity/Session TARGETS UnitySession-qml)
28add_unity8_plugin(Unity.Session 0.1 Unity/Session TARGETS UnitySession-qml)
plugins/Unity/Session/Session.qmltypes+10
@@ -34,4 +34,14 @@ Module {
34 Method { name: "RequestReboot" }
34 Method { name: "RequestReboot" }
35 Method { name: "RequestShutdown" }
35 Method { name: "RequestShutdown" }
36 }
36 }
37+ Component {
38+ name: "OrientationLock"
39+ prototype: "QObject"
40+ exports: ["Unity.Session/OrientationLock 0.1"]
41+ isCreatable: false
42+ isSingleton: true
43+ exportMetaObjectRevisions: [0]
44+ Property { name: "enabled"; type: "bool"; isReadonly: true }
45+ Property { name: "savedOrientation"; type: "Qt::ScreenOrientation" }
46+ }
37}
47}
plugins/Unity/Session/dbusunitysessionservice.cpp+2−8
@@ -21,15 +21,9 @@
21#include <QDBusConnection>
21#include <QDBusConnection>
22#include <QDBusInterface>
22#include <QDBusInterface>
23
23
24−DBusUnitySessionService::DBusUnitySessionService() : QObject()
24+DBusUnitySessionService::DBusUnitySessionService()
25+ : UnityDBusObject("/com/canonical/Unity/Session", "com.canonical.Unity")
25{
26{
26− QDBusConnection connection = QDBusConnection::sessionBus();
27−
28− connection.registerService("com.canonical.Unity");
29− connection.registerObject("/com/canonical/Unity/Session", this,
30− QDBusConnection::ExportScriptableSignals
31− | QDBusConnection::ExportScriptableSlots
32− | QDBusConnection::ExportScriptableInvokables);
33}
27}
34
28
35DBusUnitySessionService::~DBusUnitySessionService()
29DBusUnitySessionService::~DBusUnitySessionService()
plugins/Unity/Session/dbusunitysessionservice.h+2−2
@@ -17,7 +17,7 @@
17#ifndef DBUSUNITYSESSIONSERVICE_H
17#ifndef DBUSUNITYSESSIONSERVICE_H
18#define DBUSUNITYSESSIONSERVICE_H
18#define DBUSUNITYSESSIONSERVICE_H
19
19
20−#include <QObject>
20+#include "unitydbusobject.h"
21
21
22/**
22/**
23 * DBusUnitySessionService provides com.canonical.Unity.Session dbus
23 * DBusUnitySessionService provides com.canonical.Unity.Session dbus
@@ -26,7 +26,7 @@
26 * com.canonical.Unity.Session interface provides public methods
26 * com.canonical.Unity.Session interface provides public methods
27 * and signals to handle Logout/Reboot/Shutdown.
27 * and signals to handle Logout/Reboot/Shutdown.
28 */
28 */
29−class DBusUnitySessionService : public QObject
29+class DBusUnitySessionService : public UnityDBusObject
30{
30{
31 Q_OBJECT
31 Q_OBJECT
32 Q_CLASSINFO("D-Bus Interface", "com.canonical.Unity.Session")
32 Q_CLASSINFO("D-Bus Interface", "com.canonical.Unity.Session")
plugins/Utils/Utils.qmltypes+6
@@ -106,6 +106,12 @@ Module {
106 Parameter { name: "row"; type: "int" }
106 Parameter { name: "row"; type: "int" }
107 }
107 }
108 }
108 }
109+ Component {
110+ name: "RelativeTimeFormatter"
111+ prototype: "TimeFormatter"
112+ exports: ["Utils/RelativeTimeFormatter 0.1"]
113+ exportMetaObjectRevisions: [0]
114+ }
109 Component {
115 Component {
110 name: "TimeFormatter"
116 name: "TimeFormatter"
111 prototype: "QObject"
117 prototype: "QObject"
po/hy.po+63−59
@@ -8,111 +8,115 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
9"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
10"POT-Creation-Date: 2014-10-07 11:37+0300\n"
10"POT-Creation-Date: 2014-10-07 11:37+0300\n"
11−"PO-Revision-Date: 2013-06-27 04:43+0000\n"
11+"PO-Revision-Date: 2014-10-28 06:34+0000\n"
12−"Last-Translator: iAbaS <sosabazyan@yandex.ru>\n"
12+"Last-Translator: Vahan Harutyunyan <Unknown>\n"
13"Language-Team: Armenian <hy@li.org>\n"
13"Language-Team: Armenian <hy@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
17"Plural-Forms: nplurals=1; plural=0;\n"
18−"X-Launchpad-Export-Date: 2014-10-09 06:36+0000\n"
18+"X-Launchpad-Export-Date: 2014-10-29 07:27+0000\n"
19−"X-Generator: Launchpad (build 17196)\n"
19+"X-Generator: Launchpad (build 17203)\n"
20
20
21#: plugins/LightDM/Greeter.cpp:130
21#: plugins/LightDM/Greeter.cpp:130
22msgid "Password: "
22msgid "Password: "
23−msgstr ""
23+msgstr "Գաղտնաբառ: "
24
24
25#: plugins/Unity/Launcher/launcheritem.cpp:45
25#: plugins/Unity/Launcher/launcheritem.cpp:45
26#: plugins/Unity/Launcher/launcheritem.cpp:91
26#: plugins/Unity/Launcher/launcheritem.cpp:91
27msgid "Pin shortcut"
27msgid "Pin shortcut"
28−msgstr ""
28+msgstr "Փակցնել պիտակը"
29
29
30#: plugins/Unity/Launcher/launcheritem.cpp:91
30#: plugins/Unity/Launcher/launcheritem.cpp:91
31msgid "Unpin shortcut"
31msgid "Unpin shortcut"
32−msgstr ""
32+msgstr "Հեռացնել պիտակը"
33
33
34#: qml/Components/DelayedLockscreen.qml:47
34#: qml/Components/DelayedLockscreen.qml:47
35msgid "Device Locked"
35msgid "Device Locked"
36−msgstr ""
36+msgstr "Սարքը արգելափակված է"
37
37
38#: qml/Components/DelayedLockscreen.qml:62
38#: qml/Components/DelayedLockscreen.qml:62
39msgid "You have been locked out due to too many failed passphrase attempts."
39msgid "You have been locked out due to too many failed passphrase attempts."
40msgstr ""
40msgstr ""
41+"Գաղտնաբառի բավականին շատ ձախողված փորձերի պատճառով Դուք արգելափակված եք:"
41
42
42#: qml/Components/DelayedLockscreen.qml:63
43#: qml/Components/DelayedLockscreen.qml:63
43msgid "You have been locked out due to too many failed passcode attempts."
44msgid "You have been locked out due to too many failed passcode attempts."
44msgstr ""
45msgstr ""
46+"Ծածկագրի բավականին շատ ձախողված փորձերի պատճառով Դուք արգելափակված եք:"
45
47
46#: qml/Components/DelayedLockscreen.qml:72
48#: qml/Components/DelayedLockscreen.qml:72
47#, qt-format
49#, qt-format
48msgid "Please wait %1 minute and then try again…"
50msgid "Please wait %1 minute and then try again…"
49msgid_plural "Please wait %1 minutes and then try again…"
51msgid_plural "Please wait %1 minutes and then try again…"
50msgstr[0] ""
52msgstr[0] ""
51−msgstr[1] ""
53+"Խնդրում ենք սպասել %1 րոպե և փորձել կրկին...\r\n"
54+"Խնդրում ենք սպասել %1 րոպե և փորձել կրկին..."
52
55
53#: qml/Components/Dialogs.qml:70
56#: qml/Components/Dialogs.qml:70
54msgid "Log out"
57msgid "Log out"
55−msgstr ""
58+msgstr "Ելք"
56
59
57#: qml/Components/Dialogs.qml:71
60#: qml/Components/Dialogs.qml:71
58msgid "Are you sure you want to log out?"
61msgid "Are you sure you want to log out?"
59−msgstr ""
62+msgstr "Դուք համոզվա՞ծ եք, որ ցանկանում եք դուրս գալ համակարգից"
60
63
61#: qml/Components/Dialogs.qml:73 qml/Components/Dialogs.qml:97
64#: qml/Components/Dialogs.qml:73 qml/Components/Dialogs.qml:97
62#: qml/Components/Dialogs.qml:122
65#: qml/Components/Dialogs.qml:122
63msgid "No"
66msgid "No"
64−msgstr ""
67+msgstr "Ոչ"
65
68
66#: qml/Components/Dialogs.qml:80 qml/Components/Dialogs.qml:104
69#: qml/Components/Dialogs.qml:80 qml/Components/Dialogs.qml:104
67#: qml/Components/Dialogs.qml:129
70#: qml/Components/Dialogs.qml:129
68msgid "Yes"
71msgid "Yes"
69−msgstr ""
72+msgstr "Այո"
70
73
71#: qml/Components/Dialogs.qml:94
74#: qml/Components/Dialogs.qml:94
72msgid "Shut down"
75msgid "Shut down"
73−msgstr ""
76+msgstr "Անջատել"
74
77
75#: qml/Components/Dialogs.qml:95
78#: qml/Components/Dialogs.qml:95
76msgid "Are you sure you want to shut down?"
79msgid "Are you sure you want to shut down?"
77−msgstr ""
80+msgstr "Դուք համոզվա՞ծ եք, որ ցանկանում եք անջատել համակարգիչը"
78
81
79#: qml/Components/Dialogs.qml:119
82#: qml/Components/Dialogs.qml:119
80msgid "Reboot"
83msgid "Reboot"
81−msgstr ""
84+msgstr "Վերաբեռնել"
82
85
83#: qml/Components/Dialogs.qml:120
86#: qml/Components/Dialogs.qml:120
84msgid "Are you sure you want to reboot?"
87msgid "Are you sure you want to reboot?"
85−msgstr ""
88+msgstr "Դուք համոզվա՞ծ եք, որ ցանկանում եք վերաբեռնել համակարգիչը"
86
89
87#: qml/Components/Dialogs.qml:144
90#: qml/Components/Dialogs.qml:144
88msgid "Power"
91msgid "Power"
89−msgstr ""
92+msgstr "Էլեկտրասնուցում"
90
93
91#: qml/Components/Dialogs.qml:145
94#: qml/Components/Dialogs.qml:145
92msgid ""
95msgid ""
93"Are you sure you would like\n"
96"Are you sure you would like\n"
94"to power off?"
97"to power off?"
95msgstr ""
98msgstr ""
99+"Դուք համոզվա՞ծ եք, որ ցանկանում եք անջատել համակարգիչը սնուցման աղբյուրից"
96
100
97#: qml/Components/Dialogs.qml:147
101#: qml/Components/Dialogs.qml:147
98msgid "Power off"
102msgid "Power off"
99−msgstr ""
103+msgstr "Անջատել սնուցման աղբյուրից"
100
104
101#: qml/Components/Dialogs.qml:157
105#: qml/Components/Dialogs.qml:157
102msgid "Restart"
106msgid "Restart"
103−msgstr ""
107+msgstr "Վերագործարկել"
104
108
105#: qml/Components/Dialogs.qml:167
109#: qml/Components/Dialogs.qml:167
106msgid "Cancel"
110msgid "Cancel"
107−msgstr ""
111+msgstr "Չեղյալ համարել"
108
112
109#: qml/Components/EdgeDemoOverlay.qml:151
113#: qml/Components/EdgeDemoOverlay.qml:151
110msgid "Skip intro"
114msgid "Skip intro"
111−msgstr ""
115+msgstr "Բաց թողնել ներածությունը"
112
116
113#: qml/Components/EdgeDemo.qml:112
117#: qml/Components/EdgeDemo.qml:112
114msgid "Right edge"
118msgid "Right edge"
115−msgstr ""
119+msgstr "Աջ եզրը"
116
120
117#: qml/Components/EdgeDemo.qml:113
121#: qml/Components/EdgeDemo.qml:113
118msgid "Try swiping from the right edge to unlock the phone"
122msgid "Try swiping from the right edge to unlock the phone"
@@ -120,7 +124,7 @@ msgstr ""
120
124
121#: qml/Components/EdgeDemo.qml:144
125#: qml/Components/EdgeDemo.qml:144
122msgid "Top edge"
126msgid "Top edge"
123−msgstr ""
127+msgstr "Վերին եզրը"
124
128
125#: qml/Components/EdgeDemo.qml:145
129#: qml/Components/EdgeDemo.qml:145
126msgid "Try swiping from the top edge to access the indicators"
130msgid "Try swiping from the top edge to access the indicators"
@@ -128,7 +132,7 @@ msgstr ""
128
132
129#: qml/Components/EdgeDemo.qml:170
133#: qml/Components/EdgeDemo.qml:170
130msgid "Close"
134msgid "Close"
131−msgstr ""
135+msgstr "Փակել"
132
136
133#: qml/Components/EdgeDemo.qml:171
137#: qml/Components/EdgeDemo.qml:171
134msgid "Swipe up again to close the settings screen"
138msgid "Swipe up again to close the settings screen"
@@ -136,7 +140,7 @@ msgstr ""
136
140
137#: qml/Components/EdgeDemo.qml:198
141#: qml/Components/EdgeDemo.qml:198
138msgid "Left edge"
142msgid "Left edge"
139−msgstr ""
143+msgstr "Ձախ եզրը"
140
144
141#: qml/Components/EdgeDemo.qml:199
145#: qml/Components/EdgeDemo.qml:199
142msgid "Swipe from the left to reveal the launcher for quick access to apps"
146msgid "Swipe from the left to reveal the launcher for quick access to apps"
@@ -144,7 +148,7 @@ msgstr ""
144
148
145#: qml/Components/EdgeDemo.qml:226
149#: qml/Components/EdgeDemo.qml:226
146msgid "Well done"
150msgid "Well done"
147−msgstr ""
151+msgstr "Լավ է"
148
152
149#: qml/Components/EdgeDemo.qml:227
153#: qml/Components/EdgeDemo.qml:227
150msgid ""
154msgid ""
@@ -166,7 +170,7 @@ msgstr ""
166
170
167#: qml/Dash/GenericScopeView.qml:434
171#: qml/Dash/GenericScopeView.qml:434
168msgid "See all"
172msgid "See all"
169−msgstr ""
173+msgstr "Տեսնել բոլորը"
170
174
171#: qml/Dash/GenericScopeView.qml:496 qml/Dash/PageHeader.qml:258
175#: qml/Dash/GenericScopeView.qml:496 qml/Dash/PageHeader.qml:258
172#: qml/Panel/SearchIndicator.qml:27
176#: qml/Panel/SearchIndicator.qml:27
@@ -175,27 +179,27 @@ msgstr "Որոնում"
175
179
176#: qml/Dash/PageHeader.qml:268
180#: qml/Dash/PageHeader.qml:268
177msgid "Settings"
181msgid "Settings"
178−msgstr ""
182+msgstr "Կարգավորումներ"
179
183
180#: qml/Dash/PageHeader.qml:275
184#: qml/Dash/PageHeader.qml:275
181msgid "Remove from Favorites"
185msgid "Remove from Favorites"
182−msgstr ""
186+msgstr "Հեռացնել ընտրյալների ցուցակից"
183
187
184#: qml/Dash/PageHeader.qml:275
188#: qml/Dash/PageHeader.qml:275
185msgid "Add to Favorites"
189msgid "Add to Favorites"
186−msgstr ""
190+msgstr "Ավելացնել ընտրյալների ցուցակում"
187
191
188#: qml/Dash/Previews/PreviewActionCombo.qml:35
192#: qml/Dash/Previews/PreviewActionCombo.qml:35
189msgid "More..."
193msgid "More..."
190−msgstr ""
194+msgstr "Ավելին..."
191
195
192#: qml/Dash/Previews/PreviewActionCombo.qml:35
196#: qml/Dash/Previews/PreviewActionCombo.qml:35
193msgid "Less..."
197msgid "Less..."
194−msgstr ""
198+msgstr "Ավելի քիչ..."
195
199
196#: qml/Dash/Previews/PreviewRatingInput.qml:81
200#: qml/Dash/Previews/PreviewRatingInput.qml:81
197msgid "Rate this"
201msgid "Rate this"
198−msgstr ""
202+msgstr "Գնահատեք սա"
199
203
200#: qml/Dash/Previews/PreviewRatingInput.qml:126
204#: qml/Dash/Previews/PreviewRatingInput.qml:126
201msgid "Add a review"
205msgid "Add a review"
@@ -203,7 +207,7 @@ msgstr ""
203
207
204#: qml/Dash/Previews/PreviewRatingInput.qml:166
208#: qml/Dash/Previews/PreviewRatingInput.qml:166
205msgid "Send"
209msgid "Send"
206−msgstr ""
210+msgstr "Ուղղարկել"
207
211
208#: qml/Dash/PullToRefreshScopeStyle.qml:55
212#: qml/Dash/PullToRefreshScopeStyle.qml:55
209msgid "Pull to refresh…"
213msgid "Pull to refresh…"
@@ -219,23 +223,23 @@ msgstr ""
219
223
220#: qml/Dash/ScopesOverview.qml:428
224#: qml/Dash/ScopesOverview.qml:428
221msgid "Done"
225msgid "Done"
222−msgstr ""
226+msgstr "Կատարված է"
223
227
224#: qml/Dash/ScopesOverview.qml:454
228#: qml/Dash/ScopesOverview.qml:454
225msgid "Store"
229msgid "Store"
226−msgstr ""
230+msgstr "Խանութ"
227
231
228#: qml/Dash/ScopesOverviewTab.qml:36
232#: qml/Dash/ScopesOverviewTab.qml:36
229msgid "Favorites"
233msgid "Favorites"
230−msgstr ""
234+msgstr "Նախընտրելի"
231
235
232#: qml/Dash/ScopesOverviewTab.qml:54
236#: qml/Dash/ScopesOverviewTab.qml:54
233msgid "All"
237msgid "All"
234−msgstr ""
238+msgstr "Բոլորը"
235
239
236#: qml/Greeter/Greeter.qml:157
240#: qml/Greeter/Greeter.qml:157
237msgid "Unlock"
241msgid "Unlock"
238−msgstr ""
242+msgstr "Ապաբլոկավորել"
239
243
240#: qml/Hud/HudParametrizedActionsPage.qml:139
244#: qml/Hud/HudParametrizedActionsPage.qml:139
241msgid "Confirm"
245msgid "Confirm"
@@ -247,11 +251,11 @@ msgstr "Գրիր կամ ասա կոմմենտը"
247
251
248#: qml/Hud/Hud.qml:84
252#: qml/Hud/Hud.qml:84
249msgid "Loading. Please Wait..."
253msgid "Loading. Please Wait..."
250−msgstr "Խնդրում ենք սպասել..."
254+msgstr "Բեռնվում է: Խնդրում ենք սպասել..."
251
255
252#: qml/Hud/Hud.qml:89
256#: qml/Hud/Hud.qml:89
253msgid "Speak Now..."
257msgid "Speak Now..."
254−msgstr "Խոսի՛ր"
258+msgstr "Հիմա խոսեք..."
255
259
256#: qml/Hud/Hud.qml:94
260#: qml/Hud/Hud.qml:94
257msgid "Speaking..."
261msgid "Speaking..."
@@ -259,7 +263,7 @@ msgstr "Խոսում եմ..."
259
263
260#: qml/Notifications/NotificationMenuItemFactory.qml:116
264#: qml/Notifications/NotificationMenuItemFactory.qml:116
261msgid "Show password"
265msgid "Show password"
262−msgstr ""
266+msgstr "Ցույց տալ գաղտնաբառը"
263
267
264#: qml/Panel/ActiveCallHint.qml:77
268#: qml/Panel/ActiveCallHint.qml:77
265msgid "Tap to return to call..."
269msgid "Tap to return to call..."
@@ -275,11 +279,11 @@ msgstr ""
275
279
276#: qml/Panel/Indicators/MenuItemFactory.qml:748
280#: qml/Panel/Indicators/MenuItemFactory.qml:748
277msgid "In queue…"
281msgid "In queue…"
278−msgstr ""
282+msgstr "Հերթում..."
279
283
280#: qml/Panel/Indicators/MenuItemFactory.qml:752
284#: qml/Panel/Indicators/MenuItemFactory.qml:752
281msgid "Downloading"
285msgid "Downloading"
282−msgstr ""
286+msgstr "Ներբեռնում"
283
287
284#: qml/Panel/Indicators/MenuItemFactory.qml:754
288#: qml/Panel/Indicators/MenuItemFactory.qml:754
285msgid "Paused, tap to resume"
289msgid "Paused, tap to resume"
@@ -287,11 +291,11 @@ msgstr ""
287
291
288#: qml/Panel/Indicators/MenuItemFactory.qml:756
292#: qml/Panel/Indicators/MenuItemFactory.qml:756
289msgid "Canceled"
293msgid "Canceled"
290−msgstr ""
294+msgstr "Չեղյալ է համարված"
291
295
292#: qml/Panel/Indicators/MenuItemFactory.qml:758
296#: qml/Panel/Indicators/MenuItemFactory.qml:758
293msgid "Finished"
297msgid "Finished"
294−msgstr ""
298+msgstr "Ավարտված է"
295
299
296#: qml/Panel/Indicators/MenuItemFactory.qml:760
300#: qml/Panel/Indicators/MenuItemFactory.qml:760
297msgid "Failed, tap to retry"
301msgid "Failed, tap to retry"
@@ -299,53 +303,53 @@ msgstr ""
299
303
300#: qml/Panel/Indicators/ModemInfoItem.qml:105
304#: qml/Panel/Indicators/ModemInfoItem.qml:105
301msgid "Unlock SIM"
305msgid "Unlock SIM"
302−msgstr ""
306+msgstr "Ապաբլոկավորել SIM-ը"
303
307
304#: qml/Panel/Indicators/RoamingIndication.qml:27
308#: qml/Panel/Indicators/RoamingIndication.qml:27
305msgid "Roaming"
309msgid "Roaming"
306−msgstr ""
310+msgstr "Ռոումինգ"
307
311
308#: qml/Shell.qml:346
312#: qml/Shell.qml:346
309msgid "Enter passphrase"
313msgid "Enter passphrase"
310−msgstr ""
314+msgstr "Մուտքագրե՛ք անցաբառը"
311
315
312#: qml/Shell.qml:347
316#: qml/Shell.qml:347
313msgid "Sorry, incorrect passphrase"
317msgid "Sorry, incorrect passphrase"
314−msgstr ""
318+msgstr "Ներեցեք, սխալ անցաբառ է"
315
319
316#: qml/Shell.qml:348
320#: qml/Shell.qml:348
317msgid "Please re-enter"
321msgid "Please re-enter"
318−msgstr ""
322+msgstr "Խնդրում ենք նորից մուտքագրել"
319
323
320#: qml/Shell.qml:350
324#: qml/Shell.qml:350
321msgid "Enter passcode"
325msgid "Enter passcode"
322−msgstr ""
326+msgstr "Մոտքագրեք ծածկագիրը"
323
327
324#: qml/Shell.qml:351
328#: qml/Shell.qml:351
325msgid "Sorry, incorrect passcode"
329msgid "Sorry, incorrect passcode"
326−msgstr ""
330+msgstr "Ներեցեք, սխալ ծածկագիր է"
327
331
328#: qml/Shell.qml:354
332#: qml/Shell.qml:354
329#, qt-format
333#, qt-format
330msgid "Enter %1"
334msgid "Enter %1"
331−msgstr ""
335+msgstr "Մուտքագրեք %1"
332
336
333#: qml/Shell.qml:355
337#: qml/Shell.qml:355
334#, qt-format
338#, qt-format
335msgid "Sorry, incorrect %1"
339msgid "Sorry, incorrect %1"
336−msgstr ""
340+msgstr "Ներեցեք, սխալ %1"
337
341
338#: qml/Shell.qml:390
342#: qml/Shell.qml:390
339msgid "Sorry, incorrect passphrase."
343msgid "Sorry, incorrect passphrase."
340−msgstr ""
344+msgstr "Ներեցեք, սխալ անցաբառ է"
341
345
342#: qml/Shell.qml:391
346#: qml/Shell.qml:391
343msgid "Sorry, incorrect passcode."
347msgid "Sorry, incorrect passcode."
344−msgstr ""
348+msgstr "Ներեցեք, սխալ ծածկագիր է:"
345
349
346#: qml/Shell.qml:392
350#: qml/Shell.qml:392
347msgid "This will be your last attempt."
351msgid "This will be your last attempt."
348−msgstr ""
352+msgstr "Սա կլինի ձեր վերջին փորձը"
349
353
350#: qml/Shell.qml:394
354#: qml/Shell.qml:394
351msgid ""
355msgid ""
qml/Components/Carousel.qml+3−2
@@ -17,7 +17,6 @@
17import QtQuick 2.0
17import QtQuick 2.0
18import Ubuntu.Components 0.1
18import Ubuntu.Components 0.1
19import "carousel.js" as CarouselJS
19import "carousel.js" as CarouselJS
20−import "Flickables" as Flickables
21
20
22/*! The Carousel component presents the items of a model in a carousel view. It's similar to a
21/*! The Carousel component presents the items of a model in a carousel view. It's similar to a
23 cover flow. But it stops at it's boundaries (therefore no PathView is used).
22 cover flow. But it stops at it's boundaries (therefore no PathView is used).
@@ -64,7 +63,7 @@ Item {
64 and footer item to compensate that.
63 and footer item to compensate that.
65
64
66 The scaling of the items is controlled by the variable continuousIndex, described below. */
65 The scaling of the items is controlled by the variable continuousIndex, described below. */
67− Flickables.ListView {
66+ ListView {
68 id: listView
67 id: listView
69 objectName: "listView"
68 objectName: "listView"
70
69
@@ -136,6 +135,8 @@ Item {
136
135
137 boundsBehavior: Flickable.DragOverBounds
136 boundsBehavior: Flickable.DragOverBounds
138 cacheBuffer: carousel.cacheBuffer
137 cacheBuffer: carousel.cacheBuffer
138+ flickDeceleration: Math.max(1500 * Math.pow(realWidth / referenceWidth, 1.5), 1500) // 1500 is platform default
139+ maximumFlickVelocity: Math.max(2500 * Math.pow(realWidth / referenceWidth, 1.5), 2500) // 2500 is platform default
139 orientation: ListView.Horizontal
140 orientation: ListView.Horizontal
140
141
141 function getXFromContinuousIndex(index) {
142 function getXFromContinuousIndex(index) {
qml/Components/Dialogs.qml+21−17
@@ -24,6 +24,10 @@ import Ubuntu.Components.Popups 0.1
24Item {
24Item {
25 id: root
25 id: root
26
26
27+ // Explicitly use the right domain for this widget because it might be used
28+ // in other applications like the welcome wizard.
29+ property string domain: "unity8"
30+
27 function onPowerKeyPressed() {
31 function onPowerKeyPressed() {
28 // FIXME: event.isAutoRepeat is always false on Nexus 4.
32 // FIXME: event.isAutoRepeat is always false on Nexus 4.
29 // So we use powerKeyTimer.running to avoid the PowerOff key repeat
33 // So we use powerKeyTimer.running to avoid the PowerOff key repeat
@@ -67,17 +71,17 @@ Item {
67 id: logoutDialog
71 id: logoutDialog
68 Dialog {
72 Dialog {
69 id: dialogueLogout
73 id: dialogueLogout
70− title: i18n.tr("Log out")
74+ title: i18n.dtr(root.domain, "Log out")
71− text: i18n.tr("Are you sure you want to log out?")
75+ text: i18n.dtr(root.domain, "Are you sure you want to log out?")
72 Button {
76 Button {
73− text: i18n.tr("No")
77+ text: i18n.dtr(root.domain, "No")
74 onClicked: {
78 onClicked: {
75 PopupUtils.close(dialogueLogout);
79 PopupUtils.close(dialogueLogout);
76 d.dialogShown = false;
80 d.dialogShown = false;
77 }
81 }
78 }
82 }
79 Button {
83 Button {
80− text: i18n.tr("Yes")
84+ text: i18n.dtr(root.domain, "Yes")
81 onClicked: {
85 onClicked: {
82 DBusUnitySessionService.Logout();
86 DBusUnitySessionService.Logout();
83 PopupUtils.close(dialogueLogout);
87 PopupUtils.close(dialogueLogout);
@@ -91,17 +95,17 @@ Item {
91 id: shutdownDialog
95 id: shutdownDialog
92 Dialog {
96 Dialog {
93 id: dialogueShutdown
97 id: dialogueShutdown
94− title: i18n.tr("Shut down")
98+ title: i18n.dtr(root.domain, "Shut down")
95− text: i18n.tr("Are you sure you want to shut down?")
99+ text: i18n.dtr(root.domain, "Are you sure you want to shut down?")
96 Button {
100 Button {
97− text: i18n.tr("No")
101+ text: i18n.dtr(root.domain, "No")
98 onClicked: {
102 onClicked: {
99 PopupUtils.close(dialogueShutdown);
103 PopupUtils.close(dialogueShutdown);
100 d.dialogShown = false;
104 d.dialogShown = false;
101 }
105 }
102 }
106 }
103 Button {
107 Button {
104− text: i18n.tr("Yes")
108+ text: i18n.dtr(root.domain, "Yes")
105 onClicked: {
109 onClicked: {
106 dBusUnitySessionServiceConnection.closeAllApps();
110 dBusUnitySessionServiceConnection.closeAllApps();
107 DBusUnitySessionService.Shutdown();
111 DBusUnitySessionService.Shutdown();
@@ -116,17 +120,17 @@ Item {
116 id: rebootDialog
120 id: rebootDialog
117 Dialog {
121 Dialog {
118 id: dialogueReboot
122 id: dialogueReboot
119− title: i18n.tr("Reboot")
123+ title: i18n.dtr(root.domain, "Reboot")
120− text: i18n.tr("Are you sure you want to reboot?")
124+ text: i18n.dtr(root.domain, "Are you sure you want to reboot?")
121 Button {
125 Button {
122− text: i18n.tr("No")
126+ text: i18n.dtr(root.domain, "No")
123 onClicked: {
127 onClicked: {
124 PopupUtils.close(dialogueReboot)
128 PopupUtils.close(dialogueReboot)
125 d.dialogShown = false;
129 d.dialogShown = false;
126 }
130 }
127 }
131 }
128 Button {
132 Button {
129− text: i18n.tr("Yes")
133+ text: i18n.dtr(root.domain, "Yes")
130 onClicked: {
134 onClicked: {
131 dBusUnitySessionServiceConnection.closeAllApps();
135 dBusUnitySessionServiceConnection.closeAllApps();
132 DBusUnitySessionService.Reboot();
136 DBusUnitySessionService.Reboot();
@@ -141,10 +145,10 @@ Item {
141 id: powerDialog
145 id: powerDialog
142 Dialog {
146 Dialog {
143 id: dialoguePower
147 id: dialoguePower
144− title: i18n.tr("Power")
148+ title: i18n.dtr(root.domain, "Power")
145− text: i18n.tr("Are you sure you would like\nto power off?")
149+ text: i18n.dtr(root.domain, "Are you sure you would like\nto power off?")
146 Button {
150 Button {
147− text: i18n.tr("Power off")
151+ text: i18n.dtr(root.domain, "Power off")
148 onClicked: {
152 onClicked: {
149 dBusUnitySessionServiceConnection.closeAllApps();
153 dBusUnitySessionServiceConnection.closeAllApps();
150 PopupUtils.close(dialoguePower);
154 PopupUtils.close(dialoguePower);
@@ -154,7 +158,7 @@ Item {
154 color: UbuntuColors.red
158 color: UbuntuColors.red
155 }
159 }
156 Button {
160 Button {
157− text: i18n.tr("Restart")
161+ text: i18n.dtr(root.domain, "Restart")
158 onClicked: {
162 onClicked: {
159 dBusUnitySessionServiceConnection.closeAllApps();
163 dBusUnitySessionServiceConnection.closeAllApps();
160 DBusUnitySessionService.Reboot();
164 DBusUnitySessionService.Reboot();
@@ -164,7 +168,7 @@ Item {
164 color: UbuntuColors.green
168 color: UbuntuColors.green
165 }
169 }
166 Button {
170 Button {
167− text: i18n.tr("Cancel")
171+ text: i18n.dtr(root.domain, "Cancel")
168 onClicked: {
172 onClicked: {
169 PopupUtils.close(dialoguePower);
173 PopupUtils.close(dialoguePower);
170 d.dialogShown = false;
174 d.dialogShown = false;
qml/Components/DragHandle.qml+6−1
@@ -68,6 +68,7 @@ EdgeDragArea {
68 }
68 }
69
69
70 property real hintDisplacement: 0
70 property real hintDisplacement: 0
71+ property var overrideStartValue: undefined
71 SmoothedAnimation {
72 SmoothedAnimation {
72 id: hintingAnimation
73 id: hintingAnimation
73 target: hintingAnimation
74 target: hintingAnimation
@@ -196,7 +197,11 @@ EdgeDragArea {
196 } else /* Undecided || Recognized */ {
197 } else /* Undecided || Recognized */ {
197 if (d.previousStatus === DirectionalDragArea.WaitingForTouch) {
198 if (d.previousStatus === DirectionalDragArea.WaitingForTouch) {
198 dragEvaluator.reset();
199 dragEvaluator.reset();
200+ if (overrideStartValue !== undefined) {
201+ d.startValue = overrideStartValue;
202+ } else {
199− d.startValue = parent[d.targetProp];
203+ d.startValue = parent[d.targetProp];
204+ }
200
205
201 if (hintDisplacement > 0) {
206 if (hintDisplacement > 0) {
202 hintingAnimation.targetValue = d.startValue;
207 hintingAnimation.targetValue = d.startValue;
qml/Components/EdgeDemo.qml+13−10
@@ -23,7 +23,7 @@ Item {
23
23
24 property Item greeter
24 property Item greeter
25 property Item launcher
25 property Item launcher
26− property Item indicators
26+ property Item panel
27 property Item stages
27 property Item stages
28
28
29 property bool launcherEnabled: true
29 property bool launcherEnabled: true
@@ -139,11 +139,11 @@ Item {
139 function startTopEdgeDemo() {
139 function startTopEdgeDemo() {
140 demo.panelEnabled = true;
140 demo.panelEnabled = true;
141 if (demo.stages) {
141 if (demo.stages) {
142− d.topEdgeDemo = d.overlay.createObject(demo.stages, {
142+ d.topEdgeDemo = d.overlay.createObject(demo.panel, {
143 "edge": "top",
143 "edge": "top",
144 "title": i18n.tr("Top edge"),
144 "title": i18n.tr("Top edge"),
145 "text": i18n.tr("Try swiping from the top edge to access the indicators"),
145 "text": i18n.tr("Try swiping from the top edge to access the indicators"),
146− "anchors.fill": demo.stages,
146+ "anchors.fill": demo.panel,
147 });
147 });
148 }
148 }
149 if (d.topEdgeDemo) {
149 if (d.topEdgeDemo) {
@@ -154,9 +154,9 @@ Item {
154 }
154 }
155
155
156 Connections {
156 Connections {
157− target: demo.indicators
157+ target: demo.panel.indicators
158 onFullyOpenedChanged: {
158 onFullyOpenedChanged: {
159− if (d.topEdgeDemo && d.topEdgeDemo.available && demo.indicators.fullyOpened) {
159+ if (d.topEdgeDemo && d.topEdgeDemo.available && demo.panel.indicators.fullyOpened) {
160 d.topEdgeDemo.hideNow()
160 d.topEdgeDemo.hideNow()
161 startBottomEdgeDemo()
161 startBottomEdgeDemo()
162 }
162 }
@@ -164,12 +164,12 @@ Item {
164 }
164 }
165
165
166 function startBottomEdgeDemo() {
166 function startBottomEdgeDemo() {
167− if (demo.indicators) {
167+ if (demo.panel.indicators) {
168− d.bottomEdgeDemo = d.overlay.createObject(demo.indicators, {
168+ d.bottomEdgeDemo = d.overlay.createObject(demo.panel.indicators, {
169 "edge": "bottom",
169 "edge": "bottom",
170 "title": i18n.tr("Close"),
170 "title": i18n.tr("Close"),
171 "text": i18n.tr("Swipe up again to close the settings screen"),
171 "text": i18n.tr("Swipe up again to close the settings screen"),
172− "anchors.fill": demo.indicators.content,
172+ "anchors.fill": demo.panel.indicators,
173 });
173 });
174 }
174 }
175 if (d.bottomEdgeDemo) {
175 if (d.bottomEdgeDemo) {
@@ -180,9 +180,12 @@ Item {
180 }
180 }
181
181
182 Connections {
182 Connections {
183− target: demo.indicators
183+ target: demo.panel.indicators
184 onPartiallyOpenedChanged: {
184 onPartiallyOpenedChanged: {
185+ if (d.bottomEdgeDemo &&
186+ d.bottomEdgeDemo.available &&
185− if (d.bottomEdgeDemo && d.bottomEdgeDemo.available && !demo.indicators.partiallyOpened && !demo.indicators.fullyOpened) {
187+ !demo.panel.indicators.partiallyOpened &&
188+ !demo.panel.indicators.fullyOpened) {
186 d.bottomEdgeDemo.hideNow()
189 d.bottomEdgeDemo.hideNow()
187 startLeftEdgeDemo()
190 startLeftEdgeDemo()
188 }
191 }
qml/Components/Flickables/GridView.qmldeleted−25
@@ -1,25 +0,0 @@
1−/*
2− * Copyright (C) 2014 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−
17−import QtQuick 2.3 as QtQuick
18−import Ubuntu.Components 1.1
19−
20−QtQuick.GridView {
21− // Attached components and usages like GridView.onRemove are known not to work
22− // please use GridView directly from QtQuick if needed
23− flickDeceleration: 1500 * units.gridUnit / 8
24− maximumFlickVelocity: 2500 * units.gridUnit / 8
25−}
qml/Components/Flickables/ListView.qmldeleted−25
@@ -1,25 +0,0 @@
1−/*
2− * Copyright (C) 2014 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−
17−import QtQuick 2.3 as QtQuick
18−import Ubuntu.Components 1.1
19−
20−QtQuick.ListView {
21− // Attached components and usages like ListView.onRemove are known not to work
22− // please use ListView directly from QtQuick if needed
23− flickDeceleration: 1500 * units.gridUnit / 8
24− maximumFlickVelocity: 2500 * units.gridUnit / 8
25−}
qml/Components/Flickables/ListViewWithPageHeader.qmldeleted−23
@@ -1,23 +0,0 @@
1−/*
2− * Copyright (C) 2014 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−
17−import Dash 0.1
18−import Ubuntu.Components 1.1
19−
20−ListViewWithPageHeader {
21− flickDeceleration: 1500 * units.gridUnit / 8
22− maximumFlickVelocity: 2500 * units.gridUnit / 8
23−}
qml/Components/ListItems/VerticalThinDivider.qmldeleted−26
@@ -1,26 +0,0 @@
1−/*
2− * Copyright (C) 2012 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−
17−import QtQuick 2.0
18−
19−Image {
20− anchors {
21− top: (parent) ? parent.top : null
22− bottom: (parent) ? parent.bottom : null
23− }
24− width: (visible) ? units.dp(2) : 0
25− source: "graphics/ListItemDividerVertical.png"
26−}
qml/Components/ListItems/graphics/ListItemDividerVertical@18.pngdeleted

Binary file not shown.

qml/Components/Lockscreen.qml+4−1
@@ -17,6 +17,7 @@
17import QtQuick 2.0
17import QtQuick 2.0
18import Ubuntu.Components 1.0
18import Ubuntu.Components 1.0
19import Ubuntu.Components.Popups 1.0
19import Ubuntu.Components.Popups 1.0
20+import Ubuntu.Telephony 0.1 as Telephony
20
21
21Showable {
22Showable {
22 id: root
23 id: root
@@ -56,6 +57,8 @@ Showable {
56
57
57 property url background: ""
58 property url background: ""
58
59
60+ readonly property string passphrase: (pinPadLoader.item && pinPadLoader.item.passphrase) ? pinPadLoader.item.passphrase : ""
61+
59 signal entered(string passphrase)
62 signal entered(string passphrase)
60 signal cancel()
63 signal cancel()
61 signal emergencyCall()
64 signal emergencyCall()
@@ -217,7 +220,7 @@ Showable {
217 objectName: "emergencyCallLabel"
220 objectName: "emergencyCallLabel"
218 anchors.horizontalCenter: parent.horizontalCenter
221 anchors.horizontalCenter: parent.horizontalCenter
219
222
220− text: i18n.tr("Emergency Call")
223+ text: callManager.hasCalls ? i18n.tr("Return to Call") : i18n.tr("Emergency Call")
221 color: "#f3f3e7"
224 color: "#f3f3e7"
222 }
225 }
223
226
qml/Components/PassphraseLockscreen.qml+2
@@ -28,6 +28,8 @@ Item {
28 property string errorText
28 property string errorText
29 property bool entryEnabled: true
29 property bool entryEnabled: true
30
30
31+ readonly property string passphrase: pinentryField.text
32+
31 signal entered(string passphrase)
33 signal entered(string passphrase)
32 signal cancel()
34 signal cancel()
33
35
qml/Components/PinLockscreen.qml+2
@@ -35,6 +35,8 @@ Column {
35 property int maxPinLength: -1
35 property int maxPinLength: -1
36 property bool showCancelButton: true
36 property bool showCancelButton: true
37
37
38+ readonly property string passphrase: pinentryField.text
39+
38 signal entered(string passphrase)
40 signal entered(string passphrase)
39 signal cancel()
41 signal cancel()
40
42
qml/Components/ResponsiveGridView.qml+1−2
@@ -16,7 +16,6 @@
16
16
17import QtQuick 2.3
17import QtQuick 2.3
18import Ubuntu.Components 0.1
18import Ubuntu.Components 0.1
19−import "Flickables" as Flickables
20
19
21/*
20/*
22 Essentially a GridView where you can specify the maximum number of columns it can have.
21 Essentially a GridView where you can specify the maximum number of columns it can have.
@@ -56,7 +55,7 @@ Item {
56 return rows * height
55 return rows * height
57 }
56 }
58
57
59− Flickables.GridView {
58+ GridView {
60 id: gridView
59 id: gridView
61 objectName: "responsiveGridViewGrid"
60 objectName: "responsiveGridViewGrid"
62 anchors {
61 anchors {
qml/Components/ScrollCalculator.qmlnew file+81
@@ -0,0 +1,81 @@
1+/*
2+ * Copyright (C) 2014 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+
17+import QtQuick 2.2
18+import Ubuntu.Components 1.1
19+
20+Item {
21+ id: scrollArea
22+
23+ readonly property bool areaActive: lateralPosition >= 0
24+ property real stopScrollThreshold: units.gu(2)
25+ property int direction: Qt.LeftToRight
26+ property real baseScrollAmount: units.dp(3)
27+ property real maximumScrollAmount: units.dp(8)
28+ property real lateralPosition: -1
29+ property real forceScrollingPercentage: 0.4
30+
31+ signal scroll(real scrollAmount)
32+
33+ width: units.gu(5)
34+ rotation: direction === Qt.LeftToRight ? 0 : 180
35+
36+ onAreaActiveChanged: areaActive ? handleEnter() : handleExit()
37+
38+ function handleEnter() {
39+ d.thresholdAreaX = -scrollArea.stopScrollThreshold;
40+ scrollTimer.restart();
41+ }
42+
43+ function handleExit() {
44+ d.thresholdAreaX = -scrollArea.stopScrollThreshold;
45+ scrollTimer.stop();
46+ }
47+
48+ onLateralPositionChanged: {
49+ if (scrollArea.areaActive) {
50+ if (lateralPosition > width * (1 - forceScrollingPercentage)) {
51+ d.thresholdAreaX = width * (1 - forceScrollingPercentage);
52+ if (!scrollTimer.running) scrollTimer.restart();
53+ } else if (lateralPosition > d.thresholdAreaX + scrollArea.stopScrollThreshold) {
54+ d.thresholdAreaX = lateralPosition - scrollArea.stopScrollThreshold;
55+ if (!scrollTimer.running) scrollTimer.restart();
56+ } else if (lateralPosition < d.thresholdAreaX) {
57+ d.thresholdAreaX = lateralPosition;
58+ scrollTimer.stop();
59+ }
60+
61+ d.progression = lateralPosition / width;
62+ }
63+ }
64+
65+ Timer {
66+ id: scrollTimer
67+ interval: 16
68+ repeat: true
69+
70+ onTriggered: {
71+ var scrollAmount = scrollArea.baseScrollAmount + scrollArea.maximumScrollAmount * d.progression;
72+ scrollArea.scroll(scrollAmount);
73+ }
74+ }
75+
76+ QtObject {
77+ id: d
78+ property real progression: 0
79+ property real thresholdAreaX: -scrollArea.stopScrollThreshold
80+ }
81+}
qml/Components/ZoomableImage.qml+19−16
@@ -16,7 +16,7 @@
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18import Ubuntu.Components 0.1
18import Ubuntu.Components 0.1
19−import "Flickables" as Flickables
19+import "../Components"
20
20
21/*! \brief Zoomable for image.
21/*! \brief Zoomable for image.
22
22
@@ -26,16 +26,15 @@ import "Flickables" as Flickables
26
26
27Item {
27Item {
28 id: root
28 id: root
29− property alias source: lazyImage.source
29+ property alias source: imageRenderer.source
30 property var zoomable: false
30 property var zoomable: false
31− property alias imageState: lazyImage.state
31+ property alias imageStatus: imageRenderer.status
32− property alias scaleTo: lazyImage.scaleTo
33− property alias asynchronous: lazyImage.asynchronous
32+ property alias asynchronous: imageRenderer.asynchronous
34
33
35− Flickables.Flickable {
34+ Flickable {
36 id: flickable
35 id: flickable
37 objectName: "flickable"
36 objectName: "flickable"
38− clip: true
37+ clip: true // FIXME maybe we can remove this, or just not clip in few cases
39 contentHeight: imageContainer.height
38 contentHeight: imageContainer.height
40 contentWidth: imageContainer.width
39 contentWidth: imageContainer.width
41
40
@@ -52,30 +51,34 @@ Item {
52 Item {
51 Item {
53 id: image
52 id: image
54 objectName: "image"
53 objectName: "image"
55− property alias imageState: lazyImage.state
54+ property alias imageStatus: imageRenderer.status
56 property var prevScale
55 property var prevScale
57 anchors.centerIn: parent
56 anchors.centerIn: parent
58
57
59 signal imageReloaded
58 signal imageReloaded
60
59
61− LazyImage {
60+ Image {
62− id: lazyImage
61+ id: imageRenderer
63− objectName: "lazyImage"
62+ objectName: "imageRenderer"
64 smooth: !flickable.movingVertically
63 smooth: !flickable.movingVertically
65 anchors.fill: parent
64 anchors.fill: parent
66 fillMode: Image.PreserveAspectFit
65 fillMode: Image.PreserveAspectFit
67− scaleTo: "fit"
68
66
69− onStateChanged: {
67+ readonly property int sourceSizeMultiplier: 3
70− if (state == "ready") {
68+
69+ sourceSize.width: root.width * sourceSizeMultiplier <= root.height * sourceSizeMultiplier ? root.width * sourceSizeMultiplier : 0
70+ sourceSize.height: root.height * sourceSizeMultiplier <= root.width * sourceSizeMultiplier ? root.height * sourceSizeMultiplier : 0
71+
72+ onStatusChanged: {
73+ if (status === Image.Ready) {
71 image.imageReloaded();
74 image.imageReloaded();
72 }
75 }
73 }
76 }
74 }
77 }
75
78
76 onImageReloaded: {
79 onImageReloaded: {
77− image.height = lazyImage.sourceImage.implicitHeight
80+ image.height = imageRenderer.implicitHeight
78− image.width = lazyImage.sourceImage.implicitWidth
81+ image.width = imageRenderer.implicitWidth
79 image.resetScale();
82 image.resetScale();
80 }
83 }
81
84
qml/Dash/CardHorizontalList.qml+1−2
@@ -17,7 +17,6 @@
17import QtQuick 2.2
17import QtQuick 2.2
18import Ubuntu.Components 1.1
18import Ubuntu.Components 1.1
19import "../Components"
19import "../Components"
20−import "../Components/Flickables" as Flickables
21
20
22DashRenderer {
21DashRenderer {
23 id: root
22 id: root
@@ -26,7 +25,7 @@ DashRenderer {
26 collapsedHeight: expandedHeight
25 collapsedHeight: expandedHeight
27 clip: true
26 clip: true
28
27
29− Flickables.ListView {
28+ ListView {
30 id: listView
29 id: listView
31 anchors {
30 anchors {
32 fill: parent
31 fill: parent
qml/Dash/CardVerticalJournal.qml+17
@@ -33,6 +33,23 @@ DashRenderer {
33 // in case the model is non empty
33 // in case the model is non empty
34 readonly property double minHeight: root.model.count >= 1 ? cardVerticalJournal.rowSpacing + 1 : 0
34 readonly property double minHeight: root.model.count >= 1 ? cardVerticalJournal.rowSpacing + 1 : 0
35
35
36+ function clearView() {
37+ cardVerticalJournal.model = null;
38+ cardVerticalJournal.model = root.model;
39+ }
40+
41+ // Clear the view if the cardTool changes its sizes
42+ // means it is still settling. This is
43+ // necessary because the VerticalJournal does
44+ // not support its elements changing height so
45+ // we need to construct all the items with the settled
46+ // size of the cardTool
47+ Connections {
48+ target: cardTool
49+ onArtShapeSizeChanged: root.clearView();
50+ onHeaderHeightChanged: root.clearView();
51+ }
52+
36 ResponsiveVerticalJournal {
53 ResponsiveVerticalJournal {
37 id: cardVerticalJournal
54 id: cardVerticalJournal
38
55
qml/Dash/Dash.qml+1−9
@@ -28,14 +28,12 @@ Showable {
28
28
29 visible: shown
29 visible: shown
30
30
31− property string showScopeOnLoaded: "clickscope"
32−
33 DashCommunicatorService {
31 DashCommunicatorService {
34 objectName: "dashCommunicatorService"
32 objectName: "dashCommunicatorService"
35 onSetCurrentScopeRequested: {
33 onSetCurrentScopeRequested: {
36 if (!isSwipe || !window.active || overviewController.progress != 0) {
34 if (!isSwipe || !window.active || overviewController.progress != 0) {
37 if (overviewController.progress != 0 && window.active) animate = false;
35 if (overviewController.progress != 0 && window.active) animate = false;
38− dash.setCurrentScope(scopeId, animate, isSwipe)
36+ dashContent.setCurrentScopeAtIndex(index, animate, isSwipe)
39 if (overviewController.progress != 0) {
37 if (overviewController.progress != 0) {
40 if (window.active) {
38 if (window.active) {
41 dashContentCache.scheduleUpdate();
39 dashContentCache.scheduleUpdate();
@@ -184,12 +182,6 @@ Showable {
184 scopesOverview.ensureAllScopeVisible(scope.id);
182 scopesOverview.ensureAllScopeVisible(scope.id);
185 x = -width;
183 x = -width;
186 }
184 }
187− onScopeLoaded: {
188− if (scopeId == dash.showScopeOnLoaded) {
189− dash.setCurrentScope(scopeId, false, false)
190− dash.showScopeOnLoaded = ""
191− }
192− }
193 clip: scale != 1.0 || scopeItem.visible || overviewController.progress != 0
185 clip: scale != 1.0 || scopeItem.visible || overviewController.progress != 0
194 Behavior on x {
186 Behavior on x {
195 UbuntuNumberAnimation {
187 UbuntuNumberAnimation {
qml/Dash/DashContent.qml+4−3
@@ -19,7 +19,6 @@ import Ubuntu.Components 0.1
19import Unity 0.2
19import Unity 0.2
20import Utils 0.1
20import Utils 0.1
21import "../Components"
21import "../Components"
22−import "../Components/Flickables" as Flickables
23
22
24Item {
23Item {
25 id: dashContent
24 id: dashContent
@@ -99,7 +98,7 @@ Item {
99 anchors.fill: parent
98 anchors.fill: parent
100 }
99 }
101
100
102− Flickables.ListView {
101+ ListView {
103 id: dashContentList
102 id: dashContentList
104 objectName: "dashContentList"
103 objectName: "dashContentList"
105
104
@@ -108,6 +107,8 @@ Item {
108 anchors.fill: parent
107 anchors.fill: parent
109 orientation: ListView.Horizontal
108 orientation: ListView.Horizontal
110 boundsBehavior: Flickable.DragAndOvershootBounds
109 boundsBehavior: Flickable.DragAndOvershootBounds
110+ flickDeceleration: units.gu(625)
111+ maximumFlickVelocity: width * 5
111 snapMode: ListView.SnapOneItem
112 snapMode: ListView.SnapOneItem
112 highlightMoveDuration: 250
113 highlightMoveDuration: 250
113 highlightRangeMode: ListView.StrictlyEnforceRange
114 highlightRangeMode: ListView.StrictlyEnforceRange
@@ -160,7 +161,7 @@ Item {
160 }
161 }
161 asynchronous: true
162 asynchronous: true
162 source: "GenericScopeView.qml"
163 source: "GenericScopeView.qml"
163− objectName: scope.id + " loader"
164+ objectName: "scopeLoader" + index
164
165
165 readonly property bool moving: item ? item.moving : false
166 readonly property bool moving: item ? item.moving : false
166 readonly property bool navigationShown: item ? item.navigationShown : false
167 readonly property bool navigationShown: item ? item.navigationShown : false
qml/Dash/DashNavigationButton.qml+1−2
@@ -16,7 +16,6 @@
16
16
17import QtQuick 2.2
17import QtQuick 2.2
18import Ubuntu.Components 1.1
18import Ubuntu.Components 1.1
19−import "../Components/Flickables" as Flickables
20
19
21AbstractButton {
20AbstractButton {
22 id: root
21 id: root
@@ -76,7 +75,7 @@ AbstractButton {
76 }
75 }
77
76
78 // navigationListView is outside root
77 // navigationListView is outside root
79− Flickables.ListView {
78+ ListView {
80 id: navigationListView
79 id: navigationListView
81 objectName: "navigationListView"
80 objectName: "navigationListView"
82 visible: height > 0
81 visible: height > 0
qml/Dash/DashNavigationList.qml+1−2
@@ -17,7 +17,6 @@
17import QtQuick 2.2
17import QtQuick 2.2
18import Ubuntu.Components 1.1
18import Ubuntu.Components 1.1
19import "../Components"
19import "../Components"
20−import "../Components/Flickables" as Flickables
21
20
22Item {
21Item {
23 id: root
22 id: root
@@ -46,7 +45,7 @@ Item {
46 }
45 }
47 }
46 }
48
47
49− Flickables.Flickable {
48+ Flickable {
50 id: flickable
49 id: flickable
51
50
52 anchors.fill: parent
51 anchors.fill: parent
qml/Dash/PageHeader.qml+1−2
@@ -21,7 +21,6 @@ import Ubuntu.Components.Popups 1.0
21import Ubuntu.Components.ListItems 1.0
21import Ubuntu.Components.ListItems 1.0
22import "../Components"
22import "../Components"
23import "../Components/SearchHistoryModel"
23import "../Components/SearchHistoryModel"
24−import "../Components/Flickables" as Flickables
25
24
26Item {
25Item {
27 id: root
26 id: root
@@ -128,7 +127,7 @@ Item {
128 }
127 }
129 }
128 }
130
129
131− Flickables.Flickable {
130+ Flickable {
132 id: headerContainer
131 id: headerContainer
133 objectName: "headerContainer"
132 objectName: "headerContainer"
134 clip: contentY < height
133 clip: contentY < height
qml/Dash/PreviewListView.qml+3−2
@@ -18,7 +18,6 @@ import QtQuick 2.0
18import Ubuntu.Components 0.1
18import Ubuntu.Components 0.1
19import Unity 0.2
19import Unity 0.2
20import "../Components"
20import "../Components"
21−import "../Components/Flickables" as Flickables
22import "Previews" as Previews
21import "Previews" as Previews
23
22
24Item {
23Item {
@@ -53,7 +52,7 @@ Item {
53 onBackClicked: root.backClicked()
52 onBackClicked: root.backClicked()
54 }
53 }
55
54
56− Flickables.ListView {
55+ ListView {
57 id: previewListView
56 id: previewListView
58 objectName: "listView"
57 objectName: "listView"
59 anchors {
58 anchors {
@@ -67,6 +66,8 @@ Item {
67 snapMode: ListView.SnapOneItem
66 snapMode: ListView.SnapOneItem
68 boundsBehavior: Flickable.DragAndOvershootBounds
67 boundsBehavior: Flickable.DragAndOvershootBounds
69 highlightMoveDuration: 250
68 highlightMoveDuration: 250
69+ flickDeceleration: units.gu(625)
70+ maximumFlickVelocity: width * 5
70 cacheBuffer: 0
71 cacheBuffer: 0
71
72
72 property bool open: false
73 property bool open: false
qml/Dash/Previews/Preview.qml+1−2
@@ -16,7 +16,6 @@
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18import Ubuntu.Components 0.1
18import Ubuntu.Components 0.1
19−import "../../Components/Flickables" as Flickables
20
19
21/*! \brief This component constructs the Preview UI.
20/*! \brief This component constructs the Preview UI.
22 *
21 *
@@ -64,7 +63,7 @@ Item {
64 Repeater {
63 Repeater {
65 model: previewModel
64 model: previewModel
66
65
67− delegate: Flickables.ListView {
66+ delegate: ListView {
68 id: column
67 id: column
69 anchors { top: parent.top; bottom: parent.bottom }
68 anchors { top: parent.top; bottom: parent.bottom }
70 width: row.columnWidth
69 width: row.columnWidth
qml/Dash/Previews/PreviewImageGallery.qml+14−82
@@ -17,7 +17,6 @@
17import QtQuick 2.0
17import QtQuick 2.0
18import Ubuntu.Components 1.1
18import Ubuntu.Components 1.1
19import "../../Components"
19import "../../Components"
20−import "../../Components/Flickables" as Flickables
21
20
22/*! This preview widget shows a horizontal list of images.
21/*! This preview widget shows a horizontal list of images.
23 * The URIs for the images should be an array in widgetData["sources"].
22 * The URIs for the images should be an array in widgetData["sources"].
@@ -29,7 +28,7 @@ PreviewWidget {
29
28
30 property Item rootItem: QuickUtils.rootItem(root)
29 property Item rootItem: QuickUtils.rootItem(root)
31
30
32− Flickables.ListView {
31+ ListView {
33 id: previewImageListView
32 id: previewImageListView
34 objectName: "previewImageListView"
33 objectName: "previewImageListView"
35 spacing: units.gu(1)
34 spacing: units.gu(1)
@@ -66,46 +65,26 @@ PreviewWidget {
66 id: mouseArea
65 id: mouseArea
67 anchors.fill: parent
66 anchors.fill: parent
68 onClicked: {
67 onClicked: {
69− slideShowListView.currentIndex = index;
68+ overlay.delegateItem.currentIndex = index;
70− slideShow.initialX = rootItem.mapFromItem(parent, 0, 0).x
69+ overlay.initialX = rootItem.mapFromItem(parent, 0, 0).x;
71− slideShow.initialY = rootItem.mapFromItem(parent, 0, 0).y
70+ overlay.initialY = rootItem.mapFromItem(parent, 0, 0).y;
72− slideShow.visible = true;
71+ overlay.show();
73 }
72 }
74 }
73 }
75 }
74 }
76 }
75 }
77
76
78− Rectangle {
77+ PreviewOverlay {
79− id: slideShow
78+ id: overlay
80− objectName: "slideShow"
79+ objectName: "overlay"
81−
82− readonly property real initialScale: previewImageListView.height / rootItem.height
83− readonly property real scaleProgress: (scale - initialScale) / (1.0 - initialScale)
84− property real initialX: 0
85− property real initialY: 0
86−
87 parent: rootItem
80 parent: rootItem
88 width: parent.width
81 width: parent.width
89 height: parent.height
82 height: parent.height
90− visible: false
83+ initialScale: previewImageListView.height / rootItem.height
91− clip: visible && scale < 1.0
92− scale: visible ? 1.0 : initialScale
93− transformOrigin: Item.TopLeft
94− transform: Translate {
95− x: slideShow.initialX - slideShow.initialX * slideShow.scaleProgress
96− y: slideShow.initialY - slideShow.initialY * slideShow.scaleProgress
97− }
98− color: "black"
99− radius: units.gu(1) - units.gu(1) * slideShow.scaleProgress
100
84
101− Behavior on scale {
102− enabled: !slideShow.visible
103− UbuntuNumberAnimation { duration: UbuntuAnimation.FastDuration }
104− }
105−
106− Flickables.ListView {
85+ delegate: ListView {
107− id: slideShowListView
86+ id: overlayListView
108− objectName: "slideShowListView"
87+ objectName: "overlayListView"
109 anchors.fill: parent
88 anchors.fill: parent
110 orientation: ListView.Horizontal
89 orientation: ListView.Horizontal
111 highlightRangeMode: ListView.StrictlyEnforceRange
90 highlightRangeMode: ListView.StrictlyEnforceRange
@@ -120,7 +99,7 @@ PreviewWidget {
120 top: parent.top
99 top: parent.top
121 bottom: parent.bottom
100 bottom: parent.bottom
122 }
101 }
123− width: slideShow.width
102+ width: overlay.width
124 source: modelData ? modelData : ""
103 source: modelData ? modelData : ""
125 fillMode: Image.PreserveAspectFit
104 fillMode: Image.PreserveAspectFit
126 sourceSize { width: screenshot.width; height: screenshot.height }
105 sourceSize { width: screenshot.width; height: screenshot.height }
@@ -128,54 +107,7 @@ PreviewWidget {
128
107
129 MouseArea {
108 MouseArea {
130 anchors.fill: parent
109 anchors.fill: parent
131− onClicked: slideShowHeader.shown = !slideShowHeader.shown
110+ onClicked: overlay.headerShown = !overlay.headerShown
132− }
133− }
134−
135− Rectangle {
136− id: slideShowHeader
137−
138− property bool shown: true
139−
140− anchors {
141− left: parent.left
142− right: parent.right
143− }
144− height: units.gu(7)
145− visible: opacity > 0
146− opacity: slideShow.scaleProgress > 0.6 && shown ? 0.8 : 0
147− color: "black"
148−
149− Behavior on opacity {
150− UbuntuNumberAnimation { duration: UbuntuAnimation.SnapDuration }
151− }
152−
153− AbstractButton {
154− id: slideShowCloseButton
155− objectName: "slideShowCloseButton"
156− anchors {
157− top: parent.top
158− bottom: parent.bottom
159− }
160− width: units.gu(8)
161− height: width
162−
163− onClicked: slideShow.visible = false
164−
165− Rectangle {
166− anchors.fill: parent
167− color: Qt.rgba(1.0, 1.0, 1.0, 0.3)
168− visible: slideShowCloseButton.pressed
169− }
170−
171− Icon {
172− id: icon
173− anchors.centerIn: parent
174− width: units.gu(2.5)
175− height: width
176− color: Theme.palette.normal.foregroundText
177− name: "close"
178− }
179 }
111 }
180 }
112 }
181 }
113 }
qml/Dash/Previews/PreviewOverlay.qmlnew file+115
@@ -0,0 +1,115 @@
1+/*
2+ * Copyright (C) 2014 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+
17+import QtQuick 2.0
18+import Ubuntu.Components 1.1
19+import "../../Components"
20+
21+Rectangle {
22+ id: overlay
23+ objectName: "overlay"
24+
25+ readonly property real scaleProgress: (scale - initialScale) / (1.0 - initialScale)
26+
27+ property alias delegate: loader.sourceComponent
28+ property alias delegateItem: loader.item
29+ property alias headerShown: overlayHeader.shown
30+ property bool shown: false
31+ property bool opening: false
32+ property real initialX: 0
33+ property real initialY: 0
34+ property real initialScale: 0
35+
36+ visible: scale > initialScale
37+ clip: visible && scale < 1.0
38+ scale: shown ? 1.0 : initialScale
39+ transformOrigin: Item.TopLeft
40+ transform: Translate {
41+ x: overlay.initialX - overlay.initialX * overlay.scaleProgress
42+ y: overlay.initialY - overlay.initialY * overlay.scaleProgress
43+ }
44+ color: Qt.rgba(0, 0, 0, scaleProgress)
45+ radius: units.gu(1) - units.gu(1) * scaleProgress
46+
47+ function show() {
48+ opening = true;
49+ shown = true;
50+ }
51+
52+ function hide() {
53+ opening = false;
54+ shown = false;
55+ }
56+
57+ Behavior on scale {
58+ UbuntuNumberAnimation {
59+ duration: overlay.opening ? UbuntuAnimation.FastDuration :
60+ UbuntuAnimation.FastDuration / 2
61+ }
62+ }
63+
64+ Loader {
65+ id: loader
66+ anchors.fill: parent
67+ }
68+
69+ Rectangle {
70+ id: overlayHeader
71+
72+ property bool shown: true
73+
74+ anchors {
75+ left: parent.left
76+ right: parent.right
77+ }
78+ height: units.gu(7)
79+ visible: opacity > 0
80+ opacity: overlay.scaleProgress > 0.6 && shown ? 0.8 : 0
81+ color: "black"
82+
83+ Behavior on opacity {
84+ UbuntuNumberAnimation { duration: UbuntuAnimation.SnapDuration }
85+ }
86+
87+ AbstractButton {
88+ id: overlayCloseButton
89+ objectName: "overlayCloseButton"
90+ anchors {
91+ top: parent.top
92+ bottom: parent.bottom
93+ }
94+ width: units.gu(8)
95+ height: width
96+
97+ onClicked: overlay.hide()
98+
99+ Rectangle {
100+ anchors.fill: parent
101+ color: Qt.rgba(1.0, 1.0, 1.0, 0.3)
102+ visible: overlayCloseButton.pressed
103+ }
104+
105+ Icon {
106+ id: icon
107+ anchors.centerIn: parent
108+ width: units.gu(2.5)
109+ height: width
110+ color: Theme.palette.normal.foregroundText
111+ name: "close"
112+ }
113+ }
114+ }
115+}
qml/Dash/Previews/PreviewZoomableImage.qml+38−5
@@ -28,11 +28,44 @@ PreviewWidget {
28 id: root
28 id: root
29 implicitHeight: units.gu(22)
29 implicitHeight: units.gu(22)
30
30
31− ZoomableImage {
31+ property Item rootItem: QuickUtils.rootItem(root)
32− id: image
32+
33+ LazyImage {
34+ id: lazyImage
33− objectName: "image"
35+ objectName: "lazyImage"
36+ anchors {
37+ top: parent.top
38+ bottom: parent.bottom
39+ horizontalCenter: parent.horizontalCenter
40+ }
41+ scaleTo: "height"
34 source: widgetData["source"]
42 source: widgetData["source"]
43+
44+ borderSource: mouseArea.pressed ? "radius_pressed.sci" : "radius_idle.sci"
45+
46+ MouseArea {
47+ id: mouseArea
48+ anchors.fill: parent
49+ onClicked: {
50+ overlay.initialX = rootItem.mapFromItem(parent, 0, 0).x;
51+ overlay.initialY = rootItem.mapFromItem(parent, 0, 0).y;
52+ overlay.show();
53+ }
54+ }
55+ }
56+
57+ PreviewOverlay {
58+ id: overlay
59+ objectName: "overlay"
60+ parent: rootItem
61+ width: parent.width
62+ height: parent.height
63+ initialScale: lazyImage.height / rootItem.height
64+
65+ delegate: ZoomableImage {
66+ anchors.fill: parent
67+ source: widgetData["source"]
35− zoomable: widgetData["zoomable"] ? widgetData["zoomable"] : false
68+ zoomable: widgetData["zoomable"] ? widgetData["zoomable"] : false
36− anchors.fill: parent
69+ }
37 }
70 }
38}
71}
qml/Dash/ScopeListView.qml+6−3
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2013 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -14,8 +14,11 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
15 */
16
16
17−import "../Components/Flickables" as Flickables
17+import QtQuick 2.0
18+import Dash 0.1
18
19
19−Flickables.ListViewWithPageHeader {
20+ListViewWithPageHeader {
21+ maximumFlickVelocity: height * 10
22+ flickDeceleration: height * 2
20 cacheBuffer: Number.MAX_VALUE
23 cacheBuffer: Number.MAX_VALUE
21}
24}
qml/Dash/ScopeSettingsPage.qml+1−2
@@ -18,7 +18,6 @@ import QtQuick 2.2
18import Ubuntu.Components 1.1
18import Ubuntu.Components 1.1
19import Unity 0.2
19import Unity 0.2
20import "../Components"
20import "../Components"
21−import "../Components/Flickables" as Flickables
22import "ScopeSettings"
21import "ScopeSettings"
23
22
24Item {
23Item {
@@ -40,7 +39,7 @@ Item {
40 onBackClicked: root.backClicked()
39 onBackClicked: root.backClicked()
41 }
40 }
42
41
43− Flickables.ListView {
42+ ListView {
44 id: scopeSettings
43 id: scopeSettings
45 objectName: "scopeSettings"
44 objectName: "scopeSettings"
46 anchors {
45 anchors {
qml/Dash/ScopesOverviewAll.qml+1−2
@@ -16,9 +16,8 @@
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18import Ubuntu.Components 0.1
18import Ubuntu.Components 0.1
19−import "../Components/Flickables" as Flickables
20
19
21−Flickables.Flickable {
20+Flickable {
22 id: root
21 id: root
23
22
24 property alias model: cardGrid.model
23 property alias model: cardGrid.model
qml/Dash/ScopesOverviewFavorites.qml+2−4
@@ -14,11 +14,9 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
15 */
16
16
17−import QtQuick 2.3
17+import QtQuick 2.0
18−import Ubuntu.Components 1.1
19−import "../Components/Flickables" as Flickables
20
18
21−Flickables.Flickable {
19+Flickable {
22 id: root
20 id: root
23
21
24 signal clicked(int index, var result, var itemModel)
22 signal clicked(int index, var result, var itemModel)
qml/Greeter/Clock.qml+9−9
@@ -34,21 +34,21 @@ Item {
34 }
34 }
35 }
35 }
36
36
37− CachedUnityMenuModel {
37+ Indicators.SharedUnityMenuModel {
38 id: timeModel
38 id: timeModel
39 objectName: "timeModel"
39 objectName: "timeModel"
40
40
41 busName: "com.canonical.indicator.datetime"
41 busName: "com.canonical.indicator.datetime"
42− actionsObjectPath: "/com/canonical/indicator/datetime"
42+ actions: { "indicator": "/com/canonical/indicator/datetime" }
43 menuObjectPath: clock.visible ? "/com/canonical/indicator/datetime/phone" : ""
43 menuObjectPath: clock.visible ? "/com/canonical/indicator/datetime/phone" : ""
44+ }
44
45
45− Indicators.RootActionState {
46+ Indicators.RootActionState {
46− menu: timeModel.model
47+ menu: timeModel.model
47− onUpdated: {
48+ onUpdated: {
48− if (timeLabel.text != rightLabel) {
49+ if (timeLabel.text != rightLabel) {
49− timeLabel.text = rightLabel;
50+ if (rightLabel != "") timeLabel.text = rightLabel;
50− clock.currentDate = new Date();
51+ clock.currentDate = new Date();
51− }
52 }
52 }
53 }
53 }
54 }
54 }
qml/Greeter/Greeter.qml+5
@@ -129,6 +129,11 @@ Showable {
129 direction: Direction.Leftwards
129 direction: Direction.Leftwards
130 }
130 }
131 }
131 }
132+ TouchGate {
133+ targetItem: dragHandle
134+ anchors.fill: targetItem
135+ enabled: targetITem.enabled
136+ }
132
137
133 Loader {
138 Loader {
134 id: greeterContentLoader
139 id: greeterContentLoader
qml/Greeter/LoginList.qml+2−2
@@ -18,7 +18,6 @@ import QtQuick 2.0
18import Ubuntu.Components 0.1
18import Ubuntu.Components 0.1
19import LightDM 0.1 as LightDM
19import LightDM 0.1 as LightDM
20import "../Components"
20import "../Components"
21−import "../Components/Flickables" as Flickables
22
21
23Item {
22Item {
24 id: root
23 id: root
@@ -69,7 +68,7 @@ Item {
69 antialiasing: true
68 antialiasing: true
70 }
69 }
71
70
72− Flickables.ListView {
71+ ListView {
73 id: userList
72 id: userList
74 objectName: "userList"
73 objectName: "userList"
75
74
@@ -79,6 +78,7 @@ Item {
79 preferredHighlightEnd: userList.height / 2 - root.highlightedHeight / 2
78 preferredHighlightEnd: userList.height / 2 - root.highlightedHeight / 2
80 highlightRangeMode: ListView.StrictlyEnforceRange
79 highlightRangeMode: ListView.StrictlyEnforceRange
81 highlightMoveDuration: root.moveDuration
80 highlightMoveDuration: root.moveDuration
81+ flickDeceleration: 10000
82
82
83 readonly property bool movingInternally: moveTimer.running || userList.moving
83 readonly property bool movingInternally: moveTimer.running || userList.moving
84
84
qml/Hud/Hud.qml+1−2
@@ -19,7 +19,6 @@ import Ubuntu.Components 0.1
19import HudClient 0.1
19import HudClient 0.1
20
20
21import "../Components"
21import "../Components"
22−import "../Components/Flickables" as Flickables
23
22
24Item {
23Item {
25 id: hud
24 id: hud
@@ -401,7 +400,7 @@ Item {
401 }
400 }
402 progress: MathUtils.clamp((y - hudShowable.y + height - units.gu(8))/elementsYSliding, 0, 1)
401 progress: MathUtils.clamp((y - hudShowable.y + height - units.gu(8))/elementsYSliding, 0, 1)
403
402
404− Flickables.Flickable {
403+ Flickable {
405 anchors.left: parent.left
404 anchors.left: parent.left
406 anchors.right: parent.right
405 anchors.right: parent.right
407 contentHeight: resultList.height
406 contentHeight: resultList.height
qml/Hud/HudParametrizedActionsPage.qml+1−2
@@ -18,7 +18,6 @@ import QtQuick 2.0
18import Ubuntu.Components 1.1
18import Ubuntu.Components 1.1
19import Ubuntu.Components.Themes.Ambiance 1.1
19import Ubuntu.Components.Themes.Ambiance 1.1
20import "../Components"
20import "../Components"
21−import "../Components/Flickables" as Flickables
22
21
23Item {
22Item {
24 property alias header: header.title
23 property alias header: header.title
@@ -75,7 +74,7 @@ Item {
75 return values
74 return values
76 }
75 }
77
76
78− Flickables.Flickable {
77+ Flickable {
79 anchors.top: parent.top
78 anchors.top: parent.top
80 anchors.bottom: buttons.top
79 anchors.bottom: buttons.top
81 anchors.left: parent.left
80 anchors.left: parent.left
qml/Launcher/Launcher.qml+1−1
@@ -64,7 +64,7 @@ Item {
64 }
64 }
65
65
66 function tease() {
66 function tease() {
67− if (available) {
67+ if (available && !dragArea.dragging) {
68 teaseTimer.start();
68 teaseTimer.start();
69 }
69 }
70 }
70 }
qml/Launcher/LauncherDelegate.qml+2−19
@@ -27,7 +27,6 @@ Item {
27 property bool itemFocused: false
27 property bool itemFocused: false
28 property real maxAngle: 0
28 property real maxAngle: 0
29 property bool inverted: false
29 property bool inverted: false
30− property alias pinned: pin.visible
31
30
32 readonly property int effectiveHeight: Math.cos(angle * Math.PI / 180) * itemHeight
31 readonly property int effectiveHeight: Math.cos(angle * Math.PI / 180) * itemHeight
33 readonly property real foldedHeight: Math.cos(maxAngle * Math.PI / 180) * itemHeight
32 readonly property real foldedHeight: Math.cos(maxAngle * Math.PI / 180) * itemHeight
@@ -71,31 +70,15 @@ Item {
71 height: root.itemHeight - units.gu(1)
70 height: root.itemHeight - units.gu(1)
72 }
71 }
73
72
74− Rectangle {
75− id: pin
76− anchors {
77− left: iconShape.left
78− top: iconShape.top
79− topMargin: -units.dp(2)
80− }
81− width: units.gu(1)
82− height: width
83− radius: width / 2
84− color: "white"
85− }
86−
87 UbuntuShape {
73 UbuntuShape {
88 id: countEmblem
74 id: countEmblem
89 objectName: "countEmblem"
75 objectName: "countEmblem"
90−
91− readonly property real pinMargin: pin.visible ? pin.width + units.gu(1) - anchors.leftMargin : 0
92−
93 anchors {
76 anchors {
94 right: parent.right
77 right: parent.right
95 top: parent.top
78 top: parent.top
96 margins: units.dp(3)
79 margins: units.dp(3)
97 }
80 }
98− width: Math.min(root.itemWidth - pinMargin, Math.max(units.gu(2), countLabel.implicitWidth + units.gu(1)))
81+ width: Math.min(root.itemWidth, Math.max(units.gu(2), countLabel.implicitWidth + units.gu(1)))
99 height: units.gu(2)
82 height: units.gu(2)
100 color: UbuntuColors.orange
83 color: UbuntuColors.orange
101 visible: root.countVisible
84 visible: root.countVisible
@@ -109,7 +92,7 @@ Item {
109 // FIXME: verticalCenter seems to be off wee bit and QML doesn't have a centerLine
92 // FIXME: verticalCenter seems to be off wee bit and QML doesn't have a centerLine
110 // property for Text: https://bugreports.qt-project.org/browse/QTBUG-40479
93 // property for Text: https://bugreports.qt-project.org/browse/QTBUG-40479
111 anchors.verticalCenterOffset: -units.dp(.5)
94 anchors.verticalCenterOffset: -units.dp(.5)
112− width: root.itemWidth - units.gu(1) - countEmblem.pinMargin
95+ width: root.itemWidth - units.gu(1)
113 horizontalAlignment: Text.AlignHCenter
96 horizontalAlignment: Text.AlignHCenter
114 elide: Text.ElideRight
97 elide: Text.ElideRight
115 color: "white"
98 color: "white"
qml/Launcher/LauncherPanel.qml+1−7
@@ -21,7 +21,6 @@ import Unity.Launcher 0.1
21import Ubuntu.Components.Popups 0.1
21import Ubuntu.Components.Popups 0.1
22import "../Components/ListItems"
22import "../Components/ListItems"
23import "../Components/"
23import "../Components/"
24−import "../Components/Flickables" as Flickables
25
24
26Rectangle {
25Rectangle {
27 id: root
26 id: root
@@ -76,7 +75,7 @@ Rectangle {
76 anchors.fill: parent
75 anchors.fill: parent
77 clip: true
76 clip: true
78
77
79− Flickables.ListView {
78+ ListView {
80 id: launcherListView
79 id: launcherListView
81 objectName: "launcherListView"
80 objectName: "launcherListView"
82 anchors {
81 anchors {
@@ -151,7 +150,6 @@ Rectangle {
151 count: model.count
150 count: model.count
152 countVisible: model.countVisible
151 countVisible: model.countVisible
153 progress: model.progress
152 progress: model.progress
154− pinned: model.pinned
155 itemFocused: model.focused
153 itemFocused: model.focused
156 inverted: root.inverted
154 inverted: root.inverted
157 z: -Math.abs(offset)
155 z: -Math.abs(offset)
@@ -462,10 +460,6 @@ Rectangle {
462 width: itemWidth
460 width: itemWidth
463 rotation: root.rotation
461 rotation: root.rotation
464 itemOpacity: 0.9
462 itemOpacity: 0.9
465− pinned: dndArea.draggedIndex > -1 &&
466− LauncherModel.get(dndArea.draggedIndex).pinned &&
467− !dndArea.preDragging &&
468− !dndArea.dragging
469
463
470 function flatten() {
464 function flatten() {
471 fakeDragItemAnimation.start();
465 fakeDragItemAnimation.start();
qml/Notifications/Notification.qml+73−10
@@ -20,7 +20,6 @@ import Ubuntu.Components 1.1
20import Unity.Notifications 1.0
20import Unity.Notifications 1.0
21import QMenuModel 0.1
21import QMenuModel 0.1
22import Utils 0.1
22import Utils 0.1
23−import "../Components/Flickables" as Flickables
24
23
25import Ubuntu.Components.ListItems 0.1 as ListItem
24import Ubuntu.Components.ListItems 0.1 as ListItem
26
25
@@ -31,6 +30,7 @@ Item {
31 property alias secondaryIconSource: secondaryIcon.source
30 property alias secondaryIconSource: secondaryIcon.source
32 property alias summary: summaryLabel.text
31 property alias summary: summaryLabel.text
33 property alias body: bodyLabel.text
32 property alias body: bodyLabel.text
33+ property alias value: valueIndicator.value
34 property var actions
34 property var actions
35 property var notificationId
35 property var notificationId
36 property var type
36 property var type
@@ -40,6 +40,7 @@ Item {
40 property bool fullscreen: false
40 property bool fullscreen: false
41 property int maxHeight
41 property int maxHeight
42 property int margins
42 property int margins
43+ readonly property bool darkOnBright: panel.indicators.shown || type === Notification.SnapDecision
43 readonly property color red: "#fc4949"
44 readonly property color red: "#fc4949"
44 readonly property color green: "#3fb24f"
45 readonly property color green: "#3fb24f"
45 readonly property color sdLightGrey: "#eaeaea"
46 readonly property color sdLightGrey: "#eaeaea"
@@ -48,9 +49,9 @@ Item {
48 readonly property real contentSpacing: units.gu(2)
49 readonly property real contentSpacing: units.gu(2)
49
50
50 objectName: "background"
51 objectName: "background"
51− implicitHeight: type !== Notification.PlaceHolder ? (fullscreen ? maxHeight : outterColumn.height + contentSpacing * 2) : 0
52+ implicitHeight: type !== Notification.PlaceHolder ? (fullscreen ? maxHeight : outterColumn.height - shapedBack.anchors.topMargin + contentSpacing * 2) : 0
52
53
53− color: type == Notification.SnapDecision ? sdLightGrey : Qt.rgba(0.132, 0.117, 0.109, 0.97)
54+ color: (type === Notification.Confirmation && notificationList.useModal && !greeter.shown) || darkOnBright ? sdLightGrey : Qt.rgba(0.132, 0.117, 0.109, 0.97)
54 opacity: 1 // FIXME: 1 because of LP: #1354406 workaround, has to be 0 really
55 opacity: 1 // FIXME: 1 because of LP: #1354406 workaround, has to be 0 really
55
56
56 state: {
57 state: {
@@ -78,6 +79,7 @@ Item {
78 Audio {
79 Audio {
79 id: sound
80 id: sound
80 objectName: "sound"
81 objectName: "sound"
82+ audioRole: MediaPlayer.alert
81 source: hints["suppress-sound"] != "true" && hints["sound-file"] != undefined ? hints["sound-file"] : ""
83 source: hints["suppress-sound"] != "true" && hints["sound-file"] != undefined ? hints["sound-file"] : ""
82 }
84 }
83
85
@@ -88,6 +90,12 @@ Item {
88 }
90 }
89 }
91 }
90
92
93+ onHintsChanged: {
94+ if (type === Notification.Confirmation && opacity == 1.0 && hints["suppress-sound"] != "true" && sound.source != "") {
95+ sound.play();
96+ }
97+ }
98+
91 Behavior on height {
99 Behavior on height {
92 id: normalHeightBehavior
100 id: normalHeightBehavior
93
101
@@ -126,6 +134,7 @@ Item {
126 fill: parent
134 fill: parent
127 leftMargin: notification.margins
135 leftMargin: notification.margins
128 rightMargin: notification.margins
136 rightMargin: notification.margins
137+ topMargin: type === Notification.Confirmation ? units.gu(.5) : 0
129 }
138 }
130 color: parent.color
139 color: parent.color
131 opacity: parent.opacity
140 opacity: parent.opacity
@@ -206,10 +215,10 @@ Item {
206 right: parent.right
215 right: parent.right
207 top: parent.top
216 top: parent.top
208 margins: 0
217 margins: 0
209− topMargin: fullscreen ? 0 : units.gu(2)
218+ topMargin: fullscreen ? 0 : type === Notification.Confirmation ? units.gu(1) : units.gu(2)
210 }
219 }
211
220
212− spacing: units.gu(2)
221+ spacing: type === Notification.Confirmation ? units.gu(1) : units.gu(2)
213
222
214 Row {
223 Row {
215 id: topRow
224 id: topRow
@@ -228,7 +237,7 @@ Item {
228 width: type == Notification.Ephemeral && !bodyLabel.visible ? units.gu(3) : units.gu(6)
237 width: type == Notification.Ephemeral && !bodyLabel.visible ? units.gu(3) : units.gu(6)
229 height: width
238 height: width
230 shaped: notification.hints["x-canonical-non-shaped-icon"] == "true" ? false : true
239 shaped: notification.hints["x-canonical-non-shaped-icon"] == "true" ? false : true
231− visible: iconSource !== undefined && iconSource != ""
240+ visible: iconSource !== undefined && iconSource !== "" && type !== Notification.Confirmation
232 }
241 }
233
242
234 Column {
243 Column {
@@ -245,8 +254,9 @@ Item {
245 left: parent.left
254 left: parent.left
246 right: parent.right
255 right: parent.right
247 }
256 }
257+ visible: type !== Notification.Confirmation
248 fontSize: "medium"
258 fontSize: "medium"
249− color: type == Notification.SnapDecision ? sdFontColor : Theme.palette.selected.backgroundText
259+ color: darkOnBright ? sdFontColor : Theme.palette.selected.backgroundText
250 elide: Text.ElideRight
260 elide: Text.ElideRight
251 textFormat: Text.PlainText
261 textFormat: Text.PlainText
252 }
262 }
@@ -259,9 +269,9 @@ Item {
259 left: parent.left
269 left: parent.left
260 right: parent.right
270 right: parent.right
261 }
271 }
262− visible: body != ""
272+ visible: body != "" && type !== Notification.Confirmation
263 fontSize: "small"
273 fontSize: "small"
264− color: type == Notification.SnapDecision ? sdFontColor : Theme.palette.selected.backgroundText
274+ color: darkOnBright ? sdFontColor : Theme.palette.selected.backgroundText
265 wrapMode: Text.WordWrap
275 wrapMode: Text.WordWrap
266 maximumLineCount: type == Notification.SnapDecision ? 12 : 2
276 maximumLineCount: type == Notification.SnapDecision ? 12 : 2
267 elide: Text.ElideRight
277 elide: Text.ElideRight
@@ -284,6 +294,59 @@ Item {
284 visible: type == Notification.SnapDecision
294 visible: type == Notification.SnapDecision
285 }
295 }
286
296
297+ ShapedIcon {
298+ id: centeredIcon
299+ objectName: "centeredIcon"
300+ width: units.gu(5)
301+ height: width
302+ shaped: notification.hints["x-canonical-non-shaped-icon"] == "true" ? false : true
303+ fileSource: icon.fileSource
304+ visible: fileSource !== undefined && fileSource !== "" && type === Notification.Confirmation
305+ anchors.horizontalCenter: parent.horizontalCenter
306+ }
307+
308+ Label {
309+ id: valueLabel
310+ objectName: "valueLabel"
311+ text: body
312+ anchors.horizontalCenter: parent.horizontalCenter
313+ visible: type === Notification.Confirmation && body !== ""
314+ fontSize: "medium"
315+ color: darkOnBright ? sdFontColor : Theme.palette.selected.backgroundText
316+ wrapMode: Text.WordWrap
317+ maximumLineCount: 1
318+ elide: Text.ElideRight
319+ textFormat: Text.PlainText
320+ }
321+
322+ UbuntuShape {
323+ id: valueIndicator
324+ objectName: "valueIndicator"
325+ visible: type === Notification.Confirmation
326+ property double value
327+
328+ anchors {
329+ left: parent.left
330+ right: parent.right
331+ margins: contentSpacing
332+ }
333+
334+ height: units.gu(1)
335+ color: darkOnBright ? UbuntuColors.darkGrey : UbuntuColors.lightGrey
336+ borderSource: "none"
337+ radius: "small"
338+
339+ UbuntuShape {
340+ id: innerBar
341+ objectName: "innerBar"
342+ width: valueIndicator.width * valueIndicator.value / 100
343+ height: units.gu(1)
344+ color: notification.hints["x-canonical-value-bar-tint"] === "true" ? UbuntuColors.orange : darkOnBright ? UbuntuColors.lightGrey : "white"
345+ borderSource: "none"
346+ radius: "small"
347+ }
348+ }
349+
287 Column {
350 Column {
288 id: dialogColumn
351 id: dialogColumn
289 objectName: "dialogListView"
352 objectName: "dialogListView"
@@ -456,7 +519,7 @@ Item {
456 onClicked: notification.notification.invokeAction(comboRepeater.itemAt(2).actionId)
519 onClicked: notification.notification.invokeAction(comboRepeater.itemAt(2).actionId)
457 expanded: false
520 expanded: false
458 expandedHeight: (comboRepeater.count - 2) * units.gu(4) + units.gu(.5)
521 expandedHeight: (comboRepeater.count - 2) * units.gu(4) + units.gu(.5)
459− comboList: Flickables.Flickable {
522+ comboList: Flickable {
460 // this has to be wrapped inside a flickable
523 // this has to be wrapped inside a flickable
461 // to work around a feature/bug? of the
524 // to work around a feature/bug? of the
462 // ComboButton SDK-element, making a regular
525 // ComboButton SDK-element, making a regular
qml/Notifications/Notifications.qml+3−2
@@ -19,9 +19,8 @@ import Ubuntu.Components 0.1
19import Unity.Notifications 1.0 as UnityNotifications
19import Unity.Notifications 1.0 as UnityNotifications
20import Utils 0.1
20import Utils 0.1
21import "../Components"
21import "../Components"
22−import "../Components/Flickables" as Flickables
23
22
24−Flickables.ListView {
23+ListView {
25 id: notificationList
24 id: notificationList
26
25
27 objectName: "notificationList"
26 objectName: "notificationList"
@@ -32,6 +31,7 @@ Flickables.ListView {
32
31
33 SortFilterProxyModel {
32 SortFilterProxyModel {
34 id: snapDecisionProxyModel
33 id: snapDecisionProxyModel
34+ objectName: "snapDecisionProxyModel"
35
35
36 model: notificationList.model
36 model: notificationList.model
37 filterRole: UnityNotifications.ModelInterface != undefined ? UnityNotifications.ModelInterface.RoleType : 0
37 filterRole: UnityNotifications.ModelInterface != undefined ? UnityNotifications.ModelInterface.RoleType : 0
@@ -56,6 +56,7 @@ Flickables.ListView {
56 secondaryIconSource: model.secondaryIcon
56 secondaryIconSource: model.secondaryIcon
57 summary: model.summary
57 summary: model.summary
58 body: model.body
58 body: model.body
59+ value: model.value
59 actions: model.actions
60 actions: model.actions
60 notificationId: model.id
61 notificationId: model.id
61 notification: notificationList.model.getRaw(notificationId)
62 notification: notificationList.model.getRaw(notificationId)
qml/Panel/ActiveCallHint.qml+3−1
@@ -23,6 +23,8 @@ import "../Components"
23Item {
23Item {
24 id: callHint
24 id: callHint
25
25
26+ property bool greeterShown: false
27+
26 readonly property bool active: {
28 readonly property bool active: {
27 var application = ApplicationManager.findApplication("dialer-app");
29 var application = ApplicationManager.findApplication("dialer-app");
28
30
@@ -33,7 +35,7 @@ Item {
33 return application.state !== ApplicationInfoInterface.Starting;
35 return application.state !== ApplicationInfoInterface.Starting;
34 }
36 }
35 }
37 }
36− if (ApplicationManager.focusedApplicationId !== "dialer-app") {
38+ if (greeterShown || ApplicationManager.focusedApplicationId !== "dialer-app") {
37 if (application) {
39 if (application) {
38 // Don't show if application is still starting; might get a fleeting true/false.
40 // Don't show if application is still starting; might get a fleeting true/false.
39 return application.state !== ApplicationInfoInterface.Starting && callManager.hasCalls;
41 return application.state !== ApplicationInfoInterface.Starting && callManager.hasCalls;
qml/Components/Flickables/Flickable.qml → qml/Panel/Handle.qmlrenamed+24−4
@@ -14,10 +14,30 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
15 */
16
16
17−import QtQuick 2.3 as QtQuick
17+import QtQuick 2.2
18import Ubuntu.Components 1.1
18import Ubuntu.Components 1.1
19
19
20−QtQuick.Flickable {
20+Rectangle {
21− flickDeceleration: 1500 * units.gridUnit / 8
21+ id: handle
22− maximumFlickVelocity: 2500 * units.gridUnit / 8
22+ color: "#333333"
23+ height: units.gu(2)
24+ property bool active: false
25+
26+ Row {
27+ id: dots
28+ width: childrenRect.width
29+ height: childrenRect.height
30+ anchors.centerIn: parent
31+ spacing: units.gu(0.5)
32+ Repeater {
33+ model: 3
34+ delegate: Rectangle {
35+ id: dot
36+ width: units.dp(3)
37+ height: width
38+ color: handle.active ? "#de4814" : "#717171"
39+ radius: units.dp(1)
40+ }
41+ }
42+ }
23}
43}
qml/Panel/IndicatorItem.qml+232−27
@@ -1,53 +1,258 @@
1/*
1/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright 2013-2014 Canonical Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5+ * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
6 * the Free Software Foundation; version 3.
7 *
7 *
8 * This program is distributed in the hope that it will be useful,
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
11+ * GNU Lesser General Public License for more details.
12 *
12 *
13− * You should have received a copy of the GNU General Public License
13+ * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
15 */
16
16
17−import QtQuick 2.0
17+import QtQuick 2.2
18−import Ubuntu.Components 0.1
18+import Ubuntu.Components 1.1
19−import Unity.Indicators 0.1 as Indicators
19+import Ubuntu.Settings.Components 0.1
20−import "../Components"
20+import "Indicators"
21
21
22−Loader {
22+IndicatorDelegate {
23 id: root
23 id: root
24
24
25− property alias widgetSource: root.source
26− property bool dimmed: false
27− property var indicatorProperties: undefined
28− property bool indicatorVisible: item ? item.enabled : false
29 property string identifier
25 property string identifier
26+ property string title: indicatorName.text
27+ property alias leftLabel: leftLabelItem.text
28+ property alias rightLabel: rightLabelItem.text
29+ property var icons: undefined
30+ property bool expanded: false
31+ property bool selected: false
32+ property real iconHeight: units.gu(2)
33+ readonly property color color: {
34+ if (!expanded) return "#ededed";
35+ if (!selected) return "#4c4c4c";
36+ return "#ededed";
37+ }
38+
39+ signal clicked()
40+
41+ implicitWidth: mainItems.width
30
42
31− opacity: dimmed ? 0.4 : 1
43+ MouseArea {
32− Behavior on opacity { UbuntuNumberAnimation { duration: UbuntuAnimation.BriskDuration } }
44+ anchors.fill: parent
45+ onClicked: parent.clicked()
46+ }
47+
48+ // FIXME: For now we will enable led indicator support only for messaging indicator
49+ // in the future we should export a led API insted of doing that,
50+ Loader {
51+ id: indicatorLed
52+ // only load source Component if the icons contains the new message icon
53+ source: (root.icons && (String(root.icons).indexOf("indicator-messages-new") != -1)) ? Qt.resolvedUrl("Indicators/IndicatorsLight.qml") : ""
54+ }
33
55
34− onLoaded: {
56+ Item {
35− for(var pName in indicatorProperties) {
57+ id: mainItems
36− if (item.hasOwnProperty(pName)) {
58+ anchors.centerIn: parent
37− item[pName] = indicatorProperties[pName];
59+
60+ width: leftLabelItem.width + iconsItem.width + rightLabelItem.width
61+ implicitHeight: units.gu(2)
62+
63+ Label {
64+ id: leftLabelItem
65+ objectName: "leftLabel"
66+
67+ anchors {
68+ left: mainItems.left
69+ verticalCenter: parent.verticalCenter
38 }
70 }
71+ width: contentWidth > 0 ? contentWidth + units.gu(1) : 0
72+ horizontalAlignment: Text.AlignHCenter
73+
74+ opacity: 1.0
75+ font.family: "Ubuntu"
76+ fontSize: "medium"
77+ color: root.color
78+ Behavior on color { ColorAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing } }
39 }
79 }
80+
81+ Item {
82+ id: iconsItem
83+ objectName: "icons"
84+
85+ width: iconRow.width > 0 ? iconRow.width + units.gu(1) : 0
86+ anchors {
87+ left: leftLabelItem.right
88+ verticalCenter: parent.verticalCenter
89+ }
90+
91+ Row {
92+ id: iconRow
93+ anchors.centerIn: iconsItem
94+ spacing: units.gu(1)
95+
96+ Repeater {
97+ id: iconRepeater
98+ objectName: "iconRepeater"
99+
100+ model: d.useFallbackIcon ? [ "image://theme/settings" ] : root.icons
101+
102+ StatusIcon {
103+ id: itemImage
104+ objectName: "icon"+index
105+ height: iconHeight
106+ source: modelData
107+ sets: ["status", "actions"]
108+ color: root.color
109+ Behavior on color { ColorAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing } }
110+
111+ opacity: {
112+ if (!expanded) return 1.0;
113+ if (!selected) return 0.6;
114+ return 1.0;
115+ }
116+ Behavior on opacity { NumberAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing } }
117+ }
118+ }
119+ }
120+ }
121+
122+ Label {
123+ id: rightLabelItem
124+ objectName: "rightLabel"
125+
126+ anchors {
127+ left: iconsItem.right
128+ verticalCenter: parent.verticalCenter
129+ }
130+ width: contentWidth > 0 ? contentWidth + units.gu(1) : 0
131+ horizontalAlignment: Text.AlignHCenter
132+
133+ opacity: 1.0
134+ font.family: "Ubuntu"
135+ fontSize: "medium"
136+ color: root.color
137+ Behavior on color { ColorAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing } }
138+ }
139+ }
140+
141+ Label {
142+ id: indicatorName
143+ objectName: "indicatorName"
144+
145+ anchors.top: mainItems.bottom
146+ anchors.topMargin: units.gu(0.5)
147+ anchors.horizontalCenter: parent.horizontalCenter
148+ width: contentWidth > 0 ? contentWidth + units.gu(1) : 0
149+
150+ text: title !== "" ? title : identifier
151+ fontSize: "x-small"
152+ horizontalAlignment: Text.AlignHCenter
153+ opacity: 0
154+ color: root.color
155+ Behavior on color { ColorAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing } }
40 }
156 }
41
157
42− Binding {
158+ StateGroup {
43− target: item
159+ id: d
44− property: "identifier"
160+ property bool useFallbackIcon: false
45− value: identifier
161+
162+ states: [
163+ State {
164+ name: "minimised"
165+ when: !expanded && ((icons && icons.length > 0) || leftLabel !== "" || rightLabel !== "")
166+ PropertyChanges { target: indicatorName; opacity: 0}
167+ },
168+
169+ State {
170+ name: "minimised_fallback"
171+ when: !expanded && (!icons || icons.length === 0) && leftLabel == "" && rightLabel == ""
172+ PropertyChanges { target: indicatorName; opacity: 0}
173+ PropertyChanges { target: d; useFallbackIcon: true }
174+ },
175+
176+ State {
177+ name: "expanded"
178+ PropertyChanges { target: indicatorName; visible: true; opacity: 1}
179+ PropertyChanges { target: mainItems; anchors.verticalCenterOffset: -units.gu(1) }
180+ },
181+
182+ State {
183+ name: "expanded_icon"
184+ extend: "expanded"
185+ when: expanded && (icons && icons.length > 0)
186+ AnchorChanges { target: iconsItem; anchors.left: undefined; anchors.horizontalCenter: parent.horizontalCenter }
187+ AnchorChanges { target: leftLabelItem; anchors.left: undefined; anchors.right: iconsItem.left }
188+ PropertyChanges { target: leftLabelItem; opacity: 0 }
189+ PropertyChanges { target: leftLabelItem; opacity: 0 }
190+ PropertyChanges { target: rightLabelItem; opacity: 0 }
191+ PropertyChanges { target: root; width: Math.max(units.gu(10), Math.max(iconsItem.width, indicatorName.width)) }
192+ },
193+
194+ State {
195+ name: "expanded_fallback"
196+ extend: "expanded"
197+ when: expanded && (!icons || icons.length === 0) && leftLabel == "" && rightLabel == ""
198+ PropertyChanges { target: d; useFallbackIcon: true }
199+ AnchorChanges { target: iconsItem; anchors.left: undefined; anchors.horizontalCenter: parent.horizontalCenter }
200+ AnchorChanges { target: leftLabelItem; anchors.left: undefined; anchors.right: iconsItem.left }
201+ PropertyChanges { target: leftLabelItem; opacity: 0 }
202+ PropertyChanges { target: leftLabelItem; opacity: 0 }
203+ PropertyChanges { target: rightLabelItem; opacity: 0 }
204+ PropertyChanges { target: root; width: Math.max(units.gu(10), Math.max(iconsItem.width, indicatorName.width)) }
205+ },
206+
207+ State {
208+ name: "expanded_rightLabel"
209+ extend: "expanded"
210+ when: expanded && (!icons || icons.length === 0) && rightLabel !== ""
211+ AnchorChanges { target: rightLabelItem; anchors.left: undefined; anchors.horizontalCenter: parent.horizontalCenter }
212+ PropertyChanges { target: iconsItem; opacity: 0 }
213+ PropertyChanges { target: leftLabelItem; opacity: 0 }
214+ PropertyChanges { target: root; width: Math.max(units.gu(10), Math.max(rightLabelItem.width, indicatorName.width)) }
215+ },
216+
217+ State {
218+ name: "expanded_leftLabel"
219+ extend: "expanded"
220+ when: expanded && (!icons || icons.length === 0) && leftLabel !== ""
221+ AnchorChanges { target: leftLabelItem; anchors.left: undefined; anchors.horizontalCenter: parent.horizontalCenter }
222+ PropertyChanges { target: iconsItem; opacity: 0 }
223+ PropertyChanges { target: rightLabelItem; opacity: 0 }
224+ PropertyChanges { target: root; width: Math.max(units.gu(10), Math.max(leftLabelItem.width, indicatorName.width)) }
225+ }
226+ ]
227+
228+ transitions: [
229+ Transition {
230+ PropertyAction { target: d; property: "useFallbackIcon" }
231+ AnchorAnimation {
232+ targets: [ mainItems, iconsItem, leftLabelItem, rightLabelItem ]
233+ duration: UbuntuAnimation.SnapDuration; easing: UbuntuAnimation.StandardEasing
234+ }
235+ PropertyAnimation {
236+ targets: [ root, mainItems, iconsItem, leftLabelItem, rightLabelItem, indicatorName ]
237+ properties: "width, opacity, anchors.verticalCenterOffset";
238+ duration: UbuntuAnimation.SnapDuration; easing: UbuntuAnimation.StandardEasing
239+ }
240+ }
241+ ]
46 }
242 }
47
243
48− Binding {
244+ onRootActionStateChanged: {
49− target: item
245+ if (rootActionState == undefined) {
50− property: "objectName"
246+ title = "";
51− value: identifier + "-widget"
247+ leftLabel = "";
248+ rightLabel = "";
249+ icons = undefined;
250+ return;
251+ }
252+
253+ title = rootActionState.title ? rootActionState.title : "";
254+ leftLabel = rootActionState.leftLabel ? rootActionState.leftLabel : "";
255+ rightLabel = rootActionState.rightLabel ? rootActionState.rightLabel : "";
256+ icons = rootActionState.icons;
52 }
257 }
53}
258}
qml/Panel/IndicatorItemRow.qmlnew file+297
@@ -0,0 +1,297 @@
1+/*
2+ * Copyright (C) 2013-2014 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+
17+import QtQuick 2.2
18+import Ubuntu.Components 1.1
19+
20+Item {
21+ id: root
22+ width: row.width
23+ height: units.gu(3)
24+
25+ property QtObject indicatorsModel: null
26+ property real overFlowWidth: width
27+ property bool expanded: false
28+ property var currentItem
29+ readonly property int currentItemIndex: currentItem ? currentItem.ownIndex : -1
30+
31+ property real unitProgress: 0.0
32+ property real selectionChangeBuffer: units.gu(2)
33+ property bool enableLateralChanges: false
34+ property color hightlightColor: "#ededed"
35+
36+ property real lateralPosition: -1
37+ onLateralPositionChanged: {
38+ updateItemFromLateralPosition();
39+ }
40+
41+ onEnableLateralChangesChanged: {
42+ updateItemFromLateralPosition();
43+ }
44+
45+ function updateItemFromLateralPosition() {
46+ if (currentItem && !enableLateralChanges) return;
47+ if (lateralPosition === -1) return;
48+
49+ if (!currentItem) {
50+ selectItemAt(lateralPosition);
51+ return;
52+ }
53+
54+ var maximumBufferOffset = selectionChangeBuffer * unitProgress;
55+ var proposedItem = indicatorAt(lateralPosition, 0);
56+ if (proposedItem) {
57+ var bufferExceeded = false;
58+
59+ if (proposedItem !== currentItem) {
60+ // Proposed item is not directly adjacent to current?
61+ if (Math.abs(proposedItem.ownIndex - currentItem.ownIndex) > 1) {
62+ bufferExceeded = true;
63+ } else { // no
64+ var currentItemLateralPosition = root.mapToItem(proposedItem, lateralPosition, 0).x;
65+
66+ // Is the distance into proposed item greater than max buffer?
67+ // Proposed item is before current item
68+ if (proposedItem.x < currentItem.x) {
69+ bufferExceeded = (proposedItem.width - currentItemLateralPosition) > maximumBufferOffset;
70+ } else { // After
71+ bufferExceeded = currentItemLateralPosition > maximumBufferOffset;
72+ }
73+ }
74+ if (bufferExceeded) {
75+ selectItemAt(lateralPosition);
76+ }
77+ }
78+ } else {
79+ selectItemAt(lateralPosition);
80+ }
81+ }
82+
83+ function indicatorAt(x, y) {
84+ var item = row.childAt(x, y);
85+ return item && item.hasOwnProperty("ownIndex") ? item : null;
86+ }
87+
88+ function resetCurrentItem() {
89+ d.firstItemSwitch = true;
90+ d.previousItem = undefined;
91+ currentItem = undefined;
92+ }
93+
94+ function setCurrentItemIndex(index) {
95+ for (var i = 0; i < row.children.length; i++) {
96+ var item = row.children[i];
97+ if (item.hasOwnProperty("ownIndex") && item.ownIndex === index) {
98+ if (currentItem !== item) currentItem = item;
99+ break;
100+ }
101+ }
102+ }
103+
104+ function selectItemAt(lateralPosition) {
105+ var item = indicatorAt(lateralPosition, 0);
106+ if (item && item.opacity > 0) {
107+ currentItem = item;
108+ } else {
109+ // Select default item.
110+ var searchIndex = lateralPosition > width ? repeater.count - 1 : 0;
111+
112+ for (var i = 0; i < row.children.length; i++) {
113+ if (row.children[i].hasOwnProperty("ownIndex") && row.children[i].ownIndex === searchIndex) {
114+ item = row.children[i];
115+ break;
116+ }
117+ }
118+ if (currentItem !== item) currentItem = item;
119+ }
120+ }
121+
122+ QtObject {
123+ id: d
124+ property bool firstItemSwitch: true
125+ property var previousItem
126+ property bool forceAlignmentAnimationDisabled: false
127+ }
128+
129+ onCurrentItemChanged: {
130+ if (d.previousItem) {
131+ d.firstItemSwitch = false;
132+ }
133+ d.previousItem = currentItem;
134+ }
135+
136+ Row {
137+ id: row
138+ anchors {
139+ top: parent.top
140+ bottom: parent.bottom
141+ }
142+
143+ // TODO: make this better
144+ // when the width changes, the highlight will lag behind due to animation, so we need to disable the animation
145+ // and adjust the highlight X immediately.
146+ width: implicitWidth
147+ Behavior on width {
148+ SequentialAnimation {
149+ ScriptAction {
150+ script: {
151+ d.forceAlignmentAnimationDisabled = true;
152+ highlight.currentItemX = Qt.binding(function() { return currentItem ? currentItem.x : 0 });
153+ d.forceAlignmentAnimationDisabled = false;
154+ }
155+ }
156+ }
157+ }
158+
159+ Repeater {
160+ id: repeater
161+ model: indicatorsModel
162+ visible: false
163+
164+ onItemRemoved: {
165+ // current item removed.
166+ if (currentItem === item) {
167+ var i = 0;
168+ while (i < row.children.length) {
169+ var childItem = row.children[i];
170+ if (childItem !== item) {
171+ setCurrentItemIndex(i);
172+ break;
173+ }
174+ i++;
175+ }
176+ }
177+ }
178+
179+
180+ delegate: IndicatorItem {
181+ id: indicatorItem
182+ objectName: identifier+"-panelItem"
183+
184+ property int ownIndex: index
185+ property bool overflow: row.width - x > overFlowWidth
186+ property bool hidden: !expanded && (overflow || !indicatorVisible)
187+
188+ height: row.height
189+ expanded: root.expanded
190+ selected: currentItem === this
191+
192+ identifier: model.identifier
193+ busName: indicatorProperties.busName
194+ actionsObjectPath: indicatorProperties.actionsObjectPath
195+ menuObjectPath: indicatorProperties.menuObjectPath
196+
197+ opacity: hidden ? 0.0 : 1.0
198+ Behavior on opacity {
199+ NumberAnimation { duration: UbuntuAnimation.SnapDuration; easing: UbuntuAnimation.StandardEasing }
200+ }
201+
202+ width: (expanded || indicatorVisible) ? implicitWidth : 0
203+
204+ Behavior on width {
205+ NumberAnimation { duration: UbuntuAnimation.SnapDuration; easing: UbuntuAnimation.StandardEasing }
206+ }
207+
208+ Component.onDestruction: {
209+ // current item removed.
210+ if (currentItem === this) {
211+ var i = 0;
212+ while (i < row.children.length) {
213+ var childItem = row.children[i];
214+ if (childItem !== this) {
215+ setCurrentItemIndex(i);
216+ break;
217+ }
218+ i++;
219+ }
220+ }
221+ }
222+ }
223+ }
224+ }
225+
226+ Rectangle {
227+ id: highlight
228+ objectName: "highlight"
229+
230+ anchors.bottom: row.bottom
231+ height: units.dp(2)
232+ color: root.hightlightColor
233+ visible: currentItem !== undefined
234+ opacity: 0.0
235+
236+ width: currentItem ? currentItem.width : 0
237+ Behavior on width {
238+ enabled: !d.firstItemSwitch && expanded
239+ UbuntuNumberAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing }
240+ }
241+
242+ // micromovements of the highlight line when user moves the finger across the items while pulling
243+ // the handle downwards.
244+ property real highlightCenterOffset: {
245+ if (!currentItem || lateralPosition == -1 || !enableLateralChanges) return 0;
246+
247+ var itemMapped = root.mapToItem(currentItem, lateralPosition, 0);
248+
249+ var distanceFromCenter = itemMapped.x - currentItem.width / 2;
250+ if (distanceFromCenter > 0) {
251+ distanceFromCenter = Math.max(0, distanceFromCenter - currentItem.width / 8);
252+ } else {
253+ distanceFromCenter = Math.min(0, distanceFromCenter + currentItem.width / 8);
254+ }
255+
256+ if (currentItem && currentItem.ownIndex === 0 && distanceFromCenter < 0) {
257+ return 0;
258+ } else if (currentItem && currentItem.ownIndex === repeater.count-1 & distanceFromCenter > 0) {
259+ return 0;
260+ }
261+ return (distanceFromCenter / (currentItem.width / 4)) * units.gu(1);
262+ }
263+ Behavior on highlightCenterOffset {
264+ NumberAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing }
265+ }
266+
267+ property real currentItemX: currentItem ? currentItem.x : 0
268+ Behavior on currentItemX {
269+ id: currentItemXBehavior
270+ enabled: !d.firstItemSwitch && expanded && !d.forceAlignmentAnimationDisabled
271+ NumberAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing }
272+ }
273+ x: currentItemX + highlightCenterOffset
274+ }
275+
276+ states: [
277+ State {
278+ name: "minimised"
279+ when: !expanded
280+ },
281+ State {
282+ name: "expanded"
283+ when: expanded
284+ PropertyChanges { target: highlight; opacity: 0.9 }
285+ }
286+ ]
287+
288+ transitions: [
289+ Transition {
290+ PropertyAnimation {
291+ properties: "opacity";
292+ duration: UbuntuAnimation.SnapDuration
293+ easing: UbuntuAnimation.StandardEasing
294+ }
295+ }
296+ ]
297+}
qml/Panel/Indicators/DefaultIndicatorPage.qml → qml/Panel/IndicatorPage.qmlrenamed+10−17
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2013 Canonical Ltd.
2+ * Copyright 2013-2014 Canonical Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
5 * it under the terms of the GNU Lesser General Public License as published by
@@ -12,16 +12,12 @@
12 *
12 *
13 * You should have received a copy of the GNU Lesser General Public License
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Authors:
17− * Renato Araujo Oliveira Filho <renato@canonical.com>
18− * Nick Dedekind <nick.dedekind@canonical.com>
19 */
15 */
20
16
21import QtQuick 2.0
17import QtQuick 2.0
22import Ubuntu.Components 0.1 as Components
18import Ubuntu.Components 0.1 as Components
23import Unity.Indicators 0.1 as Indicators
19import Unity.Indicators 0.1 as Indicators
24−import "../../Components/Flickables" as Flickables
20+import "Indicators"
25
21
26IndicatorBase {
22IndicatorBase {
27 id: main
23 id: main
@@ -52,13 +48,13 @@ IndicatorBase {
52
48
53 Connections {
49 Connections {
54 target: menuStack.tail
50 target: menuStack.tail
51+
52+ // fix async creation with signal from model before it's finished.
53+ Component.onCompleted: update();
55− onRowsInserted: {
54+ onRowsInserted: update();
56− if (menuStack.rootMenu !== menuStack.tail && menuStack.tail.get(0, "type") === rootMenuType) {
57− menuStack.rootMenu = menuStack.tail.submenu(0);
58− menuStack.push(menuStack.rootMenu, 0);
59− }
60− }
61− onModelReset: {
55+ onModelReset: update();
56+
57+ function update() {
62 if (menuStack.rootMenu !== menuStack.tail && menuStack.tail.get(0, "type") === rootMenuType) {
58 if (menuStack.rootMenu !== menuStack.tail && menuStack.tail.get(0, "type") === rootMenuType) {
63 menuStack.rootMenu = menuStack.tail.submenu(0);
59 menuStack.rootMenu = menuStack.tail.submenu(0);
64 menuStack.push(menuStack.rootMenu, 0);
60 menuStack.push(menuStack.rootMenu, 0);
@@ -66,7 +62,7 @@ IndicatorBase {
66 }
62 }
67 }
63 }
68
64
69− Flickables.ListView {
65+ ListView {
70 id: mainMenu
66 id: mainMenu
71 objectName: "mainMenu"
67 objectName: "mainMenu"
72 model: menuStack.rootMenu
68 model: menuStack.rootMenu
@@ -92,8 +88,6 @@ IndicatorBase {
92
88
93 // Only allow flicking if the content doesn't fit on the page
89 // Only allow flicking if the content doesn't fit on the page
94 interactive: contentHeight > height
90 interactive: contentHeight > height
95− // FIXME - https://bugreports.qt-project.org/browse/QTBUG-41207
96− boundsBehavior: Flickable.StopAtBounds
97
91
98 property int selectedIndex: -1
92 property int selectedIndex: -1
99 property bool blockCurrentIndexChange: false
93 property bool blockCurrentIndexChange: false
@@ -126,7 +120,6 @@ IndicatorBase {
126 delegate: Loader {
120 delegate: Loader {
127 id: loader
121 id: loader
128 objectName: "menuItem" + index
122 objectName: "menuItem" + index
129− asynchronous: true
130 width: ListView.view.width
123 width: ListView.view.width
131 visible: status == Loader.Ready
124 visible: status == Loader.Ready
132
125
qml/Panel/IndicatorRow.qmldeleted−163
@@ -1,163 +0,0 @@
1−/*
2− * Copyright (C) 2013 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−
17−import QtQuick 2.0
18−import Ubuntu.Components 0.1
19−import Unity.Indicators 0.1 as Indicators
20−import "../Components"
21−import "../Components/Flickables" as Flickables
22−
23−Item {
24− id: indicatorRow
25−
26− readonly property alias currentItem : itemView.currentItem
27− readonly property alias currentItemIndex: itemView.currentIndex
28− readonly property alias row: itemView
29− property QtObject indicatorsModel: null
30− property int overFlowWidth: width
31− property bool showAll: false
32− property real currentItemOffset: 0.0
33− property real unitProgress: 0.0
34−
35− width: units.gu(40)
36− height: units.gu(3)
37−
38− function setDefaultItem() {
39− // The leftmost indicator
40− setCurrentItemIndex(0);
41− }
42−
43− function setCurrentItemIndex(index) {
44− itemView.currentIndex = index;
45− }
46−
47− function setCurrentItem(item) {
48− if (item && item.hasOwnProperty("ownIndex")) {
49− itemView.currentIndex = item.ownIndex;
50− } else {
51− itemView.currentIndex = -1;
52− }
53− }
54−
55− Timer {
56− id: allVisible
57− interval: 1000
58−
59− onTriggered: {
60− showAll = false;
61− }
62− }
63−
64− Flickables.ListView {
65− id: itemView
66− objectName: "indicatorRowItems"
67− interactive: false
68− model: indicatorsModel ? indicatorsModel : null
69−
70− width: childrenRect.width
71− height: indicatorRow.height
72− anchors.right: parent.right
73− orientation: ListView.Horizontal
74−
75− property int lastCount: 0
76− onCountChanged: {
77− if (lastCount < count) {
78− showAll = true;
79− allVisible.start();
80− }
81− lastCount = count;
82− }
83−
84− delegate: Item {
85− id: itemWrapper
86− objectName: "item" + index
87− height: indicatorRow.height
88− width: indicatorItem.width
89− opacity: 1 - indicatorRow.unitProgress
90− y: 0
91− state: "standard"
92−
93− property int ownIndex: index
94− property bool highlighted: indicatorRow.unitProgress > 0 ? ListView.isCurrentItem : false
95− property bool dimmed: indicatorRow.unitProgress > 0 ? !ListView.isCurrentItem : false
96−
97− property bool hidden: !showAll && !highlighted && (indicatorRow.state == "locked" || indicatorRow.state == "commit")
98− property bool overflow: row.width - itemWrapper.x > overFlowWidth
99−
100− IndicatorItem {
101− id: indicatorItem
102− identifier: model.identifier
103− height: parent.height
104−
105− dimmed: itemWrapper.dimmed
106−
107− widgetSource: model.widgetSource
108− indicatorProperties : model.indicatorProperties
109− }
110−
111− states: [
112− State {
113− name: "standard"
114− when: !hidden && !overflow && !highlighted
115− },
116− State {
117− name: "highlighted"
118− when: highlighted
119− PropertyChanges { target: itemWrapper; opacity: 1.0 }
120− },
121− State {
122− name: "hidden"
123− when: hidden || overflow
124− PropertyChanges { target: itemWrapper; opacity: 0.0 }
125− }
126− ]
127−
128− Behavior on opacity { UbuntuNumberAnimation { duration: UbuntuAnimation.BriskDuration } }
129− }
130− }
131−
132−
133− Rectangle {
134− id: highlight
135− color: Theme.palette.selected.foreground
136− objectName: "highlight"
137− height: units.dp(2)
138− anchors.top: row.bottom
139− visible: indicatorRow.currentItem != null
140−
141− property real intendedX: row.x + (indicatorRow.currentItem != null ? (indicatorRow.currentItem.x - row.originX) + centerOffset : 0)
142− x: intendedX >= row.x ? (intendedX + width <= row.x + row.width ? intendedX : row.x + row.width - width) : row.x // listview boundaries
143− width: indicatorRow.currentItem != null ? indicatorRow.currentItem.width : 0
144−
145− property real centerOffset: {
146− if (indicatorRow.currentItemOffset > 0.1) {
147− return (indicatorRow.currentItemOffset - 0.1) * units.gu(0.4);
148− } else if (indicatorRow.currentItemOffset < -0.1) {
149− return (indicatorRow.currentItemOffset + 0.1) * units.gu(0.4);
150− }
151− return 0.0;
152− }
153−
154− Behavior on width {
155− enabled: unitProgress > 0;
156− UbuntuNumberAnimation { duration: UbuntuAnimation.FastDuration }
157− }
158− Behavior on x {
159− enabled: unitProgress > 0;
160− UbuntuNumberAnimation { duration: UbuntuAnimation.FastDuration }
161− }
162− }
163−}
qml/Panel/Indicators.qmldeleted−415
@@ -1,415 +0,0 @@
1−/*
2− * Copyright (C) 2013 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−
17−import QtQuick 2.0
18−import Ubuntu.Components 0.1
19−import Ubuntu.Gestures 0.1
20−import Unity.Indicators 0.1 as Indicators
21−
22−import "../Components"
23−import "../Components/ListItems"
24−import "Indicators"
25−
26−Showable {
27− id: indicators
28−
29− property real openedHeight: units.gu(71)
30− property int panelHeight: units.gu(3)
31− property alias overFlowWidth: indicatorRow.overFlowWidth
32− property alias showAll: indicatorRow.showAll
33− // TODO: This should be sourced by device type (eg "desktop", "tablet", "phone"...)
34− property string profile: indicatorProfile
35−
36− readonly property real hintValue: panelHeight + menuContent.headerHeight
37− readonly property int lockThreshold: openedHeight / 2
38− property bool fullyOpened: height == openedHeight
39− property bool partiallyOpened: height > panelHeight && !fullyOpened
40− property bool fullyClosed: height <= panelHeight
41− property bool contentEnabled: true
42− property bool initalizeItem: true
43− readonly property alias content: menuContent
44− property real unitProgress: (height - panelHeight) / (openedHeight - panelHeight)
45− property bool enableHint: true
46− property real showHintBottomMargin: 0
47−
48− signal showTapped(point position)
49−
50− // TODO: Perhaps we need a animation standard for showing/hiding? Each showable seems to
51− // use its own values. Need to ask design about this.
52− showAnimation: StandardAnimation {
53− property: "height"
54− to: openedHeight
55− }
56−
57− hideAnimation: StandardAnimation {
58− property: "height"
59− duration: 350
60− to: panelHeight
61− easing.type: Easing.OutCubic
62− }
63−
64− onOpenedHeightChanged: {
65− if (showAnimation.running) {
66− showAnimation.restart();
67− } else if (indicators.shown) {
68− height = openedHeight;
69− }
70− }
71−
72− height: panelHeight
73− onHeightChanged: updateRevealProgressState(indicators.height - panelHeight - showHintBottomMargin, true)
74−
75− function updateRevealProgressState(revealProgress, enableRelease) {
76− if (!showAnimation.running && !hideAnimation.running) {
77− if (revealProgress === 0) {
78− indicators.state = "initial";
79− } else if (enableHint && revealProgress > 0 && revealProgress <= hintValue) {
80− indicators.state = "hint";
81− } else if ((!enableHint || revealProgress > hintValue) && revealProgress < lockThreshold) {
82− indicators.state = "reveal";
83− } else if (revealProgress >= lockThreshold && lockThreshold > 0) {
84− indicators.state = "locked";
85− }
86− }
87− }
88−
89− function calculateCurrentItem(xValue, useBuffer) {
90− var rowCoordinates;
91− var itemCoordinates;
92− var currentItem;
93− var distanceFromRightEdge;
94− var bufferExceeded = false;
95−
96− if (indicators.state == "commit" || indicators.state == "locked" || showAnimation.running || hideAnimation.running) return;
97−
98− /*
99− If user drags the indicator handle bar down a distance hintValue or less, this is 0.
100− If bar is dragged down a distance greater than or equal to lockThreshold, this is 1.
101− Otherwise it contains the bar's location as a fraction of the distance between hintValue (is 0) and lockThreshold (is 1).
102− */
103− var verticalProgress =
104− MathUtils.clamp((indicators.height - handle.height - hintValue) /
105− (lockThreshold - hintValue), 0, 1);
106−
107− /*
108− Vertical velocity check. Don't change the indicator if we're moving too quickly.
109− */
110− var verticalSpeed = Math.abs(yVelocityCalculator.calculate());
111− if (verticalSpeed >= 0.05 && !initalizeItem) {
112− return;
113− }
114−
115− /*
116− Percentage of an indicator icon's width the user's press can stray horizontally from the
117− focused icon before we change focus to another icon. E.g. a value of 0.5 means you must
118− go right a distance of half an icon's width before focus moves to the icon on the right
119− */
120− var maxBufferThreshold = 0.5;
121−
122− /*
123− To help users find the indicator of their choice while opening the indicators, we add logic to add a
124− left/right buffer to each icon so it is harder for the focus to be moved accidentally to another icon,
125− as the user moves their finger down, but yet allows them to switch indicator if they want.
126− This buffer is wider the further the user's finger is from the top of the screen.
127− */
128− var effectiveBufferThreshold = maxBufferThreshold * verticalProgress;
129−
130− rowCoordinates = indicatorRow.mapToItem(indicatorRow.row, xValue, 0);
131− // get the current delegate
132− currentItem = indicatorRow.row.itemAt(rowCoordinates.x, 0);
133− if (currentItem) {
134− itemCoordinates = indicatorRow.row.mapToItem(currentItem, rowCoordinates.x, 0);
135− distanceFromRightEdge = (currentItem.width - itemCoordinates.x) / (currentItem.width);
136− if (currentItem != indicatorRow.currentItem) {
137− if (Math.abs(currentItem.ownIndex - indicatorRow.currentItemIndex) > 1) {
138− bufferExceeded = true;
139− } else {
140− if (indicatorRow.currentItemIndex < currentItem.ownIndex && distanceFromRightEdge < (1 - effectiveBufferThreshold)) {
141− bufferExceeded = true;
142− } else if (indicatorRow.currentItemIndex > currentItem.ownIndex && distanceFromRightEdge > effectiveBufferThreshold) {
143− bufferExceeded = true;
144− }
145− }
146− if ((!useBuffer || (useBuffer && bufferExceeded)) || indicatorRow.currentItemIndex < 0 || indicatorRow.currentItem == null) {
147− indicatorRow.setCurrentItem(currentItem);
148− }
149−
150− // need to re-init the distanceFromRightEdge for offset calculation
151− itemCoordinates = indicatorRow.row.mapToItem(indicatorRow.currentItem, rowCoordinates.x, 0);
152− distanceFromRightEdge = (indicatorRow.currentItem.width - itemCoordinates.x) / (indicatorRow.currentItem.width);
153− }
154− indicatorRow.currentItemOffset = 1 - (distanceFromRightEdge * 2);
155− } else if (initalizeItem) {
156− indicatorRow.setDefaultItem();
157− indicatorRow.currentItemOffset = 0;
158− }
159− initalizeItem = indicatorRow.currentItem == null;
160− }
161−
162− // eater
163− MouseArea {
164− anchors {
165− top: parent.top
166− bottom: handle.bottom
167− left: parent.left
168− right: parent.right
169− }
170− }
171−
172− VisibleIndicators {
173− id: visibleIndicators
174− }
175−
176− MenuContent {
177− id: menuContent
178− objectName: "menuContent"
179−
180− anchors {
181− left: parent.left
182− right: parent.right
183− top: indicatorRow.bottom
184− bottom: handle.top
185− }
186− indicatorsModel: visibleIndicators.model
187− visible: indicators.partiallyOpened || indicators.fullyOpened
188− clip: indicators.partiallyOpened
189− enabled: contentEnabled
190−
191− //small shadow gradient at bottom of menu
192− Rectangle {
193− anchors {
194− left: parent.left
195− right: parent.right
196− bottom: parent.bottom
197− }
198− height: units.gu(0.5)
199− gradient: Gradient {
200− GradientStop { position: 0.0; color: "transparent" }
201− GradientStop { position: 1.0; color: "black" }
202− }
203− opacity: 0.4
204− }
205− }
206−
207− Rectangle {
208− id: handle
209−
210− color: menuContent.color
211−
212− anchors {
213− left: parent.left
214− right: parent.right
215− bottom: parent.bottom
216− }
217− height: Math.max(Math.min(handleImage.height, indicators.height - handleImage.height), 0)
218− clip: height < handleImage.height
219− visible: menuContent.visible
220−
221− BorderImage {
222− id: handleImage
223− source: "graphics/handle.sci"
224− height: panelHeight
225− anchors {
226− left: parent.left
227− right: parent.right
228− bottom: parent.bottom
229− }
230− }
231− MouseArea { //prevent clicks passing through
232− anchors.fill: parent
233− }
234− }
235−
236− IndicatorRow {
237− id: indicatorRow
238− objectName: "indicatorRow"
239− anchors {
240− left: parent.left
241− right: parent.right
242− }
243− height: indicators.panelHeight
244− indicatorsModel: visibleIndicators.model
245− state: indicators.state
246− unitProgress: indicators.unitProgress
247−
248− EdgeDragArea {
249− id: rowDragArea
250− anchors.fill: indicatorRow
251− direction: Direction.Downwards
252− maxSilenceTime: 2000
253− distanceThreshold: 0
254−
255− enabled: fullyOpened
256− onDraggingChanged: {
257− if (dragging) {
258− initalizeItem = true;
259− updateRevealProgressState(Math.max(touchSceneY - panelHeight, hintValue), false);
260− indicators.calculateCurrentItem(touchX, false);
261− } else {
262− indicators.state = "commit";
263− indicatorRow.currentItemOffset = 0;
264− }
265− }
266−
267− onTouchXChanged: {
268− indicators.calculateCurrentItem(touchX, true);
269− }
270− onTouchSceneYChanged: {
271− updateRevealProgressState(Math.max(touchSceneY - panelHeight, hintValue), false);
272− yVelocityCalculator.trackedPosition = touchSceneY;
273− }
274− }
275− }
276−
277− Connections {
278− target: showAnimation
279− onRunningChanged: {
280− if (showAnimation.running) {
281− indicators.state = "commit";
282− indicatorRow.currentItemOffset = 0;
283− }
284− }
285− }
286−
287− Connections {
288− target: hideAnimation
289− onRunningChanged: {
290− if (hideAnimation.running) {
291− indicators.state = "initial";
292− initalizeItem = true;
293− indicatorRow.currentItemOffset = 0;
294− }
295− }
296− }
297−
298− QtObject {
299− id: d
300− property bool enableIndexChangeSignal: true
301− property var activeDragHandle: showDragHandle.dragging ? showDragHandle : hideDragHandle.dragging ? hideDragHandle : null
302− }
303−
304− Connections {
305− target: menuContent
306− onCurrentMenuIndexChanged: {
307− var oldActive = d.enableIndexChangeSignal;
308− if (!oldActive) return;
309− d.enableIndexChangeSignal = false;
310−
311− indicatorRow.setCurrentItemIndex(menuContent.currentMenuIndex);
312−
313− d.enableIndexChangeSignal = oldActive;
314− }
315− }
316−
317− Connections {
318− target: indicatorRow
319− onCurrentItemIndexChanged: {
320− var oldActive = d.enableIndexChangeSignal;
321− if (!oldActive) return;
322− d.enableIndexChangeSignal = false;
323−
324− menuContent.setCurrentMenuIndex(indicatorRow.currentItemIndex, fullyOpened || partiallyOpened);
325−
326− d.enableIndexChangeSignal = oldActive;
327− }
328− }
329− // connections to the active drag handle
330− Connections {
331− target: d.activeDragHandle
332− onTouchXChanged: {
333− indicators.calculateCurrentItem(d.activeDragHandle.touchX, true);
334− }
335− onTouchSceneYChanged: {
336− yVelocityCalculator.trackedPosition = d.activeDragHandle.touchSceneY;
337− }
338− }
339−
340− DragHandle {
341− id: showDragHandle
342− anchors.bottom: parent.bottom
343− // go beyond parent so that it stays reachable, at the top of the screen.
344− anchors.bottomMargin: showHintBottomMargin
345− anchors.left: parent.left
346− anchors.right: parent.right
347− height: panelHeight
348− direction: Direction.Downwards
349− enabled: !indicators.shown && indicators.available
350− hintDisplacement: enableHint ? indicators.hintValue : 0
351− autoCompleteDragThreshold: maxTotalDragDistance / 2
352− stretch: true
353− maxTotalDragDistance: openedHeight - panelHeight
354− distanceThreshold: panelHeight
355−
356− onTapped: showTapped(Qt.point(touchSceneX, touchSceneY));
357− }
358−
359− DragHandle {
360− id: hideDragHandle
361− anchors.fill: handle
362− direction: Direction.Upwards
363− enabled: indicators.shown && indicators.available
364− hintDisplacement: indicators.hintValue
365− autoCompleteDragThreshold: maxTotalDragDistance / 6
366− stretch: true
367− maxTotalDragDistance: openedHeight - panelHeight
368− distanceThreshold: 0
369− }
370−
371− AxisVelocityCalculator {
372− id: yVelocityCalculator
373− }
374−
375− states: [
376− State {
377− name: "initial"
378− },
379− State {
380− name: "hint"
381− StateChangeScript {
382− script: {
383− if (d.activeDragHandle) {
384− calculateCurrentItem(d.activeDragHandle.touchX, false);
385− }
386− }
387− }
388− },
389− State {
390− name: "reveal"
391− extend: "hint"
392− },
393− State {
394− name: "locked"
395− extend: "hint"
396− },
397− State {
398− name: "commit"
399− extend: "hint"
400− }
401− ]
402− state: "initial"
403−
404− transitions: [
405− Transition {
406− NumberAnimation {targets: [indicatorRow, menuContent]; property: "y"; duration: 300; easing.type: Easing.OutCubic}
407− }
408− ]
409−
410− Component.onCompleted: initialise();
411− function initialise() {
412− visibleIndicators.load(profile);
413− indicatorRow.setDefaultItem();
414− }
415−}
qml/Panel/Indicators/CachedUnityMenuModel.qmldeleted−39
@@ -1,39 +0,0 @@
1−/*
2− * Copyright 2013 Canonical Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
12− *
13− * You should have received a copy of the GNU Lesser General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Authors:
17− * Nick Dedekind <nick.dedekind@canonical.com>
18− */
19−
20−import QtQuick 2.0
21−import QMenuModel 0.1
22−import Unity.Indicators 0.1 as Indicators
23−
24−// Make sure we don't duplicate models.
25−Item {
26− id: cachedModel
27− property string busName
28− property string actionsObjectPath
29− property string menuObjectPath
30− readonly property bool ready: busName!=="" && actionsObjectPath!=="" && menuObjectPath!==""
31−
32− property var model: {
33− if (!ready) return null;
34−
35− return Indicators.UnityMenuModelCache.model(cachedModel.busName,
36− cachedModel.menuObjectPath,
37− { "indicator": cachedModel.actionsObjectPath });
38− }
39−}
qml/Panel/Indicators/DefaultIndicatorWidget.qmldeleted−119
@@ -1,119 +0,0 @@
1−/*
2− * Copyright 2013 Canonical Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
12− *
13− * You should have received a copy of the GNU Lesser General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Authors:
17− * Nick Dedekind <nick.dedekind@canonical.com>
18− */
19−
20−import QtQuick 2.0
21−import Ubuntu.Components 0.1
22−import Ubuntu.Settings.Components 0.1
23−
24−IndicatorBase {
25− id: indicatorWidget
26−
27− property int iconSize: units.gu(2)
28− property alias leftLabel: itemLeftLabel.text
29− property alias rightLabel: itemRightLabel.text
30− property var icons: undefined
31−
32− width: itemRow.width
33− enabled: false
34−
35− // FIXME: For now we will enable led indicator support only for messaging indicator
36− // in the future we should export a led API insted of doing that,
37− Loader {
38− id: indicatorLed
39− // only load source Component if the icons contains the new message icon
40− source: (indicatorWidget.icons && (String(indicatorWidget.icons).indexOf("indicator-messages-new") != -1)) ? Qt.resolvedUrl("IndicatorsLight.qml") : ""
41− }
42−
43− Row {
44− id: itemRow
45− objectName: "itemRow"
46− anchors {
47− top: parent.top
48− bottom: parent.bottom
49− horizontalCenter: parent.horizontalCenter
50− }
51−
52− Label {
53− id: itemLeftLabel
54− width: contentWidth + units.gu(1)
55− objectName: "leftLabel"
56− color: Theme.palette.selected.backgroundText
57− opacity: 0.8
58− font.family: "Ubuntu"
59− fontSize: "medium"
60− anchors.verticalCenter: parent.verticalCenter
61− visible: text != ""
62− horizontalAlignment: Text.AlignHCenter
63− }
64−
65− Row {
66− id: iconRow
67− anchors {
68− top: parent.top
69− bottom: parent.bottom
70− }
71−
72− Repeater {
73− model: indicatorWidget.icons
74−
75− Item {
76− width: itemImage.width + units.gu(1)
77− height: iconRow.height
78−
79− StatusIcon {
80− id: itemImage
81− height: indicatorWidget.iconSize
82− anchors.centerIn: parent
83− source: modelData
84− sets: ["status", "actions"]
85− color: "#CCCCCC"
86− }
87− }
88− }
89− }
90−
91− Label {
92− id: itemRightLabel
93− width: contentWidth + units.gu(1)
94− objectName: "rightLabel"
95− color: Theme.palette.selected.backgroundText
96− opacity: 0.8
97− font.family: "Ubuntu"
98− fontSize: "medium"
99− anchors.verticalCenter: parent.verticalCenter
100− visible: text != ""
101− horizontalAlignment: Text.AlignHCenter
102− }
103− }
104−
105− onRootActionStateChanged: {
106− if (rootActionState == undefined) {
107− leftLabel = "";
108− rightLabel = "";
109− icons = undefined;
110− enabled = false;
111− return;
112− }
113−
114− leftLabel = rootActionState.leftLabel ? rootActionState.leftLabel : "";
115− rightLabel = rootActionState.rightLabel ? rootActionState.rightLabel : "";
116− icons = rootActionState.icons;
117− enabled = rootActionState.visible;
118− }
119−}
qml/Panel/Indicators/IndicatorBase.qml+3−7
@@ -23,8 +23,6 @@ import Unity.Indicators 0.1
23Item {
23Item {
24 id: indicatorItem
24 id: indicatorItem
25
25
26− enabled: menuObjectPath != ""
27−
28 //const
26 //const
29 property string identifier
27 property string identifier
30 property string busName
28 property string busName
@@ -32,16 +30,14 @@ Item {
32 property string menuObjectPath
30 property string menuObjectPath
33 property string rootMenuType: "com.canonical.indicator.root"
31 property string rootMenuType: "com.canonical.indicator.root"
34
32
35− property string deviceMenuObjectPath: menuObjectPath
36−
37 property alias menuModel: cachedModel.model
33 property alias menuModel: cachedModel.model
38 property alias rootActionState: rootAction
34 property alias rootActionState: rootAction
39
35
40− CachedUnityMenuModel {
36+ SharedUnityMenuModel {
41 id: cachedModel
37 id: cachedModel
42 busName: indicatorItem.busName
38 busName: indicatorItem.busName
43− actionsObjectPath: indicatorItem.actionsObjectPath
39+ actions: { "indicator": indicatorItem.actionsObjectPath }
44− menuObjectPath: indicatorItem.deviceMenuObjectPath
40+ menuObjectPath: indicatorItem.menuObjectPath
45 }
41 }
46
42
47 RootActionState {
43 RootActionState {
qml/Panel/Indicators/IndicatorDelegate.qml+2−2
@@ -20,6 +20,6 @@
20import QtQuick 2.0
20import QtQuick 2.0
21
21
22IndicatorBase {
22IndicatorBase {
23− enabled: rootActionState.visible
23+ readonly property bool indicatorVisible: rootActionState.indicatorVisible
24− property string title: rootActionState.title
24+ readonly property string title: rootActionState.title
25}
25}
qml/Panel/Indicators/VisibleIndicators.qml+4−5
@@ -24,6 +24,10 @@ import Utils 0.1
24Item {
24Item {
25 property SortFilterProxyModel model: filterModel
25 property SortFilterProxyModel model: filterModel
26
26
27+ function initialise(profile) {
28+ indicatorsModel.load(profile);
29+ }
30+
27 SortFilterProxyModel {
31 SortFilterProxyModel {
28 id: filterModel
32 id: filterModel
29 filterRole: Indicators.IndicatorsModelRole.IsVisible
33 filterRole: Indicators.IndicatorsModelRole.IsVisible
@@ -36,7 +40,6 @@ Item {
36 }
40 }
37 }
41 }
38
42
39−
40 Indicators.IndicatorsModel {
43 Indicators.IndicatorsModel {
41 id: indicatorsModel
44 id: indicatorsModel
42 }
45 }
@@ -78,8 +81,4 @@ Item {
78 }
81 }
79 }
82 }
80 }
83 }
81−
82− function load(profile) {
83− indicatorsModel.load(profile);
84− }
85}
84}
qml/Panel/Indicators/client/IndicatorsPage.qml → qml/Panel/Indicators/client/IndicatorRepresentation.qmlrenamed+21−10
@@ -21,13 +21,13 @@
21import QtQuick 2.0
21import QtQuick 2.0
22import Ubuntu.Components 0.1
22import Ubuntu.Components 0.1
23import Ubuntu.Components.ListItems 0.1 as ListItem
23import Ubuntu.Components.ListItems 0.1 as ListItem
24+import "../.."
24
25
25Page {
26Page {
26− id: page
27+ id: root
27
28
28 title: indicatorProperties && indicatorProperties.title ? indicatorProperties.title : ""
29 title: indicatorProperties && indicatorProperties.title ? indicatorProperties.title : ""
29 property variant indicatorProperties
30 property variant indicatorProperties
30− property string pageSource : pageLoader.source
31
31
32 anchors.fill: parent
32 anchors.fill: parent
33
33
@@ -44,6 +44,7 @@ Page {
44 id: pageLoader
44 id: pageLoader
45 objectName: "pageLoader"
45 objectName: "pageLoader"
46 clip:true
46 clip:true
47+ asynchronous: true
47
48
48 Rectangle {
49 Rectangle {
49 anchors.fill: pageLoader
50 anchors.fill: pageLoader
@@ -58,15 +59,25 @@ Page {
58 topMargin: units.gu(2)
59 topMargin: units.gu(2)
59 bottomMargin: units.gu(2)
60 bottomMargin: units.gu(2)
60 }
61 }
61− source : visualCheck.checked ? page.pageSource : "IndicatorsTree.qml"
62+ sourceComponent: visualCheck.checked ? page : tree
62
63
63− onLoaded: {
64+ Component {
65+ id: page
66+ IndicatorPage {
67+ identifier: model.identifier
64− for(var pName in indicatorProperties) {
68+ busName: indicatorProperties.busName
65− if (item.hasOwnProperty(pName)) {
69+ actionsObjectPath: indicatorProperties.actionsObjectPath
70+ menuObjectPath: indicatorProperties.menuObjectPath
71+ }
72+ }
73+ Component {
74+ id: tree
75+ IndicatorsTree {
76+ identifier: model.identifier
77+ busName: indicatorProperties.busName
66− item[pName] = indicatorProperties[pName];
78+ actionsObjectPath: indicatorProperties.actionsObjectPath
67− }
79+ menuObjectPath: indicatorProperties.menuObjectPath
68 }
80 }
69− item.start();
70 }
81 }
71 }
82 }
72
83
@@ -85,7 +96,7 @@ Page {
85 left: parent.left
96 left: parent.left
86 }
97 }
87 text: "Back"
98 text: "Back"
88− onClicked: page.pageStack.reset()
99+ onClicked: root.pageStack.reset()
89 }
100 }
90 }
101 }
91}
102}
qml/Panel/Indicators/client/IndicatorsList.qml+3−4
@@ -22,7 +22,6 @@ import Ubuntu.Components 0.1
22import Ubuntu.Components.ListItems 0.1 as ListItem
22import Ubuntu.Components.ListItems 0.1 as ListItem
23import Unity.Indicators 0.1 as Indicators
23import Unity.Indicators 0.1 as Indicators
24import "../.."
24import "../.."
25−import "../../../Components/Flickables" as Flickables
26
25
27Page {
26Page {
28 id: page
27 id: page
@@ -36,7 +35,7 @@ Page {
36 Component.onCompleted: load(profile)
35 Component.onCompleted: load(profile)
37 }
36 }
38
37
39− Flickables.ListView {
38+ ListView {
40 id: mainMenu
39 id: mainMenu
41 objectName: "mainMenu"
40 objectName: "mainMenu"
42 anchors.fill: parent
41 anchors.fill: parent
@@ -58,8 +57,8 @@ Page {
58 text: identifier
57 text: identifier
59
58
60 onClicked: {
59 onClicked: {
61− var new_page = Qt.createComponent("IndicatorsPage.qml");
60+ var new_page = Qt.createComponent("IndicatorRepresentation.qml");
62− page.pageStack.push(new_page.createObject(pages), {"indicatorProperties" : model.indicatorProperties, "pageSource" : model.pageSource});
61+ page.pageStack.push(new_page.createObject(pages), {"indicatorProperties" : model.indicatorProperties });
63 }
62 }
64 }
63 }
65 }
64 }
qml/Panel/Indicators/client/IndicatorsTree.qml+5−28
@@ -20,40 +20,17 @@
20import QtQuick 2.0
20import QtQuick 2.0
21import Ubuntu.Components 0.1
21import Ubuntu.Components 0.1
22import Unity.Indicators 0.1 as Indicators
22import Unity.Indicators 0.1 as Indicators
23−import QMenuModel 0.1
23+import ".."
24−import Ubuntu.Components.ListItems 0.1 as ListItem
25−import "../../../Components/Flickables" as Flickables
26
24
27−Page {
25+IndicatorBase {
28− id: page
26+ id: root
29− anchors.fill: parent
30−
31− property string busName: unityModel.busName
32− property string actionsObjectPath
33− property string menuObjectPath
34−
35− property string deviceMenuObjectPath: menuObjectPath
36−
37− function start() {
38− }
39−
40− UnityMenuModel {
41− id: unityModel
42− busName: page.busName
43− actions: { "indicator": page.actionsObjectPath }
44− menuObjectPath: page.deviceMenuObjectPath
45− }
46−
47− Indicators.RootActionState {
48− menu: unityModel
49− }
50
27
51 Indicators.ModelPrinter {
28 Indicators.ModelPrinter {
52 id: printer
29 id: printer
53− model: unityModel
30+ model: root.menuModel
54 }
31 }
55
32
56− Flickables.Flickable {
33+ Flickable {
57 anchors.fill: parent
34 anchors.fill: parent
58 contentHeight: all_data.height
35 contentHeight: all_data.height
59 clip:true
36 clip:true
qml/Panel/IndicatorsBar.qmlnew file+269
@@ -0,0 +1,269 @@
1+/*
2+ * Copyright (C) 2014 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+
17+import QtQuick 2.2
18+import Ubuntu.Components 1.1
19+import "../Components"
20+
21+Item {
22+ id: root
23+ property alias expanded: row.expanded
24+ property alias interactive: flickable.interactive
25+ property alias indicatorsModel: row.indicatorsModel
26+ property alias unitProgress: row.unitProgress
27+ property alias enableLateralChanges: row.enableLateralChanges
28+ property alias overFlowWidth: row.overFlowWidth
29+ readonly property alias currentItemIndex: row.currentItemIndex
30+ property real lateralPosition: -1
31+
32+ function selectItemAt(lateralPosition) {
33+ if (!expanded) {
34+ row.resetCurrentItem();
35+ }
36+ var mapped = root.mapToItem(row, lateralPosition, 0);
37+ row.selectItemAt(mapped.x);
38+ }
39+
40+ function setCurrentItemIndex(index) {
41+ if (!expanded) {
42+ row.resetCurrentItem();
43+ }
44+ row.setCurrentItemIndex(index);
45+ }
46+
47+ function addScrollOffset(scrollAmmout) {
48+ if (scrollAmmout < 0) { // left scroll
49+ if (flickable.contentX < 0) return; // already off the left.
50+ if (flickable.contentX + scrollAmmout < 0) scrollAmmout = -flickable.contentX; // going to be off the left
51+ } else { // right scroll
52+ if (flickable.contentX + flickable.width > row.width) return; // already off the right.
53+ if (flickable.contentX + flickable.width + scrollAmmout > row.width) { // going to be off the right
54+ scrollAmmout = row.width - (flickable.contentX + flickable.width);
55+ }
56+ }
57+ d.scrollOffset = d.scrollOffset + scrollAmmout;
58+ }
59+
60+ QtObject {
61+ id: d
62+ property var initialItem
63+ // the non-expanded distance from row offset to center of initial item
64+ property real originalDistanceFromRight: -1
65+
66+ // calculate the distance from row offset to center of initial item
67+ property real distanceFromRight: {
68+ if (originalDistanceFromRight == -1) return 0;
69+ if (!initialItem) return 0;
70+ return row.width - initialItem.x - initialItem.width /2;
71+ }
72+
73+ // offset to the intially selected expanded item
74+ property real rowOffset: 0
75+ property real scrollOffset: 0
76+ property real alignmentAdjustment: 0
77+ property real combinedOffset: 0
78+
79+ // when the scroll offset changes, we need to reclaculate the relative lateral position
80+ onScrollOffsetChanged: root.lateralPositionChanged()
81+
82+ onInitialItemChanged: {
83+ originalDistanceFromRight = initialItem ? (row.width - initialItem.x - initialItem.width/2) : -1;
84+ }
85+
86+ Behavior on alignmentAdjustment {
87+ NumberAnimation { duration: UbuntuAnimation.BriskDuration; easing: UbuntuAnimation.StandardEasing}
88+ }
89+
90+ function alignIndicators() {
91+ flickable.resetContentXComponents();
92+
93+ if (expanded && !flickable.moving) {
94+
95+ // gap between left and row?
96+ if (flickable.contentX < 0) {
97+ d.alignmentAdjustment += flickable.contentX;
98+ // gap between right and row?
99+ } else if (flickable.contentX + flickable.width > row.width) {
100+ // row width is less than flickable
101+ if (row.width < flickable.width) {
102+ d.alignmentAdjustment += flickable.contentX;
103+ } else {
104+ d.alignmentAdjustment += ((flickable.contentX + flickable.width) - row.width);
105+ }
106+ // current item overlap on left
107+ } else if (row.currentItem && row.currentItem.x < flickable.contentX) {
108+ d.alignmentAdjustment -= (row.currentItem.x - flickable.contentX);
109+ // current item overlap on right
110+ } else if (row.currentItem && row.currentItem.x + row.currentItem.width > flickable.contentX + flickable.width) {
111+ d.alignmentAdjustment -= ((row.currentItem.x + row.currentItem.width) - (flickable.contentX + flickable.width));
112+ }
113+ }
114+ }
115+ }
116+
117+ Rectangle {
118+ id: grayLine
119+ height: units.dp(2)
120+ width: parent.width
121+ anchors.bottom: parent.bottom
122+
123+ color: "#4c4c4c"
124+ opacity: expanded ? 1.0 : 0.0
125+ Behavior on opacity { NumberAnimation { duration: UbuntuAnimation.SnapDuration } }
126+ }
127+
128+ Item {
129+ id: rowContainer
130+ anchors.fill: parent
131+ clip: expanded || row.width > rowContainer.width
132+
133+ Flickable {
134+ id: flickable
135+ objectName: "flickable"
136+
137+ anchors.fill: parent
138+ contentWidth: row.width
139+ interactive: false
140+ // align right + offset from row selection + scrolling
141+ contentX: row.width - flickable.width - d.combinedOffset
142+
143+ // contentX can change by user interaction as well as user offset changes
144+ // This function re-aligns the offsets so that the offsets match the contentX
145+ function resetContentXComponents() {
146+ d.scrollOffset += (flickable.contentX - (row.width - flickable.width - d.combinedOffset));
147+ }
148+
149+ rebound: Transition {
150+ NumberAnimation {
151+ properties: "x"
152+ duration: 600
153+ easing.type: Easing.OutCubic
154+ }
155+ }
156+
157+ IndicatorItemRow {
158+ id: row
159+ objectName: "indicatorItemRow"
160+ anchors {
161+ top: parent.top
162+ bottom: parent.bottom
163+ }
164+
165+ lateralPosition: {
166+ if (root.lateralPosition == -1) return -1;
167+
168+ var mapped = root.mapToItem(row, root.lateralPosition, 0);
169+ return Math.min(Math.max(mapped.x, 0), row.width);
170+ }
171+
172+ onCurrentItemChanged: {
173+ if (!currentItem) d.initialItem = undefined;
174+ else if (!d.initialItem) d.initialItem = currentItem;
175+ }
176+
177+ MouseArea {
178+ anchors.fill: parent
179+ enabled: root.expanded
180+ onClicked: {
181+ row.selectItemAt(mouse.x);
182+ d.alignIndicators();
183+ }
184+ }
185+ }
186+
187+ }
188+ }
189+
190+ Timer {
191+ id: alignmentTimer
192+ interval: UbuntuAnimation.FastDuration // enough for row animation.
193+ repeat: false
194+
195+ onTriggered: d.alignIndicators();
196+ }
197+
198+ states: [
199+ State {
200+ name: "minimized"
201+ when: !expanded
202+ PropertyChanges {
203+ target: d
204+ rowOffset: 0
205+ scrollOffset: 0
206+ alignmentAdjustment: 0
207+ combinedOffset: 0
208+ restoreEntryValues: false
209+ }
210+ },
211+ State {
212+ name: "expanded"
213+ when: expanded && !interactive
214+
215+ PropertyChanges {
216+ target: d
217+ combinedOffset: rowOffset + alignmentAdjustment - scrollOffset
218+ }
219+ PropertyChanges {
220+ target: d
221+ rowOffset: {
222+ if (!initialItem) return 0;
223+ if (distanceFromRight - initialItem.width <= 0) return 0;
224+
225+ var rowOffset = distanceFromRight - originalDistanceFromRight;
226+ return rowOffset;
227+ }
228+ restoreEntryValues: false
229+ }
230+ }
231+ , State {
232+ name: "interactive"
233+ when: expanded && interactive
234+
235+ StateChangeScript {
236+ script: {
237+ // don't use row offset anymore.
238+ d.scrollOffset -= d.rowOffset;
239+ d.rowOffset = 0;
240+ d.initialItem = undefined;
241+ alignmentTimer.start();
242+ }
243+ }
244+ PropertyChanges {
245+ target: d
246+ combinedOffset: rowOffset + alignmentAdjustment - scrollOffset
247+ restoreEntryValues: false
248+ }
249+ }
250+ ]
251+
252+ transitions: [
253+ Transition {
254+ from: "expanded"
255+ to: "minimized"
256+ PropertyAction {
257+ target: d
258+ properties: "rowOffset, scrollOffset, alignmentAdjustment"
259+ value: 0
260+ }
261+ PropertyAnimation {
262+ target: d
263+ properties: "combinedOffset"
264+ duration: UbuntuAnimation.SnapDuration
265+ easing: UbuntuAnimation.StandardEasing
266+ }
267+ }
268+ ]
269+}
qml/Panel/IndicatorsMenu.qmlnew file+318
@@ -0,0 +1,318 @@
1+/*
2+ * Copyright (C) 2014 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+
17+import QtQuick 2.2
18+import Ubuntu.Components 1.1
19+import Ubuntu.Gestures 0.1
20+import "../Components"
21+
22+Showable {
23+ id: root
24+ property alias indicatorsModel: bar.indicatorsModel
25+ property alias showDragHandle: __showDragHandle
26+ property alias hideDragHandle: __hideDragHandle
27+ property alias overFlowWidth: bar.overFlowWidth
28+ property alias verticalVelocityThreshold: yVelocityCalculator.velocityThreshold
29+ property int minimizedPanelHeight: units.gu(3)
30+ property int expandedPanelHeight: units.gu(7)
31+ property real openedHeight: units.gu(71)
32+ readonly property real unitProgress: Math.max(0, (height - minimizedPanelHeight) / (openedHeight - minimizedPanelHeight))
33+ readonly property bool fullyOpened: unitProgress >= 1
34+ readonly property bool partiallyOpened: unitProgress > 0 && unitProgress < 1.0
35+ readonly property bool fullyClosed: unitProgress == 0
36+ property bool enableHint: true
37+ property bool contentEnabled: true
38+ property color panelColor: "black"
39+
40+ signal showTapped(point position)
41+
42+ // TODO: Perhaps we need a animation standard for showing/hiding? Each showable seems to
43+ // use its own values. Need to ask design about this.
44+ showAnimation: StandardAnimation {
45+ property: "height"
46+ to: openedHeight
47+ duration: UbuntuAnimation.BriskDuration
48+ easing.type: Easing.OutCubic
49+ }
50+
51+ hideAnimation: StandardAnimation {
52+ property: "height"
53+ to: minimizedPanelHeight
54+ duration: UbuntuAnimation.BriskDuration
55+ easing.type: Easing.OutCubic
56+ }
57+
58+ height: minimizedPanelHeight
59+
60+ onUnitProgressChanged: d.updateState()
61+ clip: root.partiallyOpened
62+
63+ // eater
64+ MouseArea {
65+ anchors.fill: parent
66+ }
67+
68+ MenuContent {
69+ id: content
70+ objectName: "menuContent"
71+
72+ anchors {
73+ left: parent.left
74+ right: parent.right
75+ top: bar.bottom
76+ }
77+ height: openedHeight - bar.height - handle.height
78+ indicatorsModel: root.indicatorsModel
79+ visible: root.unitProgress > 0
80+ enabled: contentEnabled
81+ currentMenuIndex: bar.currentItemIndex
82+ }
83+
84+ Handle {
85+ id: handle
86+ anchors {
87+ left: parent.left
88+ right: parent.right
89+ bottom: parent.bottom
90+ }
91+ height: units.gu(2)
92+ active: d.activeDragHandle ? true : false
93+
94+ //small shadow gradient at bottom of menu
95+ Rectangle {
96+ anchors {
97+ left: parent.left
98+ right: parent.right
99+ bottom: parent.top
100+ }
101+ height: units.gu(0.5)
102+ gradient: Gradient {
103+ GradientStop { position: 0.0; color: "transparent" }
104+ GradientStop { position: 1.0; color: "black" }
105+ }
106+ opacity: 0.3
107+ }
108+ }
109+
110+ Rectangle {
111+ anchors.fill: bar
112+ color: panelColor
113+ }
114+
115+ IndicatorsBar {
116+ id: bar
117+ objectName: "indicatorsBar"
118+
119+ anchors {
120+ left: parent.left
121+ right: parent.right
122+ }
123+ expanded: false
124+ enableLateralChanges: false
125+ lateralPosition: -1
126+ unitProgress: root.unitProgress
127+
128+ height: expanded ? expandedPanelHeight : minimizedPanelHeight
129+ Behavior on height { NumberAnimation { duration: UbuntuAnimation.SnapDuration; easing: UbuntuAnimation.StandardEasing } }
130+ }
131+
132+ ScrollCalculator {
133+ id: leftScroller
134+ width: units.gu(5)
135+ anchors.left: bar.left
136+ height: bar.height
137+
138+ forceScrollingPercentage: 0.33
139+ stopScrollThreshold: units.gu(0.75)
140+ direction: Qt.RightToLeft
141+ lateralPosition: -1
142+
143+ onScroll: bar.addScrollOffset(-scrollAmount);
144+ }
145+
146+ ScrollCalculator {
147+ id: rightScroller
148+ width: units.gu(5)
149+ anchors.right: bar.right
150+ height: bar.height
151+
152+ forceScrollingPercentage: 0.33
153+ stopScrollThreshold: units.gu(0.75)
154+ direction: Qt.LeftToRight
155+ lateralPosition: -1
156+
157+ onScroll: bar.addScrollOffset(scrollAmount);
158+ }
159+
160+ DragHandle {
161+ id: __showDragHandle
162+ anchors.bottom: parent.bottom
163+ anchors.left: parent.left
164+ anchors.right: parent.right
165+ height: minimizedPanelHeight
166+ direction: Direction.Downwards
167+ enabled: !root.shown && root.available
168+ autoCompleteDragThreshold: maxTotalDragDistance / 2
169+ stretch: true
170+ distanceThreshold: enableHint ? 0 : minimizedPanelHeight
171+
172+ onTapped: showTapped(Qt.point(touchSceneX, touchSceneY));
173+
174+ // using hint regulates minimum to hint displacement, but in fullscreen mode, we need to do it manually.
175+ overrideStartValue: enableHint ? minimizedPanelHeight : expandedPanelHeight + handle.height
176+ maxTotalDragDistance: openedHeight - (enableHint ? minimizedPanelHeight : expandedPanelHeight + handle.height)
177+ hintDisplacement: enableHint ? expandedPanelHeight - minimizedPanelHeight + handle.height : 0
178+ }
179+
180+ DragHandle {
181+ id: __hideDragHandle
182+ anchors.fill: handle
183+ direction: Direction.Upwards
184+ enabled: root.shown && root.available
185+ hintDisplacement: units.gu(3)
186+ autoCompleteDragThreshold: maxTotalDragDistance / 6
187+ stretch: true
188+ maxTotalDragDistance: openedHeight - expandedPanelHeight - handle.height
189+
190+ onTouchSceneXChanged: {
191+ if (root.state === "locked") {
192+ d.xDisplacementSinceLock += (touchSceneX - d.lastHideTouchSceneX)
193+ d.lastHideTouchSceneX = touchSceneX;
194+ }
195+ }
196+ }
197+
198+ PanelVelocityCalculator {
199+ id: yVelocityCalculator
200+ velocityThreshold: d.hasCommitted ? 0.1 : 0.3
201+ trackedValue: d.activeDragHandle ? d.activeDragHandle.touchSceneY : 0
202+
203+ onVelocityAboveThresholdChanged: d.updateState()
204+ }
205+
206+ Connections {
207+ target: showAnimation
208+ onRunningChanged: {
209+ if (showAnimation.running) {
210+ root.state = "commit";
211+ }
212+ }
213+ }
214+
215+ Connections {
216+ target: hideAnimation
217+ onRunningChanged: {
218+ if (hideAnimation.running) {
219+ root.state = "initial";
220+ }
221+ }
222+ }
223+
224+ QtObject {
225+ id: d
226+ property var activeDragHandle: showDragHandle.dragging ? showDragHandle : hideDragHandle.dragging ? hideDragHandle : null
227+ property bool hasCommitted: false
228+ property real lastHideTouchSceneX: 0
229+ property real xDisplacementSinceLock: 0
230+ onXDisplacementSinceLockChanged: d.updateState()
231+
232+ property real rowMappedLateralPosition: {
233+ if (!d.activeDragHandle) return -1;
234+ return d.activeDragHandle.mapToItem(bar, d.activeDragHandle.touchX, 0).x;
235+ }
236+
237+ function updateState() {
238+ if (!showAnimation.running && !hideAnimation.running) {
239+ if (unitProgress <= 0) {
240+ root.state = "initial";
241+ // lock indicator if we've been committed and aren't moving too much laterally or too fast up.
242+ } else if (d.hasCommitted && (Math.abs(d.xDisplacementSinceLock) < units.gu(2) || yVelocityCalculator.velocityAboveThreshold)) {
243+ root.state = "locked";
244+ } else {
245+ root.state = "reveal";
246+ }
247+ }
248+ }
249+ }
250+
251+ states: [
252+ State {
253+ name: "initial"
254+ PropertyChanges { target: d; hasCommitted: false; restoreEntryValues: false }
255+ },
256+ State {
257+ name: "reveal"
258+ StateChangeScript {
259+ script: {
260+ yVelocityCalculator.reset();
261+ // initial item selection
262+ if (!d.hasCommitted) bar.selectItemAt(d.activeDragHandle ? d.activeDragHandle.touchX : -1);
263+ d.hasCommitted = false;
264+ }
265+ }
266+ PropertyChanges {
267+ target: bar
268+ expanded: true
269+ // changes to lateral touch position effect which indicator is selected
270+ lateralPosition: d.rowMappedLateralPosition
271+ // vertical velocity determines if changes in lateral position has an effect
272+ enableLateralChanges: d.activeDragHandle &&
273+ !yVelocityCalculator.velocityAboveThreshold
274+ }
275+ // left scroll bar handling
276+ PropertyChanges {
277+ target: leftScroller
278+ lateralPosition: {
279+ if (!d.activeDragHandle) return -1;
280+ var mapped = d.activeDragHandle.mapToItem(leftScroller, d.activeDragHandle.touchX, 0);
281+ return mapped.x;
282+ }
283+ }
284+ // right scroll bar handling
285+ PropertyChanges {
286+ target: rightScroller
287+ lateralPosition: {
288+ if (!d.activeDragHandle) return -1;
289+ var mapped = d.activeDragHandle.mapToItem(rightScroller, d.activeDragHandle.touchX, 0);
290+ return mapped.x;
291+ }
292+ }
293+ },
294+ State {
295+ name: "locked"
296+ StateChangeScript {
297+ script: {
298+ d.xDisplacementSinceLock = 0;
299+ d.lastHideTouchSceneX = hideDragHandle.touchSceneX;
300+ }
301+ }
302+ PropertyChanges { target: bar; expanded: true }
303+ },
304+ State {
305+ name: "commit"
306+ extend: "locked"
307+ PropertyChanges { target: bar; interactive: true }
308+ PropertyChanges {
309+ target: d;
310+ hasCommitted: true
311+ lastHideTouchSceneX: 0
312+ xDisplacementSinceLock: 0
313+ restoreEntryValues: false
314+ }
315+ }
316+ ]
317+ state: "initial"
318+}
qml/Panel/MenuContent.qml+26−83
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013-2014 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -14,126 +14,69 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
15 */
16
16
17−import QtQuick 2.0
17+import QtQuick 2.2
18import QtQuick.Layouts 1.1
18import QtQuick.Layouts 1.1
19import Ubuntu.Components 1.1
19import Ubuntu.Components 1.1
20import Unity.Indicators 0.1 as Indicators
20import Unity.Indicators 0.1 as Indicators
21import Utils 0.1
21import Utils 0.1
22import "../Components"
22import "../Components"
23−import "../Components/Flickables" as Flickables
24import "Indicators"
23import "Indicators"
25
24
26Rectangle {
25Rectangle {
27 id: content
26 id: content
28
27
29 property QtObject indicatorsModel: null
28 property QtObject indicatorsModel: null
30− readonly property alias currentMenuIndex: listViewHeader.currentIndex
29+ property int currentMenuIndex: -1
31 color: "#221e1c" // FIXME not in palette yet
30 color: "#221e1c" // FIXME not in palette yet
32− property real headerHeight: listViewHeader.height
33
31
34 width: units.gu(40)
32 width: units.gu(40)
35 height: units.gu(42)
33 height: units.gu(42)
36
34
37− function setCurrentMenuIndex(index, animate) {
35+ onCurrentMenuIndexChanged: {
38− // FIXME - https://bugreports.qt-project.org/browse/QTBUG-41229
36+ listViewContent.currentIndex = currentMenuIndex;
39− listViewHeader.currentIndex = -1;
40− listViewHeader.currentIndex = index;
41 }
37 }
42
38
43− Flickables.ListView {
44− id: listViewHeader
45− objectName: "indicatorsHeaderListView"
46− model: content.indicatorsModel
47− clip: true
48−
49− anchors {
50− left: parent.left
51− right: parent.right
52− }
53− height: units.gu(8.5)
54−
55− highlightFollowsCurrentItem: true
56− highlightMoveDuration: 0
57−
58− orientation: ListView.Horizontal
59− snapMode: ListView.SnapOneItem
60− highlightRangeMode: ListView.StrictlyEnforceRange
61− boundsBehavior: Flickable.StopAtBounds
62− // Load all the indicator menus (a big number)
63− cacheBuffer: 1073741823
64−
65− delegate: Header {
66− width: ListView.view.width
67− height: implicitHeight
68−
69− title: indicatorDelegate.title !== "" ? indicatorDelegate.title : identifier
70−
71− IndicatorDelegate {
72− id: indicatorDelegate
73− Component.onCompleted: {
74− for(var pName in indicatorProperties) {
75− if (indicatorDelegate.hasOwnProperty(pName)) {
76− indicatorDelegate[pName] = indicatorProperties[pName];
77− }
78− }
79− }
80− }
81− }
82− }
83−
84− Flickables.ListView {
39+ ListView {
85 id: listViewContent
40 id: listViewContent
86 objectName: "indicatorsContentListView"
41 objectName: "indicatorsContentListView"
87− anchors {
42+ anchors.fill: parent
88− left: parent.left
89− right: parent.right
90− top: listViewHeader.bottom
91− bottom: parent.bottom
92− }
93 model: content.indicatorsModel
43 model: content.indicatorsModel
94− clip: true
95
44
96− currentIndex: listViewHeader.currentIndex
45+ highlightFollowsCurrentItem: true
97 interactive: false
46 interactive: false
98 highlightMoveDuration: 0
47 highlightMoveDuration: 0
99 orientation: ListView.Horizontal
48 orientation: ListView.Horizontal
100 // Load all the indicator menus (a big number)
49 // Load all the indicator menus (a big number)
101 cacheBuffer: 1073741823
50 cacheBuffer: 1073741823
102
51
52+ // for additions/removals.
53+ onCountChanged: {
54+ listViewContent.currentIndex = content.currentMenuIndex;
55+ }
56+
103 delegate: Loader {
57 delegate: Loader {
104 id: loader
58 id: loader
59+
105 width: ListView.view.width
60 width: ListView.view.width
106 height: ListView.view.height
61 height: ListView.view.height
107 objectName: identifier
62 objectName: identifier
108−
109− source: pageSource
110 asynchronous: true
63 asynchronous: true
64+ visible: ListView.isCurrentItem
111
65
112− onVisibleChanged: {
66+ sourceComponent: IndicatorPage {
113− // Reset the indicator states
67+ objectName: identifier + "-page"
114− if (!visible && item && item["reset"]) {
115− item.reset()
116− }
117− }
118−
119− onLoaded: {
120− for(var pName in indicatorProperties) {
121− if (item.hasOwnProperty(pName)) {
122− item[pName] = indicatorProperties[pName]
123− }
124− }
125− }
126
68
127− Binding {
69+ identifier: model.identifier
128− target: loader.item
70+ busName: indicatorProperties.busName
129− property: "identifier"
71+ actionsObjectPath: indicatorProperties.actionsObjectPath
130− value: identifier
72+ menuObjectPath: indicatorProperties.menuObjectPath
131 }
73 }
132
74
133− Binding {
75+ onVisibleChanged: {
134− target: loader.item
76+ // Reset the indicator states
135− property: "objectName"
77+ if (!visible && status == Loader.Ready) {
136− value: identifier + "-page"
78+ item.reset();
79+ }
137 }
80 }
138 }
81 }
139 }
82 }
qml/Panel/Panel.qml+48−41
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013-2014 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -14,11 +14,11 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
15 */
16
16
17−import QtQuick 2.0
17+import QtQuick 2.2
18−import Ubuntu.Components 0.1
18+import Ubuntu.Components 1.1
19import Unity.Application 0.1
19import Unity.Application 0.1
20import "../Components"
20import "../Components"
21−import "../Components/ListItems"
21+import ".."
22
22
23Item {
23Item {
24 id: root
24 id: root
@@ -53,7 +53,9 @@ Item {
53
53
54 anchors.fill: parent
54 anchors.fill: parent
55
55
56− Behavior on anchors.topMargin { StandardAnimation {} }
56+ Behavior on anchors.topMargin {
57+ NumberAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing }
58+ }
57
59
58 BorderImage {
60 BorderImage {
59 id: dropShadow
61 id: dropShadow
@@ -62,24 +64,10 @@ Item {
62 leftMargin: -units.gu(1)
64 leftMargin: -units.gu(1)
63 bottomMargin: -units.gu(1)
65 bottomMargin: -units.gu(1)
64 }
66 }
65− visible: indicators.height > indicators.panelHeight
67+ visible: !indicators.fullyClosed
66 source: "graphics/rectangular_dropshadow.sci"
68 source: "graphics/rectangular_dropshadow.sci"
67 }
69 }
68
70
69− VerticalThinDivider {
70− id: indicatorDividor
71− anchors {
72− top: indicators.top
73− bottom: indicators.bottom
74− right: indicators.left
75−
76− topMargin: indicatorArea.anchors.topMargin + indicators.panelHeight
77− }
78−
79− width: units.dp(2)
80− source: "graphics/VerticalDivider.png"
81− }
82−
83 Rectangle {
71 Rectangle {
84 id: indicatorAreaBackground
72 id: indicatorAreaBackground
85 color: callHint.visible ? "green" : "black"
73 color: callHint.visible ? "green" : "black"
@@ -88,13 +76,13 @@ Item {
88 left: parent.left
76 left: parent.left
89 right: parent.right
77 right: parent.right
90 }
78 }
91− height: indicators.panelHeight
79+ height: indicators.minimizedPanelHeight
92
80
93 Behavior on color { ColorAnimation { duration: UbuntuAnimation.FastDuration } }
81 Behavior on color { ColorAnimation { duration: UbuntuAnimation.FastDuration } }
94 }
82 }
95
83
96 PanelSeparatorLine {
84 PanelSeparatorLine {
97− id: nonIndicatorAreaSeparatorLine
85+ id: orangeLine
98 anchors {
86 anchors {
99 top: indicatorAreaBackground.bottom
87 top: indicatorAreaBackground.bottom
100 left: parent.left
88 left: parent.left
@@ -103,18 +91,29 @@ Item {
103 saturation: 1 - indicators.unitProgress
91 saturation: 1 - indicators.unitProgress
104 }
92 }
105
93
94+ Image {
95+ anchors {
96+ top: indicators.top
97+ bottom: indicators.bottom
98+ right: indicators.left
99+ topMargin: indicatorArea.anchors.topMargin + indicators.minimizedPanelHeight
100+ }
101+ width: units.dp(2)
102+ source: "graphics/VerticalDivider.png"
103+ }
104+
106 MouseArea {
105 MouseArea {
107 anchors {
106 anchors {
108 top: parent.top
107 top: parent.top
109 left: parent.left
108 left: parent.left
110 right: indicators.left
109 right: indicators.left
111 }
110 }
112− height: indicators.panelHeight
111+ height: indicators.minimizedPanelHeight
113 enabled: callHint.visible
112 enabled: callHint.visible
114 onClicked: callHint.showLiveCall()
113 onClicked: callHint.showLiveCall()
115 }
114 }
116
115
117− Indicators {
116+ IndicatorsMenu {
118 id: __indicators
117 id: __indicators
119 objectName: "indicators"
118 objectName: "indicators"
120
119
@@ -123,51 +122,55 @@ Item {
123 right: parent.right
122 right: parent.right
124 }
123 }
125
124
126− width: root.width
127 shown: false
125 shown: false
128− panelHeight: units.gu(3)
126+ width: root.width
127+ minimizedPanelHeight: units.gu(3)
128+ expandedPanelHeight: units.gu(7)
129− openedHeight: root.height
129+ openedHeight: root.height - indicatorOrangeLine.height
130+
130 overFlowWidth: {
131 overFlowWidth: {
131 if (callHint.visible) {
132 if (callHint.visible) {
132 return Math.max(root.width - (callHint.width + units.gu(2)), 0)
133 return Math.max(root.width - (callHint.width + units.gu(2)), 0)
133 }
134 }
134 return root.width
135 return root.width
135 }
136 }
136−
137 enableHint: !callHint.active && !fullscreenMode
137 enableHint: !callHint.active && !fullscreenMode
138− showHintBottomMargin: fullscreenMode ? -panelHeight : 0
138+ panelColor: indicatorAreaBackground.color
139
139
140 onShowTapped: {
140 onShowTapped: {
141 if (callHint.active) {
141 if (callHint.active) {
142 callHint.showLiveCall();
142 callHint.showLiveCall();
143 }
143 }
144 }
144 }
145− }
146
145
147− ActiveCallHint {
146+ hideDragHandle {
148− id: __callHint
147+ anchors.bottomMargin: -indicatorOrangeLine.height
149− anchors {
150− top: parent.top
151− left: parent.left
152 }
148 }
153− height: indicators.panelHeight
154− visible: active && indicators.state == "initial"
155 }
149 }
156
150
157 PanelSeparatorLine {
151 PanelSeparatorLine {
158− id: indicatorsSeparatorLine
152+ id: indicatorOrangeLine
159− visible: true
160 anchors {
153 anchors {
161 top: indicators.bottom
154 top: indicators.bottom
162− left: indicatorDividor.left
155+ left: indicators.left
163 right: indicators.right
156 right: indicators.right
164 }
157 }
165 }
158 }
159+
160+ ActiveCallHint {
161+ id: __callHint
162+ anchors {
163+ top: parent.top
164+ left: parent.left
165+ }
166+ height: indicators.minimizedPanelHeight
167+ visible: active && indicators.state == "initial"
168+ }
166 }
169 }
167
170
168 QtObject {
171 QtObject {
169 id: d
172 id: d
170− readonly property real indicatorHeight: indicators.panelHeight + indicatorsSeparatorLine.height
173+ readonly property real indicatorHeight: indicators.minimizedPanelHeight + indicatorOrangeLine.height
171 }
174 }
172
175
173 states: [
176 states: [
@@ -186,6 +189,10 @@ Item {
186 target: indicatorArea;
189 target: indicatorArea;
187 anchors.topMargin: indicators.state === "initial" ? -d.indicatorHeight : 0
190 anchors.topMargin: indicators.state === "initial" ? -d.indicatorHeight : 0
188 }
191 }
192+ PropertyChanges {
193+ target: indicators.showDragHandle;
194+ anchors.bottomMargin: -units.gu(1)
195+ }
189 }
196 }
190 ]
197 ]
191}
198}
qml/Panel/PanelVelocityCalculator.qmlnew file+54
@@ -0,0 +1,54 @@
1+/*
2+ * Copyright (C) 2014 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+
17+import QtQuick 2.2
18+import Ubuntu.Gestures 0.1
19+
20+Item {
21+ id: root
22+ property real trackedValue: 0
23+ property bool velocityAboveThreshold: false
24+ property real velocityThreshold: 0.4
25+
26+ function reset() {
27+ velocityTimer.stop();
28+ velocityAboveThreshold = false;
29+ calc.reset();
30+ }
31+
32+ function update() {
33+ calc.trackedPosition = trackedValue;
34+ velocityAboveThreshold = Math.abs(calc.calculate()) > velocityThreshold;
35+
36+ if (velocityAboveThreshold) { // only start timer if we're above the threshold.
37+ velocityTimer.start();
38+ } else {
39+ velocityTimer.stop();
40+ }
41+ }
42+
43+ onTrackedValueChanged: update();
44+
45+ AxisVelocityCalculator {
46+ id: calc
47+ }
48+
49+ Timer {
50+ id: velocityTimer
51+ interval: 50
52+ onTriggered: update();
53+ }
54+}
qml/Shell.qml+39−12
@@ -36,6 +36,7 @@ import "Panel"
36import "Components"
36import "Components"
37import "Notifications"
37import "Notifications"
38import "Stages"
38import "Stages"
39+import "Panel/Indicators"
39import Unity.Notifications 1.0 as NotificationBackend
40import Unity.Notifications 1.0 as NotificationBackend
40import Unity.Session 0.1
41import Unity.Session 0.1
41import Unity.DashCommunicator 0.1
42import Unity.DashCommunicator 0.1
@@ -243,12 +244,12 @@ Item {
243 target: applicationsDisplayLoader.item
244 target: applicationsDisplayLoader.item
244 property: "maximizedAppTopMargin"
245 property: "maximizedAppTopMargin"
245 // Not just using panel.panelHeight as that changes depending on the focused app.
246 // Not just using panel.panelHeight as that changes depending on the focused app.
246− value: panel.indicators.panelHeight
247+ value: panel.indicators.minimizedPanelHeight + units.dp(2) // dp(2) for orange line
247 }
248 }
248 Binding {
249 Binding {
249 target: applicationsDisplayLoader.item
250 target: applicationsDisplayLoader.item
250 property: "interactive"
251 property: "interactive"
251− value: edgeDemo.stagesEnabled && !greeter.shown && !lockscreen.shown && panel.indicators.fullyClosed && launcher.progress == 0
252+ value: edgeDemo.stagesEnabled && !greeter.shown && !lockscreen.shown && panel.indicators.fullyClosed && launcher.progress == 0 && !notifications.useModal
252 }
253 }
253 Binding {
254 Binding {
254 target: applicationsDisplayLoader.item
255 target: applicationsDisplayLoader.item
@@ -370,6 +371,10 @@ Item {
370 onHideGreeter: greeter.login()
371 onHideGreeter: greeter.login()
371
372
372 onShowPrompt: {
373 onShowPrompt: {
374+ shell.enabled = true;
375+ if (!LightDM.Greeter.active) {
376+ return; // could happen if hideGreeter() comes in before we prompt
377+ }
373 if (greeter.narrowMode) {
378 if (greeter.narrowMode) {
374 if (isDefaultPrompt) {
379 if (isDefaultPrompt) {
375 if (lockscreen.alphaNumeric) {
380 if (lockscreen.alphaNumeric) {
@@ -390,6 +395,9 @@ Item {
390 }
395 }
391
396
392 onPromptlessChanged: {
397 onPromptlessChanged: {
398+ if (!LightDM.Greeter.active) {
399+ return; // could happen if hideGreeter() comes in before we prompt
400+ }
393 if (greeter.narrowMode) {
401 if (greeter.narrowMode) {
394 if (LightDM.Greeter.promptless && LightDM.Greeter.authenticated) {
402 if (LightDM.Greeter.promptless && LightDM.Greeter.authenticated) {
395 lockscreen.hide()
403 lockscreen.hide()
@@ -401,6 +409,7 @@ Item {
401 }
409 }
402
410
403 onAuthenticationComplete: {
411 onAuthenticationComplete: {
412+ shell.enabled = true;
404 if (LightDM.Greeter.authenticated) {
413 if (LightDM.Greeter.authenticated) {
405 AccountsService.failedLogins = 0
414 AccountsService.failedLogins = 0
406 }
415 }
@@ -537,6 +546,11 @@ Item {
537
546
538 onShownChanged: {
547 onShownChanged: {
539 if (shown) {
548 if (shown) {
549+ // Disable everything so that user can't swipe greeter or
550+ // launcher until we get first prompt/authenticate, which
551+ // will re-enable the shell.
552+ shell.enabled = false;
553+
540 if (greeter.narrowMode) {
554 if (greeter.narrowMode) {
541 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
555 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
542 } else {
556 } else {
@@ -547,10 +561,9 @@ Item {
547 }
561 }
548 }
562 }
549
563
550− /* TODO re-enable when the corresponding changes in the service land (LP: #1361074)
551 Component.onCompleted: {
564 Component.onCompleted: {
552 Connectivity.unlockAllModems()
565 Connectivity.unlockAllModems()
553− } */
566+ }
554
567
555 onUnlocked: greeter.hide()
568 onUnlocked: greeter.hide()
556 onSelected: {
569 onSelected: {
@@ -594,7 +607,8 @@ Item {
594 target: Powerd
607 target: Powerd
595
608
596 onStatusChanged: {
609 onStatusChanged: {
610+ if (Powerd.status === Powerd.Off && reason !== Powerd.Proximity &&
597− if (Powerd.status === Powerd.Off && !callManager.hasCalls && !edgeDemo.running) {
611+ !callManager.hasCalls && !edgeDemo.running) {
598 greeter.showNow()
612 greeter.showNow()
599 }
613 }
600 }
614 }
@@ -610,7 +624,7 @@ Item {
610 }
624 }
611
625
612 var animate = !LightDM.Greeter.active && !stages.shown
626 var animate = !LightDM.Greeter.active && !stages.shown
613− dash.setCurrentScope("clickscope", animate, false)
627+ dash.setCurrentScope(0, animate, false)
614 ApplicationManager.requestFocusApplication("unity8-dash")
628 ApplicationManager.requestFocusApplication("unity8-dash")
615 }
629 }
616
630
@@ -646,7 +660,20 @@ Item {
646 available: edgeDemo.panelEnabled && (!shell.locked || AccountsService.enableIndicatorsWhileLocked) && !greeter.hasLockedApp
660 available: edgeDemo.panelEnabled && (!shell.locked || AccountsService.enableIndicatorsWhileLocked) && !greeter.hasLockedApp
647 contentEnabled: edgeDemo.panelContentEnabled
661 contentEnabled: edgeDemo.panelContentEnabled
648 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
662 width: parent.width > units.gu(60) ? units.gu(40) : parent.width
649− panelHeight: units.gu(3)
663+
664+ minimizedPanelHeight: units.gu(3)
665+ expandedPanelHeight: units.gu(7)
666+
667+ indicatorsModel: visibleIndicators.model
668+ }
669+
670+ VisibleIndicators {
671+ id: visibleIndicators
672+ // TODO: This should be sourced by device type (eg "desktop", "tablet", "phone"...)
673+ Component.onCompleted: initialise(indicatorProfile)
674+ }
675+ callHint {
676+ greeterShown: greeter.shown || lockscreen.shown
650 }
677 }
651
678
652 property bool topmostApplicationIsFullscreen:
679 property bool topmostApplicationIsFullscreen:
@@ -673,7 +700,7 @@ Item {
673 onDash: showDash()
700 onDash: showDash()
674 onDashSwipeChanged: {
701 onDashSwipeChanged: {
675 if (dashSwipe) {
702 if (dashSwipe) {
676− dash.setCurrentScope("clickscope", false, true)
703+ dash.setCurrentScope(0, false, true)
677 }
704 }
678 }
705 }
679 onLauncherApplicationSelected: {
706 onLauncherApplicationSelected: {
@@ -693,7 +720,7 @@ Item {
693 Rectangle {
720 Rectangle {
694 id: modalNotificationBackground
721 id: modalNotificationBackground
695
722
696− visible: notifications.useModal && !greeter.shown && (notifications.state == "narrow")
723+ visible: notifications.useModal && (notifications.state == "narrow")
697 color: "#000000"
724 color: "#000000"
698 anchors.fill: parent
725 anchors.fill: parent
699 opacity: 0.9
726 opacity: 0.9
@@ -709,9 +736,9 @@ Item {
709 model: NotificationBackend.Model
736 model: NotificationBackend.Model
710 margin: units.gu(1)
737 margin: units.gu(1)
711
738
712− y: panel.panelHeight
739+ y: topmostIsFullscreen ? 0 : panel.panelHeight
713 width: parent.width
740 width: parent.width
714− height: parent.height - panel.panelHeight
741+ height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
715
742
716 states: [
743 states: [
717 State {
744 State {
@@ -763,7 +790,7 @@ Item {
763 paused: Powerd.status === Powerd.Off // Saves power
790 paused: Powerd.status === Powerd.Off // Saves power
764 greeter: greeter
791 greeter: greeter
765 launcher: launcher
792 launcher: launcher
766− indicators: panel.indicators
793+ panel: panel
767 stages: stages
794 stages: stages
768 }
795 }
769
796
qml/Stages/PhoneStage.qml+7−3
@@ -21,7 +21,6 @@ import Unity.Application 0.1
21import Unity.Session 0.1
21import Unity.Session 0.1
22import Utils 0.1
22import Utils 0.1
23import "../Components"
23import "../Components"
24−import "../Components/Flickables" as Flickables
25
24
26Rectangle {
25Rectangle {
27 id: root
26 id: root
@@ -121,7 +120,7 @@ Rectangle {
121
120
122 }
121 }
123
122
124− Flickables.Flickable {
123+ Flickable {
125 id: spreadView
124 id: spreadView
126 objectName: "spreadView"
125 objectName: "spreadView"
127 anchors.fill: parent
126 anchors.fill: parent
@@ -246,12 +245,13 @@ Rectangle {
246 }
245 }
247 }
246 }
248
247
249− Item {
248+ MouseArea {
250 id: spreadRow
249 id: spreadRow
251 // This width controls how much the spread can be flicked left/right. It's composed of:
250 // This width controls how much the spread can be flicked left/right. It's composed of:
252 // tileDistance * app count (with a minimum of 3 apps, in order to also allow moving 1 and 2 apps a bit)
251 // tileDistance * app count (with a minimum of 3 apps, in order to also allow moving 1 and 2 apps a bit)
253 // + some constant value (still scales with the screen width) which looks good and somewhat fills the screen
252 // + some constant value (still scales with the screen width) which looks good and somewhat fills the screen
254 width: Math.max(3, ApplicationManager.count) * spreadView.tileDistance + (spreadView.width - spreadView.tileDistance) * 1.5
253 width: Math.max(3, ApplicationManager.count) * spreadView.tileDistance + (spreadView.width - spreadView.tileDistance) * 1.5
254+ height: parent.height
255 Behavior on width {
255 Behavior on width {
256 enabled: spreadView.closingIndex >= 0
256 enabled: spreadView.closingIndex >= 0
257 UbuntuNumberAnimation {}
257 UbuntuNumberAnimation {}
@@ -264,6 +264,10 @@ Rectangle {
264
264
265 x: spreadView.contentX
265 x: spreadView.contentX
266
266
267+ onClicked: {
268+ spreadView.snapTo(0);
269+ }
270+
267 Repeater {
271 Repeater {
268 id: spreadRepeater
272 id: spreadRepeater
269 model: ApplicationManager
273 model: ApplicationManager
qml/Stages/TabletStage.qml+6−3
@@ -20,7 +20,6 @@ import Ubuntu.Gestures 0.1
20import Unity.Application 0.1
20import Unity.Application 0.1
21import Utils 0.1
21import Utils 0.1
22import "../Components"
22import "../Components"
23−import "../Components/Flickables" as Flickables
24
23
25Rectangle {
24Rectangle {
26 id: root
25 id: root
@@ -154,7 +153,7 @@ Rectangle {
154 }
153 }
155 }
154 }
156
155
157− Flickables.Flickable {
156+ Flickable {
158 id: spreadView
157 id: spreadView
159 anchors.fill: parent
158 anchors.fill: parent
160 interactive: (spreadDragArea.status == DirectionalDragArea.Recognized || phase > 1)
159 interactive: (spreadDragArea.status == DirectionalDragArea.Recognized || phase > 1)
@@ -386,12 +385,16 @@ Rectangle {
386 }
385 }
387 }
386 }
388
387
389− Item {
388+ MouseArea {
390 id: spreadRow
389 id: spreadRow
391 x: spreadView.contentX
390 x: spreadView.contentX
392 height: root.height
391 height: root.height
393 width: spreadView.width + Math.max(spreadView.width, ApplicationManager.count * spreadView.tileDistance)
392 width: spreadView.width + Math.max(spreadView.width, ApplicationManager.count * spreadView.tileDistance)
394
393
394+ onClicked: {
395+ spreadView.snapTo(0);
396+ }
397+
395 Rectangle {
398 Rectangle {
396 id: sideStageBackground
399 id: sideStageBackground
397 color: "black"
400 color: "black"
src/libunity8-private/CMakeLists.txt+2
@@ -6,6 +6,8 @@ project(lib${LIB_NAME})
6
6
7set(lib${LIB_NAME}_SRCS
7set(lib${LIB_NAME}_SRCS
8 abstractdbusservicemonitor.cpp
8 abstractdbusservicemonitor.cpp
9+ unitydbusobject.cpp
10+ unitydbusvirtualobject.cpp
9 )
11 )
10
12
11add_library(${LIB_NAME} SHARED
13add_library(${LIB_NAME} SHARED
src/libunity8-private/unitydbusobject.cppnew file+86
@@ -0,0 +1,86 @@
1+/*
2+ * Copyright (C) 2014 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+
17+#include "unitydbusobject.h"
18+
19+#include <QDebug>
20+#include <QDBusMessage>
21+#include <QMetaClassInfo>
22+#include <QTimer>
23+
24+UnityDBusObject::UnityDBusObject(const QString &path, const QString &service, bool async, QObject *parent)
25+ : QObject(parent)
26+ , m_connection(QDBusConnection::sessionBus())
27+ , m_path(path)
28+ , m_service(service)
29+{
30+ if (async) {
31+ // Use a zero-timer to let Qml finish loading before we announce on DBus
32+ QTimer::singleShot(0, this, SLOT(registerObject()));
33+ } else {
34+ registerObject();
35+ }
36+}
37+
38+UnityDBusObject::~UnityDBusObject()
39+{
40+ // Leave service in place because multiple objects may be registered with
41+ // the same service. But we know we own the object path and can unregister it.
42+ m_connection.unregisterObject(path());
43+}
44+
45+QDBusConnection UnityDBusObject::connection() const
46+{
47+ return m_connection;
48+}
49+
50+QString UnityDBusObject::path() const
51+{
52+ return m_path;
53+}
54+
55+// Manually emit a PropertiesChanged signal over DBus, because QtDBus
56+// doesn't do it for us on Q_PROPERTIES, oddly enough.
57+void UnityDBusObject::notifyPropertyChanged(const QString& propertyName, const QVariant &value)
58+{
59+ QDBusMessage message;
60+ QString interface;
61+ QVariantMap changedProps;
62+
63+ interface = metaObject()->classInfo(metaObject()->indexOfClassInfo("D-Bus Interface")).value();
64+ changedProps.insert(propertyName, value);
65+
66+ message = QDBusMessage::createSignal(path(),
67+ "org.freedesktop.DBus.Properties",
68+ "PropertiesChanged");
69+ message << interface;
70+ message << changedProps;
71+ message << QStringList();
72+
73+ connection().send(message);
74+}
75+
76+void UnityDBusObject::registerObject()
77+{
78+ if (!m_connection.registerObject(m_path, this, QDBusConnection::ExportScriptableContents)) {
79+ qWarning() << "Unable to register DBus object" << m_path;
80+ }
81+ if (!m_service.isEmpty()) {
82+ if (!m_connection.registerService(m_service)) {
83+ qWarning() << "Unable to register DBus service" << m_service;
84+ }
85+ }
86+}
src/libunity8-private/unitydbusobject.hnew file+46
@@ -0,0 +1,46 @@
1+/*
2+ * Copyright (C) 2014 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+
17+#ifndef UNITYDBUSOBJECT_H
18+#define UNITYDBUSOBJECT_H
19+
20+#include <QDBusConnection>
21+#include <QObject>
22+
23+class Q_DECL_EXPORT UnityDBusObject : public QObject
24+{
25+ Q_OBJECT
26+
27+public:
28+ explicit UnityDBusObject(const QString &path, const QString &service = QString(), bool async = true, QObject *parent = 0);
29+ ~UnityDBusObject();
30+
31+ QDBusConnection connection() const;
32+ QString path() const;
33+
34+protected:
35+ void notifyPropertyChanged(const QString& propertyName, const QVariant &value);
36+
37+private Q_SLOTS:
38+ void registerObject();
39+
40+private:
41+ QDBusConnection m_connection;
42+ QString m_path;
43+ QString m_service;
44+};
45+
46+#endif // UNITYDBUSOBJECT_H
src/libunity8-private/unitydbusvirtualobject.cppnew file+83
@@ -0,0 +1,83 @@
1+/*
2+ * Copyright (C) 2014 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+
17+#include "unitydbusvirtualobject.h"
18+
19+#include <QDebug>
20+#include <QDBusMessage>
21+#include <QTimer>
22+
23+UnityDBusVirtualObject::UnityDBusVirtualObject(const QString &path, const QString &service, bool async, QObject *parent)
24+ : QDBusVirtualObject(parent)
25+ , m_connection(QDBusConnection::sessionBus())
26+ , m_path(path)
27+ , m_service(service)
28+{
29+ if (async) {
30+ // Use a zero-timer to let Qml finish loading before we announce on DBus
31+ QTimer::singleShot(0, this, SLOT(registerObject()));
32+ } else {
33+ registerObject();
34+ }
35+}
36+
37+UnityDBusVirtualObject::~UnityDBusVirtualObject()
38+{
39+ // Leave service in place because multiple objects may be registered with
40+ // the same service. But we know we own the object path and can unregister it.
41+ m_connection.unregisterObject(path());
42+}
43+
44+QDBusConnection UnityDBusVirtualObject::connection() const
45+{
46+ return m_connection;
47+}
48+
49+QString UnityDBusVirtualObject::path() const
50+{
51+ return m_path;
52+}
53+
54+// Manually emit a PropertiesChanged signal over DBus, because QtDBus
55+// doesn't do it for us on Q_PROPERTIES, oddly enough.
56+void UnityDBusVirtualObject::notifyPropertyChanged(const QString& interface, const QString& node, const QString& propertyName, const QVariant &value)
57+{
58+ QDBusMessage message;
59+ QVariantMap changedProps;
60+
61+ changedProps.insert(propertyName, value);
62+
63+ message = QDBusMessage::createSignal(path() + "/" + node,
64+ "org.freedesktop.DBus.Properties",
65+ "PropertiesChanged");
66+ message << interface;
67+ message << changedProps;
68+ message << QStringList();
69+
70+ connection().send(message);
71+}
72+
73+void UnityDBusVirtualObject::registerObject()
74+{
75+ if (!m_connection.registerVirtualObject(m_path, this, QDBusConnection::VirtualObjectRegisterOption::SubPath)) {
76+ qWarning() << "Unable to register DBus object" << m_path;
77+ }
78+ if (!m_service.isEmpty()) {
79+ if (!m_connection.registerService(m_service)) {
80+ qWarning() << "Unable to register DBus service" << m_service;
81+ }
82+ }
83+}
src/libunity8-private/unitydbusvirtualobject.hnew file+47
@@ -0,0 +1,47 @@
1+/*
2+ * Copyright (C) 2014 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+
17+#ifndef UNITYDBUSVIRTUALOBJECT_H
18+#define UNITYDBUSVIRTUALOBJECT_H
19+
20+#include <QDBusConnection>
21+#include <QDBusVirtualObject>
22+#include <QObject>
23+
24+class Q_DECL_EXPORT UnityDBusVirtualObject : public QDBusVirtualObject
25+{
26+ Q_OBJECT
27+
28+public:
29+ explicit UnityDBusVirtualObject(const QString &path, const QString &service = QString(), bool async = true, QObject *parent = 0);
30+ ~UnityDBusVirtualObject();
31+
32+ QDBusConnection connection() const;
33+ QString path() const;
34+
35+protected:
36+ void notifyPropertyChanged(const QString& interface, const QString& node, const QString& propertyName, const QVariant &value);
37+
38+private Q_SLOTS:
39+ void registerObject();
40+
41+private:
42+ QDBusConnection m_connection;
43+ QString m_path;
44+ QString m_service;
45+};
46+
47+#endif // UNITYDBUSVIRTUALOBJECT_H
tests/autopilot/unity8/indicators/tests/test_indicators.py+1−1
@@ -61,7 +61,7 @@ class IndicatorExistsTestCase(IndicatorTestCase):
61 self.skipTest('Nexus 10 does not have bluetooth at the moment.')
61 self.skipTest('Nexus 10 does not have bluetooth at the moment.')
62
62
63 def test_indicator_exists(self):
63 def test_indicator_exists(self):
64− self.main_window._get_indicator_widget(
64+ self.main_window._get_indicator_panel_item(
65 self.indicator_name
65 self.indicator_name
66 )
66 )
67
67
tests/autopilot/unity8/shell/emulators/dash.py+4
@@ -69,6 +69,10 @@ class Dash(emulators.UnityEmulatorBase):
69 return self.dash_content_list.wait_select_single(
69 return self.dash_content_list.wait_select_single(
70 'QQuickLoader', scopeId=scope_name)
70 'QQuickLoader', scopeId=scope_name)
71
71
72+ def get_scope_by_index(self, scope_index=0):
73+ return self.dash_content_list.wait_select_single(
74+ 'QQuickLoader', objectName=("scopeLoader%i" % scope_index))
75+
72 @autopilot_logging.log_action(logger.info)
76 @autopilot_logging.log_action(logger.info)
73 def open_scope(self, scope_id):
77 def open_scope(self, scope_id):
74 """Open a dash scope.
78 """Open a dash scope.
tests/autopilot/unity8/shell/emulators/main_window.py+6−6
@@ -75,15 +75,15 @@ class QQuickView(emulators.UnityEmulatorBase):
75 def get_pinentryField(self):
75 def get_pinentryField(self):
76 return self.select_single(objectName="pinentryField")
76 return self.select_single(objectName="pinentryField")
77
77
78− def _get_indicator_widget(self, indicator_name):
78+ def _get_indicator_panel_item(self, indicator_name):
79 return self.select_single(
79 return self.select_single(
80− 'DefaultIndicatorWidget',
80+ 'IndicatorItem',
81− objectName=indicator_name+'-widget'
81+ objectName=indicator_name+'-panelItem'
82 )
82 )
83
83
84 def _get_indicator_page(self, indicator_name):
84 def _get_indicator_page(self, indicator_name):
85 return self.select_single(
85 return self.select_single(
86− 'DefaultIndicatorPage',
86+ 'IndicatorPage',
87 objectName=indicator_name+'-page'
87 objectName=indicator_name+'-page'
88 )
88 )
89
89
@@ -93,12 +93,12 @@ class QQuickView(emulators.UnityEmulatorBase):
93
93
94 :returns: The indicator page.
94 :returns: The indicator page.
95 """
95 """
96− widget = self._get_indicator_widget(indicator_name)
96+ widget = self._get_indicator_panel_item(indicator_name)
97 start_x, start_y = input.get_center_point(widget)
97 start_x, start_y = input.get_center_point(widget)
98 end_x = start_x
98 end_x = start_x
99 end_y = self.height
99 end_y = self.height
100 self.pointing_device.drag(start_x, start_y, end_x, end_y)
100 self.pointing_device.drag(start_x, start_y, end_x, end_y)
101− self.wait_select_single('Indicators', fullyOpened=True)
101+ self.wait_select_single('IndicatorsMenu', fullyOpened=True)
102 return self._get_indicator_page(indicator_name)
102 return self._get_indicator_page(indicator_name)
103
103
104 @autopilot_logging.log_action(logger.info)
104 @autopilot_logging.log_action(logger.info)
tests/autopilot/unity8/shell/tests/__init__.py+1−1
@@ -447,7 +447,7 @@ class DashBaseTestCase(AutopilotTestCase):
447 return launch_dash_app_fixture.application_proxy
447 return launch_dash_app_fixture.application_proxy
448
448
449 def wait_for_dash(self):
449 def wait_for_dash(self):
450− home_scope = self.dash.get_scope('clickscope')
450+ home_scope = self.dash.get_scope_by_index(0)
451 # FIXME! There is a huge timeout here for when we're doing CI on
451 # FIXME! There is a huge timeout here for when we're doing CI on
452 # VMs. See lp:1203715
452 # VMs. See lp:1203715
453 self.assertThat(
453 self.assertThat(
tests/autopilot/unity8/shell/tests/test_notifications.py+6−5
@@ -248,7 +248,7 @@ class InteractiveNotificationBase(NotificationsBase):
248 self.assert_notification_action_id_was_called("action_accept")
248 self.assert_notification_action_id_was_called("action_accept")
249
249
250 def test_modal_sd_without_greeter(self):
250 def test_modal_sd_without_greeter(self):
251− """Snap-decision should block input to shell without greeter."""
251+ """Snap-decision should block input to shell without greeter/lockscreen."""
252 unity_proxy = self.launch_unity()
252 unity_proxy = self.launch_unity()
253 unlock_unity(unity_proxy)
253 unlock_unity(unity_proxy)
254
254
@@ -295,7 +295,7 @@ class InteractiveNotificationBase(NotificationsBase):
295 self.assert_notification_action_id_was_called("action_accept")
295 self.assert_notification_action_id_was_called("action_accept")
296
296
297 def test_modal_sd_with_greeter(self):
297 def test_modal_sd_with_greeter(self):
298− """A snap-decision should not block input to the greeter beneath it."""
298+ """A snap-decision should block input to the greeter/lockscreen beneath it."""
299 self.launch_unity()
299 self.launch_unity()
300
300
301 summary = "Incoming file"
301 summary = "Incoming file"
@@ -322,11 +322,12 @@ class InteractiveNotificationBase(NotificationsBase):
322 hints
322 hints
323 )
323 )
324
324
325− # verify that we can swipe away the greeter (interact with the "shell")
325+ # verify that we cannot reveal the launcher (no longer interact with
326+ # the shell)
326 time.sleep(1)
327 time.sleep(1)
327 self.main_window.show_dash_swiping()
328 self.main_window.show_dash_swiping()
328− greeter = self.main_window.get_greeter()
329− self.assertThat(greeter.shown, Eventually(Equals(False)))
329+ self.assertThat(
330+ self.main_window.is_launcher_open, Eventually(Equals(False)))
330
331
331 # verify and interact with the triggered snap-decision notification
332 # verify and interact with the triggered snap-decision notification
332 notify_list = self._get_notifications_list()
333 notify_list = self._get_notifications_list()
tests/mocks/AccountsService/AccountsService.qmltypes+2
@@ -23,6 +23,8 @@ Module {
23 }
23 }
24 Property { name: "user"; type: "string" }
24 Property { name: "user"; type: "string" }
25 Property { name: "demoEdges"; type: "bool" }
25 Property { name: "demoEdges"; type: "bool" }
26+ Property { name: "enableLauncherWhileLocked"; type: "bool" }
27+ Property { name: "enableIndicatorsWhileLocked"; type: "bool" }
26 Property { name: "backgroundFile"; type: "string" }
28 Property { name: "backgroundFile"; type: "string" }
27 Property { name: "statsWelcomeScreen"; type: "bool" }
29 Property { name: "statsWelcomeScreen"; type: "bool" }
28 Property { name: "passwordDisplayHint"; type: "PasswordDisplayHint"; isReadonly: true }
30 Property { name: "passwordDisplayHint"; type: "PasswordDisplayHint"; isReadonly: true }
tests/mocks/LightDM/CMakeLists.txt+2
@@ -15,6 +15,7 @@ include_directories(
15 ${CMAKE_CURRENT_BINARY_DIR}
15 ${CMAKE_CURRENT_BINARY_DIR}
16 ${CMAKE_SOURCE_DIR}/plugins/Utils
16 ${CMAKE_SOURCE_DIR}/plugins/Utils
17 ${CMAKE_SOURCE_DIR}/tests/mocks/libusermetrics
17 ${CMAKE_SOURCE_DIR}/tests/mocks/libusermetrics
18+ ${libunity8-private_SOURCE_DIR}
18)
19)
19
20
20set(QMLPLUGIN_SRC
21set(QMLPLUGIN_SRC
@@ -40,6 +41,7 @@ target_link_libraries(MockLightDM-qml
40 -llightdm-qt5-2
41 -llightdm-qt5-2
41 -L${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics
42 -L${CMAKE_BINARY_DIR}/tests/mocks/libusermetrics
42 -lusermetricsoutput
43 -lusermetricsoutput
44+ unity8-private
43 )
45 )
44
46
45qt5_use_modules(MockLightDM-qml DBus Gui Qml)
47qt5_use_modules(MockLightDM-qml DBus Gui Qml)
tests/mocks/LightDM/LightDM.qmltypes+1
@@ -37,6 +37,7 @@ Module {
37 Signal { name: "isActiveChanged" }
37 Signal { name: "isActiveChanged" }
38 Signal { name: "isAuthenticatedChanged" }
38 Signal { name: "isAuthenticatedChanged" }
39 Signal { name: "showGreeter" }
39 Signal { name: "showGreeter" }
40+ Signal { name: "hideGreeter" }
40 Signal {
41 Signal {
41 name: "requestAuthenticationUser"
42 name: "requestAuthenticationUser"
42 Parameter { name: "user"; type: "string" }
43 Parameter { name: "user"; type: "string" }
tests/mocks/QMenuModel/QMenuModel.qmltypes+1−1
@@ -18,12 +18,12 @@ Module {
18 prototype: "QAbstractListModel"
18 prototype: "QAbstractListModel"
19 exports: ["QMenuModel/UnityMenuModel 0.1"]
19 exports: ["QMenuModel/UnityMenuModel 0.1"]
20 exportMetaObjectRevisions: [0]
20 exportMetaObjectRevisions: [0]
21− Property { name: "modelData"; type: "QVariant" }
22 Property { name: "busName"; type: "QByteArray" }
21 Property { name: "busName"; type: "QByteArray" }
23 Property { name: "actions"; type: "QVariantMap" }
22 Property { name: "actions"; type: "QVariantMap" }
24 Property { name: "menuObjectPath"; type: "QByteArray" }
23 Property { name: "menuObjectPath"; type: "QByteArray" }
25 Property { name: "actionStateParser"; type: "ActionStateParser"; isPointer: true }
24 Property { name: "actionStateParser"; type: "ActionStateParser"; isPointer: true }
26 Property { name: "nameOwner"; type: "string"; isReadonly: true }
25 Property { name: "nameOwner"; type: "string"; isReadonly: true }
26+ Property { name: "modelData"; type: "QVariant" }
27 Method {
27 Method {
28 name: "insertRow"
28 name: "insertRow"
29 Parameter { name: "row"; type: "int" }
29 Parameter { name: "row"; type: "int" }
tests/mocks/QMenuModel/unitymenumodel.cpp+1−2
@@ -50,13 +50,12 @@ void UnityMenuModel::setModelData(const QVariant& data)
50{
50{
51 beginResetModel();
51 beginResetModel();
52
52
53− m_modelData.clear();
54 m_modelData = data.toList();
53 m_modelData = data.toList();
54+ Q_EMIT modelDataChanged();
55
55
56 endResetModel();
56 endResetModel();
57}
57}
58
58
59−
60void UnityMenuModel::insertRow(int row, const QVariant& data)
59void UnityMenuModel::insertRow(int row, const QVariant& data)
61{
60{
62 row = qMin(row, rowCount());
61 row = qMin(row, rowCount());
tests/mocks/QMenuModel/unitymenumodel.h+6−2
@@ -27,13 +27,15 @@ class ActionStateParser;
27class Q_DECL_EXPORT UnityMenuModel : public QAbstractListModel
27class Q_DECL_EXPORT UnityMenuModel : public QAbstractListModel
28{
28{
29 Q_OBJECT
29 Q_OBJECT
30− Q_PROPERTY(QVariant modelData READ modelData WRITE setModelData NOTIFY modelDataChanged)
31 Q_PROPERTY(QByteArray busName READ busName WRITE setBusName NOTIFY busNameChanged)
30 Q_PROPERTY(QByteArray busName READ busName WRITE setBusName NOTIFY busNameChanged)
32 Q_PROPERTY(QVariantMap actions READ actions WRITE setActions NOTIFY actionsChanged)
31 Q_PROPERTY(QVariantMap actions READ actions WRITE setActions NOTIFY actionsChanged)
33 Q_PROPERTY(QByteArray menuObjectPath READ menuObjectPath WRITE setMenuObjectPath NOTIFY menuObjectPathChanged)
32 Q_PROPERTY(QByteArray menuObjectPath READ menuObjectPath WRITE setMenuObjectPath NOTIFY menuObjectPathChanged)
34 Q_PROPERTY(ActionStateParser* actionStateParser READ actionStateParser WRITE setActionStateParser NOTIFY actionStateParserChanged)
33 Q_PROPERTY(ActionStateParser* actionStateParser READ actionStateParser WRITE setActionStateParser NOTIFY actionStateParserChanged)
35 Q_PROPERTY(QString nameOwner READ nameOwner NOTIFY nameOwnerChanged)
34 Q_PROPERTY(QString nameOwner READ nameOwner NOTIFY nameOwnerChanged)
36
35
36+ // internal mock properties
37+ Q_PROPERTY(QVariant modelData READ modelData WRITE setModelData NOTIFY modelDataChanged)
38+
37public:
39public:
38 UnityMenuModel(QObject *parent = nullptr);
40 UnityMenuModel(QObject *parent = nullptr);
39 virtual ~UnityMenuModel();
41 virtual ~UnityMenuModel();
@@ -81,9 +83,11 @@ Q_SIGNALS:
81 void actionsChanged();
83 void actionsChanged();
82 void menuObjectPathChanged();
84 void menuObjectPathChanged();
83 void actionStateParserChanged();
85 void actionStateParserChanged();
84− void modelDataChanged();
85 void nameOwnerChanged();
86 void nameOwnerChanged();
86
87
88+ // Internal mock usage
89+ void modelDataChanged();
90+
87private:
91private:
88 QVariantMap rowData(int row) const;
92 QVariantMap rowData(int row) const;
89 QVariant subMenuData(int row) const;
93 QVariant subMenuData(int row) const;
tests/mocks/Ubuntu/Payments/Payments.qmltypes+2
@@ -37,6 +37,8 @@ Module {
37 Parameter { name: "error"; type: "string" }
37 Parameter { name: "error"; type: "string" }
38 }
38 }
39 Signal { name: "purchaseCompleted" }
39 Signal { name: "purchaseCompleted" }
40+ Signal { name: "purchaseCancelled" }
40 Method { name: "start" }
41 Method { name: "start" }
42+ Method { name: "process" }
41 }
43 }
42}
44}
tests/mocks/Ubuntu/Telephony/Telephony.qmltypes+2−1
@@ -22,7 +22,8 @@ Module {
22 exportMetaObjectRevisions: [0]
22 exportMetaObjectRevisions: [0]
23 Property { name: "phoneNumber"; type: "string" }
23 Property { name: "phoneNumber"; type: "string" }
24 Property { name: "isConference"; type: "bool" }
24 Property { name: "isConference"; type: "bool" }
25− Property { name: "elapsedTime"; type: "int"; isReadonly: true }
25+ Property { name: "elapsedTime"; type: "int" }
26+ Property { name: "elapsedTimerRunning"; type: "bool" }
26 }
27 }
27 Component {
28 Component {
28 name: "MockCallManager"
29 name: "MockCallManager"
tests/mocks/Unity/Application/Application.qmltypes+13
@@ -238,6 +238,7 @@ Module {
238 Property { name: "state"; type: "State"; isReadonly: true }
238 Property { name: "state"; type: "State"; isReadonly: true }
239 Property { name: "name"; type: "string"; isReadonly: true }
239 Property { name: "name"; type: "string"; isReadonly: true }
240 Property { name: "live"; type: "bool"; isReadonly: true }
240 Property { name: "live"; type: "bool"; isReadonly: true }
241+ Property { name: "orientation"; type: "Qt::ScreenOrientation" }
241 Signal {
242 Signal {
242 name: "typeChanged"
243 name: "typeChanged"
243 Parameter { type: "Type" }
244 Parameter { type: "Type" }
@@ -268,9 +269,14 @@ Module {
268 exportMetaObjectRevisions: [0]
269 exportMetaObjectRevisions: [0]
269 Property { name: "name"; type: "string"; isReadonly: true }
270 Property { name: "name"; type: "string"; isReadonly: true }
270 Property { name: "surface"; type: "MirSurfaceItem"; isReadonly: true; isPointer: true }
271 Property { name: "surface"; type: "MirSurfaceItem"; isReadonly: true; isPointer: true }
272+ Property { name: "application"; type: "ApplicationInfo"; isReadonly: true; isPointer: true }
271 Property { name: "parentSession"; type: "Session"; isReadonly: true; isPointer: true }
273 Property { name: "parentSession"; type: "Session"; isReadonly: true; isPointer: true }
272 Property { name: "childSessions"; type: "SessionModel"; isReadonly: true; isPointer: true }
274 Property { name: "childSessions"; type: "SessionModel"; isReadonly: true; isPointer: true }
273 Property { name: "live"; type: "bool"; isReadonly: true }
275 Property { name: "live"; type: "bool"; isReadonly: true }
276+ Signal {
277+ name: "applicationChanged"
278+ Parameter { type: "ApplicationInfo"; isPointer: true }
279+ }
274 Signal {
280 Signal {
275 name: "surfaceChanged"
281 name: "surfaceChanged"
276 Parameter { type: "MirSurfaceItem"; isPointer: true }
282 Parameter { type: "MirSurfaceItem"; isPointer: true }
@@ -368,6 +374,12 @@ Module {
368 Property { name: "stage"; type: "Stage"; isReadonly: true }
374 Property { name: "stage"; type: "Stage"; isReadonly: true }
369 Property { name: "state"; type: "State"; isReadonly: true }
375 Property { name: "state"; type: "State"; isReadonly: true }
370 Property { name: "focused"; type: "bool"; isReadonly: true }
376 Property { name: "focused"; type: "bool"; isReadonly: true }
377+ Property { name: "splashTitle"; type: "string"; isReadonly: true }
378+ Property { name: "splashImage"; type: "QUrl"; isReadonly: true }
379+ Property { name: "splashShowHeader"; type: "bool"; isReadonly: true }
380+ Property { name: "splashColor"; type: "QColor"; isReadonly: true }
381+ Property { name: "splashColorHeader"; type: "QColor"; isReadonly: true }
382+ Property { name: "splashColorFooter"; type: "QColor"; isReadonly: true }
371 Signal {
383 Signal {
372 name: "nameChanged"
384 name: "nameChanged"
373 Parameter { name: "name"; type: "string" }
385 Parameter { name: "name"; type: "string" }
@@ -414,6 +426,7 @@ Module {
414 Property { name: "count"; type: "int"; isReadonly: true }
426 Property { name: "count"; type: "int"; isReadonly: true }
415 Property { name: "focusedApplicationId"; type: "string"; isReadonly: true }
427 Property { name: "focusedApplicationId"; type: "string"; isReadonly: true }
416 Property { name: "suspended"; type: "bool" }
428 Property { name: "suspended"; type: "bool" }
429+ Property { name: "forceDashActive"; type: "bool" }
417 Signal {
430 Signal {
418 name: "focusRequested"
431 name: "focusRequested"
419 Parameter { name: "appId"; type: "string" }
432 Parameter { name: "appId"; type: "string" }
tests/mocks/Unity/DashCommunicator/DashCommunicator.qmltypes+6−6
@@ -14,13 +14,13 @@ Module {
14 exportMetaObjectRevisions: [0]
14 exportMetaObjectRevisions: [0]
15 Signal {
15 Signal {
16 name: "setCurrentScopeCalled"
16 name: "setCurrentScopeCalled"
17− Parameter { name: "scopeId"; type: "string" }
17+ Parameter { name: "index"; type: "int" }
18 Parameter { name: "animate"; type: "bool" }
18 Parameter { name: "animate"; type: "bool" }
19 Parameter { name: "reset"; type: "bool" }
19 Parameter { name: "reset"; type: "bool" }
20 }
20 }
21 Method {
21 Method {
22 name: "setCurrentScope"
22 name: "setCurrentScope"
23− Parameter { name: "scopeId"; type: "string" }
23+ Parameter { name: "index"; type: "int" }
24 Parameter { name: "animate"; type: "bool" }
24 Parameter { name: "animate"; type: "bool" }
25 Parameter { name: "reset"; type: "bool" }
25 Parameter { name: "reset"; type: "bool" }
26 }
26 }
@@ -32,15 +32,15 @@ Module {
32 exportMetaObjectRevisions: [0]
32 exportMetaObjectRevisions: [0]
33 Signal {
33 Signal {
34 name: "setCurrentScopeRequested"
34 name: "setCurrentScopeRequested"
35− Parameter { name: "scopeId"; type: "string" }
35+ Parameter { name: "index"; type: "int" }
36 Parameter { name: "animate"; type: "bool" }
36 Parameter { name: "animate"; type: "bool" }
37− Parameter { name: "reset"; type: "bool" }
37+ Parameter { name: "isSwipe"; type: "bool" }
38 }
38 }
39 Method {
39 Method {
40 name: "mockSetCurrentScope"
40 name: "mockSetCurrentScope"
41− Parameter { name: "scopeId"; type: "string" }
41+ Parameter { name: "index"; type: "int" }
42 Parameter { name: "animate"; type: "bool" }
42 Parameter { name: "animate"; type: "bool" }
43− Parameter { name: "reset"; type: "bool" }
43+ Parameter { name: "isSwipe"; type: "bool" }
44 }
44 }
45 }
45 }
46}
46}
tests/mocks/Unity/DashCommunicator/dashcommunicator.cpp+2−2
@@ -31,7 +31,7 @@ DashCommunicator::~DashCommunicator()
31}
31}
32
32
33
33
34−void DashCommunicator::setCurrentScope(const QString &scopeId, bool animate, bool reset)
34+void DashCommunicator::setCurrentScope(int index, bool animate, bool reset)
35{
35{
36− Q_EMIT setCurrentScopeCalled(scopeId, animate, reset);
36+ Q_EMIT setCurrentScopeCalled(index, animate, reset);
37}
37}
tests/mocks/Unity/DashCommunicator/dashcommunicator.h+2−2
@@ -29,11 +29,11 @@ public:
29 ~DashCommunicator();
29 ~DashCommunicator();
30
30
31public Q_SLOTS:
31public Q_SLOTS:
32− void setCurrentScope(const QString &scopeId, bool animate, bool reset);
32+ void setCurrentScope(int index, bool animate, bool reset);
33
33
34Q_SIGNALS:
34Q_SIGNALS:
35 // This mock just emits calls back to the QML api for the plugin to verify calls
35 // This mock just emits calls back to the QML api for the plugin to verify calls
36− void setCurrentScopeCalled(const QString &scopeId, bool animate, bool reset);
36+ void setCurrentScopeCalled(int index, bool animate, bool reset);
37};
37};
38
38
39#endif
39#endif
tests/mocks/Unity/DashCommunicator/dashcommunicatorservice.cpp+2−2
@@ -27,7 +27,7 @@ DashCommunicatorService::~DashCommunicatorService()
27
27
28}
28}
29
29
30−void DashCommunicatorService::mockSetCurrentScope(const QString &scopeId, bool animate, bool isSwipe)
30+void DashCommunicatorService::mockSetCurrentScope(int index, bool animate, bool isSwipe)
31{
31{
32− Q_EMIT setCurrentScopeRequested(scopeId, animate, isSwipe);
32+ Q_EMIT setCurrentScopeRequested(index, animate, isSwipe);
33}
33}
tests/mocks/Unity/DashCommunicator/dashcommunicatorservice.h+2−2
@@ -27,10 +27,10 @@ public:
27 ~DashCommunicatorService();
27 ~DashCommunicatorService();
28
28
29Q_SIGNALS:
29Q_SIGNALS:
30− void setCurrentScopeRequested(const QString &scopeId, bool animate, bool isSwipe);
30+ void setCurrentScopeRequested(int index, bool animate, bool isSwipe);
31
31
32public Q_SLOTS:
32public Q_SLOTS:
33− void mockSetCurrentScope(const QString &scopeId, bool animate, bool isSwipe);
33+ void mockSetCurrentScope(int index, bool animate, bool isSwipe);
34};
34};
35
35
36#endif // DBUSUNITYSESSIONSERVICE_H
36#endif // DBUSUNITYSESSIONSERVICE_H
tests/mocks/Unity/Indicators/CMakeLists.txt+3−10
@@ -12,24 +12,17 @@ include_directories(
12
12
13set(IndicatorsFakeQml_SOURCES
13set(IndicatorsFakeQml_SOURCES
14 fakeplugin.cpp
14 fakeplugin.cpp
15+ fakeindicatorsmodel.cpp
16+ fakeunitymenumodelcache.cpp
15 ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/indicators.h
17 ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/indicators.h
16 ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/menucontentactivator.cpp
18 ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/menucontentactivator.cpp
19+ ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/sharedunitymenumodel.cpp
17 ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/unitymenumodelcache.cpp
20 ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/unitymenumodelcache.cpp
18 ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/unitymenumodelstack.cpp
21 ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/unitymenumodelstack.cpp
19 ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/visibleindicatorsmodel.cpp
22 ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/visibleindicatorsmodel.cpp
20− fakeindicatorsmodel.cpp
21−)
22−
23−set(IndicatorsFakeQml_RESOURCES
24− indicators_fake.qrc
25−)
26−
27−qt5_add_resources(IndicatorsFakeQml_RESOURCES_RCC
28− ${IndicatorsFakeQml_RESOURCES}
29)
23)
30
24
31add_library(IndicatorsFakeQml SHARED
25add_library(IndicatorsFakeQml SHARED
32− ${IndicatorsFakeQml_RESOURCES_RCC}
33 ${IndicatorsFakeQml_SOURCES}
26 ${IndicatorsFakeQml_SOURCES}
34)
27)
35add_definitions(-DUNITYINDICATORS_LIBRARY)
28add_definitions(-DUNITYINDICATORS_LIBRARY)
tests/mocks/Unity/Indicators/FakeMenuPage.qmldeleted−27
@@ -1,27 +0,0 @@
1−/*
2− * Copyright 2013 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−
17−import QtQuick 2.0
18−
19−Flickable {
20− objectName: "fakeMenuPlugin"
21− // Make it compatible with the PluginItem interface
22− function reset() {
23− if (shell != undefined && shell.indicator_status != undefined) {
24− shell.indicator_status[objectName].reset++;
25− }
26− }
27−}
tests/mocks/Unity/Indicators/Indicators.qmltypes+43−15
@@ -29,6 +29,7 @@ Module {
29 exports: ["Unity.Indicators/FakeIndicatorsModel 0.1"]
29 exports: ["Unity.Indicators/FakeIndicatorsModel 0.1"]
30 exportMetaObjectRevisions: [0]
30 exportMetaObjectRevisions: [0]
31 Property { name: "count"; type: "int"; isReadonly: true }
31 Property { name: "count"; type: "int"; isReadonly: true }
32+ Property { name: "modelData"; type: "QVariant" }
32 Method {
33 Method {
33 name: "load"
34 name: "load"
34 Parameter { name: "profile"; type: "string" }
35 Parameter { name: "profile"; type: "string" }
@@ -38,6 +39,15 @@ Module {
38 name: "append"
39 name: "append"
39 Parameter { name: "row"; type: "QVariantMap" }
40 Parameter { name: "row"; type: "QVariantMap" }
40 }
41 }
42+ Method {
43+ name: "insert"
44+ Parameter { name: "row"; type: "int" }
45+ Parameter { name: "data"; type: "QVariantMap" }
46+ }
47+ Method {
48+ name: "remove"
49+ Parameter { name: "row"; type: "int" }
50+ }
41 Method {
51 Method {
42 name: "data"
52 name: "data"
43 type: "QVariant"
53 type: "QVariant"
@@ -45,6 +55,27 @@ Module {
45 Parameter { name: "role"; type: "int" }
55 Parameter { name: "role"; type: "int" }
46 }
56 }
47 }
57 }
58+ Component {
59+ name: "FakeUnityMenuModelCache"
60+ prototype: "UnityMenuModelCache"
61+ exports: ["Unity.Indicators/UnityMenuModelCache 0.1"]
62+ isCreatable: false
63+ isSingleton: true
64+ exportMetaObjectRevisions: [0]
65+ Method {
66+ name: "setCachedModelData"
67+ Parameter { name: "bus"; type: "QByteArray" }
68+ Parameter { name: "path"; type: "QByteArray" }
69+ Parameter { name: "actions"; type: "QVariantMap" }
70+ Parameter { name: "data"; type: "QVariant" }
71+ }
72+ Method {
73+ name: "setCachedModelData"
74+ Parameter { name: "bus"; type: "QByteArray" }
75+ Parameter { name: "path"; type: "QByteArray" }
76+ Parameter { name: "actions"; type: "QVariantMap" }
77+ }
78+ }
48 Component {
79 Component {
49 name: "FlatMenuProxyModelRole"
80 name: "FlatMenuProxyModelRole"
50 prototype: "QObject"
81 prototype: "QObject"
@@ -74,10 +105,8 @@ Module {
74 values: {
105 values: {
75 "Identifier": 0,
106 "Identifier": 0,
76 "Position": 1,
107 "Position": 1,
77− "WidgetSource": 2,
108+ "IndicatorProperties": 2,
78− "PageSource": 3,
109+ "IsVisible": 3
79− "IndicatorProperties": 4,
80− "IsVisible": 5
81 }
110 }
82 }
111 }
83 }
112 }
@@ -160,19 +189,18 @@ Module {
160 }
189 }
161 Component { name: "QIdentityProxyModel"; prototype: "QAbstractProxyModel" }
190 Component { name: "QIdentityProxyModel"; prototype: "QAbstractProxyModel" }
162 Component {
191 Component {
163− name: "UnityMenuModelCache"
192+ name: "SharedUnityMenuModel"
164 prototype: "QObject"
193 prototype: "QObject"
165− exports: ["Unity.Indicators/UnityMenuModelCache 0.1"]
194+ exports: ["Unity.Indicators/SharedUnityMenuModel 0.1"]
166− isCreatable: false
167− isSingleton: true
168 exportMetaObjectRevisions: [0]
195 exportMetaObjectRevisions: [0]
169− Method {
196+ Property { name: "busName"; type: "QByteArray" }
170− name: "model"
197+ Property { name: "menuObjectPath"; type: "QByteArray" }
198+ Property { name: "actions"; type: "QVariantMap" }
171− type: "UnityMenuModel*"
199+ Property { name: "model"; type: "UnityMenuModel"; isReadonly: true; isPointer: true }
172− Parameter { name: "bus"; type: "QByteArray" }
200+ }
173− Parameter { name: "path"; type: "QByteArray" }
201+ Component {
174− Parameter { name: "actions"; type: "QVariantMap" }
202+ name: "UnityMenuModelCache"
175− }
203+ prototype: "QObject"
176 Method {
204 Method {
177 name: "contains"
205 name: "contains"
178 type: "bool"
206 type: "bool"
tests/mocks/Unity/Indicators/IndicatorsModel.qml+152−34
@@ -18,43 +18,161 @@ import QtQuick 2.0
18import Unity.Indicators 0.1 as Indicators
18import Unity.Indicators 0.1 as Indicators
19
19
20Indicators.FakeIndicatorsModel {
20Indicators.FakeIndicatorsModel {
21+ id: root
22+
23+ property var originalModelData: [
24+ {
25+ "identifier": "fake-indicator-bluetooth",
26+ "indicatorProperties": {
27+ "enabled": true,
28+ "busName": "com.canonical.indicators.fake1",
29+ "menuObjectPath": "/com/canonical/indicators/fake1",
30+ "actionsObjectPath": "/com/canonical/indicators/fake1"
31+ }
32+ },
33+ {
34+ "identifier": "fake-indicator-network",
35+ "indicatorProperties": {
36+ "enabled": true,
37+ "busName": "com.canonical.indicators.fake2",
38+ "menuObjectPath": "/com/canonical/indicators/fake2",
39+ "actionsObjectPath": "/com/canonical/indicators/fake2"
40+ }
41+ },
42+ {
43+ "identifier": "fake-indicator-messages",
44+ "indicatorProperties": {
45+ "enabled": true,
46+ "busName": "com.canonical.indicators.fake3",
47+ "menuObjectPath": "/com/canonical/indicators/fake3",
48+ "actionsObjectPath": "/com/canonical/indicators/fake3"
49+ }
50+ },
51+ {
52+ "identifier": "fake-indicator-sound",
53+ "indicatorProperties": {
54+ "enabled": true,
55+ "busName": "com.canonical.indicators.fake4",
56+ "menuObjectPath": "/com/canonical/indicators/fake4",
57+ "actionsObjectPath": "/com/canonical/indicators/fake4"
58+ }
59+ },
60+ {
61+ "identifier": "fake-indicator-power",
62+ "indicatorProperties": {
63+ "enabled": true,
64+ "busName": "com.canonical.indicators.fake5",
65+ "menuObjectPath": "/com/canonical/indicators/fake5",
66+ "actionsObjectPath": "/com/canonical/indicators/fake5"
67+ }
68+ },
69+ {
70+ "identifier": "fake-indicator-datetime",
71+ "indicatorProperties": {
72+ "enabled": true,
73+ "busName": "com.canonical.indicators.fake6",
74+ "menuObjectPath": "/com/canonical/indicators/fake6",
75+ "actionsObjectPath": "/com/canonical/indicators/fake6"
76+ }
77+ }
78+ ]
21
79
22 function load(profile) {
80 function load(profile) {
23 unload();
81 unload();
82+ root.modelData = originalModelData;
83+
84+ Indicators.UnityMenuModelCache.setCachedModelData("/com/canonical/indicators/fake1",
85+ getUnityMenuModelData("fake-indicator-bluetooth",
86+ "Bluetooth (F)",
87+ "",
88+ [ "image://theme/bluetooth-active" ]));
89+ Indicators.UnityMenuModelCache.setCachedModelData("/com/canonical/indicators/fake2",
90+ getUnityMenuModelData("fake-indicator-network",
91+ "Network (F)",
92+ "",
93+ [ "image://theme/simcard-error", "image://theme/wifi-high" ]));
94+ Indicators.UnityMenuModelCache.setCachedModelData("/com/canonical/indicators/fake3",
95+ getUnityMenuModelData("fake-indicator-messages",
96+ "Messages (F)",
97+ "",
98+ [ "image://theme/messages-new" ]));
99+ Indicators.UnityMenuModelCache.setCachedModelData("/com/canonical/indicators/fake4",
100+ getUnityMenuModelData("fake-indicator-sound",
101+ "Sound (F)",
102+ "",
103+ [ "image://theme/audio-volume-high" ]));
104+ Indicators.UnityMenuModelCache.setCachedModelData("/com/canonical/indicators/fake5",
105+ getUnityMenuModelData("fake-indicator-power",
106+ "Battery (F)",
107+ "",
108+ [ "image://theme/battery-020" ]));
109+ Indicators.UnityMenuModelCache.setCachedModelData("/com/canonical/indicators/fake6",
110+ getUnityMenuModelData("fake-indicator-datetime",
111+ "Upcoming Events (F)",
112+ "12:04",
113+ []));
114+ }
115+
116+ function getUnityMenuModelData(identifier, title, label, icons) {
117+ var root = [{
118+ "rowData": { // 1
119+ "label": "",
120+ "sensitive": true,
121+ "isSeparator": false,
122+ "icon": "",
123+ "type": "com.canonical.indicator.root",
124+ "ext": {},
125+ "action": "",
126+ "actionState": {
127+ "title": title,
128+ "label": label,
129+ "icons": icons,
130+ "visible": true
131+ },
132+ "isCheck": false,
133+ "isRadio": false,
134+ "isToggled": false
135+ },
136+ "submenu": []
137+ }];
138+
139+ var submenus = [];
140+ for (var i = 0; i < 8; i++) {
141+ var submenu = {
142+ "rowData": { // 1.1
143+ "label": identifier,
144+ "sensitive": true,
145+ "isSeparator": false,
146+ "icon": "",
147+ "type": undefined,
148+ "ext": {},
149+ "action": "",
150+ "actionState": {},
151+ "isCheck": false,
152+ "isRadio": false,
153+ "isToggled": false
154+ }};
155+ submenus.push(submenu);
156+ }
157+ root[0]["submenu"] = submenus;
158+
159+ return root;
160+ }
161+
162+ function setIndicatorVisible(identifier, visible) {
163+ for (var i = 0; i < originalModelData.length; i++) {
164+ if (originalModelData[i]["identifier"] === identifier) {
165+ var data = Indicators.UnityMenuModelCache.getCachedModelData(
166+ originalModelData[i]["indicatorProperties"]["menuObjectPath"]);
167+
168+ data[0]["rowData"]["actionState"]["visible"] = visible;
169+
170+ Indicators.UnityMenuModelCache.setCachedModelData(
171+ originalModelData[i]["indicatorProperties"]["menuObjectPath"],
172+ data);
173+ break;
174+ }
175+ }
24
176
25− append({ "identifier": "indicator-fake1",
26− "position": 0,
27− "widgetSource": "qrc:/tests/indciators/qml/fake_menu_widget1.qml",
28− "pageSource": "qrc:/tests/indciators/qml/fake_menu_page1.qml",
29− "indicatorProperties": { enabled: true }
30− });
31−
32− append({ "identifier": "indicator-fake2",
33− "position": 1,
34− "widgetSource": "qrc:/tests/indciators/qml/fake_menu_widget2.qml",
35− "pageSource": "qrc:/tests/indciators/qml/fake_menu_page2.qml",
36− "indicatorProperties": { enabled: true }
37− });
38−
39− append({ "identifier": "indicator-fake3",
40− "position": 2,
41− "widgetSource": "qrc:/tests/indciators/qml/fake_menu_widget3.qml",
42− "pageSource": "qrc:/tests/indciators/qml/fake_menu_page3.qml",
43− "indicatorProperties": { enabled: true }
44− });
45−
46− append({ "identifier": "indicator-fake4",
47− "position": 3,
48− "widgetSource": "qrc:/tests/indciators/qml/fake_menu_widget4.qml",
49− "pageSource": "qrc:/tests/indciators/qml/fake_menu_page4.qml",
50− "indicatorProperties": { enabled: true }
51− });
52−
53− append({ "identifier": "indicator-fake5",
54− "position": 4,
55− "widgetSource": "qrc:/tests/indciators/qml/fake_menu_widget5.qml",
56− "pageSource": "qrc:/tests/indciators/qml/fake_menu_page5.qml",
57− "indicatorProperties": { enabled: true }
58− });
59 }
177 }
60}
178}
tests/mocks/Unity/Indicators/RootActionState.qml+18−10
@@ -19,15 +19,23 @@
19
19
20import QtQuick 2.0
20import QtQuick 2.0
21
21
22−QtObject {
22+Item {
23− property var menu
23+ property var menu: null
24− property bool valid: false
24+ property bool valid: cachedState !== undefined
25− property string title
25+ property string title: cachedState && cachedState.hasOwnProperty("title") ? cachedState["title"] : ""
26− property string leftLabel
26+ property string leftLabel: cachedState && cachedState.hasOwnProperty("pre-label") ? cachedState["pre-label"] : ""
27− property string rightLabel
27+ property string rightLabel: cachedState && cachedState.hasOwnProperty("label") ? cachedState["label"] : ""
28− property var icons
28+ property var icons: cachedState && cachedState.hasOwnProperty("icons") ? cachedState["icons"] : []
29− property string accessibleName
29+ property string accessibleName: cachedState && cachedState.hasOwnProperty("accessible-desc") ? cachedState["accessible-desc"] : ""
30− property bool visible: true
30+ property bool indicatorVisible: cachedState && cachedState.hasOwnProperty("visible") ? cachedState["visible"] : true
31+
32+ property var cachedState: menu ? menu.get(0, "actionState") : undefined
33+ Connections {
34+ target: menu
35+ onModelDataChanged: {
36+ cachedState = menu.get(0, "actionState");
37+ }
38+ }
31
39
32 signal updated
40 signal updated
33
41
@@ -37,5 +45,5 @@ QtObject {
37 onRightLabelChanged: updated()
45 onRightLabelChanged: updated()
38 onIconsChanged: updated()
46 onIconsChanged: updated()
39 onAccessibleNameChanged: updated()
47 onAccessibleNameChanged: updated()
40− onVisibleChanged: updated()
48+ onIndicatorVisibleChanged: updated()
41}
49}
tests/mocks/Unity/Indicators/fakeindicatorsmodel.cpp+50−21
@@ -31,7 +31,6 @@ FakeIndicatorsModel::FakeIndicatorsModel(QObject *parent)
31/*! \internal */
31/*! \internal */
32FakeIndicatorsModel::~FakeIndicatorsModel()
32FakeIndicatorsModel::~FakeIndicatorsModel()
33{
33{
34− qDeleteAll(m_indicators);
35}
34}
36
35
37int FakeIndicatorsModel::count() const
36int FakeIndicatorsModel::count() const
@@ -47,31 +46,63 @@ void FakeIndicatorsModel::unload()
47{
46{
48 beginResetModel();
47 beginResetModel();
49
48
50− qDeleteAll(m_indicators);
49+ m_modelData.clear();
51− m_indicators.clear();
50+ Q_EMIT modelDataChanged();
52
51
53 endResetModel();
52 endResetModel();
54}
53}
55
54
56
55
57−void FakeIndicatorsModel::append(const QVariantMap& row)
56+void FakeIndicatorsModel::append(const QVariantMap& data)
58{
57{
59− Indicator* new_row = new QHash<int, QVariant>();
58+ QList<QVariant> allData = m_modelData.toList();
60− for (auto iter = row.begin(); iter != row.end(); ++iter )
59+ beginInsertRows(QModelIndex(), allData.count(), allData.count());
61− {
60+
62− int key = roleNames().key(iter.key().toUtf8(), -1);
61+ allData.append(data);
63− if (key != -1) {
62+ m_modelData = allData;
64− new_row->insert(key, iter.value());
63+ Q_EMIT modelDataChanged();
65− }
66− }
67
64
68− beginInsertRows(QModelIndex(), m_indicators.count(), m_indicators.count());
65+ endInsertRows();
66+}
69
67
70− m_indicators.append(new_row);
68+void FakeIndicatorsModel::insert(int row, const QVariantMap& data)
69+{
70+ QList<QVariant> allData = m_modelData.toList();
71+ row = qMax(0, qMin(row, allData.count()));
72+
73+ beginInsertRows(QModelIndex(), row, row);
74+
75+ allData.insert(row, data);
76+ m_modelData = allData;
77+ Q_EMIT modelDataChanged();
71
78
72 endInsertRows();
79 endInsertRows();
73}
80}
74
81
82+void FakeIndicatorsModel::remove(int row)
83+{
84+ QList<QVariant> allData = m_modelData.toList();
85+ row = qMax(0, qMin(row, allData.count()));
86+
87+ beginRemoveRows(QModelIndex(), row, row);
88+
89+ allData.removeAt(row);
90+ m_modelData = allData;
91+ Q_EMIT modelDataChanged();
92+
93+ endRemoveRows();
94+}
95+
96+void FakeIndicatorsModel::setModelData(const QVariant& modelData)
97+{
98+ beginResetModel();
99+
100+ m_modelData = modelData;
101+ Q_EMIT modelDataChanged();
102+
103+ endResetModel();
104+}
105+
75QHash<int, QByteArray> FakeIndicatorsModel::roleNames() const
106QHash<int, QByteArray> FakeIndicatorsModel::roleNames() const
76{
107{
77 static QHash<int, QByteArray> roles;
108 static QHash<int, QByteArray> roles;
@@ -79,8 +110,6 @@ QHash<int, QByteArray> FakeIndicatorsModel::roleNames() const
79 {
110 {
80 roles[IndicatorsModelRole::Identifier] = "identifier";
111 roles[IndicatorsModelRole::Identifier] = "identifier";
81 roles[IndicatorsModelRole::Position] = "position";
112 roles[IndicatorsModelRole::Position] = "position";
82− roles[IndicatorsModelRole::WidgetSource] = "widgetSource";
83− roles[IndicatorsModelRole::PageSource] = "pageSource";
84 roles[IndicatorsModelRole::IndicatorProperties] = "indicatorProperties";
113 roles[IndicatorsModelRole::IndicatorProperties] = "indicatorProperties";
85 }
114 }
86 return roles;
115 return roles;
@@ -91,18 +120,18 @@ int FakeIndicatorsModel::columnCount(const QModelIndex &) const
91 return 1;
120 return 1;
92}
121}
93
122
94−Q_INVOKABLE QVariant FakeIndicatorsModel::data(int row, int role) const
123+QVariant FakeIndicatorsModel::data(int row, int role) const
95{
124{
96 return data(index(row, 0), role);
125 return data(index(row, 0), role);
97}
126}
98
127
99QVariant FakeIndicatorsModel::data(const QModelIndex &index, int role) const
128QVariant FakeIndicatorsModel::data(const QModelIndex &index, int role) const
100{
129{
130+ QList<QVariant> dataList = m_modelData.toList();
101− if (!index.isValid() || index.row() >= m_indicators.size())
131+ if (!index.isValid() || index.row() >= dataList.size())
102 return QVariant();
132 return QVariant();
103
133
104− Indicator* indicator = m_indicators[index.row()];
134+ return dataList[index.row()].toMap()[roleNames()[role]];
105− return indicator->value(role, QVariant());
106}
135}
107
136
108QModelIndex FakeIndicatorsModel::parent(const QModelIndex&) const
137QModelIndex FakeIndicatorsModel::parent(const QModelIndex&) const
@@ -112,5 +141,5 @@ QModelIndex FakeIndicatorsModel::parent(const QModelIndex&) const
112
141
113int FakeIndicatorsModel::rowCount(const QModelIndex&) const
142int FakeIndicatorsModel::rowCount(const QModelIndex&) const
114{
143{
115− return m_indicators.count();
144+ return m_modelData.toList().count();
116}
145}
tests/mocks/Unity/Indicators/fakeindicatorsmodel.h+8−2
@@ -27,6 +27,7 @@ class FakeIndicatorsModel : public QAbstractListModel
27 Q_OBJECT
27 Q_OBJECT
28 Q_ENUMS(Roles)
28 Q_ENUMS(Roles)
29 Q_PROPERTY(int count READ count NOTIFY countChanged)
29 Q_PROPERTY(int count READ count NOTIFY countChanged)
30+ Q_PROPERTY(QVariant modelData READ modelData WRITE setModelData NOTIFY modelDataChanged)
30public:
31public:
31
32
32 FakeIndicatorsModel(QObject *parent=0);
33 FakeIndicatorsModel(QObject *parent=0);
@@ -36,9 +37,14 @@ public:
36 Q_INVOKABLE void unload();
37 Q_INVOKABLE void unload();
37
38
38 Q_INVOKABLE void append(const QVariantMap& row);
39 Q_INVOKABLE void append(const QVariantMap& row);
40+ Q_INVOKABLE void insert(int row, const QVariantMap& data);
41+ Q_INVOKABLE void remove(int row);
39
42
40 Q_INVOKABLE QVariant data(int row, int role) const;
43 Q_INVOKABLE QVariant data(int row, int role) const;
41
44
45+ void setModelData(const QVariant& data);
46+ QVariant modelData() const { return m_modelData; }
47+
42 /* QAbstractItemModel */
48 /* QAbstractItemModel */
43 QHash<int, QByteArray> roleNames() const;
49 QHash<int, QByteArray> roleNames() const;
44 int columnCount(const QModelIndex &parent = QModelIndex()) const;
50 int columnCount(const QModelIndex &parent = QModelIndex()) const;
@@ -48,12 +54,12 @@ public:
48
54
49Q_SIGNALS:
55Q_SIGNALS:
50 void countChanged();
56 void countChanged();
57+ void modelDataChanged();
51
58
52private:
59private:
53 int count() const;
60 int count() const;
54
61
55− typedef QHash<int, QVariant> Indicator;
62+ QVariant m_modelData;
56− QList<Indicator*> m_indicators;
57};
63};
58
64
59#endif // FAKE_INDICATORSMODEL_H
65#endif // FAKE_INDICATORSMODEL_H
tests/mocks/Unity/Indicators/fakeplugin.cpp+8−4
@@ -26,19 +26,22 @@
26#include "fakeindicatorsmodel.h"
26#include "fakeindicatorsmodel.h"
27#include "indicators.h"
27#include "indicators.h"
28#include "menucontentactivator.h"
28#include "menucontentactivator.h"
29−#include "unitymenumodelcache.h"
29+#include "sharedunitymenumodel.h"
30+#include "fakeunitymenumodelcache.h"
30#include "unitymenumodelstack.h"
31#include "unitymenumodelstack.h"
31#include "visibleindicatorsmodel.h"
32#include "visibleindicatorsmodel.h"
32
33
34+#include <unitymenumodel.h>
35+
33static QObject* menuModelCacheSingleton(QQmlEngine* engine, QJSEngine* scriptEngine) {
36static QObject* menuModelCacheSingleton(QQmlEngine* engine, QJSEngine* scriptEngine) {
34 Q_UNUSED(engine);
37 Q_UNUSED(engine);
35 Q_UNUSED(scriptEngine);
38 Q_UNUSED(scriptEngine);
36− return new UnityMenuModelCache;
39+ return FakeUnityMenuModelCache::singleton();
37}
40}
38
41
39void IndicatorsFakePlugin::registerTypes(const char * uri)
42void IndicatorsFakePlugin::registerTypes(const char * uri)
40{
43{
41− Q_INIT_RESOURCE(indicators_fake);
44+ qRegisterMetaType<UnityMenuModel*>("UnityMenuModel*");
42
45
43 // internal
46 // internal
44 qmlRegisterType<FakeIndicatorsModel>(uri, 0, 1, "FakeIndicatorsModel");
47 qmlRegisterType<FakeIndicatorsModel>(uri, 0, 1, "FakeIndicatorsModel");
@@ -47,8 +50,9 @@ void IndicatorsFakePlugin::registerTypes(const char * uri)
47 qmlRegisterType<MenuContentActivator>(uri, 0, 1, "MenuContentActivator");
50 qmlRegisterType<MenuContentActivator>(uri, 0, 1, "MenuContentActivator");
48 qmlRegisterType<UnityMenuModelStack>(uri, 0, 1, "UnityMenuModelStack");
51 qmlRegisterType<UnityMenuModelStack>(uri, 0, 1, "UnityMenuModelStack");
49 qmlRegisterType<VisibleIndicatorsModel>(uri, 0, 1, "VisibleIndicatorsModel");
52 qmlRegisterType<VisibleIndicatorsModel>(uri, 0, 1, "VisibleIndicatorsModel");
53+ qmlRegisterType<SharedUnityMenuModel>(uri, 0, 1, "SharedUnityMenuModel");
50
54
51− qmlRegisterSingletonType<UnityMenuModelCache>(uri, 0, 1, "UnityMenuModelCache", menuModelCacheSingleton);
55+ qmlRegisterSingletonType<FakeUnityMenuModelCache>(uri, 0, 1, "UnityMenuModelCache", menuModelCacheSingleton);
52
56
53 // external uncreatables
57 // external uncreatables
54 qmlRegisterUncreatableType<MenuContentState>(uri, 0, 1, "MenuContentState", "Can't create MenuContentState class");
58 qmlRegisterUncreatableType<MenuContentState>(uri, 0, 1, "MenuContentState", "Can't create MenuContentState class");
tests/mocks/Unity/Indicators/fakeunitymenumodelcache.cppnew file+59
@@ -0,0 +1,59 @@
1+/*
2+ * Copyright 2014 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "fakeunitymenumodelcache.h"
18+#include <unitymenumodel.h>
19+
20+QPointer<FakeUnityMenuModelCache> FakeUnityMenuModelCache::theFakeCache = nullptr;
21+
22+FakeUnityMenuModelCache* FakeUnityMenuModelCache::singleton()
23+{
24+ if (theFakeCache.isNull()) {
25+ theFakeCache = new FakeUnityMenuModelCache();
26+ }
27+ return theFakeCache.data();
28+}
29+
30+FakeUnityMenuModelCache::FakeUnityMenuModelCache(QObject* parent)
31+ : UnityMenuModelCache(parent)
32+{
33+}
34+
35+QSharedPointer<UnityMenuModel> FakeUnityMenuModelCache::model(const QByteArray& path)
36+{
37+ return UnityMenuModelCache::singleton()->model(path);
38+}
39+
40+bool FakeUnityMenuModelCache::contains(const QByteArray& path)
41+{
42+ return UnityMenuModelCache::singleton()->contains(path);
43+}
44+
45+void FakeUnityMenuModelCache::setCachedModelData(const QByteArray& path,
46+ const QVariant& data)
47+{
48+ // keep a ref forever!
49+ if (!m_models.contains(path)) {
50+ m_models[path] = model(path);
51+ }
52+ m_models[path]->setModelData(data);
53+}
54+
55+QVariant FakeUnityMenuModelCache::getCachedModelData(const QByteArray& path)
56+{
57+ QSharedPointer<UnityMenuModel> model = this->model(path);
58+ return model.isNull() ? QVariant() : model->modelData();
59+}
tests/mocks/Unity/Indicators/fakeunitymenumodelcache.hnew file+45
@@ -0,0 +1,45 @@
1+/*
2+ * Copyright 2014 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef FAKEUNITYMENUMODELCACHE_H
18+#define FAKEUNITYMENUMODELCACHE_H
19+
20+#include "unitymenumodelcache.h"
21+
22+#include <QVariantMap>
23+
24+class FakeUnityMenuModelCache : public UnityMenuModelCache
25+{
26+ Q_OBJECT
27+public:
28+ FakeUnityMenuModelCache(QObject* parent = nullptr);
29+
30+ static FakeUnityMenuModelCache* singleton();
31+
32+ QSharedPointer<UnityMenuModel> model(const QByteArray& path) override;
33+ bool contains(const QByteArray& path) override;
34+
35+ Q_INVOKABLE void setCachedModelData(const QByteArray& path,
36+ const QVariant& data = QVariant());
37+
38+ Q_INVOKABLE QVariant getCachedModelData(const QByteArray& path);
39+
40+private:
41+ static QPointer<FakeUnityMenuModelCache> theFakeCache;
42+ QHash<QByteArray, QSharedPointer<UnityMenuModel>> m_models;
43+};
44+
45+#endif // FAKEUNITYMENUMODELCACHE_H
tests/mocks/Unity/Indicators/indicators_fake.qrcdeleted−14
@@ -1,14 +0,0 @@
1−<RCC>
2− <qresource prefix="/tests/indciators">
3− <file>qml/fake_menu_widget1.qml</file>
4− <file>qml/fake_menu_widget2.qml</file>
5− <file>qml/fake_menu_widget3.qml</file>
6− <file>qml/fake_menu_widget4.qml</file>
7− <file>qml/fake_menu_widget5.qml</file>
8− <file>qml/fake_menu_page1.qml</file>
9− <file>qml/fake_menu_page2.qml</file>
10− <file>qml/fake_menu_page3.qml</file>
11− <file>qml/fake_menu_page4.qml</file>
12− <file>qml/fake_menu_page5.qml</file>
13− </qresource>
14−</RCC>
tests/mocks/Unity/Indicators/qml/fake_menu_page1.qmldeleted−35
@@ -1,35 +0,0 @@
1−/*
2− * Copyright 2013 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−
17−import QtQuick 2.0
18−import Unity.Indicators 0.1 as Indicators
19−
20−Indicators.FakeMenuPage {
21− id: menu_plugin1
22−
23− Rectangle {
24− id: contents
25− color: "red"
26−
27− height: 150
28− width: 150
29−
30− anchors {
31− horizontalCenter: parent.horizontalCenter
32− verticalCenter: parent.verticalCenter
33− }
34− }
35−}
tests/mocks/Unity/Indicators/qml/fake_menu_page2.qmldeleted−35
@@ -1,35 +0,0 @@
1−/*
2− * Copyright 2013 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−
17−import QtQuick 2.0
18−import Unity.Indicators 0.1 as Indicators
19−
20−Indicators.FakeMenuPage {
21− id: menu_plugin2
22−
23− Rectangle {
24− id: contents
25− color: "blue"
26−
27− height: 150
28− width: 150
29−
30− anchors {
31− horizontalCenter: parent.horizontalCenter
32− verticalCenter: parent.verticalCenter
33− }
34− }
35−}
tests/mocks/Unity/Indicators/qml/fake_menu_page3.qmldeleted−35
@@ -1,35 +0,0 @@
1−/*
2− * Copyright 2013 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−
17−import QtQuick 2.0
18−import Unity.Indicators 0.1 as Indicators
19−
20−Indicators.FakeMenuPage {
21− id: menu_plugin3
22−
23− Rectangle {
24− id: contents
25− color: "yellow"
26−
27− height: 150
28− width: 150
29−
30− anchors {
31− horizontalCenter: parent.horizontalCenter
32− verticalCenter: parent.verticalCenter
33− }
34− }
35−}
tests/mocks/Unity/Indicators/qml/fake_menu_page4.qmldeleted−35
@@ -1,35 +0,0 @@
1−/*
2− * Copyright 2013 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−
17−import QtQuick 2.0
18−import Unity.Indicators 0.1 as Indicators
19−
20−Indicators.FakeMenuPage {
21− id: menu_plugin4
22−
23− Rectangle {
24− id: contents
25− color: "green"
26−
27− height: 150
28− width: 150
29−
30− anchors {
31− horizontalCenter: parent.horizontalCenter
32− verticalCenter: parent.verticalCenter
33− }
34− }
35−}
tests/mocks/Unity/Indicators/qml/fake_menu_page5.qmldeleted−35
@@ -1,35 +0,0 @@
1−/*
2− * Copyright 2013 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−
17−import QtQuick 2.0
18−import Unity.Indicators 0.1 as Indicators
19−
20−Indicators.FakeMenuPage {
21− id: menu_plugin5
22−
23− Rectangle {
24− id: contents
25− color: "orange"
26−
27− height: 150
28− width: 150
29−
30− anchors {
31− horizontalCenter: parent.horizontalCenter
32− verticalCenter: parent.verticalCenter
33− }
34− }
35−}
tests/mocks/Unity/Indicators/qml/fake_menu_widget1.qmldeleted−23
@@ -1,23 +0,0 @@
1−/*
2− * Copyright 2013 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−
17−import QtQuick 2.0
18−
19−Rectangle {
20− width: 40
21− height: 40
22− color : "red"
23−}
tests/mocks/Unity/Indicators/qml/fake_menu_widget2.qmldeleted−23
@@ -1,23 +0,0 @@
1−/*
2− * Copyright 2013 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−
17−import QtQuick 2.0
18−
19−Rectangle {
20− width: 40
21− height: 40
22− color : "blue"
23−}
tests/mocks/Unity/Indicators/qml/fake_menu_widget4.qmldeleted−23
@@ -1,23 +0,0 @@
1−/*
2− * Copyright 2013 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−
17−import QtQuick 2.0
18−
19−Rectangle {
20− width: 40
21− height: 40
22− color : "green"
23−}
tests/mocks/Unity/Indicators/qml/fake_menu_widget5.qmldeleted−23
@@ -1,23 +0,0 @@
1−/*
2− * Copyright 2013 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−
17−import QtQuick 2.0
18−
19−Rectangle {
20− width: 40
21− height: 40
22− color : "orange"
23−}
tests/mocks/Unity/Indicators/qmldir−1
@@ -3,5 +3,4 @@ plugin IndicatorsFakeQml
3typeinfo Indicators.qmltypes
3typeinfo Indicators.qmltypes
4
4
5IndicatorsModel 0.1 IndicatorsModel.qml
5IndicatorsModel 0.1 IndicatorsModel.qml
6−FakeMenuPage 0.1 FakeMenuPage.qml
7RootActionState 0.1 RootActionState.qml
6RootActionState 0.1 RootActionState.qml
tests/mocks/Unity/Launcher/Launcher.qmltypes+13
@@ -85,6 +85,7 @@ Module {
85 Property { name: "recent"; type: "bool"; isReadonly: true }
85 Property { name: "recent"; type: "bool"; isReadonly: true }
86 Property { name: "progress"; type: "int"; isReadonly: true }
86 Property { name: "progress"; type: "int"; isReadonly: true }
87 Property { name: "count"; type: "int"; isReadonly: true }
87 Property { name: "count"; type: "int"; isReadonly: true }
88+ Property { name: "countVisible"; type: "bool"; isReadonly: true }
88 Property { name: "focused"; type: "bool"; isReadonly: true }
89 Property { name: "focused"; type: "bool"; isReadonly: true }
89 Property {
90 Property {
90 name: "quickList"
91 name: "quickList"
@@ -92,6 +93,14 @@ Module {
92 isReadonly: true
93 isReadonly: true
93 isPointer: true
94 isPointer: true
94 }
95 }
96+ Signal {
97+ name: "nameChanged"
98+ Parameter { name: "name"; type: "string" }
99+ }
100+ Signal {
101+ name: "iconChanged"
102+ Parameter { name: "icon"; type: "string" }
103+ }
95 Signal {
104 Signal {
96 name: "pinnedChanged"
105 name: "pinnedChanged"
97 Parameter { name: "pinned"; type: "bool" }
106 Parameter { name: "pinned"; type: "bool" }
@@ -112,6 +121,10 @@ Module {
112 name: "countChanged"
121 name: "countChanged"
113 Parameter { name: "count"; type: "int" }
122 Parameter { name: "count"; type: "int" }
114 }
123 }
124+ Signal {
125+ name: "countVisibleChanged"
126+ Parameter { name: "countVisible"; type: "bool" }
127+ }
115 Signal {
128 Signal {
116 name: "focusedChanged"
129 name: "focusedChanged"
117 Parameter { name: "focused"; type: "bool" }
130 Parameter { name: "focused"; type: "bool" }
tests/mocks/Unity/Unity.qmltypes+32
@@ -30,6 +30,25 @@ Module {
30 Parameter { name: "row"; type: "int" }
30 Parameter { name: "row"; type: "int" }
31 Parameter { name: "role"; type: "int" }
31 Parameter { name: "role"; type: "int" }
32 }
32 }
33+ Method {
34+ name: "setCount"
35+ Parameter { name: "count"; type: "int" }
36+ }
37+ Method {
38+ name: "resultModel"
39+ type: "ResultsModel*"
40+ Parameter { name: "row"; type: "int" }
41+ }
42+ Method {
43+ name: "setLayout"
44+ Parameter { name: "row"; type: "int" }
45+ Parameter { name: "layout"; type: "string" }
46+ }
47+ Method {
48+ name: "setHeaderLink"
49+ Parameter { name: "row"; type: "int" }
50+ Parameter { name: "headerLink"; type: "string" }
51+ }
33 }
52 }
34 Component {
53 Component {
35 name: "PreviewModel"
54 name: "PreviewModel"
@@ -66,6 +85,10 @@ Module {
66 exports: ["Unity/FakeResultsModel 0.2", "Unity/ResultsModel 0.2"]
85 exports: ["Unity/FakeResultsModel 0.2", "Unity/ResultsModel 0.2"]
67 isCreatable: false
86 isCreatable: false
68 exportMetaObjectRevisions: [0, 0]
87 exportMetaObjectRevisions: [0, 0]
88+ Method {
89+ name: "setResultCount"
90+ Parameter { name: "result_count"; type: "int" }
91+ }
69 }
92 }
70 Component {
93 Component {
71 name: "Scope"
94 name: "Scope"
@@ -76,6 +99,15 @@ Module {
76 name: "performQuery"
99 name: "performQuery"
77 Parameter { name: "query"; type: "string" }
100 Parameter { name: "query"; type: "string" }
78 }
101 }
102+ Signal { name: "refreshed" }
103+ Method {
104+ name: "setId"
105+ Parameter { name: "id"; type: "string" }
106+ }
107+ Method {
108+ name: "setName"
109+ Parameter { name: "name"; type: "string" }
110+ }
79 Method {
111 Method {
80 name: "setSearchInProgress"
112 name: "setSearchInProgress"
81 Parameter { name: "inProg"; type: "bool" }
113 Parameter { name: "inProg"; type: "bool" }
tests/plugins/Dash/cardcreator/1.res+2
@@ -13,6 +13,8 @@ AbstractButton {
13 property bool asynchronous: true;
13 property bool asynchronous: true;
14 property bool showHeader: true;
14 property bool showHeader: true;
15 implicitWidth: childrenRect.width;
15 implicitWidth: childrenRect.width;
16+ enabled: root.template == null ? true : (root.template["non-interactive"] !== undefined ? !root.template["non-interactive"] : true);
17+
16onArtShapeBorderSourceChanged: { if (artShapeBorderSource !== undefined && artShapeLoader.item) artShapeLoader.item.borderSource = artShapeBorderSource; }
18onArtShapeBorderSourceChanged: { if (artShapeBorderSource !== undefined && artShapeLoader.item) artShapeLoader.item.borderSource = artShapeBorderSource; }
17readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
19readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
18Item {
20Item {
tests/plugins/Dash/cardcreator/2.res+2
@@ -13,6 +13,8 @@ AbstractButton {
13 property bool asynchronous: true;
13 property bool asynchronous: true;
14 property bool showHeader: true;
14 property bool showHeader: true;
15 implicitWidth: childrenRect.width;
15 implicitWidth: childrenRect.width;
16+ enabled: root.template == null ? true : (root.template["non-interactive"] !== undefined ? !root.template["non-interactive"] : true);
17+
16Loader {
18Loader {
17 id: backgroundLoader;
19 id: backgroundLoader;
18 objectName: "backgroundLoader";
20 objectName: "backgroundLoader";
tests/plugins/Dash/cardcreator/3.res+2
@@ -13,6 +13,8 @@ AbstractButton {
13 property bool asynchronous: true;
13 property bool asynchronous: true;
14 property bool showHeader: true;
14 property bool showHeader: true;
15 implicitWidth: childrenRect.width;
15 implicitWidth: childrenRect.width;
16+ enabled: root.template == null ? true : (root.template["non-interactive"] !== undefined ? !root.template["non-interactive"] : true);
17+
16onArtShapeBorderSourceChanged: { if (artShapeBorderSource !== undefined && artShapeLoader.item) artShapeLoader.item.borderSource = artShapeBorderSource; }
18onArtShapeBorderSourceChanged: { if (artShapeBorderSource !== undefined && artShapeLoader.item) artShapeLoader.item.borderSource = artShapeBorderSource; }
17readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
19readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
18Item {
20Item {
tests/plugins/Dash/cardcreator/4.res+2
@@ -13,6 +13,8 @@ AbstractButton {
13 property bool asynchronous: true;
13 property bool asynchronous: true;
14 property bool showHeader: true;
14 property bool showHeader: true;
15 implicitWidth: childrenRect.width;
15 implicitWidth: childrenRect.width;
16+ enabled: root.template == null ? true : (root.template["non-interactive"] !== undefined ? !root.template["non-interactive"] : true);
17+
16readonly property size artShapeSize: Qt.size(-1, -1);
18readonly property size artShapeSize: Qt.size(-1, -1);
17readonly property int headerHeight: row.height;
19readonly property int headerHeight: row.height;
18Row {
20Row {
tests/plugins/Dash/cardcreator/5.res+3−1
@@ -13,6 +13,8 @@ AbstractButton {
13 property bool asynchronous: true;
13 property bool asynchronous: true;
14 property bool showHeader: true;
14 property bool showHeader: true;
15 implicitWidth: childrenRect.width;
15 implicitWidth: childrenRect.width;
16+ enabled: root.template == null ? true : (root.template["non-interactive"] !== undefined ? !root.template["non-interactive"] : true);
17+
16onArtShapeBorderSourceChanged: { if (artShapeBorderSource !== undefined && artShapeLoader.item) artShapeLoader.item.borderSource = artShapeBorderSource; }
18onArtShapeBorderSourceChanged: { if (artShapeBorderSource !== undefined && artShapeLoader.item) artShapeLoader.item.borderSource = artShapeBorderSource; }
17readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
19readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
18Item {
20Item {
@@ -143,7 +145,7 @@ UbuntuShape {
143 id: touchdown;
145 id: touchdown;
144 objectName: "touchdown";
146 objectName: "touchdown";
145 anchors { fill: artShapeHolder }
147 anchors { fill: artShapeHolder }
146− visible: root.pressed;
148+ visible: root.pressed;
147 radius: "medium";
149 radius: "medium";
148 borderSource: "radius_pressed.sci"
150 borderSource: "radius_pressed.sci"
149}
151}
tests/plugins/Dash/cardcreator/6.res+2
@@ -13,6 +13,8 @@ AbstractButton {
13 property bool asynchronous: true;
13 property bool asynchronous: true;
14 property bool showHeader: true;
14 property bool showHeader: true;
15 implicitWidth: childrenRect.width;
15 implicitWidth: childrenRect.width;
16+ enabled: root.template == null ? true : (root.template["non-interactive"] !== undefined ? !root.template["non-interactive"] : true);
17+
16Loader {
18Loader {
17 id: backgroundLoader;
19 id: backgroundLoader;
18 objectName: "backgroundLoader";
20 objectName: "backgroundLoader";
tests/plugins/Dash/cardcreator/7.res+2
@@ -13,6 +13,8 @@ AbstractButton {
13 property bool asynchronous: true;
13 property bool asynchronous: true;
14 property bool showHeader: true;
14 property bool showHeader: true;
15 implicitWidth: childrenRect.width;
15 implicitWidth: childrenRect.width;
16+ enabled: root.template == null ? true : (root.template["non-interactive"] !== undefined ? !root.template["non-interactive"] : true);
17+
16Loader {
18Loader {
17 id: backgroundLoader;
19 id: backgroundLoader;
18 objectName: "backgroundLoader";
20 objectName: "backgroundLoader";
tests/plugins/Ubuntu/Gestures/CMakeLists.txt+1
@@ -45,6 +45,7 @@ endmacro(add_gesture_test)
45
45
46add_gesture_ui_test(DirectionalDragArea)
46add_gesture_ui_test(DirectionalDragArea)
47add_gesture_ui_test(PressedOutsideNotifier)
47add_gesture_ui_test(PressedOutsideNotifier)
48+add_gesture_ui_test(TouchDispatcher)
48add_gesture_ui_test(TouchGate)
49add_gesture_ui_test(TouchGate)
49add_gesture_test(Damper)
50add_gesture_test(Damper)
50add_gesture_test(AxisVelocityCalculator)
51add_gesture_test(AxisVelocityCalculator)
tests/plugins/Ubuntu/Gestures/GestureTest.cpp+70
@@ -21,6 +21,8 @@
21#include <QQuickView>
21#include <QQuickView>
22#include <QtTest>
22#include <QtTest>
23
23
24+#include <TouchRegistry.h>
25+
24GestureTest::GestureTest(const QString &qmlFilename)
26GestureTest::GestureTest(const QString &qmlFilename)
25 : QObject(), m_device(nullptr), m_view(nullptr), m_qmlFilename(qmlFilename)
27 : QObject(), m_device(nullptr), m_view(nullptr), m_qmlFilename(qmlFilename)
26{
28{
@@ -44,11 +46,79 @@ void GestureTest::init()
44 m_view->show();
46 m_view->show();
45 QVERIFY(QTest::qWaitForWindowExposed(m_view));
47 QVERIFY(QTest::qWaitForWindowExposed(m_view));
46 QVERIFY(m_view->rootObject() != 0);
48 QVERIFY(m_view->rootObject() != 0);
49+
50+ m_touchRegistry = new TouchRegistry;
51+ m_view->installEventFilter(m_touchRegistry);
52+
47 qApp->processEvents();
53 qApp->processEvents();
48}
54}
49
55
50void GestureTest::cleanup()
56void GestureTest::cleanup()
51{
57{
58+ m_view->removeEventFilter(m_touchRegistry);
59+ delete m_touchRegistry;
60+ m_touchRegistry = nullptr;
61+
52 delete m_view;
62 delete m_view;
53 m_view = nullptr;
63 m_view = nullptr;
54}
64}
65+
66+////////////////////////// TouchMemento /////////////////////////////
67+
68+TouchMemento::TouchMemento(const QTouchEvent *touchEvent)
69+ : touchPointStates(touchEvent->touchPointStates()), touchPoints(touchEvent->touchPoints())
70+{
71+
72+}
73+
74+bool TouchMemento::containsTouchWithId(int touchId) const
75+{
76+ for (int i = 0; i < touchPoints.count(); ++i) {
77+ if (touchPoints.at(i).id() == touchId) {
78+ return true;
79+ }
80+ }
81+ return false;
82+}
83+
84+////////////////////////// DummyItem /////////////////////////////
85+
86+DummyItem::DummyItem(QQuickItem *parent)
87+ : QQuickItem(parent)
88+{
89+ touchEventHandler = defaultTouchEventHandler;
90+ mousePressEventHandler = defaultMouseEventHandler;
91+ mouseMoveEventHandler = defaultMouseEventHandler;
92+ mouseReleaseEventHandler = defaultMouseEventHandler;
93+}
94+
95+void DummyItem::touchEvent(QTouchEvent *event)
96+{
97+ touchEvents.append(TouchMemento(event));
98+ touchEventHandler(event);
99+}
100+
101+void DummyItem::mousePressEvent(QMouseEvent *event)
102+{
103+ mousePressEventHandler(event);
104+}
105+
106+void DummyItem::mouseMoveEvent(QMouseEvent *event)
107+{
108+ mouseMoveEventHandler(event);
109+}
110+
111+void DummyItem::mouseReleaseEvent(QMouseEvent *event)
112+{
113+ mouseReleaseEventHandler(event);
114+}
115+
116+void DummyItem::defaultTouchEventHandler(QTouchEvent *event)
117+{
118+ event->accept();
119+}
120+
121+void DummyItem::defaultMouseEventHandler(QMouseEvent *event)
122+{
123+ event->accept();
124+}
tests/plugins/Ubuntu/Gestures/GestureTest.h+40−1
@@ -17,14 +17,52 @@
17#ifndef GESTURETEST_H
17#ifndef GESTURETEST_H
18#endif // GESTURETEST_H
18#endif // GESTURETEST_H
19
19
20−#include <QObject>
20+#include <QQuickItem>
21+#include <QTouchEvent>
21
22
22class QQuickView;
23class QQuickView;
23class QTouchDevice;
24class QTouchDevice;
24
25
26+class TouchRegistry;
27+
28+// C++ std lib
29+#include <functional>
30+
25/*
31/*
26 The common stuff among tests come here
32 The common stuff among tests come here
27 */
33 */
34+
35+class TouchMemento {
36+public:
37+ TouchMemento(const QTouchEvent *touchEvent);
38+ Qt::TouchPointStates touchPointStates;
39+ QList<QTouchEvent::TouchPoint> touchPoints;
40+
41+ bool containsTouchWithId(int touchId) const;
42+};
43+
44+class DummyItem : public QQuickItem
45+{
46+ Q_OBJECT
47+public:
48+ DummyItem(QQuickItem *parent = 0);
49+
50+ QList<TouchMemento> touchEvents;
51+ std::function<void(QTouchEvent*)> touchEventHandler;
52+ std::function<void(QMouseEvent*)> mousePressEventHandler;
53+ std::function<void(QMouseEvent*)> mouseMoveEventHandler;
54+ std::function<void(QMouseEvent*)> mouseReleaseEventHandler;
55+protected:
56+ void touchEvent(QTouchEvent *event) override;
57+
58+ void mousePressEvent(QMouseEvent *event) override;
59+ void mouseMoveEvent(QMouseEvent *event) override;
60+ void mouseReleaseEvent(QMouseEvent *event) override;
61+private:
62+ static void defaultTouchEventHandler(QTouchEvent *event);
63+ static void defaultMouseEventHandler(QMouseEvent *event);
64+};
65+
28class GestureTest : public QObject
66class GestureTest : public QObject
29{
67{
30 Q_OBJECT
68 Q_OBJECT
@@ -40,6 +78,7 @@ protected Q_SLOTS:
40protected:
78protected:
41 QTouchDevice *m_device;
79 QTouchDevice *m_device;
42 QQuickView *m_view;
80 QQuickView *m_view;
81+ TouchRegistry *m_touchRegistry;
43 QString m_qmlFilename;
82 QString m_qmlFilename;
44};
83};
45
84
tests/mocks/Unity/Indicators/qml/fake_menu_widget3.qml → tests/plugins/Ubuntu/Gestures/empty.qmlrenamed+4−5
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2013 Canonical Ltd.
2+ * Copyright (C) 2014 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -15,9 +15,8 @@
15 */
15 */
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18+import Ubuntu.Gestures 0.1
18
19
19−Rectangle {
20+Item {
20− width: 40
21− height: 40
21+ width: 720; height: 720
22− color : "yellow"
23}
22}
tests/plugins/Ubuntu/Gestures/tst_DirectionalDragArea.cpp+4−73
@@ -21,8 +21,6 @@
21#include <QPointer>
21#include <QPointer>
22#include <private/qquickmousearea_p.h>
22#include <private/qquickmousearea_p.h>
23
23
24−// C++ std lib
25−#include <functional>
26
24
27#include <DirectionalDragArea.h>
25#include <DirectionalDragArea.h>
28#include <TouchRegistry.h>
26#include <TouchRegistry.h>
@@ -31,28 +29,6 @@
31
29
32using namespace UbuntuGestures;
30using namespace UbuntuGestures;
33
31
34−class TouchMemento {
35−public:
36− TouchMemento(const QTouchEvent *touchEvent);
37− Qt::TouchPointStates touchPointStates;
38− QList<QTouchEvent::TouchPoint> touchPoints;
39−
40− bool containsTouchWithId(int touchId) const;
41−};
42−
43−class DummyItem : public QQuickItem
44−{
45− Q_OBJECT
46−public:
47− DummyItem(QQuickItem *parent = 0);
48−
49− QList<TouchMemento> touchEvents;
50− std::function<void(QTouchEvent*)> touchEventHandler;
51−protected:
52− void touchEvent(QTouchEvent *event) override;
53−private:
54− static void defaultTouchEventHandler(QTouchEvent *event);
55−};
56
32
57class ComplexFakeTimer : public FakeTimer
33class ComplexFakeTimer : public FakeTimer
58{
34{
@@ -149,7 +125,6 @@ private Q_SLOTS:
149
125
150private:
126private:
151 void passTime(qint64 timeSpanMs);
127 void passTime(qint64 timeSpanMs);
152− TouchRegistry *touchRegistry;
153 ComplexFakeTimer *fakeTimer;
128 ComplexFakeTimer *fakeTimer;
154 QSharedPointer<FakeTimeSource> fakeTimeSource;
129 QSharedPointer<FakeTimeSource> fakeTimeSource;
155};
130};
@@ -171,19 +146,12 @@ void tst_DirectionalDragArea::init()
171 QTRY_COMPARE(m_view->width(), (int)m_view->rootObject()->width());
146 QTRY_COMPARE(m_view->width(), (int)m_view->rootObject()->width());
172 QTRY_COMPARE(m_view->height(), (int)m_view->rootObject()->height());
147 QTRY_COMPARE(m_view->height(), (int)m_view->rootObject()->height());
173
148
174− touchRegistry = new TouchRegistry;
175− m_view->installEventFilter(touchRegistry);
176−
177 fakeTimeSource.reset(new FakeTimeSource);
149 fakeTimeSource.reset(new FakeTimeSource);
178 fakeTimer = new ComplexFakeTimer(fakeTimeSource);
150 fakeTimer = new ComplexFakeTimer(fakeTimeSource);
179}
151}
180
152
181void tst_DirectionalDragArea::cleanup()
153void tst_DirectionalDragArea::cleanup()
182{
154{
183− m_view->removeEventFilter(touchRegistry);
184− delete touchRegistry;
185− touchRegistry = nullptr;
186−
187 delete fakeTimer;
155 delete fakeTimer;
188 fakeTimer = 0;
156 fakeTimer = 0;
189
157
@@ -1015,7 +983,7 @@ void tst_DirectionalDragArea::givesUpWhenLosesTouch()
1015 QPoint touchPos(edgeDragArea->width()/2.0f, m_view->height()/2.0f);
983 QPoint touchPos(edgeDragArea->width()/2.0f, m_view->height()/2.0f);
1016
984
1017 dummyItem->touchEventHandler = [&](QTouchEvent *event) {
985 dummyItem->touchEventHandler = [&](QTouchEvent *event) {
1018− touchRegistry->addCandidateOwnerForTouch(0, dummyItem);
986+ m_touchRegistry->addCandidateOwnerForTouch(0, dummyItem);
1019 event->ignore();
987 event->ignore();
1020 };
988 };
1021
989
@@ -1023,7 +991,7 @@ void tst_DirectionalDragArea::givesUpWhenLosesTouch()
1023
991
1024 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
992 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
1025
993
1026− touchRegistry->requestTouchOwnership(0, dummyItem);
994+ m_touchRegistry->requestTouchOwnership(0, dummyItem);
1027
995
1028 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
996 QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::WaitingForTouch);
1029
997
@@ -1172,7 +1140,7 @@ void tst_DirectionalDragArea::withdrawTouchOwnershipCandidacyIfDisabledDuringRec
1172
1140
1173 // edgeDragArea should be an undecided candidate
1141 // edgeDragArea should be an undecided candidate
1174 {
1142 {
1175− auto touchInfo = touchRegistry->findTouchInfo(0);
1143+ auto touchInfo = m_touchRegistry->findTouchInfo(0);
1176 QCOMPARE(touchInfo->candidates.size(), 1);
1144 QCOMPARE(touchInfo->candidates.size(), 1);
1177 QCOMPARE(touchInfo->candidates.at(0).item.data(), edgeDragArea);
1145 QCOMPARE(touchInfo->candidates.at(0).item.data(), edgeDragArea);
1178 QCOMPARE(touchInfo->candidates.at(0).undecided, true);
1146 QCOMPARE(touchInfo->candidates.at(0).undecided, true);
@@ -1188,7 +1156,7 @@ void tst_DirectionalDragArea::withdrawTouchOwnershipCandidacyIfDisabledDuringRec
1188
1156
1189 // edgeDragArea should no longer be a candidate
1157 // edgeDragArea should no longer be a candidate
1190 {
1158 {
1191− auto touchInfo = touchRegistry->findTouchInfo(0);
1159+ auto touchInfo = m_touchRegistry->findTouchInfo(0);
1192 QCOMPARE(touchInfo->candidates.size(), 0);
1160 QCOMPARE(touchInfo->candidates.size(), 0);
1193 }
1161 }
1194
1162
@@ -1289,43 +1257,6 @@ void tst_DirectionalDragArea::gettingTouchOwnershipMakesMouseAreaBehindGetCancel
1289 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
1257 QTest::touchEvent(m_view, m_device).release(0, touchPoint.toPoint());
1290}
1258}
1291
1259
1292−////////////////////////// TouchMemento /////////////////////////////
1293−
1294−TouchMemento::TouchMemento(const QTouchEvent *touchEvent)
1295− : touchPointStates(touchEvent->touchPointStates()), touchPoints(touchEvent->touchPoints())
1296−{
1297−
1298−}
1299−
1300−bool TouchMemento::containsTouchWithId(int touchId) const
1301−{
1302− for (int i = 0; i < touchPoints.count(); ++i) {
1303− if (touchPoints.at(i).id() == touchId) {
1304− return true;
1305− }
1306− }
1307− return false;
1308−}
1309−
1310−////////////////////////// DummyItem /////////////////////////////
1311−
1312−DummyItem::DummyItem(QQuickItem *parent)
1313− : QQuickItem(parent)
1314−{
1315− touchEventHandler = defaultTouchEventHandler;
1316−}
1317−
1318−void DummyItem::touchEvent(QTouchEvent *event)
1319−{
1320− touchEvents.append(TouchMemento(event));
1321− touchEventHandler(event);
1322−}
1323−
1324−void DummyItem::defaultTouchEventHandler(QTouchEvent *event)
1325−{
1326− event->accept();
1327−}
1328−
1329QTEST_MAIN(tst_DirectionalDragArea)
1260QTEST_MAIN(tst_DirectionalDragArea)
1330
1261
1331#include "tst_DirectionalDragArea.moc"
1262#include "tst_DirectionalDragArea.moc"
tests/plugins/Ubuntu/Gestures/tst_TouchDispatcher.cppnew file+90
@@ -0,0 +1,90 @@
1+/*
2+ * Copyright (C) 2014 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+
17+#include <QtTest>
18+#include <QQuickView>
19+
20+#include "GestureTest.h"
21+
22+#include <TouchDispatcher.h>
23+
24+class tst_TouchDispatcher : public GestureTest
25+{
26+ Q_OBJECT
27+public:
28+ tst_TouchDispatcher();
29+private Q_SLOTS:
30+ void sendMouseEventIfTouchIgnored_data();
31+ void sendMouseEventIfTouchIgnored();
32+};
33+
34+tst_TouchDispatcher::tst_TouchDispatcher()
35+ : GestureTest(QStringLiteral("empty.qml"))
36+{
37+}
38+
39+void tst_TouchDispatcher::sendMouseEventIfTouchIgnored_data()
40+{
41+ QTest::addColumn<bool>("itemAcceptsTouch");
42+ QTest::addColumn<int>("acceptedMouseButtons");
43+ QTest::addColumn<bool>("shouldGetMousePress");
44+
45+ QTest::newRow("accepts touch and doesn't accept mouse buttons") << true << (int)Qt::NoButton << false;
46+ QTest::newRow("ignores touch and accepts mouse buttons") << false << (int)Qt::LeftButton << true;
47+ QTest::newRow("ignores touch and doesn't accept mouse buttons") << false << (int)Qt::NoButton << false;
48+}
49+
50+void tst_TouchDispatcher::sendMouseEventIfTouchIgnored()
51+{
52+ QFETCH(bool, itemAcceptsTouch);
53+ QFETCH(int, acceptedMouseButtons);
54+ QFETCH(bool, shouldGetMousePress);
55+ DummyItem *dummyItem = new DummyItem(m_view->rootObject());
56+
57+ TouchDispatcher touchDispatcher;
58+ touchDispatcher.setTargetItem(dummyItem);
59+
60+ QList<QTouchEvent::TouchPoint> touchPoints;
61+ QTouchEvent::TouchPoint touchPoint;
62+ touchPoint.setId(0);
63+ touchPoint.setState(Qt::TouchPointPressed);
64+ touchPoints.append(touchPoint);
65+
66+ bool gotTouchEvent = false;
67+ bool gotMousePressEvent = false;
68+
69+ dummyItem->setAcceptedMouseButtons((Qt::MouseButtons)acceptedMouseButtons);
70+
71+ dummyItem->touchEventHandler = [&](QTouchEvent *event) {
72+ gotTouchEvent = true;
73+ event->setAccepted(itemAcceptsTouch);
74+ };
75+
76+ dummyItem->mousePressEventHandler = [&](QMouseEvent *event) {
77+ gotMousePressEvent = true;
78+ event->accept();
79+ };
80+
81+ touchDispatcher.dispatch(QEvent::TouchBegin, m_device, Qt::NoModifier, touchPoints, m_view,
82+ 12345 /* timestamp */);
83+
84+ QVERIFY(gotTouchEvent);
85+ QCOMPARE(gotMousePressEvent, shouldGetMousePress);
86+}
87+
88+QTEST_MAIN(tst_TouchDispatcher)
89+
90+#include "tst_TouchDispatcher.moc"
tests/plugins/Unity/Indicators/CMakeLists.txt+1
@@ -40,3 +40,4 @@ indicator_test(indicatorsmodeltest ADDITIONAL_LIBS IndicatorsQml)
40indicator_test(menucontentactivatortest ADDITIONAL_CPPS ${INDICATORS_DIR}/menucontentactivator.cpp)
40indicator_test(menucontentactivatortest ADDITIONAL_CPPS ${INDICATORS_DIR}/menucontentactivator.cpp)
41indicator_test(unitymenumodelstacktest ADDITIONAL_CPPS ${TEST_DIR}/mocks/QMenuModel/unitymenumodel.cpp ${INDICATORS_DIR}/unitymenumodelstack.cpp)
41indicator_test(unitymenumodelstacktest ADDITIONAL_CPPS ${TEST_DIR}/mocks/QMenuModel/unitymenumodel.cpp ${INDICATORS_DIR}/unitymenumodelstack.cpp)
42indicator_test(rootactionstatetest ADDITIONAL_LIBS IndicatorsQml)
42indicator_test(rootactionstatetest ADDITIONAL_LIBS IndicatorsQml)
43+indicator_test(sharedunitymenumodeltest ADDITIONAL_LIBS IndicatorsQml)
tests/plugins/Unity/Indicators/sharedunitymenumodeltest.cppnew file+102
@@ -0,0 +1,102 @@
1+/*
2+ * Copyright 2014 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ */
17+
18+#include "sharedunitymenumodel.h"
19+#include "unitymenumodelcache.h"
20+
21+#include <QtTest>
22+#include <unitymenumodel.h>
23+
24+class SharedUnityMenuModelTest : public QObject
25+{
26+ Q_OBJECT
27+
28+ SharedUnityMenuModel* createFullModel(const QByteArray& testId)
29+ {
30+ SharedUnityMenuModel* model = new SharedUnityMenuModel;
31+ model->setBusName("com.canonical." + testId);
32+ model->setMenuObjectPath("/com/canonical/" + testId);
33+ QVariantMap actions;
34+ actions["test"] = QString("/com/canonical/%1/actions").arg(QString(testId));
35+ model->setActions(actions);
36+
37+ return model;
38+ }
39+
40+private Q_SLOTS:
41+
42+ void testCreateModel()
43+ {
44+ QSharedPointer<SharedUnityMenuModel> model(createFullModel("test1"));
45+ QVERIFY(model->model() != nullptr);
46+ }
47+
48+ void testDifferentDataCreatesDifferentModels()
49+ {
50+ QSharedPointer<SharedUnityMenuModel> model1(createFullModel("test1"));
51+ QSharedPointer<SharedUnityMenuModel> model2(createFullModel("test2"));
52+
53+ QVERIFY(model1->model() != model2->model());
54+ }
55+
56+ void testSameDataCreatesSameModels()
57+ {
58+ QSharedPointer<SharedUnityMenuModel> model1(createFullModel("test1"));
59+ QSharedPointer<SharedUnityMenuModel> model2(createFullModel("test1"));
60+
61+ QCOMPARE(model1->model(), model2->model());
62+ }
63+
64+ void testSharedOwnership()
65+ {
66+ QSharedPointer<SharedUnityMenuModel> model1(createFullModel("test1"));
67+ QSharedPointer<SharedUnityMenuModel> model2(createFullModel("test1"));
68+
69+ QCOMPARE(UnityMenuModelCache::singleton()->contains("/com/canonical/test1"), true);
70+ model1.clear();
71+ QCOMPARE(UnityMenuModelCache::singleton()->contains("/com/canonical/test1"), true);
72+ model2.clear();
73+ QCOMPARE(UnityMenuModelCache::singleton()->contains("/com/canonical/test1"), false);
74+ }
75+
76+ // Tests that changing cached model data does not change the model path of others
77+ void testLP1328646()
78+ {
79+ QSharedPointer<SharedUnityMenuModel> model1(createFullModel("test1"));
80+ QSharedPointer<SharedUnityMenuModel> model2(createFullModel("test1"));
81+
82+ model2->setMenuObjectPath("/com/canonical/LP1328646");
83+
84+ QVERIFY(model1->model() != model2->model());
85+ QCOMPARE(model1->model()->menuObjectPath(), QByteArray("/com/canonical/test1"));
86+ QCOMPARE(model2->model()->menuObjectPath(), QByteArray("/com/canonical/LP1328646"));
87+ }
88+
89+ // Tests that the cache is recreated if deleted.
90+ void testDeletedCache()
91+ {
92+ QSharedPointer<SharedUnityMenuModel> model1(createFullModel("test1"));
93+
94+ QCOMPARE(UnityMenuModelCache::singleton()->contains("/com/canonical/test1"), true);
95+ delete UnityMenuModelCache::singleton();
96+ QCOMPARE(UnityMenuModelCache::singleton()->contains("/com/canonical/test1"), false);
97+ }
98+
99+};
100+
101+QTEST_GUILESS_MAIN(SharedUnityMenuModelTest)
102+#include "sharedunitymenumodeltest.moc"
tests/plugins/Unity/Launcher/CMakeLists.txt+5−1
@@ -7,6 +7,7 @@ include_directories(
7 ${CMAKE_SOURCE_DIR}/plugins/AccountsService
7 ${CMAKE_SOURCE_DIR}/plugins/AccountsService
8 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher
8 ${CMAKE_SOURCE_DIR}/plugins/Unity/Launcher
9 ${GSETTINGS_QT_INCLUDE_DIRS}
9 ${GSETTINGS_QT_INCLUDE_DIRS}
10+ ${libunity8-private_SOURCE_DIR}
10 )
11 )
11
12
12add_definitions(-DSM_BUSNAME=sessionBus)
13add_definitions(-DSM_BUSNAME=sessionBus)
@@ -34,7 +35,10 @@ add_executable(launchermodeltestExec
34 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
35 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
35 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
36 ${LAUNCHER_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
36 )
37 )
37−target_link_libraries(launchermodeltestExec ${GSETTINGS_QT_LDFLAGS})
38+target_link_libraries(launchermodeltestExec
39+ unity8-private
40+ ${GSETTINGS_QT_LDFLAGS}
41+ )
38qt5_use_modules(launchermodeltestExec Test Core DBus Xml Gui)
42qt5_use_modules(launchermodeltestExec Test Core DBus Xml Gui)
39
43
40# copy .desktop files into build directory for shadow builds
44# copy .desktop files into build directory for shadow builds
tests/plugins/Unity/Launcher/gsettings.cpp+8−4
@@ -19,8 +19,6 @@
19
19
20#include "gsettings.h"
20#include "gsettings.h"
21
21
22−#include <QDebug>
23−
24// This is a mock implementation to not touch GSettings for real during tests
22// This is a mock implementation to not touch GSettings for real during tests
25
23
26GSettings::GSettings(QObject *parent):
24GSettings::GSettings(QObject *parent):
@@ -31,10 +29,16 @@ GSettings::GSettings(QObject *parent):
31
29
32QStringList GSettings::storedApplications() const
30QStringList GSettings::storedApplications() const
33{
31{
34− return QStringList();
32+ return m_entries;
35}
33}
36
34
37void GSettings::setStoredApplications(const QStringList &storedApplications)
35void GSettings::setStoredApplications(const QStringList &storedApplications)
38{
36{
37+ m_entries = storedApplications;
38+}
39+
40+void GSettings::simulateDConfChanged(const QStringList &storedApplications)
41+{
39− Q_UNUSED(storedApplications)
42+ setStoredApplications(storedApplications);
43+ Q_EMIT changed();
40}
44}
tests/plugins/Unity/Launcher/gsettings.h+8
@@ -33,6 +33,14 @@ public:
33
33
34 QStringList storedApplications() const;
34 QStringList storedApplications() const;
35 void setStoredApplications(const QStringList &storedApplications);
35 void setStoredApplications(const QStringList &storedApplications);
36+
37+ void simulateDConfChanged(const QStringList &storedApplications);
38+
39+Q_SIGNALS:
40+ void changed();
41+
42+private:
43+ QStringList m_entries;
36};
44};
37
45
38#endif
46#endif
tests/plugins/Unity/Launcher/launchermodeltest.cpp+88−11
@@ -24,6 +24,7 @@
24#include "launcheritem.h"
24#include "launcheritem.h"
25#include "launchermodel.h"
25#include "launchermodel.h"
26#include "dbusinterface.h"
26#include "dbusinterface.h"
27+#include "gsettings.h"
27
28
28#include <QtTest>
29#include <QtTest>
29#include <QDBusInterface>
30#include <QDBusInterface>
@@ -81,7 +82,15 @@ public:
81 return nullptr;
82 return nullptr;
82 }
83 }
83 unity::shell::application::ApplicationInfoInterface *startApplication(const QString &, const QStringList &) { return nullptr; }
84 unity::shell::application::ApplicationInfoInterface *startApplication(const QString &, const QStringList &) { return nullptr; }
84− bool stopApplication(const QString &) { return false; }
85+ bool stopApplication(const QString &appId) {
86+ Q_FOREACH(MockApp* app, m_list) {
87+ if (app->appId() == appId) {
88+ removeApplication(m_list.indexOf(app));
89+ return true;
90+ }
91+ }
92+ return false;
93+ }
85 bool focusApplication(const QString &appId) {
94 bool focusApplication(const QString &appId) {
86 Q_FOREACH(MockApp* app, m_list) {
95 Q_FOREACH(MockApp* app, m_list) {
87 app->setFocused(app->appId() == appId);
96 app->setFocused(app->appId() == appId);
@@ -124,6 +133,7 @@ private Q_SLOTS:
124
133
125 void initTestCase() {
134 void initTestCase() {
126 launcherModel = new LauncherModel(this);
135 launcherModel = new LauncherModel(this);
136+ QCoreApplication::processEvents(); // to let the model register on DBus
127 QCOMPARE(launcherModel->rowCount(QModelIndex()), 0);
137 QCOMPARE(launcherModel->rowCount(QModelIndex()), 0);
128
138
129 appManager = new MockAppManager(this);
139 appManager = new MockAppManager(this);
@@ -137,6 +147,8 @@ private Q_SLOTS:
137
147
138 appManager->addApplication(new MockApp("no-icon"));
148 appManager->addApplication(new MockApp("no-icon"));
139 QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
149 QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
150+
151+ launcherModel->m_settings->setStoredApplications(QStringList());
140 }
152 }
141
153
142 // Removing apps from appmanager and launcher as pinned ones would stick
154 // Removing apps from appmanager and launcher as pinned ones would stick
@@ -172,14 +184,15 @@ private Q_SLOTS:
172 launcherModel->pin(launcherModel->get(0)->appId());
184 launcherModel->pin(launcherModel->get(0)->appId());
173 QCOMPARE(launcherModel->get(0)->pinned(), true);
185 QCOMPARE(launcherModel->get(0)->pinned(), true);
174 QCOMPARE(launcherModel->get(1)->pinned(), false);
186 QCOMPARE(launcherModel->get(1)->pinned(), false);
175− QCOMPARE(spy.count(), 1);
187+ QCOMPARE(spy.count() > 0, true);
176 QCOMPARE(spy.at(0).at(2).value<QVector<int>>().first(), (int)LauncherModelInterface::RolePinned);
188 QCOMPARE(spy.at(0).at(2).value<QVector<int>>().first(), (int)LauncherModelInterface::RolePinned);
177
189
190+ spy.clear();
178 launcherModel->requestRemove(launcherModel->get(0)->appId());
191 launcherModel->requestRemove(launcherModel->get(0)->appId());
179 QCOMPARE(launcherModel->get(0)->pinned(), false);
192 QCOMPARE(launcherModel->get(0)->pinned(), false);
180 QCOMPARE(launcherModel->get(1)->pinned(), false);
193 QCOMPARE(launcherModel->get(1)->pinned(), false);
181− QCOMPARE(spy.count(), 2);
194+ QCOMPARE(spy.count(), 1);
182− QCOMPARE(spy.at(1).at(2).value<QVector<int>>().first(), (int)LauncherModelInterface::RolePinned);
195+ QCOMPARE(spy.at(0).at(2).value<QVector<int>>().first(), (int)LauncherModelInterface::RolePinned);
183 }
196 }
184
197
185 void testRemove_data() {
198 void testRemove_data() {
@@ -188,7 +201,7 @@ private Q_SLOTS:
188 QTest::addColumn<bool>("running");
201 QTest::addColumn<bool>("running");
189
202
190 QTest::newRow("non-pinned, running") << false << true;
203 QTest::newRow("non-pinned, running") << false << true;
191− QTest::newRow("pinned, running") << true << false;
204+ QTest::newRow("pinned, running") << true << true;
192 QTest::newRow("pinned, non-running") << true << false;
205 QTest::newRow("pinned, non-running") << true << false;
193 }
206 }
194
207
@@ -201,23 +214,24 @@ private Q_SLOTS:
201
214
202 // pin one if required
215 // pin one if required
203 if (pinned) {
216 if (pinned) {
204− launcherModel->pin(launcherModel->get(1)->appId());
217+ launcherModel->pin("abs-icon");
205 }
218 }
206
219
207 // stop it if required
220 // stop it if required
208 if (!running) {
221 if (!running) {
209− appManager->removeApplication(1);
222+ appManager->stopApplication("abs-icon");
210 }
223 }
211
224
212 // Now remove it
225 // Now remove it
213− launcherModel->requestRemove(launcherModel->get(1)->appId());
226+ launcherModel->requestRemove("abs-icon");
214
227
215 if (running) {
228 if (running) {
216 // both apps are running, both apps must still be here
229 // both apps are running, both apps must still be here
217 QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
230 QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
218
231
219 // Item must be unpinned now
232 // Item must be unpinned now
233+ int index = launcherModel->findApplication("abs-icon");
220− QCOMPARE(launcherModel->get(1)->pinned(), false);
234+ QCOMPARE(launcherModel->get(index)->pinned(), false);
221
235
222 } else if (pinned) {
236 } else if (pinned) {
223 // Item 1 must go away, item 0 is here to stay
237 // Item 1 must go away, item 0 is here to stay
@@ -226,7 +240,7 @@ private Q_SLOTS:
226
240
227 // done our checks. now stop the app if was still running
241 // done our checks. now stop the app if was still running
228 if (running) {
242 if (running) {
229− appManager->removeApplication(1);
243+ appManager->stopApplication("abs-icon");
230 }
244 }
231
245
232 // It needs to go away in any case now
246 // It needs to go away in any case now
@@ -371,12 +385,75 @@ private Q_SLOTS:
371 QCOMPARE(launcherModel->get(index)->count(), 55);
385 QCOMPARE(launcherModel->get(index)->count(), 55);
372 }
386 }
373
387
374− void testRefresh() {
388+ void testRefreshAfterDeletedDesktopFiles_data() {
389+ QTest::addColumn<bool>("deleted");
390+ QTest::newRow("have .desktop files") << false;
391+ QTest::newRow("deleted .desktop files") << true;
392+ }
393+
394+ void testRefreshAfterDeletedDesktopFiles() {
395+ QFETCH(bool, deleted);
396+
397+ // pin both apps
398+ launcherModel->pin("abs-icon");
399+ launcherModel->pin("no-icon");
400+ // close both apps
401+ appManager->removeApplication(0);
402+ appManager->removeApplication(0);
403+
404+ // "delete" the .desktop files
405+ QString oldCurrent = QDir::currentPath();
406+ if (deleted) {
407+ // In testing mode, the launcher searches the current dir for the sample .desktop file
408+ // We can make that fail by changing the current dir
409+ QDir::setCurrent("..");
410+ }
411+
412+ // Call refresh
375 QDBusInterface interface("com.canonical.Unity.Launcher", "/com/canonical/Unity/Launcher", "com.canonical.Unity.Launcher");
413 QDBusInterface interface("com.canonical.Unity.Launcher", "/com/canonical/Unity/Launcher", "com.canonical.Unity.Launcher");
376 QDBusReply<void> reply = interface.call("Refresh");
414 QDBusReply<void> reply = interface.call("Refresh");
377
415
378 // Make sure the call to Refresh returned without error.
416 // Make sure the call to Refresh returned without error.
379 QCOMPARE(reply.isValid(), true);
417 QCOMPARE(reply.isValid(), true);
418+
419+ QCOMPARE(launcherModel->rowCount(), deleted ? 0 : 2);
420+
421+ // Restoring current dir
422+ QDir::setCurrent(oldCurrent);
423+ }
424+
425+ void testSettings() {
426+ GSettings *settings = launcherModel->m_settings;
427+
428+ // Nothing pinned at startup
429+ QCOMPARE(settings->storedApplications().count(), 0);
430+
431+ // pin both apps
432+ launcherModel->pin("abs-icon");
433+ launcherModel->pin("no-icon");
434+
435+ // Now settings should have 2 apps
436+ QCOMPARE(settings->storedApplications().count(), 2);
437+
438+ // close both apps
439+ appManager->removeApplication(0);
440+ appManager->removeApplication(0);
441+
442+ // Now settings should have 2 apps
443+ QCOMPARE(settings->storedApplications().count(), 2);
444+
445+ // Now remove 1 app through the backend, make sure one is still there
446+ settings->simulateDConfChanged(QStringList() << "abs-icon");
447+ QCOMPARE(settings->storedApplications().count(), 1);
448+
449+ // Check if it disappeared from the frontend too
450+ QCOMPARE(launcherModel->rowCount(), 1);
451+
452+ // Add them back but in reverse order
453+ settings->simulateDConfChanged(QStringList() << "no-icon" << "abs-icon");
454+ QCOMPARE(launcherModel->rowCount(), 2);
455+ QCOMPARE(launcherModel->get(0)->appId(), QString("no-icon"));
456+ QCOMPARE(launcherModel->get(1)->appId(), QString("abs-icon"));
380 }
457 }
381};
458};
382
459
tests/plugins/Unity/Session/CMakeLists.txt+4
@@ -1,6 +1,7 @@
1include_directories(
1include_directories(
2 ${CMAKE_CURRENT_BINARY_DIR}
2 ${CMAKE_CURRENT_BINARY_DIR}
3 ${CMAKE_SOURCE_DIR}/plugins/Unity/Session
3 ${CMAKE_SOURCE_DIR}/plugins/Unity/Session
4+ ${libunity8-private_SOURCE_DIR}
4)
5)
5
6
6add_definitions(-DSM_BUSNAME=sessionBus)
7add_definitions(-DSM_BUSNAME=sessionBus)
@@ -17,4 +18,7 @@ add_executable(sessionbackendtestExec
17 sessionbackendtest.cpp
18 sessionbackendtest.cpp
18 ${CMAKE_SOURCE_DIR}/plugins/Unity/Session/dbusunitysessionservice.cpp
19 ${CMAKE_SOURCE_DIR}/plugins/Unity/Session/dbusunitysessionservice.cpp
19)
20)
21+target_link_libraries(sessionbackendtestExec
22+ unity8-private
23+ )
20qt5_use_modules(sessionbackendtestExec Test Core Qml DBus)
24qt5_use_modules(sessionbackendtestExec Test Core Qml DBus)
tests/plugins/Unity/Session/sessionbackendtest.cpp+1
@@ -45,6 +45,7 @@ private Q_SLOTS:
45 QFETCH(QString, method);
45 QFETCH(QString, method);
46
46
47 DBusUnitySessionService dbusUnitySessionService;
47 DBusUnitySessionService dbusUnitySessionService;
48+ QCoreApplication::processEvents(); // to let the service register on DBus
48
49
49 QDBusConnection con = QDBusConnection::sessionBus();
50 QDBusConnection con = QDBusConnection::sessionBus();
50 QDBusInterface interface ("com.canonical.Unity",
51 QDBusInterface interface ("com.canonical.Unity",
tests/qmltests/CMakeLists.txt+10−11
@@ -12,16 +12,15 @@ set(qmltest_DEFAULT_IMPORT_PATHS
12set(qmltest_DEFAULT_TARGETS qmlunittests)
12set(qmltest_DEFAULT_TARGETS qmlunittests)
13set(qmltest_DEFAULT_NO_ADD_TEST FALSE)
13set(qmltest_DEFAULT_NO_ADD_TEST FALSE)
14set(qmltest_DEFAULT_PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=minimal")
14set(qmltest_DEFAULT_PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=minimal")
15−add_qml_test(Panel IndicatorItem)
16add_qml_test(utils/Unity/Test UnityTest)
15add_qml_test(utils/Unity/Test UnityTest)
17
16
18set(qmltest_DEFAULT_TARGETS qmluitests)
17set(qmltest_DEFAULT_TARGETS qmluitests)
19set(qmltest_DEFAULT_NO_ADD_TEST TRUE)
18set(qmltest_DEFAULT_NO_ADD_TEST TRUE)
20set(qmltest_DEFAULT_PROPERTIES ENVIRONMENT "LC_ALL=C")
19set(qmltest_DEFAULT_PROPERTIES ENVIRONMENT "LC_ALL=C")
21
20
22−add_qml_test(. Shell ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single")
21+add_qml_test(. Shell ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single:${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
23−add_qml_test(. ShellWithPin ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single-pin")
22+add_qml_test(. ShellWithPin ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/single-pin:${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
24−add_qml_test(. TabletShell ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/full")
23+add_qml_test(. TabletShell ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/LightDM/full:${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
25add_qml_test(Components Background)
24add_qml_test(Components Background)
26add_qml_test(Components Carousel)
25add_qml_test(Components Carousel)
27add_qml_test(Components DraggingArea)
26add_qml_test(Components DraggingArea)
@@ -72,14 +71,14 @@ add_qml_test(Launcher Launcher)
72add_qml_test(Notifications Notifications)
71add_qml_test(Notifications Notifications)
73add_qml_test(Notifications VisualSnapDecisionsQueue)
72add_qml_test(Notifications VisualSnapDecisionsQueue)
74add_qml_test(Panel ActiveCallHint)
73add_qml_test(Panel ActiveCallHint)
75−add_qml_test(Panel IndicatorRow)
74+add_qml_test(Panel IndicatorItem)
76−add_qml_test(Panel Indicators)
75+add_qml_test(Panel IndicatorItemRow ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
76+add_qml_test(Panel IndicatorPage ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
77+add_qml_test(Panel IndicatorsBar ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
78+add_qml_test(Panel IndicatorsMenu ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
77−add_qml_test(Panel MenuContent)
79+add_qml_test(Panel MenuContent ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
78−add_qml_test(Panel Panel)
80+add_qml_test(Panel Panel ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
79add_qml_test(Panel SearchIndicator)
81add_qml_test(Panel SearchIndicator)
80−add_qml_test(Panel/Indicators DefaultIndicatorWidget ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
81−add_qml_test(Panel/Indicators DefaultIndicatorPage ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
82−add_qml_test(Panel/Indicators CachedUnityMenuModel ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/tests/mocks/QMenuModel")
83# These MenuItemFactory tests need the test/mocks/ to come before plugins/
82# These MenuItemFactory tests need the test/mocks/ to come before plugins/
84add_qml_test(Panel/Indicators MenuItemFactory IMPORT_PATHS ${CMAKE_BINARY_DIR}/tests/mocks ${qmltest_DEFAULT_IMPORT_PATHS})
83add_qml_test(Panel/Indicators MenuItemFactory IMPORT_PATHS ${CMAKE_BINARY_DIR}/tests/mocks ${qmltest_DEFAULT_IMPORT_PATHS})
85add_qml_test(Panel/Indicators MessageMenuItemFactory IMPORT_PATHS ${CMAKE_BINARY_DIR}/tests/mocks ${qmltest_DEFAULT_IMPORT_PATHS})
84add_qml_test(Panel/Indicators MessageMenuItemFactory IMPORT_PATHS ${CMAKE_BINARY_DIR}/tests/mocks ${qmltest_DEFAULT_IMPORT_PATHS})
tests/qmltests/Components/tst_ZoomableImage.qml+21−21
@@ -56,23 +56,23 @@ Rectangle {
56 when: windowShown
56 when: windowShown
57
57
58 function test_loadImage() {
58 function test_loadImage() {
59− var lazyImage = findChild(zoomableImage, "lazyImage");
59+ var imageRenderer = findChild(zoomableImage, "imageRenderer");
60
60
61 zoomableImage.source = widgetData0["source"];
61 zoomableImage.source = widgetData0["source"];
62 zoomableImage.zoomable = widgetData0["zoomable"];
62 zoomableImage.zoomable = widgetData0["zoomable"];
63 waitForRendering(zoomableImage);
63 waitForRendering(zoomableImage);
64− tryCompare(zoomableImage, "imageState", "default");
64+ tryCompare(zoomableImage, "imageStatus", Image.Null);
65
65
66− signalSpy.signalName = "onStateChanged";
66+ signalSpy.signalName = "onStatusChanged";
67− signalSpy.target = lazyImage;
67+ signalSpy.target = imageRenderer;
68 signalSpy.clear();
68 signalSpy.clear();
69
69
70 zoomableImage.source = widgetData1["source"];
70 zoomableImage.source = widgetData1["source"];
71 zoomableImage.zoomable = widgetData1["zoomable"];
71 zoomableImage.zoomable = widgetData1["zoomable"];
72− waitForRendering(lazyImage);
72+ waitForRendering(imageRenderer);
73− tryCompareFunction(function() { return get_filename(lazyImage.source.toString()) === get_filename(widgetData1["source"]); }, true);
73+ tryCompareFunction(function() { return get_filename(imageRenderer.source.toString()) === get_filename(widgetData1["source"]); }, true);
74 waitForRendering(zoomableImage);
74 waitForRendering(zoomableImage);
75− tryCompare(zoomableImage, "imageState", "ready");
75+ tryCompare(zoomableImage, "imageStatus", Image.Ready);
76 compare (signalSpy.count, 1);
76 compare (signalSpy.count, 1);
77 }
77 }
78
78
@@ -84,15 +84,15 @@ Rectangle {
84
84
85 function test_mousewheel() {
85 function test_mousewheel() {
86 var image = findChild(zoomableImage, "image");
86 var image = findChild(zoomableImage, "image");
87− var lazyImage = findChild(zoomableImage, "lazyImage");
87+ var imageRenderer = findChild(zoomableImage, "imageRenderer");
88 var flickable = findChild(zoomableImage, "flickable");
88 var flickable = findChild(zoomableImage, "flickable");
89
89
90 zoomableImage.source = widgetData2["source"];
90 zoomableImage.source = widgetData2["source"];
91 zoomableImage.zoomable = true;
91 zoomableImage.zoomable = true;
92 waitForRendering(zoomableImage);
92 waitForRendering(zoomableImage);
93
93
94− tryCompare(zoomableImage, "imageState", "ready");
94+ tryCompare(zoomableImage, "imageStatus", Image.Ready);
95− tryCompareFunction(function() { return get_filename(lazyImage.source.toString()) === get_filename(widgetData2["source"]); }, true);
95+ tryCompareFunction(function() { return get_filename(imageRenderer.source.toString()) === get_filename(widgetData2["source"]); }, true);
96 waitForRendering(image);
96 waitForRendering(image);
97
97
98 // move mouse to center
98 // move mouse to center
@@ -105,16 +105,16 @@ Rectangle {
105 for (var i=0; i<10; i++) {
105 for (var i=0; i<10; i++) {
106 mouseWheel(zoomableImage, zoomableImage.width / 2, zoomableImage.height / 2, 0, 10);
106 mouseWheel(zoomableImage, zoomableImage.width / 2, zoomableImage.height / 2, 0, 10);
107 tryCompare(image, "scale", oldScale + (i + 1) * 0.1);
107 tryCompare(image, "scale", oldScale + (i + 1) * 0.1);
108− compare(flickable.contentWidth, Math.max(lazyImage.width * image.scale, flickable.width));
108+ compare(flickable.contentWidth, Math.max(imageRenderer.width * image.scale, flickable.width));
109− compare(flickable.contentHeight, Math.max(lazyImage.height * image.scale, flickable.height));
109+ compare(flickable.contentHeight, Math.max(imageRenderer.height * image.scale, flickable.height));
110 }
110 }
111
111
112 // zoom out
112 // zoom out
113 for (var i=0; i<10; i++) {
113 for (var i=0; i<10; i++) {
114 mouseWheel(zoomableImage, zoomableImage.width / 2, zoomableImage.height / 2, 0, -10);
114 mouseWheel(zoomableImage, zoomableImage.width / 2, zoomableImage.height / 2, 0, -10);
115 tryCompare(image, "scale", oldScale + 1.0 - (i + 1) * 0.1);
115 tryCompare(image, "scale", oldScale + 1.0 - (i + 1) * 0.1);
116− compare(flickable.contentWidth, Math.max(lazyImage.width * image.scale, flickable.width));
116+ compare(flickable.contentWidth, Math.max(imageRenderer.width * image.scale, flickable.width));
117− compare(flickable.contentHeight, Math.max(lazyImage.height * image.scale, flickable.height));
117+ compare(flickable.contentHeight, Math.max(imageRenderer.height * image.scale, flickable.height));
118 }
118 }
119 }
119 }
120 }
120 }
@@ -135,7 +135,7 @@ Rectangle {
135
135
136 function test_pinch(data) {
136 function test_pinch(data) {
137 var image = findChild(zoomableImage, "image");
137 var image = findChild(zoomableImage, "image");
138− var lazyImage = findChild(zoomableImage, "lazyImage");
138+ var imageRenderer = findChild(zoomableImage, "imageRenderer");
139 var flickable = findChild(zoomableImage, "flickable");
139 var flickable = findChild(zoomableImage, "flickable");
140
140
141 signalSpy.signalName = "onScaleChanged";
141 signalSpy.signalName = "onScaleChanged";
@@ -146,8 +146,8 @@ Rectangle {
146 zoomableImage.zoomable = data.zoomable;
146 zoomableImage.zoomable = data.zoomable;
147 waitForRendering(zoomableImage);
147 waitForRendering(zoomableImage);
148
148
149− tryCompare(zoomableImage, "imageState", "ready");
149+ tryCompare(zoomableImage, "imageStatus", Image.Ready);
150− tryCompareFunction(function() { return get_filename(lazyImage.source.toString()) === get_filename(data.source); }, true);
150+ tryCompareFunction(function() { return get_filename(imageRenderer.source.toString()) === get_filename(data.source); }, true);
151 waitForRendering(image);
151 waitForRendering(image);
152
152
153 var x1Start = zoomableImage.width * 2 / 6;
153 var x1Start = zoomableImage.width * 2 / 6;
@@ -179,15 +179,15 @@ Rectangle {
179 compare(newScale > oldScale, data.answer2, "scale factor didn't changed");
179 compare(newScale > oldScale, data.answer2, "scale factor didn't changed");
180 compare(signalSpy.count == 0, data.answer3, "scale signal count error");
180 compare(signalSpy.count == 0, data.answer3, "scale signal count error");
181 compare(image.scale, newScale, "scale factor error");
181 compare(image.scale, newScale, "scale factor error");
182− compare(flickable.contentWidth, Math.max(lazyImage.width * image.scale, flickable.width));
182+ compare(flickable.contentWidth, Math.max(imageRenderer.width * image.scale, flickable.width));
183− compare(flickable.contentHeight, Math.max(lazyImage.height * image.scale, flickable.height));
183+ compare(flickable.contentHeight, Math.max(imageRenderer.height * image.scale, flickable.height));
184
184
185 wait(3000); // have to delay between two consequent pinch event.
185 wait(3000); // have to delay between two consequent pinch event.
186 // pinch zoom-out
186 // pinch zoom-out
187 touchPinch(zoomableImage, x1End, y1End, x1Start, y1Start, x2End, y2End, x2Start, y2Start);
187 touchPinch(zoomableImage, x1End, y1End, x1Start, y1Start, x2End, y2End, x2Start, y2Start);
188 tryCompare(image, "scale", oldScale);
188 tryCompare(image, "scale", oldScale);
189− compare(flickable.contentWidth, Math.max(lazyImage.width * image.scale, flickable.width));
189+ compare(flickable.contentWidth, Math.max(imageRenderer.width * image.scale, flickable.width));
190− compare(flickable.contentHeight, Math.max(lazyImage.height * image.scale, flickable.height));
190+ compare(flickable.contentHeight, Math.max(imageRenderer.height * image.scale, flickable.height));
191 }
191 }
192 }
192 }
193 }
193 }
tests/qmltests/Dash/Previews/tst_PreviewImageGallery.qml+25−24
@@ -48,12 +48,13 @@ Rectangle {
48 name: "PreviewImageGalleryTest"
48 name: "PreviewImageGalleryTest"
49 when: windowShown
49 when: windowShown
50
50
51− property Item slideShow: findChild(imageGallery.rootItem, "slideShow")
51+ property Item overlay: findChild(imageGallery.rootItem, "overlay")
52− property Item slideShowCloseButton: findChild(slideShow, "slideShowCloseButton")
52+ property Item overlayCloseButton: findChild(overlay, "overlayCloseButton")
53− property Item slideShowListView: findChild(slideShow, "slideShowListView")
53+ property Item overlayListView: findChild(overlay, "overlayListView")
54
54
55 function cleanup() {
55 function cleanup() {
56+ overlay.hide();
56− slideShow.visible = false;
57+ tryCompare(overlay, "visible", false);
57 imageGallery.widgetData = sourcesModel1;
58 imageGallery.widgetData = sourcesModel1;
58 waitForRendering(imageGallery);
59 waitForRendering(imageGallery);
59 }
60 }
@@ -64,29 +65,29 @@ Rectangle {
64 compare(placeholderScreenshot.visible, true);
65 compare(placeholderScreenshot.visible, true);
65 }
66 }
66
67
67− function test_slideShowOpenClose() {
68+ function test_overlayOpenClose() {
68 var image0 = findChild(imageGallery, "previewImage0");
69 var image0 = findChild(imageGallery, "previewImage0");
69 mouseClick(image0, image0.width / 2, image0.height / 2);
70 mouseClick(image0, image0.width / 2, image0.height / 2);
70− tryCompare(slideShow, "visible", true);
71+ tryCompare(overlay, "visible", true);
71− tryCompare(slideShow, "scale", 1.0);
72+ tryCompare(overlay, "scale", 1.0);
72− tryCompare(slideShowCloseButton, "visible", true);
73+ tryCompare(overlayCloseButton, "visible", true);
73− mouseClick(slideShowCloseButton, slideShowCloseButton.width / 2, slideShowCloseButton.height / 2);
74+ mouseClick(overlayCloseButton, overlayCloseButton.width / 2, overlayCloseButton.height / 2);
74− tryCompare(slideShow, "visible", false);
75+ tryCompare(overlay, "visible", false);
75 }
76 }
76
77
77− function test_slideShowShowHideHeader() {
78+ function test_overlayShowHideHeader() {
78 var image0 = findChild(imageGallery, "previewImage0");
79 var image0 = findChild(imageGallery, "previewImage0");
79 mouseClick(image0, image0.width / 2, image0.height / 2);
80 mouseClick(image0, image0.width / 2, image0.height / 2);
80− tryCompare(slideShow, "visible", true);
81+ tryCompare(overlay, "visible", true);
81− tryCompare(slideShow, "scale", 1.0);
82+ tryCompare(overlay, "scale", 1.0);
82− tryCompare(slideShowCloseButton, "visible", true);
83+ tryCompare(overlayCloseButton, "visible", true);
83− mouseClick(slideShow, slideShow.width / 2, slideShow.height / 2);
84+ mouseClick(overlay, overlay.width / 2, overlay.height / 2);
84− tryCompare(slideShowCloseButton, "visible", false);
85+ tryCompare(overlayCloseButton, "visible", false);
85− mouseClick(slideShow, slideShow.width / 2, slideShow.height / 2);
86+ mouseClick(overlay, overlay.width / 2, overlay.height / 2);
86− tryCompare(slideShowCloseButton, "visible", true);
87+ tryCompare(overlayCloseButton, "visible", true);
87 }
88 }
88
89
89− function test_slideShowOpenCorrectImage_data() {
90+ function test_overlayOpenCorrectImage_data() {
90 return [
91 return [
91 { tag: "Image 0", index: 0 },
92 { tag: "Image 0", index: 0 },
92 { tag: "Image 1", index: 1 },
93 { tag: "Image 1", index: 1 },
@@ -94,13 +95,13 @@ Rectangle {
94 ];
95 ];
95 }
96 }
96
97
97− function test_slideShowOpenCorrectImage(data) {
98+ function test_overlayOpenCorrectImage(data) {
98 var image = findChild(imageGallery, "previewImage" + data.index);
99 var image = findChild(imageGallery, "previewImage" + data.index);
99 mouseClick(image, image.width / 2, image.height / 2);
100 mouseClick(image, image.width / 2, image.height / 2);
100− tryCompare(slideShow, "visible", true);
101+ tryCompare(overlay, "visible", true);
101− tryCompare(slideShow, "scale", 1.0);
102+ tryCompare(overlay, "scale", 1.0);
102− tryCompare(slideShowListView, "currentIndex", data.index);
103+ tryCompare(overlayListView, "currentIndex", data.index);
103− verify(image.source === slideShowListView.currentItem.source);
104+ verify(image.source === overlayListView.currentItem.source);
104 }
105 }
105 }
106 }
106}
107}
tests/qmltests/Dash/Previews/tst_PreviewZoomableImage.qml+30−8
@@ -36,26 +36,48 @@ Rectangle {
36
36
37 PreviewZoomableImage {
37 PreviewZoomableImage {
38 id: zoomableImage
38 id: zoomableImage
39− anchors.fill: parent
39+ anchors.centerIn: parent
40− widgetData: widgetData1
40+ widgetData: widgetData0
41 }
41 }
42
42
43 UT.UnityTestCase {
43 UT.UnityTestCase {
44 name: "PreviewZoomableImageTest"
44 name: "PreviewZoomableImageTest"
45 when: windowShown
45 when: windowShown
46
46
47+ property Item lazyImage: findChild(zoomableImage, "lazyImage");
48+ property Item overlay: findChild(zoomableImage.rootItem, "overlay");
49+
47− function test_loadImage() {
50+ function init() {
48− var image = findChild(zoomableImage, "image");
51+ waitForRendering(zoomableImage);
52+ waitForRendering(overlay);
53+ }
54+
55+ function cleanup() {
56+ overlay.hide();
57+ tryCompare(overlay, "visible", false);
58+ zoomableImage.widgetData = widgetData0;
59+ }
49
60
61+ function test_loadImage() {
50 zoomableImage.widgetData = widgetData0;
62 zoomableImage.widgetData = widgetData0;
51 waitForRendering(zoomableImage);
63 waitForRendering(zoomableImage);
52− waitForRendering(image);
64+ waitForRendering(lazyImage);
53− tryCompare(image, "imageState", "ready");
65+ tryCompare(lazyImage, "state", "ready");
54
66
55 zoomableImage.widgetData = widgetData1;
67 zoomableImage.widgetData = widgetData1;
56 waitForRendering(zoomableImage);
68 waitForRendering(zoomableImage);
57− waitForRendering(image);
69+ waitForRendering(lazyImage);
58− tryCompare(image, "imageState", "default");
70+ tryCompare(lazyImage, "state", "default");
71+ }
72+
73+ function test_zoomableImageOpenClose() {
74+ var overlayCloseButton = findChild(overlay, "overlayCloseButton");
75+ mouseClick(lazyImage, lazyImage.width / 2, lazyImage.height / 2);
76+ tryCompare(overlay, "visible", true);
77+ tryCompare(overlay, "scale", 1.0);
78+ tryCompare(overlayCloseButton, "visible", true);
79+ mouseClick(overlayCloseButton, overlayCloseButton.width / 2, overlayCloseButton.height / 2);
80+ tryCompare(overlay, "visible", false);
59 }
81 }
60 }
82 }
61}
83}
tests/qmltests/Dash/tst_Card.qml+13
@@ -186,6 +186,19 @@ Rectangle {
186 height: units.gu(4)
186 height: units.gu(4)
187 color: "orange"
187 color: "orange"
188 }
188 }
189+
190+ Rectangle {
191+ color: "red"
192+ anchors { left: parent.left; right: parent.right }
193+ height: units.gu(4)
194+ Label {
195+ text: "Toggle Visible"
196+ }
197+ MouseArea {
198+ anchors.fill: parent
199+ onClicked: loader.visible = !loader.visible
200+ }
201+ }
189 }
202 }
190 }
203 }
191
204
tests/qmltests/Dash/tst_Dash.qml+6−26
@@ -36,7 +36,6 @@ Item {
36 Dash {
36 Dash {
37 id: dash
37 id: dash
38 anchors.fill: parent
38 anchors.fill: parent
39− showScopeOnLoaded: "MockScope2"
40 }
39 }
41
40
42 UT.UnityTestCase {
41 UT.UnityTestCase {
@@ -57,14 +56,14 @@ Item {
57 tryCompare(dashContentList, "count", 6);
56 tryCompare(dashContentList, "count", 6);
58 tryCompare(scopes, "loaded", true);
57 tryCompare(scopes, "loaded", true);
59 tryCompareFunction(function() {
58 tryCompareFunction(function() {
60− var mockScope1Loader = findChild(dash, "MockScope1 loader");
59+ var mockScope1Loader = findChild(dash, "scopeLoader0");
61 return mockScope1Loader && mockScope1Loader.item != null; },
60 return mockScope1Loader && mockScope1Loader.item != null; },
62 true, 15000);
61 true, 15000);
63 tryCompareFunction(function() {
62 tryCompareFunction(function() {
64− var mockScope1Loader = findChild(dash, "MockScope1 loader");
63+ var mockScope1Loader = findChild(dash, "scopeLoader0");
65 return mockScope1Loader && mockScope1Loader.status === Loader.Ready; },
64 return mockScope1Loader && mockScope1Loader.status === Loader.Ready; },
66 true, 15000);
65 true, 15000);
67− waitForRendering(findChild(dash, "MockScope1 loader").item);
66+ waitForRendering(findChild(dash, "scopeLoader0").item);
68 }
67 }
69
68
70 function get_scope_data() {
69 function get_scope_data() {
@@ -80,27 +79,6 @@ Item {
80 return get_scope_data()
79 return get_scope_data()
81 }
80 }
82
81
83− function test_show_scope_on_load(data) {
84− var dashContentList = findChild(dash, "dashContentList");
85−
86− dash.showScopeOnLoaded = data.tag
87− scopes.clear();
88− tryCompare(dashContentList, "count", 0);
89− scopes.load();
90− tryCompare(scopes, "loaded", true);
91− tryCompare(dashContentList, "count", 6);
92−
93− verify(dashContentList != undefined);
94− if (data.visualIndex == -1) {
95− tryCompare(dashContentList, "currentIndex", 0);
96− expectFail(data.tag, "non favorite scopes should not be visble in the scopes model");
97− compare(dashContentList.currentItem.scopeId, data.tag); // this should fail
98− } else {
99− tryCompare(dashContentList, "currentIndex", data.visualIndex);
100− compare(dashContentList.currentItem.scopeId, data.tag);
101− }
102− }
103−
104 function test_dash_overview_show_select_same_favorite() {
82 function test_dash_overview_show_select_same_favorite() {
105 // Show the overview
83 // Show the overview
106 touchFlick(dash, dash.width / 2, dash.height - 1, dash.width / 2, dash.height - units.gu(18));
84 touchFlick(dash, dash.width / 2, dash.height - 1, dash.width / 2, dash.height - units.gu(18));
@@ -314,7 +292,9 @@ Item {
314 mouseFlick(dash, startX, startY, stopX, stopY);
292 mouseFlick(dash, startX, startY, stopX, stopY);
315 compare(dashContentList.currentIndex, 2, "Could not flick to scope id 2");
293 compare(dashContentList.currentIndex, 2, "Could not flick to scope id 2");
316 var dashCommunicatorService = findInvisibleChild(dash, "dashCommunicatorService");
294 var dashCommunicatorService = findInvisibleChild(dash, "dashCommunicatorService");
317− dashCommunicatorService.mockSetCurrentScope("clickscope", true, false);
295+ dashCommunicatorService.mockSetCurrentScope(0, true, false);
296+ tryCompare(dashContentList, "currentIndex", 0)
297+ dashCommunicatorService.mockSetCurrentScope(1, true, false);
318 tryCompare(dashContentList, "currentIndex", 1)
298 tryCompare(dashContentList, "currentIndex", 1)
319 }
299 }
320
300
tests/qmltests/Dash/tst_DashContent.qml+8−8
@@ -73,14 +73,14 @@ Item {
73 tryCompare(scopeLoadedSpy, "count", 6, 15000);
73 tryCompare(scopeLoadedSpy, "count", 6, 15000);
74 tryCompare(scopesModel, "loaded", true);
74 tryCompare(scopesModel, "loaded", true);
75 tryCompareFunction(function() {
75 tryCompareFunction(function() {
76− var mockScope1Loader = findChild(shell, "MockScope1 loader");
76+ var mockScope1Loader = findChild(shell, "scopeLoader0");
77 return mockScope1Loader && mockScope1Loader.item != null; },
77 return mockScope1Loader && mockScope1Loader.item != null; },
78 true, 15000);
78 true, 15000);
79 tryCompareFunction(function() {
79 tryCompareFunction(function() {
80− var mockScope1Loader = findChild(shell, "MockScope1 loader");
80+ var mockScope1Loader = findChild(shell, "scopeLoader0");
81 return mockScope1Loader && mockScope1Loader.status === Loader.Ready; },
81 return mockScope1Loader && mockScope1Loader.status === Loader.Ready; },
82 true, 15000);
82 true, 15000);
83− waitForRendering(findChild(shell, "MockScope1 loader").item);
83+ waitForRendering(findChild(shell, "scopeLoader0").item);
84 }
84 }
85
85
86 function init() {
86 function init() {
@@ -131,7 +131,7 @@ Item {
131 function test_show_header_on_list_movement() {
131 function test_show_header_on_list_movement() {
132 var dashContentList = findChild(dashContent, "dashContentList");
132 var dashContentList = findChild(dashContent, "dashContentList");
133 verify(dashContentList !== null);
133 verify(dashContentList !== null);
134− var scope = findChild(dashContent, "MockScope1 loader");
134+ var scope = findChild(dashContent, "scopeLoader0");
135 waitForRendering(scope);
135 waitForRendering(scope);
136
136
137 var categoryListView = findChild(scope, "categoryListView");
137 var categoryListView = findChild(scope, "categoryListView");
@@ -149,7 +149,7 @@ Item {
149 function test_set_current_scope_reset() {
149 function test_set_current_scope_reset() {
150 var dashContentList = findChild(dashContent, "dashContentList");
150 var dashContentList = findChild(dashContent, "dashContentList");
151 verify(dashContentList, "Couldn't find dashContentList");
151 verify(dashContentList, "Couldn't find dashContentList");
152− var scope = findChild(dashContent, "MockScope1 loader");
152+ var scope = findChild(dashContent, "scopeLoader0");
153
153
154 tryCompare(scope, "status", Loader.Ready);
154 tryCompare(scope, "status", Loader.Ready);
155
155
@@ -261,7 +261,7 @@ Item {
261
261
262 function test_carouselAspectRatio() {
262 function test_carouselAspectRatio() {
263 tryCompareFunction(function() {
263 tryCompareFunction(function() {
264− var scope = findChild(dashContent, "MockScope1 loader");
264+ var scope = findChild(dashContent, "scopeLoader0");
265 if (scope != null) {
265 if (scope != null) {
266 var dashCategory1 = findChild(scope, "dashCategory1");
266 var dashCategory1 = findChild(scope, "dashCategory1");
267 if (dashCategory1 != null) {
267 if (dashCategory1 != null) {
@@ -273,7 +273,7 @@ Item {
273 },
273 },
274 true);
274 true);
275
275
276− var scope = findChild(dashContent, "MockScope1 loader");
276+ var scope = findChild(dashContent, "scopeLoader0");
277 var dashCategory1 = findChild(scope, "dashCategory1");
277 var dashCategory1 = findChild(scope, "dashCategory1");
278 var cardTool = findChild(dashCategory1, "cardTool");
278 var cardTool = findChild(dashCategory1, "cardTool");
279 var carouselLV = findChild(dashCategory1, "listView");
279 var carouselLV = findChild(dashCategory1, "listView");
@@ -501,7 +501,7 @@ Item {
501 function test_searchHint() {
501 function test_searchHint() {
502 var dashContentList = findChild(dashContent, "dashContentList");
502 var dashContentList = findChild(dashContent, "dashContentList");
503 verify(dashContentList !== null);
503 verify(dashContentList !== null);
504− var scope = findChild(dashContent, "MockScope1 loader");
504+ var scope = findChild(dashContent, "scopeLoader0");
505 waitForRendering(scope);
505 waitForRendering(scope);
506
506
507 var categoryListView = findChild(scope, "categoryListView");
507 var categoryListView = findChild(scope, "categoryListView");
tests/qmltests/Dash/tst_GenericScopeView.qml+33
@@ -603,6 +603,39 @@ Item {
603 spy.wait()
603 spy.wait()
604 compare(spy.count, 1)
604 compare(spy.count, 1)
605 }
605 }
606+
607+ function test_item_noninteractive() {
608+ waitForRendering(genericScopeView);
609+
610+ var categoryListView = findChild(genericScopeView, "categoryListView");
611+ waitForRendering(categoryListView);
612+
613+ var category0 = findChild(categoryListView, "dashCategory0");
614+ waitForRendering(category0);
615+
616+ var cardTool = findChild(category0, "cardTool");
617+ var cardGrid = category0.item;
618+
619+ cardTool.template["non-interactive"] = true;
620+ compare(cardGrid.cardTool.template["non-interactive"], true);
621+
622+ var item0 = findChild(cardGrid, "delegate0");
623+ waitForRendering(item0);
624+ item0.template = cardTool.template;
625+ compare(item0.template["non-interactive"], true);
626+ compare(item0.enabled, false);
627+ var touchdown = findChild(item0, "touchdown");
628+
629+ compare(touchdown.visible, false);
630+ mouseClick(item0, item0.width / 2, item0.height / 2);
631+ compare(touchdown.visible, false);
632+
633+ cardTool.template["non-interactive"] = false;
634+ compare(cardGrid.cardTool.template["non-interactive"], false);
635+ item0.template = cardTool.template;
636+ compare(item0.template["non-interactive"], false);
637+ compare(item0.enabled, true);
638+ }
606 }
639 }
607 }
640 }
608}
641}
tests/qmltests/Greeter/tst_Clock.qml+49−34
@@ -19,7 +19,7 @@ import QtTest 1.0
19import ".."
19import ".."
20import "../../../qml/Greeter"
20import "../../../qml/Greeter"
21import Ubuntu.Components 0.1
21import Ubuntu.Components 0.1
22−import QMenuModel 0.1
22+import Unity.Indicators 0.1 as Indicators
23import Unity.Test 0.1 as UT
23import Unity.Test 0.1 as UT
24
24
25Rectangle {
25Rectangle {
@@ -36,56 +36,71 @@ Rectangle {
36 }
36 }
37 }
37 }
38
38
39− UnityMenuModel {
39+ function updateDatetimeModelTime(label) {
40− id: menuModel
40+ Indicators.UnityMenuModelCache.setCachedModelData("/com/canonical/indicator/datetime/phone",
41− modelData: [{
41+ [{
42− "rowData": {
42+ "rowData": {
43− "actionState": { "label": Qt.formatTime(new Date("October 13, 1975 11:13:00")) }
43+ "actionState": { "label": label }
44− }
44+ }
45− }]
45+ }]);
46 }
46 }
47
47
48 UT.UnityTestCase {
48 UT.UnityTestCase {
49 name: "Clock"
49 name: "Clock"
50+ when: windowShown
50
51
51 function init() {
52 function init() {
52− var cachedModel = findChild(clock, "timeModel");
53+ updateDatetimeModelTime(Qt.formatTime(new Date("October 13, 1975 12:14:00")));
53− verify(cachedModel !== undefined);
54+ clock.visible = true;
54− cachedModel.model = menuModel;
55 }
55 }
56
56
57− function test_customDate() {
57+ // Test that the date portion of the clock updates with custom value.
58− var dateObj = new Date("October 13, 1975 11:13:00")
58+ // Time portion is controlled by indicators
59− var dateString = Qt.formatDate(dateObj, Qt.DefaultLocaleLongDate)
59+ function test_updateDate() {
60− var timeString = Qt.formatTime(dateObj)
60+ var dateLabel = findChild(clock, "dateLabel");
61+ var timeLabel = findChild(clock, "timeLabel");
61
62
62− clock.currentDate = dateObj
63+ var timeString = Qt.formatTime(new Date("October 13, 1975 12:14:00"));
63− var dateLabel = findChild(clock, "dateLabel")
64+
65+ // initial date.
66+ var dateObj = new Date("October 13, 1975 11:13:00");
67+ var dateString = Qt.formatDate(dateObj, Qt.DefaultLocaleLongDate);
68+ clock.currentDate = dateObj;
69+
64− compare(dateLabel.text, dateString, "Not the expected date")
70+ compare(dateLabel.text, dateString, "Not the expected date");
65− var timeLabel = findChild(clock, "timeLabel")
71+ compare(timeLabel.text, timeString, "Time should come from indicators");
72+
73+ // update date.
74+ var dateObj2 = new Date("October 14, 1976 13:15:00");
75+ var dateString2 = Qt.formatDate(dateObj2, Qt.DefaultLocaleLongDate);
76+ clock.currentDate = dateObj2;
77+
66− compare(timeLabel.text, timeString, "Not the expected time")
78+ compare(dateLabel.text, dateString2, "Not the expected date");
79+ compare(timeLabel.text,timeString, "Time should come from indicators");
67 }
80 }
68
81
69− function test_dateUpdate() {
82+ // Test that the date portion of the clock updates with custom value.
70− var dateObj = new Date("October 13, 1975 11:13:00")
83+ // Time portion is controlled by indicators
71− var dateString = Qt.formatDate(dateObj, Qt.DefaultLocaleLongDate)
84+ function test_updateTime() {
72− var timeString = Qt.formatTime(dateObj)
85+ var timeLabel = findChild(clock, "timeLabel");
73
86
74− clock.visible = false
87+ var timeString1 = Qt.formatTime(new Date("October 13, 1975 11:15:00"));
75− var timeModel = findInvisibleChild(clock, "timeModel")
88+ var timeString2 = Qt.formatTime(new Date("October 14, 1976 12:16:00"));
76
89
77− compare(timeModel.menuObjectPath, "", "Clock shouldn't be connected to Indicators when not visible.")
90+ updateDatetimeModelTime(timeString1);
91+ compare(timeLabel.text, timeString1, "Time should come from indicators");
78
92
79− clock.currentDate = dateObj
93+ updateDatetimeModelTime(timeString2);
94+ compare(timeLabel.text, timeString2, "Time should come from indicators");
95+ }
80
96
81− var dateLabel = findChild(clock, "dateLabel")
97+ function test_indicatorDisconnect() {
82− compare(dateLabel.text, dateString, "Not the expected date")
98+ clock.visible = false
83− var timeLabel = findChild(clock, "timeLabel")
99+ var timeModel = findInvisibleChild(clock, "timeModel")
84− compare(timeLabel.text, timeString, "Not the expected time")
100+ compare(timeModel.menuObjectPath, "", "Clock shouldn't be connected to Indicators when not visible.")
85
101
86 clock.visible = true
102 clock.visible = true
87−
88− verify(timeModel.menuObjectPath != "", "Should be connected to Indicators.")
103+ verify(timeModel.menuObjectPath !== "", "Should be connected to Indicators.")
89 }
104 }
90 }
105 }
91}
106}
tests/qmltests/Notifications/tst_Notifications.qml+172−41
@@ -53,6 +53,7 @@ Row {
53 summary: "",
53 summary: "",
54 body: "",
54 body: "",
55 icon: "",
55 icon: "",
56+ value: 0,
56 secondaryIcon: "",
57 secondaryIcon: "",
57 actions: []
58 actions: []
58 }
59 }
@@ -125,10 +126,11 @@ Row {
125 function addEphemeralIconSummaryNotification() {
126 function addEphemeralIconSummaryNotification() {
126 var n = {
127 var n = {
127 type: Notification.Ephemeral,
128 type: Notification.Ephemeral,
129+ hints: {"x-canonical-non-shaped-icon": "false"},
128 summary: "Photo upload completed",
130 summary: "Photo upload completed",
129 body: "",
131 body: "",
130− icon: "",
131− secondaryIcon: "../graphics/applicationIcons/facebook.png",
132+ icon: "../graphics/applicationIcons/facebook.png",
133+ secondaryIcon: "",
132 actions: []
134 actions: []
133 }
135 }
134
136
@@ -148,20 +150,53 @@ Row {
148 mockModel.append(n)
150 mockModel.append(n)
149 }
151 }
150
152
153+ function addConfirmationNotification() {
154+ var n = {
155+ type: Notification.Confirmation,
156+ hints: {"x-canonical-non-shaped-icon": "true"},
157+ summary: "Confirmation notification",
158+ body: "",
159+ icon: "image://theme/audio-volume-medium",
160+ secondaryIcon: "",
161+ value: 50,
162+ actions: [],
163+ }
164+
165+ mockModel.append(n)
166+ }
167+
168+ function add2ndConfirmationNotification() {
169+ var n = {
170+ type: Notification.Confirmation,
171+ hints: {"x-canonical-non-shaped-icon": "true",
172+ "x-canonical-value-bar-tint": "true"},
173+ summary: "Confirmation notification",
174+ body: "High Volume",
175+ icon: "image://theme/audio-volume-high",
176+ secondaryIcon: "",
177+ value: 85,
178+ actions: [],
179+ }
180+
181+ mockModel.append(n)
182+ }
183+
151 function clearNotifications() {
184 function clearNotifications() {
152− mockModel.clear()
185+ while(mockModel.count > 1) {
186+ remove1stNotification()
187+ }
153 }
188 }
154
189
155 function remove1stNotification() {
190 function remove1stNotification() {
156− if (mockModel.count > 0)
191+ if (mockModel.count > 1)
157− mockModel.remove(0)
192+ mockModel.remove(1)
158 }
193 }
159
194
160 Rectangle {
195 Rectangle {
161 id: notificationsRect
196 id: notificationsRect
162
197
163 width: units.gu(40)
198 width: units.gu(40)
164− height: units.gu(71)
199+ height: units.gu(115)
165
200
166 MouseArea{
201 MouseArea{
167 id: clickThroughCatcher
202 id: clickThroughCatcher
@@ -183,7 +218,7 @@ Row {
183 id: interactiveControls
218 id: interactiveControls
184
219
185 width: units.gu(30)
220 width: units.gu(30)
186− height: units.gu(81)
221+ height: units.gu(115)
187 color: "grey"
222 color: "grey"
188
223
189 Column {
224 Column {
@@ -227,6 +262,18 @@ Row {
227 onClicked: addInteractiveNotification()
262 onClicked: addInteractiveNotification()
228 }
263 }
229
264
265+ Button {
266+ width: parent.width
267+ text: "add a confirmation"
268+ onClicked: addConfirmationNotification()
269+ }
270+
271+ Button {
272+ width: parent.width
273+ text: "add a 2nd confirmation"
274+ onClicked: add2ndConfirmationNotification()
275+ }
276+
230 Button {
277 Button {
231 width: parent.width
278 width: parent.width
232 text: "remove 1st notification"
279 text: "remove 1st notification"
@@ -264,12 +311,16 @@ Row {
264 summaryVisible: true,
311 summaryVisible: true,
265 bodyVisible: true,
312 bodyVisible: true,
266 iconVisible: true,
313 iconVisible: true,
267− shapedIcon: true,
314+ centeredIconVisible: false,
268− nonShapedIcon: false,
315+ shaped: true,
316+ nonShaped: false,
269 secondaryIconVisible: true,
317 secondaryIconVisible: true,
270 buttonRowVisible: true,
318 buttonRowVisible: true,
271 buttonTinted: true,
319 buttonTinted: true,
272− hasSound: false
320+ hasSound: false,
321+ valueVisible: false,
322+ valueLabelVisible: false,
323+ valueTinted: false
273 },
324 },
274 {
325 {
275 tag: "2-over-1 Snap Decision with button-tint",
326 tag: "2-over-1 Snap Decision with button-tint",
@@ -285,31 +336,37 @@ Row {
285 summaryVisible: true,
336 summaryVisible: true,
286 bodyVisible: true,
337 bodyVisible: true,
287 iconVisible: false,
338 iconVisible: false,
288− shapedIcon: false,
339+ centeredIconVisible: false,
289− nonShapedIcon: false,
340+ shaped: false,
290 secondaryIconVisible: false,
341 secondaryIconVisible: false,
291 buttonRowVisible: false,
342 buttonRowVisible: false,
292 buttonTinted: true,
343 buttonTinted: true,
293− hasSound: false
344+ hasSound: false,
345+ valueVisible: false,
346+ valueLabelVisible: false,
347+ valueTinted: false
294 },
348 },
295 {
349 {
296 tag: "Ephemeral notification - icon-summary layout",
350 tag: "Ephemeral notification - icon-summary layout",
297 type: Notification.Ephemeral,
351 type: Notification.Ephemeral,
298− hints: {"x-canonical-private-affirmative-tint": "false"},
352+ hints: {},
299 summary: "Photo upload completed",
353 summary: "Photo upload completed",
300 body: "",
354 body: "",
301− icon: "",
302− secondaryIcon: "../graphics/applicationIcons/facebook.png",
355+ icon: "../graphics/applicationIcons/facebook.png",
356+ secondaryIcon: "",
303 actions: [],
357 actions: [],
304 summaryVisible: true,
358 summaryVisible: true,
305 bodyVisible: false,
359 bodyVisible: false,
306− iconVisible: false,
360+ iconVisible: true,
307− shapedIcon: false,
361+ centeredIconVisible: false,
308− nonShapedIcon: false,
362+ shaped: true,
309− secondaryIconVisible: true,
363+ secondaryIconVisible: false,
310 buttonRowVisible: false,
364 buttonRowVisible: false,
311 buttonTinted: false,
365 buttonTinted: false,
312− hasSound: false
366+ hasSound: false,
367+ valueVisible: false,
368+ valueLabelVisible: false,
369+ valueTinted: false
313 },
370 },
314 {
371 {
315 tag: "Ephemeral notification - check suppression of secondary icon for icon-summary layout",
372 tag: "Ephemeral notification - check suppression of secondary icon for icon-summary layout",
@@ -326,12 +383,15 @@ Row {
326 bodyVisible: false,
383 bodyVisible: false,
327 interactiveAreaEnabled: false,
384 interactiveAreaEnabled: false,
328 iconVisible: false,
385 iconVisible: false,
329− shapedIcon: false,
386+ centeredIconVisible: false,
330− nonShapedIcon: false,
387+ shaped: false,
331 secondaryIconVisible: true,
388 secondaryIconVisible: true,
332 buttonRowVisible: false,
389 buttonRowVisible: false,
333 buttonTinted: false,
390 buttonTinted: false,
334− hasSound: false
391+ hasSound: false,
392+ valueVisible: false,
393+ valueLabelVisible: false,
394+ valueTinted: false
335 },
395 },
336 {
396 {
337 tag: "Interactive notification",
397 tag: "Interactive notification",
@@ -346,12 +406,15 @@ Row {
346 summaryVisible: true,
406 summaryVisible: true,
347 bodyVisible: true,
407 bodyVisible: true,
348 iconVisible: true,
408 iconVisible: true,
349− shapedIcon: true,
409+ centeredIconVisible: false,
350− nonShapedIcon: false,
410+ shaped: true,
351 secondaryIconVisible: false,
411 secondaryIconVisible: false,
352 buttonRowVisible: false,
412 buttonRowVisible: false,
353 buttonTinted: false,
413 buttonTinted: false,
354− hasSound: true
414+ hasSound: true,
415+ valueVisible: false,
416+ valueLabelVisible: false,
417+ valueTinted: false
355 },
418 },
356 {
419 {
357 tag: "Snap Decision without secondary icon and no button-tint",
420 tag: "Snap Decision without secondary icon and no button-tint",
@@ -367,12 +430,15 @@ Row {
367 summaryVisible: true,
430 summaryVisible: true,
368 bodyVisible: true,
431 bodyVisible: true,
369 iconVisible: true,
432 iconVisible: true,
370− shapedIcon: true,
433+ centeredIconVisible: false,
371− nonShapedIcon: false,
434+ shaped: true,
372 secondaryIconVisible: false,
435 secondaryIconVisible: false,
373 buttonRowVisible: true,
436 buttonRowVisible: true,
374 buttonTinted: false,
437 buttonTinted: false,
375− hasSound: true
438+ hasSound: true,
439+ valueVisible: false,
440+ valueLabelVisible: false,
441+ valueTinted: false
376 },
442 },
377 {
443 {
378 tag: "Ephemeral notification",
444 tag: "Ephemeral notification",
@@ -387,12 +453,15 @@ Row {
387 summaryVisible: true,
453 summaryVisible: true,
388 bodyVisible: true,
454 bodyVisible: true,
389 iconVisible: true,
455 iconVisible: true,
390− shapedIcon: true,
456+ centeredIconVisible: false,
391− nonShapedIcon: false,
457+ shaped: true,
392 secondaryIconVisible: true,
458 secondaryIconVisible: true,
393 buttonRowVisible: false,
459 buttonRowVisible: false,
394 buttonTinted: false,
460 buttonTinted: false,
395− hasSound: true
461+ hasSound: true,
462+ valueVisible: false,
463+ valueLabelVisible: false,
464+ valueTinted: false
396 },
465 },
397 {
466 {
398 tag: "Ephemeral notification with non-shaped icon",
467 tag: "Ephemeral notification with non-shaped icon",
@@ -401,18 +470,68 @@ Row {
401 "x-canonical-non-shaped-icon": "true"},
470 "x-canonical-non-shaped-icon": "true"},
402 summary: "Contacts",
471 summary: "Contacts",
403 body: "Synchronised contacts-database with cloud-storage.",
472 body: "Synchronised contacts-database with cloud-storage.",
404− icon: "../graphics/applicationIcons/contacts-app.png",
473+ icon: "image://theme/contacts-app",
405 secondaryIcon: "",
474 secondaryIcon: "",
406 actions: [],
475 actions: [],
407 summaryVisible: true,
476 summaryVisible: true,
408 bodyVisible: true,
477 bodyVisible: true,
409 iconVisible: true,
478 iconVisible: true,
410− shapedIcon: false,
479+ centeredIconVisible: false,
411− nonShapedIcon: true,
480+ shaped: false,
481+ secondaryIconVisible: false,
482+ buttonRowVisible: false,
483+ buttonTinted: false,
484+ hasSound: false,
485+ valueVisible: false,
486+ valueLabelVisible: false,
487+ valueTinted: false
488+ },
489+ {
490+ tag: "Confirmation notification with value",
491+ type: Notification.Confirmation,
492+ hints: {"x-canonical-non-shaped-icon": "true"},
493+ summary: "",
494+ body: "",
495+ icon: "image://theme/audio-volume-medium",
496+ secondaryIcon: "",
497+ value: 50,
498+ actions: [],
499+ summaryVisible: false,
500+ bodyVisible: false,
501+ iconVisible: false,
502+ centeredIconVisible: true,
503+ shaped: false,
412 secondaryIconVisible: false,
504 secondaryIconVisible: false,
413 buttonRowVisible: false,
505 buttonRowVisible: false,
414 buttonTinted: false,
506 buttonTinted: false,
415− hasSound: false
507+ hasSound: false,
508+ valueVisible: true,
509+ valueLabelVisible: false,
510+ valueTinted: false
511+ },
512+ {
513+ tag: "Confirmation notification with value, label and tint",
514+ type: Notification.Confirmation,
515+ hints: {"x-canonical-non-shaped-icon": "true",
516+ "x-canonical-value-bar-tint" : "true"},
517+ summary: "",
518+ body: "High Volume",
519+ icon: "image://theme/audio-volume-high",
520+ secondaryIcon: "",
521+ value: 85,
522+ actions: [],
523+ summaryVisible: false,
524+ bodyVisible: false,
525+ iconVisible: false,
526+ centeredIconVisible: true,
527+ shaped: false,
528+ secondaryIconVisible: false,
529+ buttonRowVisible: false,
530+ buttonTinted: false,
531+ hasSound: false,
532+ valueVisible: true,
533+ valueLabelVisible: true,
534+ valueTinted: true
416 }
535 }
417 ]
536 ]
418 }
537 }
@@ -450,17 +569,29 @@ Row {
450 waitForRendering(notification);
569 waitForRendering(notification);
451
570
452 var icon = findChild(notification, "icon")
571 var icon = findChild(notification, "icon")
453− var shapedIcon = findChild(notification, "shapedIcon")
454− var nonShapedIcon = findChild(notification, "nonShapedIcon")
572+ var centeredIcon = findChild(notification, "centeredIcon")
455 var interactiveArea = findChild(notification, "interactiveArea")
573 var interactiveArea = findChild(notification, "interactiveArea")
456 var secondaryIcon = findChild(notification, "secondaryIcon")
574 var secondaryIcon = findChild(notification, "secondaryIcon")
457 var summaryLabel = findChild(notification, "summaryLabel")
575 var summaryLabel = findChild(notification, "summaryLabel")
458 var bodyLabel = findChild(notification, "bodyLabel")
576 var bodyLabel = findChild(notification, "bodyLabel")
459 var buttonRow = findChild(notification, "buttonRow")
577 var buttonRow = findChild(notification, "buttonRow")
578+ var valueIndicator = findChild(notification, "valueIndicator")
579+ var valueLabel = findChild(notification, "valueLabel")
580+ var innerBar = findChild(notification, "innerBar")
460
581
461 compare(icon.visible, data.iconVisible, "avatar-icon visibility is incorrect")
582 compare(icon.visible, data.iconVisible, "avatar-icon visibility is incorrect")
583+ if (icon.visible) {
584+ compare(icon.shaped, data.shaped, "shaped-status is incorrect")
585+ }
462− compare(shapedIcon.visible, data.shapedIcon, "shaped-icon visibility is incorrect")
586+ compare(centeredIcon.visible, data.centeredIconVisible, "centered-icon visibility is incorrect")
463− compare(nonShapedIcon.visible, data.nonShapedIcon, "non-shaped-icon visibility is incorrect")
587+ if (centeredIcon.visible) {
588+ compare(centeredIcon.shaped, data.shaped, "shaped-status is incorrect")
589+ }
590+ compare(valueIndicator.visible, data.valueVisible, "value-indicator visibility is incorrect")
591+ if (valueIndicator.visible) {
592+ verify(innerBar.color === data.valueTinted ? UbuntuColors.orange : "white", "value-bar has the wrong color-tint")
593+ }
594+ compare(valueLabel.visible, data.valueLabelVisible, "value-label visibility is incorrect")
464
595
465 // test input does not fall through
596 // test input does not fall through
466 mouseClick(notification, notification.width / 2, notification.height / 2)
597 mouseClick(notification, notification.width / 2, notification.height / 2)
tests/qmltests/Panel/IndicatorTest.qmlnew file+80
@@ -0,0 +1,80 @@
1+/*
2+ * Copyright 2014 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+
17+import QtQuick 2.1
18+import QtQuick.Layouts 1.1
19+import QtTest 1.0
20+import "../../../qml/Panel"
21+import Ubuntu.Components 0.1
22+import Unity.Test 0.1 as UT
23+import Unity.Indicators 0.1 as Indicators
24+
25+Rectangle {
26+ id: root
27+ color: "white"
28+
29+ property alias indicatorsModel: __visibleIndicatorsModel
30+ property alias originalModelData: __indicatorsModel.originalModelData
31+
32+ Indicators.VisibleIndicatorsModel {
33+ id: __visibleIndicatorsModel
34+
35+ model: __indicatorsModel
36+ }
37+
38+ Indicators.IndicatorsModel {
39+ id: __indicatorsModel
40+ Component.onCompleted: load();
41+ }
42+
43+ function insertIndicator(index) {
44+ var i;
45+ var insertIndex = 0;
46+ var done = false;
47+ for (i = index; !done && i >= 1; i--) {
48+
49+ var lookFor = __indicatorsModel.originalModelData[i-1]["identifier"]
50+
51+ var j;
52+ for (j = __indicatorsModel.modelData.length-1; !done && j >= 0; j--) {
53+ if (__indicatorsModel.modelData[j]["identifier"] === lookFor) {
54+ insertIndex = j+1;
55+ done = true;
56+ }
57+ }
58+ }
59+ __indicatorsModel.insert(insertIndex, __indicatorsModel.originalModelData[index]);
60+ }
61+
62+ function removeIndicator(index) {
63+ var i;
64+ for (i = 0; i < __indicatorsModel.modelData.length; i++) {
65+ if (__indicatorsModel.modelData[i]["identifier"] === __indicatorsModel.originalModelData[index]["identifier"]) {
66+ __indicatorsModel.remove(i);
67+ break;
68+ }
69+ }
70+ }
71+
72+ function setIndicatorVisible(index, visible) {
73+ var identifier = __indicatorsModel.originalModelData[index]["identifier"];
74+ __indicatorsModel.setIndicatorVisible(identifier, visible);
75+ }
76+
77+ function resetData() {
78+ __indicatorsModel.load();
79+ }
80+}
tests/qmltests/Panel/Indicators/tst_CachedUnityMenuModel.qmldeleted−137
@@ -1,137 +0,0 @@
1−/*
2− * Copyright 2014 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−
17−import QtQuick 2.0
18−import QtTest 1.0
19−import Unity.Test 0.1 as UT
20−import QMenuModel 0.1
21−import Unity.Indicators 0.1 as Indicators
22−import "../../../../qml/Panel/Indicators"
23−
24−Item {
25− id: root
26− width: units.gu(40)
27− height: units.gu(70)
28−
29− Component {
30− id: model
31− CachedUnityMenuModel {}
32− }
33−
34− UT.UnityTestCase {
35− name: "CachedUnityMenuModel"
36− when: windowShown
37−
38− function cleanup() {
39− doGC();
40− }
41−
42− function doGC() {
43− // need to put some wait cycles here to get gc going properly.
44− wait(10);
45− gc();
46− wait(10);
47− }
48−
49− function test_createDifferent() {
50− var cachedObject = model.createObject(null,
51− {
52− "busName": "com.canonical.test1",
53− "menuObjectPath": "/com/canonical/test1",
54− "actionsObjectPath": "/com/canonical/test1"
55− });
56−
57− var cachedObject2 = model.createObject(null,
58− {
59− "busName": "com.canonical.test2",
60− "menuObjectPath": "/com/canonical/test2",
61− "actionsObjectPath": "/com/canonical/test2"
62− });
63−
64− verify(cachedObject.model !== cachedObject2.model);
65− }
66−
67− function test_createSame() {
68− var cachedObject = model.createObject(null,
69− {
70− "busName": "com.canonical.test3",
71− "menuObjectPath": "/com/canonical/test3",
72− "actionsObjectPath": "/com/canonical/test3"
73− });
74−
75− var cachedObject2 = model.createObject(null,
76− {
77− "busName": "com.canonical.test3",
78− "menuObjectPath": "/com/canonical/test3",
79− "actionsObjectPath": "/com/canonical/test3"
80− });
81−
82− verify(cachedObject.model === cachedObject2.model);
83− }
84−
85− // Tests that changing cached model data does not change the model path of others
86− function test_lp1328646() {
87− var cachedObject = model.createObject(null,
88− {
89− "busName": "com.canonical.test4",
90− "menuObjectPath": "/com/canonical/test4",
91− "actionsObjectPath": "/com/canonical/test4"
92− });
93−
94− var cachedObject2 = model.createObject(null,
95− {
96− "busName": "com.canonical.test4",
97− "menuObjectPath": "/com/canonical/test4",
98− "actionsObjectPath": "/com/canonical/test4"
99− });
100−
101− cachedObject.menuObjectPath = "/com/canonical/test5";
102− compare(cachedObject.model.menuObjectPath, "/com/canonical/test5");
103− compare(cachedObject2.model.menuObjectPath, "/com/canonical/test4");
104−
105− verify(cachedObject.model !== cachedObject2.model);
106− }
107−
108− function createAndDestroy(test) {
109− var cachedObject = model.createObject(null,
110− {
111− "busName": "com.canonical."+test,
112− "menuObjectPath": "/com/canonical/"+test,
113− "actionsObjectPath": "/com/canonical/"+test
114− });
115− var cachedObject2 = model.createObject(null,
116− {
117− "busName": "com.canonical."+test,
118− "menuObjectPath": "/com/canonical/"+test,
119− "actionsObjectPath": "/com/canonical/"+test
120− });
121− cachedObject.destroy();
122− return cachedObject2;
123− }
124−
125− function test_destroyAllDeletesModel() {
126− createAndDestroy("test6");
127− doGC();
128− compare(Indicators.UnityMenuModelCache.contains("/com/canonical/test6"), false);
129− }
130−
131− function test_destroyPartialKeepsModel() {
132− var model = createAndDestroy("test7");
133− doGC();
134− compare(Indicators.UnityMenuModelCache.contains("/com/canonical/test7"), true);
135− }
136− }
137−}
tests/qmltests/Panel/Indicators/tst_DefaultIndicatorWidget.qmldeleted−60
@@ -1,60 +0,0 @@
1−/*
2− * Copyright 2013 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−
17−import QtQuick 2.0
18−import QtTest 1.0
19−import Unity.Test 0.1 as UT
20−import QMenuModel 0.1
21−import "../../../../qml/Panel/Indicators"
22−
23−Item {
24− id: testView
25− width: units.gu(40)
26− height: units.gu(70)
27−
28− DefaultIndicatorWidget {
29− id: widget
30−
31− anchors {
32− left: parent.left
33− top: parent.top
34− }
35−
36− menuModel: UnityMenuModel {}
37− busName: "test"
38− actionsObjectPath: "test"
39− deviceMenuObjectPath: "test"
40−
41− rootMenuType: ""
42−
43− iconSize: units.gu(3.2)
44− height: units.gu(3)
45− }
46−
47− UT.UnityTestCase {
48− name: "DefaultIndicatorWidget"
49− when: windowShown
50−
51− function init() {
52− widget.rootActionState.icons = [];
53− widget.rootActionState.leftLabel = "";
54− widget.rootActionState.rightLabel = "";
55− waitForRendering(widget)
56− }
57−
58− // FIXME: add tests
59− }
60−}
tests/qmltests/Panel/tst_ActiveCallHint.qml−1
@@ -19,7 +19,6 @@ import QtTest 1.0
19import Unity.Test 0.1 as UT
19import Unity.Test 0.1 as UT
20import Ubuntu.Telephony 0.1 as Telephony
20import Ubuntu.Telephony 0.1 as Telephony
21import Unity.Application 0.1
21import Unity.Application 0.1
22−import ".."
23import "../../../qml/Panel"
22import "../../../qml/Panel"
24
23
25Item {
24Item {
tests/qmltests/Panel/tst_IndicatorItem.qml+173−18
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2013 Canonical Ltd.
2+ * Copyright 2013-2014 Canonical Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -14,37 +14,192 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
15 */
16
16
17−import QtQuick 2.0
17+import QtQuick 2.1
18+import QtQuick.Layouts 1.1
18import QtTest 1.0
19import QtTest 1.0
19−import ".."
20−import "../../../qml/Panel"
21import Ubuntu.Components 0.1
20import Ubuntu.Components 0.1
22import Unity.Test 0.1 as UT
21import Unity.Test 0.1 as UT
22+import "../../../qml/Panel"
23
23
24Rectangle {
24Rectangle {
25− width: units.gu(10)
25+ width: units.gu(80)
26− height: units.gu(5)
26+ height: units.gu(30)
27− color: "black"
27+ color: "white"
28
28
29− IndicatorItem {
29+ RowLayout {
30− id: indicatorItem
31 anchors.fill: parent
30 anchors.fill: parent
31+ anchors.margins: units.gu(1)
32+
33+ Rectangle {
34+ id: itemArea
35+ color: "blue"
36+ Layout.fillWidth: true
37+ Layout.fillHeight: true
38+
39+ Rectangle {
40+ color: "black"
41+ anchors.fill: indicatorItem
42+ }
43+
44+ IndicatorItem {
45+ id: indicatorItem
46+ height: expanded ? units.gu(7) : units.gu(3)
47+ anchors.centerIn: parent
48+ identifier: "indicator-test"
49+
50+ rootActionState {
51+ title: titleLabel.text
52+ leftLabel : leftLabel.text
53+ rightLabel : rightLabel.text
54+ icons : {
55+ var icons = [];
56+ var i = 0;
57+ if (iconEnabled.checked) {
58+ for (i = 0; i < String(iconCount.text); i++) {
59+ icons.push("image://theme/audio-volume-high");
60+ }
61+ }
62+ return icons;
63+ }
64+ }
65+
66+ Behavior on height {
67+ NumberAnimation {
68+ id: heightAnimation
69+ duration: UbuntuAnimation.SnapDuration; easing: UbuntuAnimation.StandardEasing
70+ }
71+ }
72+ }
73+ }
74+
75+ ColumnLayout {
76+ Layout.alignment: Qt.AlignTop
77+ Layout.fillWidth: false
78+
79+ Button {
80+ id: expandButton
81+ Layout.fillWidth: true
82+ text: indicatorItem.expanded ? "Collapse" : "Expand"
83+ onClicked: indicatorItem.expanded = !indicatorItem.expanded
84+ }
85+
86+ Button {
87+ id: selectButton
88+ Layout.fillWidth: true
89+ text: indicatorItem.selected ? "Unselect" : "Select"
90+ onClicked: indicatorItem.selected = !indicatorItem.selected
91+ }
92+
93+ Rectangle {
94+ Layout.preferredHeight: units.dp(1);
95+ Layout.fillWidth: true;
96+ color: "black"
97+ }
98+
99+ RowLayout {
100+ CheckBox { id: iconEnabled; checked: true }
101+ Label { text: "icons Count:" }
102+ TextField { id: iconCount; text: "1"; enabled: iconEnabled.checked }
103+ }
104+
105+ RowLayout {
106+ Label { text: "Left Label:" }
107+ TextField { id: leftLabel; text: "Left"}
108+ }
109+
110+ RowLayout {
111+ Label { text: "Right Label:" }
112+ TextField { id: rightLabel; text: "Right"}
113+ }
114+
115+ RowLayout {
116+ Label { text: "Title:" }
117+ TextField { id: titleLabel; text: "Title"}
118+ }
119+ }
32 }
120 }
33
121
34 UT.UnityTestCase {
122 UT.UnityTestCase {
35 name: "IndicatorItem"
123 name: "IndicatorItem"
124+ when: windowShown
125+
126+ function init() {
127+ indicatorItem.selected = false;
128+ indicatorItem.expanded = false;
129+ indicatorItem.rootActionState.title = "Test Title";
130+ indicatorItem.rootActionState.leftLabel = "TestLeftLabel";
131+ indicatorItem.rootActionState.rightLabel = "TestRightLabel";
132+ indicatorItem.rootActionState.icons = [ "image://theme/audio-volume-high" ];
36
133
37− function test_dimmed() {
134+ tryCompare(heightAnimation, "running", false);
38− indicatorItem.dimmed = false;
39− tryCompareFunction(function(){return indicatorItem.opacity}, 1.0);
40− indicatorItem.dimmed = true;
41− tryCompareFunction(function(){return indicatorItem.opacity < 1.0}, true);
42 }
135 }
43
136
44− function test_empty() {
137+ function test_expand() {
45− compare(indicatorItem.indicatorVisible, false, "IndicatorItem should not be visible.");
138+ indicatorItem.expanded = true;
46− indicatorItem.widgetSource = "qrc:/tests/indciators/qml/fake_menu_widget1.qml";
139+ tryCompare(indicatorItem, "height", units.gu(7));
140+ indicatorItem.expanded = false;
141+ tryCompare(indicatorItem, "height", units.gu(3));
142+ }
143+
144+ function test_minimizedVisibility() {
145+ compare(findChild(indicatorItem, "leftLabel").opacity, 1.0);
146+ compare(findChild(indicatorItem, "rightLabel").opacity, 1.0);
147+ compare(findChild(indicatorItem, "icons").opacity, 1.0);
148+ compare(findChild(indicatorItem, "indicatorName").opacity, 0.0);
149+ }
150+
151+ function test_expandIcon() {
152+ indicatorItem.expanded = true;
153+
154+ tryCompare(findChild(indicatorItem, "leftLabel"), "opacity", 0.0);
155+ tryCompare(findChild(indicatorItem, "rightLabel"), "opacity", 0.0);
47− tryCompare(indicatorItem, "indicatorVisible", true);
156+ tryCompare(findChild(indicatorItem, "icons"), "opacity", 1.0);
157+ tryCompare(findChild(indicatorItem, "indicatorName"), "opacity", 1.0);
158+ }
159+
160+ function test_expandRightLabel() {
161+ indicatorItem.expanded = true;
162+
163+ indicatorItem.rootActionState.icons = [];
164+
165+ tryCompare(findChild(indicatorItem, "leftLabel"), "opacity", 0.0);
166+ tryCompare(findChild(indicatorItem, "rightLabel"), "opacity", 1.0);
167+ tryCompare(findChild(indicatorItem, "icons"), "opacity", 0.0);
168+ tryCompare(findChild(indicatorItem, "indicatorName"), "opacity", 1.0);
169+ }
170+
171+ function test_expandLeftLabel() {
172+ indicatorItem.expanded = true;
173+
174+ indicatorItem.rootActionState.rightLabel = "";
175+ indicatorItem.rootActionState.icons = [];
176+
177+ tryCompare(findChild(indicatorItem, "rightLabel"), "opacity", 0.0);
178+ tryCompare(findChild(indicatorItem, "leftLabel"), "opacity", 1.0);
179+ tryCompare(findChild(indicatorItem, "icons"), "opacity", 0.0);
180+ tryCompare(findChild(indicatorItem, "indicatorName"), "opacity", 1.0);
181+ }
182+
183+ function test_select() {
184+ tryCompare(findChild(indicatorItem, "icon0"), "color", "#ededed");
185+ tryCompare(findChild(indicatorItem, "icon0"), "opacity", 1.0);
186+ tryCompare(findChild(indicatorItem, "leftLabel"), "color", "#ededed");
187+ tryCompare(findChild(indicatorItem, "rightLabel"), "color", "#ededed");
188+ tryCompare(findChild(indicatorItem, "indicatorName"), "color", "#ededed");
189+
190+ indicatorItem.expanded = true;
191+ tryCompare(findChild(indicatorItem, "icon0"), "color", "#4c4c4c");
192+ tryCompare(findChild(indicatorItem, "icon0"), "opacity", 0.6);
193+ tryCompare(findChild(indicatorItem, "leftLabel"), "color", "#4c4c4c");
194+ tryCompare(findChild(indicatorItem, "rightLabel"), "color", "#4c4c4c");
195+ tryCompare(findChild(indicatorItem, "indicatorName"), "color", "#4c4c4c");
196+
197+ indicatorItem.selected = true;
198+ tryCompare(findChild(indicatorItem, "icon0"), "color", "#ededed");
199+ tryCompare(findChild(indicatorItem, "icon0"), "opacity", 1.0);
200+ tryCompare(findChild(indicatorItem, "leftLabel"), "color", "#ededed");
201+ tryCompare(findChild(indicatorItem, "rightLabel"), "color", "#ededed");
202+ tryCompare(findChild(indicatorItem, "indicatorName"), "color", "#ededed");
48 }
203 }
49 }
204 }
50}
205}
tests/qmltests/Panel/tst_IndicatorItemRow.qmlnew file+290
@@ -0,0 +1,290 @@
1+/*
2+ * Copyright 2013-2014 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+
17+import QtQuick 2.1
18+import QtQuick.Layouts 1.1
19+import QtTest 1.0
20+import "../../../qml/Panel"
21+import Ubuntu.Components 0.1
22+import Unity.Test 0.1 as UT
23+import Unity.Indicators 0.1 as Indicators
24+
25+IndicatorTest {
26+ id: root
27+ width: units.gu(120)
28+ height: units.gu(40)
29+ color: "white"
30+
31+ RowLayout {
32+ anchors.fill: parent
33+ anchors.margins: units.gu(1)
34+
35+ Rectangle {
36+ Layout.fillWidth: true
37+ Layout.fillHeight: true
38+
39+ id: itemArea
40+ color: "blue"
41+
42+ Rectangle {
43+ color: "black"
44+ anchors.fill: indicatorsRow
45+ }
46+
47+ IndicatorItemRow {
48+ id: indicatorsRow
49+ height: expanded ? units.gu(7) : units.gu(3)
50+ anchors.centerIn: parent
51+ indicatorsModel: root.indicatorsModel
52+ enableLateralChanges: ma.pressed
53+
54+ Behavior on height {
55+ NumberAnimation {
56+ id: heightAnimation
57+ duration: UbuntuAnimation.SnapDuration; easing: UbuntuAnimation.StandardEasing
58+ }
59+ }
60+
61+ MouseArea {
62+ id: ma
63+ anchors.fill: parent
64+ onPositionChanged: {
65+ indicatorsRow.lateralPosition = mouse.x;
66+ }
67+ onPressed: {
68+ if (pressed) {
69+ indicatorsRow.lateralPosition = mouse.x;
70+ indicatorsRow.selectItemAt(mouse.x);
71+ }
72+ }
73+ }
74+ }
75+ }
76+
77+ ColumnLayout {
78+ Layout.alignment: Qt.AlignTop
79+ Layout.fillWidth: false
80+
81+ Button {
82+ Layout.fillWidth: true
83+ text: indicatorsRow.expanded ? "Collapse" : "Expand"
84+ onClicked: indicatorsRow.expanded = !indicatorsRow.expanded
85+ }
86+
87+ Rectangle {
88+ Layout.preferredHeight: units.dp(1);
89+ Layout.fillWidth: true;
90+ color: "black"
91+ }
92+
93+ Repeater {
94+ model: root.originalModelData
95+ RowLayout {
96+ CheckBox {
97+ checked: true
98+ onCheckedChanged: checked ? insertIndicator(index) : removeIndicator(index);
99+ }
100+ Label {
101+ Layout.fillWidth: true
102+ text: modelData["identifier"]
103+ }
104+
105+ CheckBox {
106+ checked: true
107+ onCheckedChanged: setIndicatorVisible(index, checked);
108+ }
109+ Label {
110+ text: "visible"
111+ }
112+ }
113+ }
114+ }
115+ }
116+
117+ UT.UnityTestCase {
118+ name: "IndicatorItemRow"
119+ when: windowShown
120+
121+ function init() {
122+ root.resetData();
123+
124+ indicatorsRow.resetCurrentItem();
125+ indicatorsRow.lateralPosition = -1;
126+
127+ indicatorsRow.expanded = false;
128+ tryCompare(heightAnimation, "running", false);
129+ tryCompare(findChild(indicatorsRow, "highlight"), "highlightCenterOffset", 0);
130+ wait(1); // row seems to take a bit of time for item x values to update.
131+ }
132+
133+ function wait_for_expansion_to_settle() {
134+ tryCompare(heightAnimation, "running", false);
135+ wait(200); // put a little extra wait in for things to settle
136+ }
137+
138+ function test_indicatorRowChanges_data() {
139+ return [
140+ { remove: [0, 2] },
141+ { remove: [0, 1, 2, 3, 4] },
142+ ];
143+ }
144+
145+ // test the changes in the available indicators updates the
146+ // indicators that are visible.
147+ function test_indicatorRowChanges(data) {
148+ var i;
149+ var item;
150+ var itemsToRemove = [0, 2];
151+
152+ verify(root.originalModelData.length > 0);
153+ for (i = 0; i < root.originalModelData.length; i++) {
154+ item = findChild(indicatorsRow, root.originalModelData[i]["identifier"] + "-panelItem");
155+ verify(item);
156+
157+ compare(item.ownIndex, i, "Item at incorrect index");
158+ }
159+
160+ for (i = data.remove.length-1; i >= 0; i--) {
161+ removeIndicator(data.remove[i]);
162+ }
163+
164+ // test removals
165+ for (i = 0; i < root.originalModelData.length; i++) {
166+ item = findChild(indicatorsRow, root.originalModelData[i]["identifier"] + "-panelItem");
167+
168+ verify(data.remove.indexOf(i) !== -1 ? (item === null) : (item !== null));
169+ }
170+
171+ // test insertion
172+ for (i = 0; i < data.remove.length; i++) {
173+ insertIndicator(data.remove[i]);
174+ }
175+
176+ for (i = 0; i < root.originalModelData.length; i++) {
177+ item = findChild(indicatorsRow, root.originalModelData[i]["identifier"] + "-panelItem");
178+ verify(item);
179+
180+ compare(item.ownIndex, i, "Item at incorrect index");
181+ }
182+ }
183+
184+ function test_validCurrentItem_data() {
185+ return [
186+ { index: 0 },
187+ { index: 2 },
188+ { index: 4 }
189+ ];
190+ }
191+
192+ // test selecting the item at it's position sets the current item of the row.
193+ function test_validCurrentItem(data) {
194+ var dataItem = findChild(indicatorsRow, root.originalModelData[data.index]["identifier"] + "-panelItem");
195+ verify(dataItem !== null);
196+
197+ indicatorsRow.selectItemAt(dataItem.x + dataItem.width/2);
198+ compare(indicatorsRow.currentItem, dataItem);
199+ }
200+
201+ // tests item default selection (no item at position X)
202+ function test_invalidCurrentItem() {
203+ indicatorsRow.selectItemAt(-100);
204+ var item = findChild(indicatorsRow, root.originalModelData[0]["identifier"] + "-panelItem");
205+ compare(indicatorsRow.currentItem, item);
206+ }
207+
208+ // testing that changing the lateral position offset of the row changes the current item.
209+ function test_lateralPositionChangesCurrentItem_data() {
210+ return [
211+ { tag: "0 -> 4", from: 0, to: 4 },
212+ { tag: "3 -> 1", from: 3, to: 1 }
213+ ];
214+ }
215+
216+ function test_lateralPositionChangesCurrentItem(data) {
217+ indicatorsRow.expanded = true;
218+ wait_for_expansion_to_settle();
219+
220+ var fromItem = findChild(indicatorsRow, root.originalModelData[data.from]["identifier"] + "-panelItem");
221+ verify(fromItem !== null);
222+
223+ var toItem = findChild(indicatorsRow, root.originalModelData[data.to]["identifier"] + "-panelItem");
224+ verify(toItem !== null);
225+
226+ var fromPosition = indicatorsRow.mapFromItem(fromItem, fromItem.width/2, fromItem.height/2);
227+ var toPosition = indicatorsRow.mapFromItem(toItem, toItem.width/2, toItem.height/2);
228+
229+ mousePress(indicatorsRow, fromPosition.x, fromPosition.y);
230+ compare(indicatorsRow.currentItem, fromItem, "Initial item not selected");
231+
232+ // this uses the MouseArea above to change the indicatorRow lateralPosition
233+ mouseFlick(indicatorsRow, fromPosition.x, fromPosition.y, toPosition.x, toPosition.y, false, false, units.gu(5), 30);
234+
235+ mouseRelease(indicatorsRow, fromPosition.x, fromPosition.y);
236+ compare(indicatorsRow.currentItem, toItem, "Current item did not change to expected item");
237+ }
238+
239+ // testing that positive changes to the lateral position offset shifts the highlight offset to the right
240+ function test_positiveLateralPositionChangesHighlightOffset() {
241+ indicatorsRow.expanded = true;
242+ wait_for_expansion_to_settle();
243+
244+ var highlight = findChild(indicatorsRow, "highlight");
245+ var item = findChild(indicatorsRow, root.originalModelData[2]["identifier"] + "-panelItem");
246+ verify(item !== null);
247+ var mappedPosition = indicatorsRow.mapFromItem(item, item.width/2, item.height/2);
248+
249+ mousePress(indicatorsRow, mappedPosition.x, mappedPosition.y);
250+ var originalHightlightX = highlight.x;
251+ var offset = 1;
252+ while((highlight.x - originalHightlightX) <= units.gu(0.5) && offset < units.gu(10)) {
253+ mouseMove(indicatorsRow, mappedPosition.x + offset, mappedPosition.y, 10);
254+ offset = offset + 2;
255+ }
256+ // verify that we hit the offset
257+ verify((highlight.x - originalHightlightX) >= units.gu(0.5));
258+ mouseRelease(indicatorsRow);
259+
260+ // should go back to 0
261+ tryCompare(highlight, "highlightCenterOffset", 0);
262+ }
263+
264+ // testing that negative changes to the lateral position offset shifts the highlight offset to the left
265+ function test_negativeLateralPositionChangesHighlightOffset() {
266+ indicatorsRow.expanded = true;
267+ wait_for_expansion_to_settle();
268+
269+ var highlight = findChild(indicatorsRow, "highlight");
270+ var item = findChild(indicatorsRow, root.originalModelData[2]["identifier"] + "-panelItem");
271+ verify(item !== null);
272+ var mappedPosition = indicatorsRow.mapFromItem(item, item.width/2, item.height/2);
273+
274+ mousePress(indicatorsRow, mappedPosition.x, mappedPosition.y);
275+ var originalHightlightX = highlight.x;
276+ var offset = 1;
277+ while((highlight.x - originalHightlightX) >= -units.gu(0.5) && offset < units.gu(10)) {
278+ mouseMove(indicatorsRow, mappedPosition.x - offset, mappedPosition.y, 10);
279+ offset = offset + 2;
280+ }
281+
282+ // verify that we hit the offset
283+ verify((highlight.x - originalHightlightX) <= -units.gu(0.5));
284+ mouseRelease(indicatorsRow);
285+
286+ // should go back to 0
287+ tryCompare(findChild(indicatorsRow, "highlight"), "highlightCenterOffset", 0);
288+ }
289+ }
290+}
tests/qmltests/Panel/Indicators/tst_DefaultIndicatorPage.qml → tests/qmltests/Panel/tst_IndicatorPage.qmlrenamed+17−22
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2013 Canonical Ltd.
2+ * Copyright 2013-2014 Canonical Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -17,26 +17,22 @@
17import QtQuick 2.0
17import QtQuick 2.0
18import QtTest 1.0
18import QtTest 1.0
19import Unity.Test 0.1 as UT
19import Unity.Test 0.1 as UT
20−import QMenuModel 0.1
20+import Unity.Indicators 0.1 as Indicators
21−import "../../../../qml/Panel/Indicators"
21+import "../../../qml/Panel"
22
22
23Item {
23Item {
24 id: testView
24 id: testView
25 width: units.gu(40)
25 width: units.gu(40)
26 height: units.gu(70)
26 height: units.gu(70)
27
27
28− DefaultIndicatorPage {
28+ IndicatorPage {
29 id: page
29 id: page
30−
31 anchors.fill: parent
30 anchors.fill: parent
32
31
33− menuModel: UnityMenuModel {}
34− busName: "test"
35− actionsObjectPath: "test"
36− menuObjectPath: "test"
37−
38 identifier: "test-indicator"
32 identifier: "test-indicator"
39− rootMenuType: "com.canonical.indicator.root"
33+ busName: "com.caninical.indicator.test"
34+ actionsObjectPath: "/com/canonical/indicator/test"
35+ menuObjectPath: "/com/canonical/indicator/test"
40 }
36 }
41
37
42 property var fullMenuData: [{
38 property var fullMenuData: [{
@@ -113,29 +109,28 @@ Item {
113 "submenu": []
109 "submenu": []
114 }]; // end row 1
110 }]; // end row 1
115
111
112+ function initializeMenuData(data) {
113+ Indicators.UnityMenuModelCache.setCachedModelData("/com/canonical/indicator/test",
114+ data);
115+ }
116+
116 UT.UnityTestCase {
117 UT.UnityTestCase {
117− name: "DefaultIndicatorPage"
118+ name: "IndicatorPage"
118
119
119 function init() {
120 function init() {
120− var mainMenu = findChild(page, "mainMenu");
121+ initializeMenuData([]);
121− page.menuModel.modelData = [];
122− verify(mainMenu.model !== null);
123 }
122 }
124
123
125 function test_reloadData() {
124 function test_reloadData() {
126 var mainMenu = findChild(page, "mainMenu");
125 var mainMenu = findChild(page, "mainMenu");
127
126
128− page.menuModel.modelData = [];
129 tryCompare(mainMenu, "count", 0);
127 tryCompare(mainMenu, "count", 0);
130
128
131− page.menuModel.modelData = fullMenuData;
129+ initializeMenuData(fullMenuData);
132 tryCompare(mainMenu, "count", 3);
130 tryCompare(mainMenu, "count", 3);
133
131
134− page.menuModel.modelData = [];
132+ initializeMenuData([]);
135 tryCompare(mainMenu, "count", 0);
133 tryCompare(mainMenu, "count", 0);
136−
137− page.menuModel.modelData = fullMenuData;
138− tryCompare(mainMenu, "count", 3);
139 }
134 }
140
135
141 function test_traverse_rootMenuType_data() {
136 function test_traverse_rootMenuType_data() {
@@ -147,7 +142,7 @@ Item {
147
142
148 function test_traverse_rootMenuType(data) {
143 function test_traverse_rootMenuType(data) {
149 page.rootMenuType = data.rootMenuType;
144 page.rootMenuType = data.rootMenuType;
150− page.menuModel.modelData = fullMenuData;
145+ initializeMenuData(fullMenuData);
151
146
152 var mainMenu = findChild(page, "mainMenu");
147 var mainMenu = findChild(page, "mainMenu");
153 tryCompare(mainMenu, "count", data.expectedCount);
148 tryCompare(mainMenu, "count", data.expectedCount);
tests/qmltests/Panel/tst_IndicatorRow.qmldeleted−160
@@ -1,160 +0,0 @@
1−/*
2− * Copyright 2013 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−
17−import QtQuick 2.0
18−import QtTest 1.0
19−import Unity.Test 0.1 as UT
20−import ".."
21−import "../../../qml/Panel"
22−import Unity.Indicators 0.1 as Indicators
23−
24−/*
25− This tests the IndicatorRow component by using a fake model to stage data in the indicators
26− A view will show with indicators at the top, as does in the shell.
27−*/
28−Item {
29− id: rootItem
30− width: units.gu(40)
31− height: units.gu(60)
32−
33− function init_test()
34− {
35− indicatorModel.load("test1");
36−
37− indicatorRow.state = "initial";
38− indicatorRow.setCurrentItemIndex(-1);
39− indicatorRow.unitProgress = 0.0;
40− }
41−
42− PanelBackground {
43− anchors.fill: indicatorRow
44− }
45−
46− IndicatorRow {
47− id: indicatorRow
48− anchors {
49− left: parent.left
50− right: parent.right
51− }
52−
53− indicatorsModel: indicatorModel
54−
55− Component.onCompleted: indicatorModel.load("test1")
56− }
57−
58− Indicators.IndicatorsModel {
59− id: indicatorModel
60− }
61−
62− UT.UnityTestCase {
63− name: "IndicatorRow"
64− when: windowShown
65−
66− function get_indicator_item(index) {
67− return findChild(indicatorRow.row, "item" + index);
68− }
69−
70− function test_set_current_item() {
71− init_test();
72− indicatorRow.setCurrentItemIndex(0);
73− compare(indicatorRow.indicatorsModel.data(indicatorRow.currentItemIndex, Indicators.IndicatorsModelRole.Identifier), "indicator-fake1", "Incorrect item at position 0");
74−
75− indicatorRow.setCurrentItemIndex(1);
76− compare(indicatorRow.indicatorsModel.data(indicatorRow.currentItemIndex, Indicators.IndicatorsModelRole.Identifier), "indicator-fake2", "Incorrect item at position 1");
77−
78− indicatorRow.setCurrentItemIndex(2);
79− compare(indicatorRow.indicatorsModel.data(indicatorRow.currentItemIndex, Indicators.IndicatorsModelRole.Identifier), "indicator-fake3", "Incorrect item at position 2");
80− }
81−
82− function test_highlight_data() {
83− return [
84− { index: 0, progress: 0.0, current: false, other: false },
85− { index: 0, progress: 0.1, current: true, other: false },
86− { index: 0, progress: 0.5, current: true, other: false },
87− { index: 0, progress: 1.0, current: true, other: false },
88− { index: 2, progress: 0.0, current: false, other: false },
89− { index: 2, progress: 0.1, current: true, other: false },
90− { index: 2, progress: 0.5, current: true, other: false },
91− { index: 2, progress: 1.0, current: true, other: false }
92− ];
93− }
94−
95− function test_highlight(data) {
96− init_test();
97−
98− indicatorRow.unitProgress = data.progress;
99− indicatorRow.setCurrentItemIndex(data.index);
100−
101− compare(indicatorRow.currentItem.highlighted, data.current, "Indicator hightlight did not match for current item");
102−
103− for (var i = 0; i < indicatorRow.row.count; i++) {
104− compare(get_indicator_item(i).highlighted, i === data.index ? data.current: data.other, "Indicator hightlight did not match for item iter");
105− }
106− }
107−
108− function test_opacity_data() {
109− return [
110− { index: 0, progress: 0.0, current: 1.0, other: 1.0 },
111− { index: 0, progress: 0.1, current: 1.0, other: 0.9 },
112− { index: 0, progress: 0.5, current: 1.0, other: 0.5 },
113− { index: 0, progress: 1.0, current: 1.0, other: 0.0 },
114− { index: 2, progress: 0.0, current: 1.0, other: 1.0 },
115− { index: 2, progress: 0.1, current: 1.0, other: 0.9 },
116− { index: 2, progress: 0.5, current: 1.0, other: 0.5 },
117− { index: 2, progress: 1.0, current: 1.0, other: 0.0 }
118− ];
119− }
120−
121− function test_opacity(data) {
122− init_test();
123−
124− indicatorRow.unitProgress = data.progress;
125− indicatorRow.setCurrentItemIndex(data.index);
126−
127− tryCompare(indicatorRow.currentItem, "opacity", data.current);
128−
129− for (var i = 0; i < indicatorRow.row.count; i++) {
130− tryCompare(get_indicator_item(i), "opacity", i === data.index ? data.current: data.other);
131− }
132− }
133−
134− function test_dimmed_data() {
135− return [
136− { index: 0, progress: 0.0, current: false, other: false },
137− { index: 0, progress: 0.1, current: false, other: true },
138− { index: 0, progress: 0.5, current: false, other: true },
139− { index: 0, progress: 1.0, current: false, other: true },
140− { index: 2, progress: 0.0, current: false, other: false },
141− { index: 2, progress: 0.1, current: false, other: true },
142− { index: 2, progress: 0.5, current: false, other: true },
143− { index: 2, progress: 1.0, current: false, other: true }
144− ];
145− }
146−
147− function test_dimmed(data) {
148− init_test();
149−
150− indicatorRow.unitProgress = data.progress;
151− indicatorRow.setCurrentItemIndex(data.index);
152−
153− compare(indicatorRow.currentItem.dimmed, data.current, "Indicator dim did not match for current item");
154−
155− for (var i = 0; i < indicatorRow.row.count; i++) {
156− compare(get_indicator_item(i).dimmed, i === data.index ? data.current: data.other, "Indicator dim did not match for item iter");
157− }
158− }
159− }
160−}
tests/qmltests/Panel/tst_Indicators.qmldeleted−230
@@ -1,230 +0,0 @@
1−/*
2− * Copyright 2013 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−
17−import QtQuick 2.0
18−import QtTest 1.0
19−import Unity.Test 0.1 as UT
20−import Ubuntu.Components 0.1 as UC
21−import ".."
22−import "../../../qml/Panel"
23−import "../../../qml/Components"
24−
25−/*
26− This tests the Indicators component by using a fake model to stage data in the indicators
27− A view will show with indicators at the top, as does in the shell. There is a clickable area
28− marked "Click Me" which can be used to expose the indicators.
29−*/
30−Item {
31− id: shell
32− width: units.gu(40)
33− height: units.gu(80)
34−
35− PanelBackground {
36− anchors.fill: indicators
37− }
38−
39− Indicators {
40− id: indicators
41− anchors {
42− right: parent.right
43− }
44− width: (shell.width > units.gu(60)) ? units.gu(40) : shell.width
45− y: 0
46− shown: false
47− profile: "test1"
48−
49− openedHeight: parent.height - button.height
50− }
51−
52− UC.Button {
53− id: button
54− text: indicators.shown ? "Hide" : "Show"
55− anchors {
56− bottom: shell.bottom
57− left: parent.left
58− right: parent.right
59− }
60− height: 50
61−
62− onClicked: {
63− if (!indicators.shown) {
64− indicators.show();
65− } else {
66− indicators.hide();
67− }
68− }
69− }
70−
71− UT.UnityTestCase {
72− name: "Indicators"
73− when: windowShown
74−
75− function init() {
76− indicators.initialise();
77−
78− indicators.hide();
79− tryCompare(indicators.hideAnimation, "running", false);
80− tryCompare(indicators, "state", "initial");
81− }
82−
83− // Showing the indicators should fully open the indicator panel.
84− function test_show() {
85− indicators.show()
86− tryCompare(indicators, "fullyOpened", true);
87− }
88−
89− // Test the change in the revealer lateral position changes the current panel menu to fit the position
90− // of the indicator in the row.
91− function test_change_revealer_lateral_position()
92− {
93− // tests changing the lateral position of the revealer activates the correct indicator items.
94−
95− var indicatorRow = findChild(indicators, "indicatorRow")
96− verify(indicatorRow !== null);
97− var indicatorRowItems = findChild(indicatorRow, "indicatorRowItems");
98− verify(indicatorRowItems !== null);
99−
100− for (var i = 0; i < indicatorRowItems.count; i++) {
101− var indicatorItem = findChild(indicatorRowItems, "item" + i);
102−
103− if (!indicatorItem.visible)
104− continue;
105−
106− var indicatorPosition = indicators.mapFromItem(indicatorItem,
107− indicatorItem.width/2, indicatorItem.height/2);
108−
109− touchFlick(indicators,
110− indicatorPosition.x, indicatorPosition.y,
111− indicatorPosition.x, indicators.openedHeight * 0.4,
112− true /* beginTouch */, false /* endTouch */);
113−
114− compare(indicatorRow.currentItem, indicatorItem,
115− "Incorrect item activated at position " + i);
116−
117− touchFlick(indicators,
118− indicatorPosition.x, indicators.openedHeight * 0.4,
119− indicatorPosition.x, indicatorPosition.y,
120− false /* beginTouch */, true /* endTouch */);
121−
122− // wait until fully closed
123− tryCompare(indicators, "height", indicators.panelHeight);
124− }
125− }
126−
127− // values for specific state changes are subject to internal decisions, so we can't
128− // determine the true height value which would cause the state to change without making
129− // too many assuptyions
130− // However, we can assume that a partially opened panel will not be initial, and fully
131− // opened panel will be locked.
132−
133− function test_progress_changes_state_to_not_initial() {
134− indicators.height = indicators.openedHeight / 2
135− compare(indicators.state!="initial", true,
136− "Indicators should not be in initial state when partially opened.");
137− }
138−
139− function test_progress_changes_state_to_locked() {
140− indicators.height = indicators.openedHeight - indicators.panelHeight
141− compare(indicators.state, "locked", "Indicators should be locked when fully opened.");
142− }
143−
144− function test_partially_open() {
145− indicators.height = indicators.openedHeight / 2
146− compare(indicators.partiallyOpened, true,
147− "Indicator should show as partially opened when height is half of openedHeight");
148− compare(indicators.fullyOpened, false,
149− "Indicator should not show as fully opened when height is half of openedHeight");
150− }
151−
152− function test_fully_open() {
153− indicators.height = indicators.openedHeight
154− compare(indicators.partiallyOpened, false);
155− compare(indicators.fullyOpened, true);
156− }
157−
158− function init_invisible_indicator(identifier) {
159− tryCompareFunction(function() { return findChild(indicators, identifier+"-delegate") !== undefined }, true);
160− var item = findChild(indicators, identifier+"-delegate");
161−
162− item.enabled = false;
163− }
164−
165− function test_row_visible_menuContent_visible_data() { return [
166− {tag: "first", visible: [false, true, true, true, true] },
167− {tag: "adjacent", visible: [true, false, false, true, true] },
168− {tag: "bounds", visible: [false, true, true, true, false] },
169− {tag: "disjoint", visible: [true, false, true, false, true] },
170− {tag: "last", visible: [true, true, true, true, false] }];
171− }
172−
173− function test_row_visible_menuContent_visible(data) {
174− indicators.show();
175−
176− var contentListView = findChild(indicators, "indicatorsContentListView");
177− var indicatorRowItems = findChild(indicators, "indicatorRowItems");
178−
179− var count = data.visible.length
180− for (var i = 0; i< data.visible.length; i++) {
181− if (data.visible[i] === false) {
182− init_invisible_indicator("indicator-fake" + (i + 1));
183− count--;
184− }
185− }
186−
187− tryCompare(indicatorRowItems, "count", count);
188−
189− for (i = 0; i < data.visible.length; i++) {
190− var widgetName = "indicator-fake" + (i + 1 + "-widget");
191− var pageName = "indicator-fake" + (i + 1 + "-page");
192−
193− // check for item
194− tryCompareFunction(function() { return findChild(indicatorRowItems, widgetName) !== null }, data.visible[i]);
195−
196− // check for tab
197− tryCompareFunction(function() { return findChild(contentListView, pageName) !== null }, data.visible[i]);
198− }
199− }
200−
201− function test_indicator_visible_correct_menu_data() { return [
202− {tag: "current-first", currentIndex: 0, visible: [false, true, true, true, true], expectedIndex: 0, expextedMenu: "indicator-fake2" },
203− {tag: "current-last", currentIndex: 4, visible: [true, true, true, true, false], expectedIndex: 3, expextedMenu: "indicator-fake4" },
204− {tag: "after", currentIndex: 0, visible: [true, false, true, true, true], expectedIndex: 0, expextedMenu: "indicator-fake1" },
205− {tag: "before", currentIndex: 1, visible: [false, true, true, true, true], expectedIndex: 1, expextedMenu: "indicator-fake2" }];
206− }
207−
208− function test_indicator_visible_correct_menu(data) {
209− var contentListView = findChild(indicators, "indicatorsContentListView");
210− var indicatorRow = findChild(indicators, "indicatorRow");
211−
212− indicators.show();
213− indicatorRow.setCurrentItemIndex(data.currentIndex);
214− tryCompare(indicators, "fullyOpened", true);
215−
216− for (var i = 0; i< data.visible.length; i++) {
217− if (data.visible[i] === false) {
218− init_invisible_indicator("indicator-fake" + (i + 1));
219− }
220− }
221−
222− // check for current selected item
223− tryCompare(indicatorRow, "currentItemIndex", data.expectedIndex);
224−
225− // check for current selected tab
226− tryCompareFunction(function() { return findChild(contentListView, data.expextedMenu) === contentListView.currentItem }, true);
227−
228− }
229− }
230−}
tests/qmltests/Panel/tst_IndicatorsBar.qmlnew file+224
@@ -0,0 +1,224 @@
1+/*
2+ * Copyright 2013-2014 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+
17+import QtQuick 2.1
18+import QtQuick.Layouts 1.1
19+import QtTest 1.0
20+import "../../../qml/Panel"
21+import Ubuntu.Components 0.1
22+import Unity.Test 0.1 as UT
23+import Unity.Indicators 0.1 as Indicators
24+
25+IndicatorTest {
26+ id: root
27+ width: units.gu(100)
28+ height: units.gu(40)
29+
30+
31+ RowLayout {
32+ anchors.fill: parent
33+ anchors.margins: units.gu(1)
34+
35+ Rectangle {
36+ id: itemArea
37+ color: "blue"
38+ Layout.fillWidth: true
39+ Layout.fillHeight: true
40+
41+ Rectangle {
42+ color: "black"
43+ anchors.fill: indicatorsBar
44+ }
45+
46+ IndicatorsBar {
47+ id: indicatorsBar
48+ height: expanded ? units.gu(7) : units.gu(3)
49+ width: units.gu(30)
50+ anchors.centerIn: parent
51+ indicatorsModel: root.indicatorsModel
52+ interactive: expanded && height === units.gu(7)
53+
54+ Behavior on height {
55+ NumberAnimation {
56+ id: heightAnimation
57+ duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing
58+ }
59+ }
60+
61+ MouseArea {
62+ anchors.fill: parent
63+ enabled: !indicatorsBar.expanded
64+ onPressed: {
65+ indicatorsBar.selectItemAt(mouse.x);
66+ indicatorsBar.expanded = true
67+ }
68+ }
69+ }
70+ }
71+
72+ ColumnLayout {
73+ Layout.alignment: Qt.AlignTop
74+ Layout.fillWidth: false
75+
76+ Button {
77+ Layout.fillWidth: true
78+ text: indicatorsBar.expanded ? "Collapse" : "Expand"
79+ onClicked: indicatorsBar.expanded = !indicatorsBar.expanded
80+ }
81+
82+ Rectangle {
83+ Layout.preferredHeight: units.dp(1);
84+ Layout.fillWidth: true;
85+ color: "black"
86+ }
87+
88+ Repeater {
89+ model: root.originalModelData
90+ RowLayout {
91+ CheckBox {
92+ checked: true
93+ onCheckedChanged: checked ? insertIndicator(index) : removeIndicator(index);
94+ }
95+ Label {
96+ Layout.fillWidth: true
97+ text: modelData["identifier"]
98+ }
99+
100+ CheckBox {
101+ checked: true
102+ onCheckedChanged: setIndicatorVisible(index, checked);
103+ }
104+ Label {
105+ text: "visible"
106+ }
107+ }
108+ }
109+ }
110+ }
111+
112+ UT.UnityTestCase {
113+ name: "IndicatorsBar"
114+ when: windowShown
115+
116+ function init() {
117+ indicatorsBar.expanded = false;
118+ wait_for_expansion_to_settle();
119+ }
120+
121+ function test_expandSelectedItem_data() {
122+ return [
123+ { index: 0 },
124+ { index: 2 },
125+ { index: 4 }
126+ ];
127+ }
128+
129+ function wait_for_expansion_to_settle() {
130+ tryCompare(heightAnimation, "running", false);
131+ wait(UbuntuAnimation.SnapDuration); // put a little extra wait in for things to settle
132+ }
133+
134+ // Rough check that expanding a selected item keeps it within the area of the original item.
135+ function test_expandSelectedItem(data) {
136+ var dataItem = findChild(indicatorsBar, root.originalModelData[data.index]["identifier"] + "-panelItem");
137+ verify(dataItem !== null);
138+
139+ var mappedPosition = indicatorsBar.mapFromItem(dataItem, dataItem.width/2, dataItem.height/2);
140+
141+ indicatorsBar.selectItemAt(mappedPosition.x);
142+ indicatorsBar.expanded = true;
143+ wait_for_expansion_to_settle();
144+
145+
146+ // mappedPosition contained within mappedRect
147+ tryCompareFunction(function() {
148+ var mappedRect = indicatorsBar.mapFromItem(dataItem, 0, 0, dataItem.width, dataItem.height);
149+ return mappedRect.x <= mappedPosition.x; },
150+ true);
151+ tryCompareFunction(function() {
152+ var mappedRect = indicatorsBar.mapFromItem(dataItem, 0, 0, dataItem.width, dataItem.height);
153+ return mappedRect.x + mappedRect.width >= mappedPosition.x;
154+ }, true);
155+ }
156+
157+ function test_scrollOffset() {
158+ indicatorsBar.expanded = true;
159+ wait_for_expansion_to_settle();
160+
161+ var lastItemIndex = root.originalModelData.length-1;
162+ var dataItem = findChild(indicatorsBar, root.originalModelData[lastItemIndex]["identifier"] + "-panelItem");
163+ verify(dataItem !== null);
164+
165+ var row = findChild(indicatorsBar, "indicatorItemRow");
166+ // test will not work without these conditions
167+ verify(row.width >= indicatorsBar.width + dataItem.width);
168+
169+ var mappedPosition = indicatorsBar.mapFromItem(dataItem, dataItem.width/2, dataItem.height/2);
170+ indicatorsBar.addScrollOffset(-dataItem.width);
171+ var newMappedPosition = indicatorsBar.mapFromItem(dataItem, dataItem.width/2, dataItem.height/2);
172+
173+ compare(mappedPosition.x, newMappedPosition.x - dataItem.width);
174+ }
175+
176+ function test_selectItemWhenExpanded_data() {
177+ return [
178+ { index: 3 },
179+ { index: 4 }
180+ ];
181+ }
182+
183+ function test_selectItemWhenExpanded(data) {
184+ indicatorsBar.expanded = true;
185+ wait_for_expansion_to_settle();
186+
187+ var dataItem = findChild(indicatorsBar, root.originalModelData[data.index]["identifier"] + "-panelItem");
188+ if (indicatorsBar.mapFromItem(dataItem, dataItem.width/2, dataItem.height/2).x < 0) {
189+ skip("Out of bounds");
190+ }
191+ mouseClick(dataItem, dataItem.width/2, dataItem.height/2);
192+ verify(dataItem.selected === true);
193+ }
194+
195+ function test_visibleIndicators_data() {
196+ return [
197+ { visible: [true, false, true, false, true, true] },
198+ { visible: [false, false, false, false, false, false] }
199+ ];
200+ }
201+
202+ function test_visibleIndicators(data) {
203+ for (var i = 0; i < data.visible.length; i++) {
204+ var visible = data.visible[i];
205+ root.setIndicatorVisible(i, visible);
206+
207+ var dataItem = findChild(indicatorsBar, root.originalModelData[i]["identifier"] + "-panelItem");
208+ tryCompare(dataItem, "opacity", visible ? 1.0 : 0.0);
209+ tryCompareFunction(function() { return dataItem.width > 0.0; }, visible);
210+ }
211+
212+ indicatorsBar.expanded = true;
213+ wait_for_expansion_to_settle();
214+
215+ for (var i = 0; i < data.visible.length; i++) {
216+ root.setIndicatorVisible(i, data.visible[i]);
217+
218+ var dataItem = findChild(indicatorsBar, root.originalModelData[i]["identifier"] + "-panelItem");
219+ tryCompareFunction(function() { return dataItem.opacity > 0.0; }, true);
220+ tryCompareFunction(function() { return dataItem.width > 0.0; }, true);
221+ }
222+ }
223+ }
224+}
tests/qmltests/Panel/tst_IndicatorsMenu.qmlnew file+258
@@ -0,0 +1,258 @@
1+/*
2+ * Copyright 2013-2014 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+
17+import QtQuick 2.1
18+import QtQuick.Layouts 1.1
19+import QtTest 1.0
20+import "../../../qml/Panel"
21+import Ubuntu.Components 0.1
22+import Unity.Test 0.1 as UT
23+import Unity.Indicators 0.1 as Indicators
24+
25+IndicatorTest {
26+ id: root
27+ width: units.gu(80)
28+ height: units.gu(71)
29+ color: "white"
30+
31+ property string indicatorProfile: "phone"
32+
33+ RowLayout {
34+ anchors.fill: parent
35+ anchors.margins: units.gu(1)
36+
37+ Rectangle {
38+ Layout.fillWidth: true
39+ Layout.fillHeight: true
40+
41+ id: itemArea
42+ color: "blue"
43+
44+ IndicatorsMenu {
45+ id: indicatorsMenu
46+ width: units.gu(40)
47+ anchors {
48+ top: parent.top
49+ right: parent.right
50+ }
51+ minimizedPanelHeight: units.gu(3)
52+ expandedPanelHeight: units.gu(7)
53+ openedHeight: parent.height
54+ indicatorsModel: root.indicatorsModel
55+ shown: false
56+ }
57+ }
58+
59+ ColumnLayout {
60+ Layout.alignment: Qt.AlignTop
61+ Layout.fillWidth: false
62+
63+ Button {
64+ Layout.fillWidth: true
65+ text: indicatorsMenu.shown ? "Hide" : "Show"
66+ onClicked: {
67+ if (indicatorsMenu.shown) {
68+ indicatorsMenu.hide();
69+ } else {
70+ indicatorsMenu.show();
71+ }
72+ }
73+ }
74+
75+ Rectangle {
76+ Layout.preferredHeight: units.dp(1);
77+ Layout.fillWidth: true;
78+ color: "black"
79+ }
80+
81+ Repeater {
82+ model: root.originalModelData
83+ RowLayout {
84+ CheckBox {
85+ checked: true
86+ onCheckedChanged: checked ? insertIndicator(index) : removeIndicator(index);
87+ }
88+ Label {
89+ Layout.fillWidth: true
90+ text: modelData["identifier"]
91+ }
92+
93+ CheckBox {
94+ checked: true
95+ onCheckedChanged: setIndicatorVisible(index, checked);
96+ }
97+ Label {
98+ text: "visible"
99+ }
100+ }
101+ }
102+ }
103+ }
104+
105+ UT.UnityTestCase {
106+ id: testCase
107+ name: "IndicatorsMenu"
108+ when: windowShown
109+
110+ function init() {
111+ indicatorsMenu.hide();
112+ tryCompare(indicatorsMenu.hideAnimation, "running", false);
113+ compare(indicatorsMenu.state, "initial");
114+
115+ indicatorsMenu.verticalVelocityThreshold = 0.5
116+ }
117+
118+ function get_indicator_item(index) {
119+ var indicatorItem = findChild(indicatorsMenu, root.originalModelData[index]["identifier"] + "-panelItem");
120+ verify(indicatorItem !== null);
121+
122+ return indicatorItem;
123+ }
124+
125+ // Showing the indicators should fully open the indicator panel.
126+ function test_showAndHide() {
127+ indicatorsMenu.show();
128+ tryCompare(indicatorsMenu, "fullyOpened", true);
129+
130+ indicatorsMenu.hide();
131+ tryCompare(indicatorsMenu, "fullyClosed", true);
132+ }
133+
134+ // Test that closing the indicators ends up in the correct position.
135+ function test_hideEndsInCorrectPosition() {
136+ var indicatorsBar = findChild(indicatorsMenu, "indicatorsBar");
137+ var flickable = findChild(indicatorsBar, "flickable");
138+
139+ var originalContentX = flickable.contentX;
140+
141+ indicatorsMenu.show();
142+ indicatorsBar.setCurrentItemIndex(0);
143+ tryCompare(indicatorsMenu, "fullyOpened", true);
144+
145+ indicatorsMenu.hide();
146+ tryCompare(flickable, "contentX", originalContentX);
147+ }
148+
149+ function test_progress_changes_state_to_reveal() {
150+ indicatorsMenu.height = indicatorsMenu.openedHeight / 2;
151+ compare(indicatorsMenu.state, "reveal", "Indicators should be revealing when partially opened.");
152+
153+ indicatorsMenu.height = indicatorsMenu.openedHeight;
154+ compare(indicatorsMenu.state, "reveal", "Indicators should still be revealing when fully opened.");
155+ }
156+
157+ function test_open_state() {
158+ compare(indicatorsMenu.fullyClosed, true, "Indicator should show as fully closed.");
159+ compare(indicatorsMenu.partiallyOpened, false, "Indicator should not show as partially opened");
160+ compare(indicatorsMenu.fullyOpened, false, "Indicator should not show as fully opened");
161+
162+ indicatorsMenu.height = indicatorsMenu.openedHeight / 2
163+ compare(indicatorsMenu.fullyClosed, false, "Indicator should not show as fully closed.");
164+ compare(indicatorsMenu.partiallyOpened, true, "Indicator should show as partially opened");
165+ compare(indicatorsMenu.fullyOpened, false, "Indicator should not show as fully opened");
166+
167+ indicatorsMenu.height = indicatorsMenu.openedHeight;
168+ compare(indicatorsMenu.fullyClosed, false, "Indicator should not show as fully closed.");
169+ compare(indicatorsMenu.partiallyOpened, false, "Indicator should show as partially opened");
170+ compare(indicatorsMenu.fullyOpened, true, "Indicator should not show as fully opened");
171+ }
172+
173+ // Pressing on the indicator panel should activate the indicator hints
174+ // and expose the header
175+ function test_hint() {
176+ var indicatorItem = get_indicator_item(0);
177+ var mappedPosition = root.mapFromItem(indicatorItem, indicatorItem.width/2, indicatorItem.height/2);
178+
179+ touchPress(indicatorsMenu, mappedPosition.x, indicatorsMenu.minimizedPanelHeight / 2);
180+
181+ // hint animation should be run, meaning that indicators will move downwards
182+ // by hintValue pixels without any drag taking place
183+ tryCompareFunction(function() { return indicatorsMenu.height }, indicatorsMenu.expandedPanelHeight + units.gu(2));
184+ tryCompare(indicatorsMenu, "partiallyOpened", true);
185+
186+ touchRelease(indicatorsMenu, mappedPosition.x, indicatorsMenu.minimizedPanelHeight / 2);
187+ }
188+
189+ // tests swiping on an indicator item activates the correct item.
190+ function test_swipeForCurrentItem()
191+ {
192+ var indicatorItemRow = findChild(indicatorsMenu, "indicatorItemRow");
193+ verify(indicatorItemRow !== null);
194+
195+ for (var i = 0; i < root.originalModelData.length; i++) {
196+ var indicatorItem = get_indicator_item(i);
197+
198+ var mappedPosition = root.mapFromItem(indicatorItem, indicatorItem.width/2, indicatorItem.height/2);
199+
200+ touchFlick(indicatorsMenu,
201+ mappedPosition.x, mappedPosition.y,
202+ mappedPosition.x, indicatorsMenu.openedHeight / 2,
203+ true /* beginTouch */, false /* endTouch */);
204+
205+ compare(indicatorItemRow.currentItem, indicatorItem,
206+ "Incorrect item activated at position " + i);
207+
208+ touchFlick(indicatorItemRow,
209+ mappedPosition.x, indicatorsMenu.openedHeight / 2,
210+ mappedPosition.x, mappedPosition.y,
211+ false /* beginTouch */, true /* endTouch */);
212+
213+ // wait until fully closed
214+ tryCompare(indicatorsMenu, "fullyClosed", true);
215+ }
216+ }
217+
218+ // Test the vertical velocity check when flicking the indicators open at an angle.
219+ // If the vertical velocity is above a specific point, we shouldnt change active indicators
220+ // if the x position changes
221+ function test_verticalVelocityDetector() {
222+ indicatorsMenu.verticalVelocityThreshold = 0;
223+ verify(root.originalModelData.length >= 2);
224+
225+ var indicatorItemRow = findChild(indicatorsMenu, "indicatorItemRow");
226+ verify(indicatorItemRow !== null);
227+
228+ // Get the first indicator
229+ var firstItem = get_indicator_item(0);
230+ var firstItemMappedPosition = root.mapFromItem(firstItem, firstItem.width/2, firstItem.height/2);
231+
232+ // 1) Drag the mouse down to hint a bit
233+ touchFlick(indicatorsMenu,
234+ firstItemMappedPosition.x, indicatorsMenu.minimizedPanelHeight / 2,
235+ firstItemMappedPosition.x, indicatorsMenu.minimizedPanelHeight * 2,
236+ true /* beginTouch */, false /* endTouch */);
237+
238+ tryCompare(indicatorItemRow, "currentItem", firstItem)
239+
240+ // next time position will have moved.
241+ var nextItem = get_indicator_item(1);
242+ var nextItemMappedPosition = root.mapFromItem(nextItem, nextItem.width/2, nextItem.height/2);
243+
244+ // 1) Flick mouse down to bottom
245+ touchFlick(indicatorsMenu,
246+ firstItemMappedPosition.x, indicatorsMenu.minimizedPanelHeight * 2,
247+ nextItemMappedPosition.x, indicatorsMenu.openedHeight / 3,
248+ false /* beginTouch */, false /* endTouch */,
249+ units.gu(50) /* speed */, 5 /* iterations */); // more samples needed for accurate velocity
250+
251+ compare(indicatorItemRow.currentItem, firstItem, "First indicator should still be the current item");
252+ // after waiting in the same spot with touch down, it should update to the next item.
253+ tryCompare(indicatorItemRow, "currentItem", nextItem);
254+
255+ touchRelease(indicatorsMenu, nextItemMappedPosition.x, indicatorsMenu.openedHeight / 3);
256+ }
257+ }
258+}
tests/qmltests/Panel/tst_MenuContent.qml+9−25
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2013 Canonical Ltd.
2+ * Copyright 2013-2014 Canonical Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -17,36 +17,21 @@
17import QtQuick 2.0
17import QtQuick 2.0
18import QtTest 1.0
18import QtTest 1.0
19import Unity.Test 0.1 as UT
19import Unity.Test 0.1 as UT
20−import ".."
21import "../../../qml/Panel"
20import "../../../qml/Panel"
22import Unity.Indicators 0.1 as Indicators
21import Unity.Indicators 0.1 as Indicators
23
22
24−Item {
23+IndicatorTest {
25− id: shell
24+ id: root
26 width: units.gu(40)
25 width: units.gu(40)
27 height: units.gu(70)
26 height: units.gu(70)
28
27
29− property var indicator_status: {
30− 'indicator-fake1-page': { 'started': false, 'reset': 0 },
31− 'indicator-fake2-page': { 'started': false, 'reset': 0 },
32− 'indicator-fake3-page': { 'started': false, 'reset': 0 },
33− 'indicator-fake4-page': { 'started': false, 'reset': 0 },
34− 'indicator-fake5-page': { 'started': false, 'reset': 0 }
35− }
36−
37 // Dummy objects
28 // Dummy objects
38 Item { id: greeter }
29 Item { id: greeter }
39 Item { id: handle }
30 Item { id: handle }
40
31
41−
42− Indicators.IndicatorsModel {
43− id: indicatorsModel
44− Component.onCompleted: load("test1")
45− }
46−
47 MenuContent {
32 MenuContent {
48 id: menuContent
33 id: menuContent
49− indicatorsModel: indicatorsModel
34+ indicatorsModel: root.indicatorsModel
50 height: parent.height - 50
35 height: parent.height - 50
51 }
36 }
52
37
@@ -78,12 +63,12 @@ Item {
78 if (menuContent.currentMenuIndex == -1)
63 if (menuContent.currentMenuIndex == -1)
79 activate_content(0);
64 activate_content(0);
80 else
65 else
81− activate_content((menuContent.currentMenuIndex + 1) % indicatorsModel.count)
66+ activate_content((menuContent.currentMenuIndex + 1) % root.originalModelData.length)
82 }
67 }
83
68
84 function activate_content(index)
69 function activate_content(index)
85 {
70 {
86− menuContent.setCurrentMenuIndex(index)
71+ menuContent.currentMenuIndex = index;
87 }
72 }
88
73
89 function get_test_menu_objecName(index) {
74 function get_test_menu_objecName(index) {
@@ -115,7 +100,7 @@ Item {
115
100
116 // Check that the correct menus are displayed for the requested item.
101 // Check that the correct menus are displayed for the requested item.
117 function test_show_menu() {
102 function test_show_menu() {
118− var menuCount = indicatorsModel.count;
103+ var menuCount = root.originalModelData.length;
119 verify(menuCount > 0, "Menu count should be greater than zero");
104 verify(menuCount > 0, "Menu count should be greater than zero");
120
105
121 var listView = menu_content_test.findChild(menuContent, "indicatorsContentListView")
106 var listView = menu_content_test.findChild(menuContent, "indicatorsContentListView")
@@ -126,7 +111,7 @@ Item {
126 var menuIndex = i%menuCount;
111 var menuIndex = i%menuCount;
127
112
128 activate_content(menuIndex);
113 activate_content(menuIndex);
129− testItemObjectName = indicatorsModel.data(menuIndex, Indicators.IndicatorsModelRole.Identifier);
114+ testItemObjectName = indicatorsModel.model.data(menuIndex, Indicators.IndicatorsModelRole.Identifier);
130 compare(listView.currentIndex, menuIndex, "Current tab index does not match selected tab index");
115 compare(listView.currentIndex, menuIndex, "Current tab index does not match selected tab index");
131 tryCompareFunction(current_item_equals_test_item, true);
116 tryCompareFunction(current_item_equals_test_item, true);
132 }
117 }
@@ -134,14 +119,13 @@ Item {
134
119
135 // Tests QTBUG-30632 - asynchronous loader crashes when changing index quickly.
120 // Tests QTBUG-30632 - asynchronous loader crashes when changing index quickly.
136 function test_multi_activate() {
121 function test_multi_activate() {
137− var menuCount = indicatorsModel.count;
122+ var menuCount = root.originalModelData.length;
138 verify(menuCount > 0, "Menu count should be greater than zero");
123 verify(menuCount > 0, "Menu count should be greater than zero");
139
124
140 for (var i = 0; i < 100; i++) {
125 for (var i = 0; i < 100; i++) {
141 activate_content(i % menuCount);
126 activate_content(i % menuCount);
142 compare(menuContent.currentMenuIndex, i%menuCount);
127 compare(menuContent.currentMenuIndex, i%menuCount);
143 }
128 }
144− wait(100);
145 }
129 }
146 }
130 }
147}
131}
tests/qmltests/Panel/tst_Panel.qml+185−214
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2013 Canonical Ltd.
2+ * Copyright 2013-2014 Canonical Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -14,95 +14,106 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
15 */
16
16
17−import QtQuick 2.0
17+import QtQuick 2.1
18+import QtQuick.Layouts 1.1
18import QtTest 1.0
19import QtTest 1.0
19import Unity.Test 0.1 as UT
20import Unity.Test 0.1 as UT
20−import Ubuntu.Components 0.1 as UC
21+import Ubuntu.Components 0.1
22+import Unity.Indicators 0.1 as Indicators
21import Ubuntu.Telephony 0.1 as Telephony
23import Ubuntu.Telephony 0.1 as Telephony
22−import ".."
23import "../../../qml/Panel"
24import "../../../qml/Panel"
24
25
25−/*
26+IndicatorTest {
26− This tests the Panel component using a fake model to stage data in the indicators
27+ id: root
27− A view will show with indicators at the top, as does in the shell. This can be controlled
28− as in the shell.
29−*/
30−Item {
31− id: shell
32− width: units.gu(40)
28+ width: units.gu(100)
33− height: units.gu(80)
29+ height: units.gu(71)
34−
30+ color: "white"
35− Panel {
36− id: panel
37− anchors {
38− left: parent.left
39− right: parent.right
40− }
41− height: parent.height - row.height
42− fullscreenMode: false
43
31
44− indicators {
32+ property string indicatorProfile: "phone"
45− profile: "test1"
46− panelHeight: units.gu(5)
47− }
48− callHint {
49− height: units.gu(4)
50− }
51
33
52− property real panelAndSeparatorHeight: panel.indicators.panelHeight + units.dp(2)
34+ RowLayout {
53− }
35+ anchors.fill: parent
36+ anchors.margins: units.gu(1)
37+ clip: true
54
38
39+ Rectangle {
40+ Layout.fillWidth: true
41+ Layout.fillHeight: true
55
42
56− Row {
43+ id: itemArea
57− id: row
44+ color: "blue"
58− anchors {
59− bottom: shell.bottom
60− left: parent.left
61− right: parent.right
62− }
63− height: 50
64
45
65− UC.Button {
46+ Panel {
66− text: panel.indicators.shown ? "Hide" : "Show"
47+ id: panel
67− anchors {
48+ anchors.fill: parent
68− top: parent.top
49+ indicators {
69− bottom: parent.bottom
50+ width: parent.width > units.gu(60) ? units.gu(40) : parent.width
51+ indicatorsModel: root.indicatorsModel
52+ }
53+
54+ property real panelAndSeparatorHeight: panel.indicators.minimizedPanelHeight + units.dp(2)
70 }
55 }
71− width: parent.width/3
56+ }
72
57
58+ ColumnLayout {
59+ Layout.alignment: Qt.AlignTop
60+ Layout.fillWidth: false
61+
62+ Button {
63+ Layout.fillWidth: true
64+ text: panel.indicators.shown ? "Hide" : "Show"
73− onClicked: {
65+ onClicked: {
74− if (panel.indicators.shown) {
66+ if (panel.indicators.shown) {
75− panel.indicators.hide();
67+ panel.indicators.hide();
76− } else {
68+ } else {
77− panel.indicators.show();
69+ panel.indicators.show();
70+ }
78 }
71 }
79 }
72 }
80− }
81
73
82− UC.Button {
74+ Button {
83− text: panel.fullscreenMode ? "Maximize" : "FullScreen"
75+ text: panel.fullscreenMode ? "Maximize" : "FullScreen"
84− anchors {
76+ Layout.fillWidth: true
85− top: parent.top
77+ onClicked: panel.fullscreenMode = !panel.fullscreenMode
86− bottom: parent.bottom
87 }
78 }
88− width: parent.width/3
89
79
90− onClicked: panel.fullscreenMode = !panel.fullscreenMode
80+ Button {
81+ Layout.fillWidth: true
82+ text: callManager.hasCalls ? "Called" : "No Calls"
83+ onClicked: {
84+ if (callManager.foregroundCall) {
85+ callManager.foregroundCall = null;
86+ } else {
87+ callManager.foregroundCall = phoneCall;
88+ }
89+ }
91− }
90+ }
92
91
93− UC.Button {
92+ Rectangle {
94− text: callManager.hasCalls ? "Called" : "No Calls"
93+ Layout.preferredHeight: units.dp(1);
95− anchors {
94+ Layout.fillWidth: true;
96− top: parent.top
95+ color: "black"
97− bottom: parent.bottom
98 }
96 }
99− width: parent.width/3
100
97
101− onClicked: {
98+ Repeater {
102− if (callManager.foregroundCall) {
99+ model: root.originalModelData
103− callManager.foregroundCall = null;
100+ RowLayout {
104− } else {
101+ CheckBox {
105− callManager.foregroundCall = phoneCall;
102+ checked: true
103+ onCheckedChanged: checked ? insertIndicator(index) : removeIndicator(index);
104+ }
105+ Label {
106+ Layout.fillWidth: true
107+ text: modelData["identifier"]
108+ }
109+
110+ CheckBox {
111+ checked: true
112+ onCheckedChanged: setIndicatorVisible(index, checked);
113+ }
114+ Label {
115+ text: "visible"
116+ }
106 }
117 }
107 }
118 }
108 }
119 }
@@ -118,13 +129,12 @@ Item {
118 when: windowShown
129 when: windowShown
119
130
120 function init() {
131 function init() {
121− panel.indicators.initialise();
122 panel.fullscreenMode = false;
132 panel.fullscreenMode = false;
133+ callManager.foregroundCall = null;
123
134
124 panel.indicators.hide();
135 panel.indicators.hide();
125 // Wait for animation to complete
136 // Wait for animation to complete
126 tryCompare(panel.indicators.hideAnimation, "running", false);
137 tryCompare(panel.indicators.hideAnimation, "running", false);
127− callManager.foregroundCall = null;
128
138
129 // Wait for the indicators to get into position.
139 // Wait for the indicators to get into position.
130 // (switches between normal and fullscreen modes are animated)
140 // (switches between normal and fullscreen modes are animated)
@@ -133,100 +143,21 @@ Item {
133 }
143 }
134
144
135 function get_indicator_item(index) {
145 function get_indicator_item(index) {
136− var indicatorRow = findChild(panel.indicators, "indicatorRow");
146+ var indicatorItem = findChild(panel, root.originalModelData[index]["identifier"]+"-panelItem");
137− verify(indicatorRow !== null);
138−
139− return findChild(indicatorRow.row, "item" + index);
140− }
141−
142− function get_indicator_item_position(index) {
143− var indicatorRow = findChild(panel.indicators, "indicatorRow");
144− verify(indicatorRow !== null);
145−
146− var indicatorItem = get_indicator_item(index);
147 verify(indicatorItem !== null);
147 verify(indicatorItem !== null);
148
148
149− return panel.mapFromItem(indicatorItem, indicatorItem.width/2, indicatorItem.height/2);
149+ return indicatorItem;
150− }
151−
152− // Pressing on the indicator panel should activate the indicator hints
153− // and expose a portion of the conent.
154− function test_hint() {
155− var indicatorItemCoord = get_indicator_item_position(0);
156−
157− touchPress(panel, indicatorItemCoord.x, panel.indicators.panelHeight / 2);
158−
159− // hint animation should be run, meaning that indicators will move downwards
160− // by hintValue pixels without any drag taking place
161− tryCompareFunction(function() { return panel.indicators.height },
162− panel.indicators.panelHeight + panel.indicators.hintValue);
163− tryCompare(panel.indicators, "partiallyOpened", true);
164− tryCompare(panel.indicators, "fullyOpened", false);
165−
166− touchRelease(panel, indicatorItemCoord.x, panel.indicators.panelHeight/2);
167− }
168−
169− // Pressing on the top edge of the screen should have no effect if the panel
170− // is hidden (fullscreen), which is the case when a fullscreen app is being shown
171− function test_noHintOnFullscreenMode() {
172− panel.fullscreenMode = true;
173− // Wait for the indicators to get into position.
174− // (switches between normal and fullscreen modes are animated)
175− var indicatorArea = findChild(panel, "indicatorArea");
176− tryCompare(indicatorArea, "y", -panel.panelHeight);
177−
178− var indicatorItemCoord = get_indicator_item_position(0);
179−
180− touchPress(panel, indicatorItemCoord.x, panel.indicators.panelHeight / 2);
181−
182− // Give some time for a hint animation to change things, if any
183− wait(500);
184−
185− // no hint animation when fullscreen
186− compare(panel.indicators.partiallyOpened, false,
187− "Indicator should not be partially opened when panel is pressed in" +
188− " fullscreenmode");
189− compare(panel.indicators.fullyOpened, false, "Indicator should not be partially" +
190− " opened when panel is pressed in fullscreenmode");
191−
192− touchRelease(panel, indicatorItemCoord.x, panel.panelHeight/2);
193− }
194−
195− // Pressing on the top edge of the indicator should have no effect if the panel
196− // has an active call
197− function test_noHintOnActiveCall() {
198− callManager.foregroundCall = phoneCall;
199−
200− var indicatorItemCoord = get_indicator_item_position(0);
201−
202− touchPress(panel, indicatorItemCoord.x, panel.callHint.height + panel.indicators.panelHeight / 2);
203−
204− // Give some time for a hint animation to change things, if any
205− wait(500);
206−
207− // no hint animation when fullscreen
208− compare(panel.indicators.partiallyOpened, false,
209− "Indicator should not be partially opened when panel is pressed in" +
210− " fullscreenmode");
211− compare(panel.indicators.fullyOpened, false, "Indicator should not be partially" +
212− " opened when panel is pressed in fullscreenmode");
213−
214− touchRelease(panel, indicatorItemCoord.x, panel.panelHeight/2);
215 }
150 }
216
151
217 function test_drag_show_data() {
152 function test_drag_show_data() {
218 return [
153 return [
219− { tag: "pinned", fullscreenFlag: false, alreadyOpen: false, call: null,
154+ { tag: "pinned", fullscreen: false, call: null,
220 indicatorY: 0 },
155 indicatorY: 0 },
221− { tag: "fullscreen", fullscreenFlag: true, alreadyOpen: false, call: null,
156+ { tag: "fullscreen", fullscreen: true, call: null,
222 indicatorY: -panel.panelAndSeparatorHeight },
157 indicatorY: -panel.panelAndSeparatorHeight },
223− { tag: "pinned-alreadyOpen", fullscreenFlag: false, alreadyOpen: true, call: null,
224− indicatorY: 0 },
225− { tag: "fullscreen-alreadyOpen", fullscreenFlag: true, alreadyOpen: true, call: null,
226− indicatorY: 0 },
227− { tag: "pinned-callActive", fullscreenFlag: false, alreadyOpen: false, call: phoneCall,
158+ { tag: "pinned-callActive", fullscreen: false, call: phoneCall,
228 indicatorY: 0},
159 indicatorY: 0},
229− { tag: "fullscreen-callActive", fullscreenFlag: true, alreadyOpen: false, call: phoneCall,
160+ { tag: "fullscreen-callActive", fullscreen: true, call: phoneCall,
230 indicatorY: -panel.panelAndSeparatorHeight }
161 indicatorY: -panel.panelAndSeparatorHeight }
231 ];
162 ];
232 }
163 }
@@ -234,16 +165,12 @@ Item {
234 // Dragging from a indicator item in the panel will gradually expose the
165 // Dragging from a indicator item in the panel will gradually expose the
235 // indicators, first by running the hint animation, then after dragging down will
166 // indicators, first by running the hint animation, then after dragging down will
236 // expose more of the panel, binding it to the selected indicator and opening it's menu.
167 // expose more of the panel, binding it to the selected indicator and opening it's menu.
168+ // Tested from first Y pixel to check for swipe from offscreen.
237 function test_drag_show(data) {
169 function test_drag_show(data) {
238− panel.fullscreenMode = data.fullscreenFlag;
170+ panel.fullscreenMode = data.fullscreen;
239 callManager.foregroundCall = data.call;
171 callManager.foregroundCall = data.call;
240
172
241− if (data.alreadyOpen) {
242− panel.indicators.show();
243− tryCompare(panel.indicators, "fullyOpened", true);
244− }
245−
246− var indicatorRow = findChild(panel.indicators, "indicatorRow");
173+ var indicatorRow = findChild(panel.indicators, "indicatorItemRow");
247 verify(indicatorRow !== null);
174 verify(indicatorRow !== null);
248
175
249 var menuContent = findChild(panel.indicators, "menuContent");
176 var menuContent = findChild(panel.indicators, "menuContent");
@@ -256,25 +183,16 @@ Item {
256 // (switches between normal and fullscreen modes are animated)
183 // (switches between normal and fullscreen modes are animated)
257 tryCompareFunction(function() { return indicatorArea.y }, data.indicatorY);
184 tryCompareFunction(function() { return indicatorArea.y }, data.indicatorY);
258
185
259− // do this for each indicator item
186+ for (var i = 0; i < root.originalModelData.length; i++) {
260− for (var i = 0; i < indicatorRow.row.count; i++) {
261−
262 var indicatorItem = get_indicator_item(i);
187 var indicatorItem = get_indicator_item(i);
263− verify(indicatorItem !== null);
264
188
265− if (!indicatorItem.visible)
189+ var startXPosition = root.mapFromItem(indicatorItem, indicatorItem.width / 2, 0).x;
266− continue;
190+ var startYPosition = root.mapFromItem(panel, 0, 0).y;
267
191
268− var indicatorItemCoord = get_indicator_item_position(i);
269−
270− touchPress(panel,
271− indicatorItemCoord.x, panel.indicators.panelHeight / 2);
272−
273− // 1) Drag the mouse down
274 touchFlick(panel,
192 touchFlick(panel,
275− indicatorItemCoord.x, panel.indicators.panelHeight / 2,
193+ startXPosition, startYPosition,
276− indicatorItemCoord.x, panel.height,
194+ startXPosition, panel.height,
277− false /* beginTouch */, false /* endTouch */);
195+ true /* beginTouch */, false /* endTouch */, units.gu(5), 15);
278
196
279 // Indicators height should follow the drag, and therefore increase accordingly.
197 // Indicators height should follow the drag, and therefore increase accordingly.
280 // They should be at least half-way through the screen
198 // They should be at least half-way through the screen
@@ -282,56 +200,109 @@ Item {
282 function() {return panel.indicators.height >= panel.height * 0.5},
200 function() {return panel.indicators.height >= panel.height * 0.5},
283 true);
201 true);
284
202
285− touchRelease(panel, indicatorItemCoord.x, panel.height);
203+ touchRelease(panel, startXPosition, panel.height);
286
204
287− compare(indicatorRow.currentItem, indicatorItem,
288− "Incorrect item activated at position " + i);
205+ compare(indicatorRow.currentItemIndex, i, "Indicator item should be activated at position " + i);
289− compare(menuContent.currentMenuIndex, i, "Menu conetent should be enabled for item at position " + i);
206+ compare(menuContent.currentMenuIndex, i, "Menu conetent should be activated for item at position " + i);
290
207
291 // init for next indicatorItem
208 // init for next indicatorItem
292− if (!data.alreadyOpen) {
293− panel.indicators.hide();
209+ panel.indicators.hide();
294− tryCompare(panel.indicators.hideAnimation, "running", false);
210+ tryCompare(panel.indicators.hideAnimation, "running", false);
295− tryCompare(panel.indicators, "state", "initial");
211+ tryCompare(panel.indicators, "state", "initial");
296− }
297 }
212 }
298 }
213 }
299
214
300− // Test the vertical velocity check when flicking the indicators open at an angle.
215+ function test_drag_hide_data() {
301− // If the vertical velocity is above a specific point, we shouldnt change active indicators
216+ return [
302− // if the x position changes
217+ { tag: "pinned", fullscreen: false, call: null,
303− function test_vertical_velocity_detector() {
218+ indicatorY: 0 },
219+ { tag: "fullscreen", fullscreen: true, call: null,
220+ indicatorY: -panel.panelAndSeparatorHeight },
221+ { tag: "pinned-callActive", fullscreen: false, call: phoneCall,
222+ indicatorY: 0},
223+ { tag: "fullscreen-callActive", fullscreen: true, call: phoneCall,
224+ indicatorY: -panel.panelAndSeparatorHeight }
225+ ];
226+ }
227+
228+ // Dragging the shown indicators up from bottom of panel will hide the indicators
229+ // Tested from last Y pixel to check for swipe from offscreen.
230+ function test_drag_hide(data) {
304− panel.fullscreenMode = false;
231+ panel.fullscreenMode = data.fullscreen;
232+ callManager.foregroundCall = data.call;
305
233
306− var indicatorRow = findChild(panel.indicators, "indicatorRow");
234+ var indicatorRow = findChild(panel.indicators, "indicatorItemRow");
307 verify(indicatorRow !== null);
235 verify(indicatorRow !== null);
308
236
309− // Get the first indicator
237+ var menuContent = findChild(panel.indicators, "menuContent");
310− var indicatorItemFirst = get_indicator_item(0);
311− verify(indicatorItemFirst !== null);
238+ verify(menuContent !== null);
312
239
313− var indicatorItemCoordFirst = get_indicator_item_position(0);
240+ var indicatorArea = findChild(panel, "indicatorArea");
314− var indicatorItemCoordNext = get_indicator_item_position(indicatorRow.row.count - 1);
241+ verify(indicatorArea !== null);
315
242
316− touchPress(panel,
243+ // Wait for the indicators to get into position.
317− indicatorItemCoordFirst.x, panel.indicators.panelHeight / 2);
244+ // (switches between normal and fullscreen modes are animated)
245+ tryCompareFunction(function() { return indicatorArea.y }, data.indicatorY);
246+
247+ var startXPosition = root.mapFromItem(panel.indicators, panel.indicators.width / 2, 0).x;
248+ var startYPosition = root.mapFromItem(panel, 0, panel.height).y;
249+
250+ panel.indicators.show();
251+ tryCompare(panel.indicators.showAnimation, "running", false);
252+ tryCompare(panel.indicators, "unitProgress", 1);
253+
254+
255+ touchFlick(panel.indicators,
256+ startXPosition, startYPosition,
257+ startXPosition, 0,
258+ true /* beginTouch */, false /* endTouch */, units.gu(5), 15);
318
259
319− // 1) Drag the mouse down to hint a bit
260+ // Indicators height should follow the drag, and therefore increase accordingly.
320− touchFlick(panel,
261+ // They should be at least half-way through the screen
321− indicatorItemCoordFirst.x, panel.indicators.panelHeight / 2,
262+ tryCompareFunction(
322− indicatorItemCoordFirst.x, panel.indicators.panelHeight * 2,
263+ function() {return panel.indicators.height <= panel.height * 0.5},
323− false /* beginTouch */, false /* endTouch */);
264+ true);
324
265
325− tryCompare(indicatorRow, "currentItem", indicatorItemFirst)
266+ touchRelease(panel.indicators, startXPosition, 0);
326
267
327− // 1) Flick mouse down to bottom
268+ tryCompare(panel.indicators.hideAnimation, "running", true);
328− touchFlick(panel,
269+ tryCompare(panel.indicators.hideAnimation, "running", false);
329− indicatorItemCoordFirst.x, panel.indicators.panelHeight * 2,
270+ tryCompare(panel.indicators, "state", "initial");
330− indicatorItemCoordNext.x, panel.height,
271+ }
331− false /* beginTouch */, true /* endTouch */,
272+
332− units.gu(10) /* speed */, 30 /* iterations */); // more samples needed for accurate velocity
273+ function test_hint_data() {
274+ return [
275+ { tag: "normal", fullscreen: false, call: null, hintExpected: true},
276+ { tag: "fullscreen", fullscreen: true, call: null, hintExpected: false},
277+ { tag: "call hint", fullscreen: false, call: phoneCall, hintExpected: false},
278+ ];
279+ }
280+
281+ function test_hint(data) {
282+ panel.fullscreenMode = data.fullscreen;
283+ callManager.foregroundCall = data.call;
284+
285+ if (data.fullscreen) {
286+ // Wait for the indicators to get into position.
287+ // (switches between normal and fullscreen modes are animated)
288+ var indicatorArea = findChild(panel, "indicatorArea");
289+ tryCompare(indicatorArea, "y", -panel.panelHeight);
290+ }
291+
292+ var indicatorItem = get_indicator_item(0);
293+ var mappedPosition = root.mapFromItem(indicatorItem, indicatorItem.width / 2, indicatorItem.height / 2);
294+
295+ touchPress(panel, mappedPosition.x, panel.indicators.minimizedPanelHeight / 2);
296+
297+ // Give some time for a hint animation to change things, if any
298+ wait(500);
299+
300+ // no hint animation when fullscreen
301+ compare(panel.indicators.fullyClosed, !data.hintExpected, "Indicator should be fully closed");
302+ compare(panel.indicators.partiallyOpened, data.hintExpected, "Indicator should be partialy opened");
303+ compare(panel.indicators.fullyOpened, false, "Indicator should not be fully opened");
333
304
334− compare(indicatorRow.currentItem, indicatorItemFirst, "First indicator should still be the current item");
305+ touchRelease(panel, mappedPosition.x, panel.minimizedPanelHeight / 2);
335 }
306 }
336 }
307 }
337}
308}
tests/qmltests/Panel/tst_SearchIndicator.qml−1
@@ -16,7 +16,6 @@
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18import QtTest 1.0
18import QtTest 1.0
19−import ".."
20import "../../../qml/Panel"
19import "../../../qml/Panel"
21import Ubuntu.Components 0.1
20import Ubuntu.Components 0.1
22import Unity.Test 0.1 as UT
21import Unity.Test 0.1 as UT
tests/qmltests/Stages/tst_PhoneStage.qml+17
@@ -314,6 +314,23 @@ Item {
314 tryCompare(app, "orientation", Qt.LandscapeOrientation);
314 tryCompare(app, "orientation", Qt.LandscapeOrientation);
315 }
315 }
316
316
317+ function test_backgroundClickCancelsSpread() {
318+ addApps(3);
319+
320+ var focusedAppId = ApplicationManager.focusedApplicationId;
321+
322+ goToSpread();
323+
324+ mouseClick(phoneStage, units.gu(1), units.gu(1));
325+
326+ // Make sure the spread is in the idle position
327+ var spreadView = findChild(phoneStage, "spreadView");
328+ tryCompare(spreadView, "contentX", -spreadView.shift);
329+
330+ // Make sure the same app is still focused
331+ compare(focusedAppId, ApplicationManager.focusedApplicationId);
332+ }
333+
317 function cleanup() {
334 function cleanup() {
318 while (ApplicationManager.count > 1) {
335 while (ApplicationManager.count > 1) {
319 var oldCount = ApplicationManager.count;
336 var oldCount = ApplicationManager.count;
tests/qmltests/tst_Shell.qml+133−44
@@ -25,14 +25,16 @@ import Ubuntu.Components 1.1
25import Ubuntu.Telephony 0.1 as Telephony
25import Ubuntu.Telephony 0.1 as Telephony
26import Unity.Application 0.1
26import Unity.Application 0.1
27import Unity.Connectivity 0.1
27import Unity.Connectivity 0.1
28+import Unity.Notifications 1.0
28import Unity.Test 0.1 as UT
29import Unity.Test 0.1 as UT
29import Powerd 0.1
30import Powerd 0.1
30
31
31import "../../qml"
32import "../../qml"
32
33
33−Row {
34+Item {
34 id: root
35 id: root
35− spacing: 0
36+ width: units.gu(60)
37+ height: units.gu(71)
36
38
37 QtObject {
39 QtObject {
38 id: applicationArguments
40 id: applicationArguments
@@ -50,47 +52,43 @@ Row {
50 }
52 }
51 }
53 }
52
54
55+ Row {
56+ anchors.fill: parent
53− Loader {
57+ Loader {
54− id: shellLoader
58+ id: shellLoader
55−
59+
56− // Copied from Shell.qml
57− property bool tablet: false
58− width: tablet ? units.gu(160)
59− : applicationArguments.hasGeometry() ? applicationArguments.width()
60− : units.gu(40)
61− height: tablet ? units.gu(100)
62− : applicationArguments.hasGeometry() ? applicationArguments.height()
63− : units.gu(71)
64−
65− property bool itemDestroyed: false
60+ property bool itemDestroyed: false
66− sourceComponent: Component {
61+ sourceComponent: Component {
67− Shell {
62+ Shell {
63+ property string indicatorProfile: "phone"
64+
68− Component.onDestruction: {
65+ Component.onDestruction: {
69− shellLoader.itemDestroyed = true;
66+ shellLoader.itemDestroyed = true;
67+ }
70 }
68 }
71 }
69 }
72 }
70 }
73− }
74
71
75− Rectangle {
72+ Rectangle {
76− color: "white"
73+ color: "white"
77− width: units.gu(30)
74+ width: units.gu(30)
78− height: shellLoader.height
75+ height: shellLoader.height
79−
76+
80− Column {
77+ Column {
81− anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
78+ anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
82− spacing: units.gu(1)
79+ spacing: units.gu(1)
83− Row {
80+ Row {
84− anchors { left: parent.left; right: parent.right }
81+ anchors { left: parent.left; right: parent.right }
85− Button {
82+ Button {
86− text: "Show Greeter"
83+ text: "Show Greeter"
87− onClicked: {
84+ onClicked: {
88− if (shellLoader.status !== Loader.Ready)
85+ if (shellLoader.status !== Loader.Ready)
89− return;
86+ return;
90−
87+
91− var greeter = testCase.findChild(shellLoader.item, "greeter");
88+ var greeter = testCase.findChild(shellLoader.item, "greeter");
92− if (!greeter.shown) {
89+ if (!greeter.shown) {
93− greeter.show();
90+ greeter.show();
91+ }
94 }
92 }
95 }
93 }
96 }
94 }
@@ -98,6 +96,31 @@ Row {
98 }
96 }
99 }
97 }
100
98
99+ Component {
100+ id: mockNotification
101+
102+ QtObject {
103+ function invokeAction(actionId) {
104+ mockNotificationsModel.actionInvoked(actionId)
105+ }
106+ }
107+ }
108+ ListModel {
109+ id: mockNotificationsModel
110+
111+ signal actionInvoked(string actionId)
112+
113+ function getRaw(id) {
114+ return mockNotification.createObject(mockNotificationsModel)
115+ }
116+
117+ onActionInvoked: {
118+ if(actionId == "ok_id") {
119+ mockNotificationsModel.clear()
120+ }
121+ }
122+ }
123+
101 SignalSpy {
124 SignalSpy {
102 id: launcherShowDashHomeSpy
125 id: launcherShowDashHomeSpy
103 signalName: "showDashHome"
126 signalName: "showDashHome"
@@ -119,6 +142,12 @@ Row {
119 signalName: "unlockingAllModems"
142 signalName: "unlockingAllModems"
120 }
143 }
121
144
145+ SignalSpy {
146+ id: notificationActionSpy
147+ target: mockNotificationsModel
148+ signalName: "actionInvoked"
149+ }
150+
122 Telephony.CallEntry {
151 Telephony.CallEntry {
123 id: phoneCall
152 id: phoneCall
124 phoneNumber: "+447812221111"
153 phoneNumber: "+447812221111"
@@ -132,6 +161,8 @@ Row {
132 property Item shell: shellLoader.status === Loader.Ready ? shellLoader.item : null
161 property Item shell: shellLoader.status === Loader.Ready ? shellLoader.item : null
133
162
134 function init() {
163 function init() {
164+ tryCompare(shell, "enabled", true); // enabled by greeter when ready
165+
135 swipeAwayGreeter();
166 swipeAwayGreeter();
136
167
137 sessionSpy.target = findChild(shell, "greeter")
168 sessionSpy.target = findChild(shell, "greeter")
@@ -142,6 +173,7 @@ Row {
142 }
173 }
143
174
144 function cleanup() {
175 function cleanup() {
176+ tryCompare(shell, "enabled", true); // make sure greeter didn't leave us in disabled state
145 launcherShowDashHomeSpy.target = null;
177 launcherShowDashHomeSpy.target = null;
146
178
147 shellLoader.itemDestroyed = false;
179 shellLoader.itemDestroyed = false;
@@ -178,6 +210,65 @@ Row {
178 compare(ApplicationManager.count, 1)
210 compare(ApplicationManager.count, 1)
179 }
211 }
180
212
213+ function test_snapDecisionDismissalReturnsFocus() {
214+ var notifications = findChild(shell, "notificationList");
215+ var app = ApplicationManager.startApplication("camera-app");
216+ var stage = findChild(shell, "stage")
217+ // Open an application and focus
218+ waitUntilApplicationWindowIsFullyVisible(app);
219+ ApplicationManager.focusApplication(app);
220+ tryCompare(app.session.surface, "activeFocus", true);
221+
222+ notifications.model = mockNotificationsModel;
223+
224+ // FIXME: Hack: SortFilterProxyModelQML doesn't work with QML ListModels which we use
225+ // for mocking here (RoleType can't be found in the QML model). As we only need to show
226+ // one SnapDecision lets just disable the filtering and make appear any notification as a
227+ // SnapDecision.
228+ var snapDecisionProxyModel = findInvisibleChild(shell, "snapDecisionProxyModel");
229+ snapDecisionProxyModel.filterRegExp = RegExp("");
230+
231+ // Pop-up a notification
232+ addSnapDecisionNotification();
233+ waitForRendering(shell);
234+
235+ // Make sure the notification really opened
236+ var notification = findChild(notifications, "notification" + (mockNotificationsModel.count - 1));
237+ verify(notification !== undefined && notification != null, "notification wasn't found");
238+ tryCompare(notification, "height", notification.implicitHeight)
239+ waitForRendering(notification);
240+
241+ // Make sure activeFocus went away from the app window
242+ tryCompare(app.session.surface, "activeFocus", false);
243+ tryCompare(stage, "interactive", false);
244+
245+ // Clicking the button should dismiss the notification and return focus
246+ var buttonAccept = findChild(notification, "notify_button0");
247+ mouseClick(buttonAccept, buttonAccept.width / 2, buttonAccept.height / 2);
248+
249+ // Make sure we're back to normal
250+ tryCompare(app.session.surface, "activeFocus", true);
251+ compare(stage.interactive, true, "Stages not interactive again after modal notification has closed");
252+ }
253+
254+ function addSnapDecisionNotification() {
255+ var n = {
256+ type: Notification.SnapDecision,
257+ hints: {"x-canonical-private-affirmative-tint": "true"},
258+ summary: "Tom Ato",
259+ body: "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.",
260+ icon: "../graphics/avatars/funky.png",
261+ secondaryIcon: "../graphics/applicationIcons/facebook.png",
262+ actions: [{ id: "ok_id", label: "Ok"},
263+ { id: "cancel_id", label: "Cancel"},
264+ { id: "notreally_id", label: "Not really"},
265+ { id: "noway_id", label: "messages:No way"},
266+ { id: "nada_id", label: "messages:Nada"}]
267+ }
268+
269+ mockNotificationsModel.append(n)
270+ }
271+
181 function test_leftEdgeDrag_data() {
272 function test_leftEdgeDrag_data() {
182 return [
273 return [
183 {tag: "without launcher", revealLauncher: false, swipeLength: units.gu(27), appHides: true, focusedApp: "dialer-app", launcherHides: true},
274 {tag: "without launcher", revealLauncher: false, swipeLength: units.gu(27), appHides: true, focusedApp: "dialer-app", launcherHides: true},
@@ -294,7 +385,7 @@ Row {
294 tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
385 tryCompare(ApplicationManager, "focusedApplicationId", "unity8-dash");
295
386
296 compare(dashCommunicatorSpy.count, 1);
387 compare(dashCommunicatorSpy.count, 1);
297− compare(dashCommunicatorSpy.signalArguments[0][0], "clickscope");
388+ compare(dashCommunicatorSpy.signalArguments[0][0], 0);
298 }
389 }
299
390
300 function test_showInputMethod() {
391 function test_showInputMethod() {
@@ -345,7 +436,7 @@ Row {
345 var touchX = shell.width / 2;
436 var touchX = shell.width / 2;
346 var indicators = findChild(shell, "indicators");
437 var indicators = findChild(shell, "indicators");
347 touchFlick(indicators,
438 touchFlick(indicators,
348− touchX /* fromX */, indicators.panelHeight * 0.5 /* fromY */,
439+ touchX /* fromX */, indicators.minimizedPanelHeight * 0.5 /* fromY */,
349 touchX /* toX */, shell.height * 0.5 /* toY */,
440 touchX /* toX */, shell.height * 0.5 /* toY */,
350 true /* beginTouch */, false /* endTouch */);
441 true /* beginTouch */, false /* endTouch */);
351 verify(indicators.partiallyOpened);
442 verify(indicators.partiallyOpened);
@@ -382,7 +473,7 @@ Row {
382 var indicators = findChild(shell, "indicators");
473 var indicators = findChild(shell, "indicators");
383
474
384 var touchStartX = shell.width / 2;
475 var touchStartX = shell.width / 2;
385− var touchStartY = shell.height - (indicators.panelHeight * 0.5);
476+ var touchStartY = shell.height - (indicators.minimizedPanelHeight * 0.5);
386 touchFlick(shell,
477 touchFlick(shell,
387 touchStartX, touchStartY,
478 touchStartX, touchStartY,
388 touchStartX, shell.height * 0.1);
479 touchStartX, shell.height * 0.1);
@@ -558,8 +649,6 @@ Row {
558 }
649 }
559
650
560 function test_unlockAllModemsOnBoot() {
651 function test_unlockAllModemsOnBoot() {
561− // TODO reenable when service ready (LP: #1361074)
562− expectFail("", "Unlock on boot temporarily disabled");
563 tryCompare(unlockAllModemsSpy, "count", 1)
652 tryCompare(unlockAllModemsSpy, "count", 1)
564 }
653 }
565
654
tests/qmltests/tst_ShellWithPin.qml+5
@@ -61,6 +61,8 @@ Item {
61 property bool itemDestroyed: false
61 property bool itemDestroyed: false
62 sourceComponent: Component {
62 sourceComponent: Component {
63 Shell {
63 Shell {
64+ property string indicatorProfile: "phone"
65+
64 Component.onDestruction: {
66 Component.onDestruction: {
65 shellLoader.itemDestroyed = true
67 shellLoader.itemDestroyed = true
66 }
68 }
@@ -127,6 +129,7 @@ Item {
127 property Item shell: shellLoader.status === Loader.Ready ? shellLoader.item : null
129 property Item shell: shellLoader.status === Loader.Ready ? shellLoader.item : null
128
130
129 function init() {
131 function init() {
132+ tryCompare(shell, "enabled", true); // will be enabled when greeter is all ready
130 sessionSpy.target = findChild(shell, "greeter")
133 sessionSpy.target = findChild(shell, "greeter")
131 swipeAwayGreeter()
134 swipeAwayGreeter()
132 waitForLockscreen()
135 waitForLockscreen()
@@ -135,6 +138,8 @@ Item {
135 }
138 }
136
139
137 function cleanup() {
140 function cleanup() {
141+ tryCompare(shell, "enabled", true); // make sure greeter didn't leave us in disabled state
142+
138 shellLoader.itemDestroyed = false
143 shellLoader.itemDestroyed = false
139
144
140 shellLoader.active = false
145 shellLoader.active = false
tests/qmltests/tst_TabletShell.qml+5
@@ -57,6 +57,8 @@ Row {
57 property bool itemDestroyed: false
57 property bool itemDestroyed: false
58 sourceComponent: Component {
58 sourceComponent: Component {
59 Shell {
59 Shell {
60+ property string indicatorProfile: "phone"
61+
60 Component.onDestruction: {
62 Component.onDestruction: {
61 shellLoader.itemDestroyed = true
63 shellLoader.itemDestroyed = true
62 }
64 }
@@ -119,12 +121,15 @@ Row {
119 property Item shell: shellLoader.status === Loader.Ready ? shellLoader.item : null
121 property Item shell: shellLoader.status === Loader.Ready ? shellLoader.item : null
120
122
121 function init() {
123 function init() {
124+ tryCompare(shell, "enabled", true); // will be enabled when greeter is all ready
122 sessionSpy.clear()
125 sessionSpy.clear()
123 sessionSpy.target = findChild(shell, "greeter")
126 sessionSpy.target = findChild(shell, "greeter")
124 dashCommunicatorSpy.target = findInvisibleChild(shell, "dashCommunicator")
127 dashCommunicatorSpy.target = findInvisibleChild(shell, "dashCommunicator")
125 }
128 }
126
129
127 function cleanup() {
130 function cleanup() {
131+ tryCompare(shell, "enabled", true); // make sure greeter didn't leave us in disabled state
132+
128 shellLoader.itemDestroyed = false
133 shellLoader.itemDestroyed = false
129
134
130 shellLoader.active = false
135 shellLoader.active = false
tests/uqmlscene/main.cpp+5−5
@@ -428,6 +428,10 @@ int main(int argc, char ** argv)
428
428
429 MouseTouchAdaptor mouseTouchAdaptor;
429 MouseTouchAdaptor mouseTouchAdaptor;
430 app.installNativeEventFilter(&mouseTouchAdaptor);
430 app.installNativeEventFilter(&mouseTouchAdaptor);
431+ // Create it before loading the module, so that TestUtil
432+ // doesn't create one with a fake timer factory.
433+ // When interacting manually with a test we want the real deal.
434+ new TouchRegistry;
431
435
432#ifndef QT_NO_TRANSLATION
436#ifndef QT_NO_TRANSLATION
433 QTranslator translator;
437 QTranslator translator;
@@ -499,11 +503,7 @@ int main(int argc, char ** argv)
499 QQuickItem *contentItem = qobject_cast<QQuickItem *>(topLevel);
503 QQuickItem *contentItem = qobject_cast<QQuickItem *>(topLevel);
500 if (contentItem) {
504 if (contentItem) {
501 qxView = new QQuickView(&engine, nullptr);
505 qxView = new QQuickView(&engine, nullptr);
502− if (TouchRegistry::instance() == nullptr) {
503− new TouchRegistry(qxView);
504− } else {
505− TouchRegistry::instance()->setParent(qxView);
506+ TouchRegistry::instance()->setParent(qxView);
506− }
507 qxView->installEventFilter(TouchRegistry::instance());
507 qxView->installEventFilter(TouchRegistry::instance());
508 window = qxView;
508 window = qxView;
509 // Set window default properties; the qml can still override them
509 // Set window default properties; the qml can still override them
tests/utils/modules/Unity/Test/testutil.cpp+10−22
@@ -31,7 +31,6 @@ TestUtil::TestUtil(QObject *parent)
31 : QObject(parent)
31 : QObject(parent)
32 , m_targetWindow(0)
32 , m_targetWindow(0)
33 , m_touchDevice(0)
33 , m_touchDevice(0)
34− , m_installedTouchRegistry(false)
35{
34{
36}
35}
37
36
@@ -83,30 +82,19 @@ void TestUtil::ensureTouchDevice()
83
82
84void TestUtil::ensureTouchRegistryInstalled()
83void TestUtil::ensureTouchRegistryInstalled()
85{
84{
86− if (m_installedTouchRegistry)
85+ if (TouchRegistry::instance())
87 return;
86 return;
88
87
89− TouchRegistry *touchRegistry;
88+ if (!m_targetWindow)
90− if (TouchRegistry::instance() == nullptr) {
89+ return;
91− // Tests can be *very* slow to run and we don't want things timing out because
92− // of that. So give it fake timers to use (they will never time out)
93− touchRegistry = new TouchRegistry(this, new FakeTimerFactory);
94− } else {
95− touchRegistry = TouchRegistry::instance();
96− if (touchRegistry->parent() != this) {
97− // someone else created it. leave it alone
98− m_installedTouchRegistry = true;
99− }
100− }
101
90
102− ensureTargetWindow();
91+ // Tests can be *very* slow to run and we don't want things timing out because
92+ // of that. So give it fake timers to use (they will never time out)
93+ TouchRegistry *touchRegistry = new TouchRegistry(this, new FakeTimerFactory);
103
94
104− if (m_targetWindow) {
105− QQuickView *view = qobject_cast<QQuickView*>(m_targetWindow);
95+ QQuickView *view = qobject_cast<QQuickView*>(m_targetWindow);
106− if (view) {
96+ if (view) {
107− view->installEventFilter(touchRegistry);
97+ view->installEventFilter(touchRegistry);
108− touchRegistry->setParent(view);
98+ touchRegistry->setParent(view);
109− m_installedTouchRegistry = true;
110− }
111 }
99 }
112}
100}
tests/utils/modules/Unity/Test/testutil.h−1
@@ -42,7 +42,6 @@ private:
42
42
43 QWindow *m_targetWindow;
43 QWindow *m_targetWindow;
44 QTouchDevice *m_touchDevice;
44 QTouchDevice *m_touchDevice;
45− bool m_installedTouchRegistry;
46};
45};
47
46
48QML_DECLARE_TYPE(TestUtil)
47QML_DECLARE_TYPE(TestUtil)