on xenial help sign in
merge trunk
c00ac66Lukáš Tinkl
CMakeLists.txt+3
@@ -101,6 +101,9 @@ add_definitions(-DQT_NO_KEYWORDS)
101# Save a few container detach and conversions
101# Save a few container detach and conversions
102add_definitions(-DQT_STRICT_ITERATORS)
102add_definitions(-DQT_STRICT_ITERATORS)
103
103
104+# Use the fast string builder
105+add_definitions(-DQT_USE_QSTRINGBUILDER)
106+
104# Autopilot tests
107# Autopilot tests
105include(autopilot)
108include(autopilot)
106declare_autopilot_test(shell unity8.shell ${CMAKE_SOURCE_DIR}/tests/autopilot/)
109declare_autopilot_test(shell unity8.shell ${CMAKE_SOURCE_DIR}/tests/autopilot/)
data/unity8.conf+10
@@ -46,6 +46,16 @@ pre-start script
46 initctl set-env --global MIR_SERVER_PROMPT_FILE=1
46 initctl set-env --global MIR_SERVER_PROMPT_FILE=1
47
47
48 initctl emit --no-wait indicator-services-start
48 initctl emit --no-wait indicator-services-start
49+
50+ # Disable Qt's stuttering 'touch compression' to fix scrolling smoothness
51+ # issues (LP: #1486341). As a bonus, this eliminates most of the
52+ # lag seen in the indicator panel pull-down (LP: #1488327) and also
53+ # reduces lag seen in apps:
54+ initctl set-env --global QML_NO_TOUCH_COMPRESSION=1
55+
56+ # For twice the fun and half the latency, try this (Warning: not all
57+ # devices are fast enough to keep up smoothly yet)...
58+ # initctl set-env MIR_SERVER_NBUFFERS=2
49end script
59end script
50
60
51exec ${BINARY:-unity8} $ARGS
61exec ${BINARY:-unity8} $ARGS
debian/changelog+36
@@ -1,3 +1,39 @@
1+unity8 (8.11+15.10.20151021-0ubuntu1) wily; urgency=medium
2+
3+ [ Albert Astals Cid ]
4+ * Clazy fixes
5+ * Enable Efficient String Construction by default
6+
7+ [ CI Train Bot ]
8+ * New rebuild forced.
9+
10+ [ Daniel d'Andrada ]
11+ * Have unity8 drawing its own cursor (LP: #1488417)
12+ * Initial multi-monitor support
13+
14+ [ Daniel van Vugt ]
15+ * Disable Qt's stuttering 'touch compression' to fix scrolling
16+ smoothness (LP: #1486341, #1488327)
17+
18+ [ Lukáš Tinkl ]
19+ * Fix autopilot wizard test skipping the reporting page
20+ * Implement Unity.Platform plugin wrapping org.freedesktop.hostname1
21+ (LP: #1504318)
22+ * React to window title (aka surface name) changes (LP: #1497092)
23+ * Rotate the screenshots according to the actual orientation
24+
25+ [ Michał Sawicz ]
26+ * Fix application API dependency
27+ * Have unity8 drawing its own cursor (LP: #1488417)
28+ * Initial multi-monitor support
29+ * Rotate the screenshots according to the actual orientation
30+
31+ [ Nick Dedekind ]
32+ * Fixed leak in UnityMenuModelStackTest
33+ * Moved time translation to SDK (LP: #1372061)
34+
35+ -- Michał Sawicz <michal.sawicz@canonical.com> Wed, 21 Oct 2015 11:51:53 +0000
36+
1unity8 (8.11+15.10.20151009-0ubuntu1) wily; urgency=medium
37unity8 (8.11+15.10.20151009-0ubuntu1) wily; urgency=medium
2
38
3 [ CI Train Bot ]
39 [ CI Train Bot ]
debian/control+3−3
@@ -50,7 +50,7 @@ Build-Depends: cmake,
50 qtdeclarative5-private-dev (>= 5.2.1),
50 qtdeclarative5-private-dev (>= 5.2.1),
51 qtdeclarative5-qtmultimedia-plugin,
51 qtdeclarative5-qtmultimedia-plugin,
52 qtdeclarative5-ubuntu-settings-components (>= 0.6),
52 qtdeclarative5-ubuntu-settings-components (>= 0.6),
53− qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 1.1.1239) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 1.1.1239),
53+ qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 1.3.1627) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 1.3.1627),
54 qtdeclarative5-ubuntu-web-plugin,
54 qtdeclarative5-ubuntu-web-plugin,
55 ttf-ubuntu-font-family,
55 ttf-ubuntu-font-family,
56Standards-Version: 3.9.4
56Standards-Version: 3.9.4
@@ -65,7 +65,7 @@ X-Ubuntu-Use-Langpack: yes
65Package: indicators-client
65Package: indicators-client
66Architecture: amd64 armhf i386
66Architecture: amd64 armhf i386
67Depends: qmenumodel-qml (>= 0.2.9),
67Depends: qmenumodel-qml (>= 0.2.9),
68− qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 1.1.1239) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 1.1.1239),
68+ qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 1.3.1627) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 1.3.1627),
69 unity8 (= ${binary:Version}),
69 unity8 (= ${binary:Version}),
70 ${misc:Depends},
70 ${misc:Depends},
71 ${shlibs:Depends},
71 ${shlibs:Depends},
@@ -124,7 +124,7 @@ Architecture: all
124Depends: qml-module-qtquick-layouts,
124Depends: qml-module-qtquick-layouts,
125 qtdeclarative5-ubuntu-settings-components (>= 0.6),
125 qtdeclarative5-ubuntu-settings-components (>= 0.6),
126 qtdeclarative5-ubuntu-thumbnailer0.1 | ubuntu-thumbnailer-impl,
126 qtdeclarative5-ubuntu-thumbnailer0.1 | ubuntu-thumbnailer-impl,
127− qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 1.1.1239) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 1.1.1239),
127+ qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 1.3.1627) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 1.3.1627),
128 qtdeclarative5-unity-notifications-plugin (>= 0.1.2) | unity-notifications-impl,
128 qtdeclarative5-unity-notifications-plugin (>= 0.1.2) | unity-notifications-impl,
129 ubuntu-thumbnailer-impl-0,
129 ubuntu-thumbnailer-impl-0,
130 unity-application-impl-9,
130 unity-application-impl-9,
debian/unity8.install+1
@@ -12,6 +12,7 @@ usr/share/unity8/Panel
12usr/share/unity8/Rotation
12usr/share/unity8/Rotation
13usr/share/unity8/DeviceConfiguration.qml
13usr/share/unity8/DeviceConfiguration.qml
14usr/share/unity8/OrientedShell.qml
14usr/share/unity8/OrientedShell.qml
15+usr/share/unity8/DisabledScreenNotice.qml
15usr/share/unity8/Shell.qml
16usr/share/unity8/Shell.qml
16usr/share/unity8/Stages
17usr/share/unity8/Stages
17usr/share/unity8/Tutorial
18usr/share/unity8/Tutorial
plugins/Cursor/Cursor.qml+16
@@ -1,3 +1,19 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
1import QtQuick 2.4
17import QtQuick 2.4
2import Cursor 1.0 // For MousePointer
18import Cursor 1.0 // For MousePointer
3
19
plugins/IntegratedLightDM/UsersModel.cpp+7−6
@@ -43,16 +43,17 @@ MangleModel::MangleModel(QObject* parent)
43
43
44QVariant MangleModel::data(const QModelIndex &index, int role) const
44QVariant MangleModel::data(const QModelIndex &index, int role) const
45{
45{
46− QVariant data = QSortFilterProxyModel::data(index, role);
46+ QVariant variantData = QSortFilterProxyModel::data(index, role);
47
47
48 // If user's real name is empty, switch to unix name
48 // If user's real name is empty, switch to unix name
49− if (role == QLightDM::UsersModel::RealNameRole && data.toString().isEmpty()) {
49+ if (role == QLightDM::UsersModel::RealNameRole && variantData.toString().isEmpty()) {
50− data = QSortFilterProxyModel::data(index, QLightDM::UsersModel::NameRole);
50+ variantData = QSortFilterProxyModel::data(index, QLightDM::UsersModel::NameRole);
51− } else if (role == QLightDM::UsersModel::BackgroundPathRole && data.toString().startsWith('#')) {
51+ } else if (role == QLightDM::UsersModel::BackgroundPathRole && variantData.toString().startsWith('#')) {
52− data = "data:image/svg+xml,<svg><rect width='100%' height='100%' fill='" + data.toString() + "'/></svg>";
52+ const QString stringData = "data:image/svg+xml,<svg><rect width='100%' height='100%' fill='" + variantData.toString() + "'/></svg>";
53+ variantData = stringData;
53 }
54 }
54
55
55− return data;
56+ return variantData;
56}
57}
57
58
58// **** Now we continue with actual UsersModel class ****
59// **** Now we continue with actual UsersModel class ****
plugins/IntegratedLightDM/liblightdm/UsersModelPrivate.cpp+2−1
@@ -34,7 +34,8 @@ UsersModelPrivate::UsersModelPrivate(UsersModel* parent)
34 entries.reserve(users.count());
34 entries.reserve(users.count());
35 Q_FOREACH(const QString &user, users)
35 Q_FOREACH(const QString &user, users)
36 {
36 {
37− QString name = settings.value(user + "/name", user[0].toUpper() + user.mid(1)).toString();
37+ QVariant defaultValue = QString(user[0].toUpper() + user.mid(1));
38+ QString name = settings.value(user + "/name", defaultValue).toString();
38 entries.append({user, name, 0, 0, false, false, 0, 0});
39 entries.append({user, name, 0, 0, false, false, 0, 0});
39 }
40 }
40}
41}
plugins/ScreenGrabber/screengrabber.cpp+2−2
@@ -60,7 +60,7 @@ ScreenGrabber::ScreenGrabber(QObject *parent)
60 }
60 }
61}
61}
62
62
63−void ScreenGrabber::captureAndSave()
63+void ScreenGrabber::captureAndSave(int angle)
64{
64{
65 if (fileNamePrefix.isEmpty())
65 if (fileNamePrefix.isEmpty())
66 {
66 {
@@ -82,7 +82,7 @@ void ScreenGrabber::captureAndSave()
82 return;
82 return;
83 }
83 }
84
84
85− const QImage screenshot = main_window->grabWindow();
85+ const QImage screenshot = main_window->grabWindow().transformed(QTransform().rotate(angle));
86 const QString filename = makeFileName();
86 const QString filename = makeFileName();
87 qDebug() << "Saving screenshot to" << filename;
87 qDebug() << "Saving screenshot to" << filename;
88 auto saveOp = QtConcurrent::run(saveScreenshot, screenshot, filename, getFormat(), screenshotQuality);
88 auto saveOp = QtConcurrent::run(saveScreenshot, screenshot, filename, getFormat(), screenshotQuality);
plugins/ScreenGrabber/screengrabber.h+1−1
@@ -29,7 +29,7 @@ public:
29 ~ScreenGrabber() = default;
29 ~ScreenGrabber() = default;
30
30
31public Q_SLOTS:
31public Q_SLOTS:
32− void captureAndSave();
32+ void captureAndSave(int angle = 0);
33
33
34Q_SIGNALS:
34Q_SIGNALS:
35 void screenshotSaved(const QString &filename);
35 void screenshotSaved(const QString &filename);
plugins/Unity/CMakeLists.txt+1
@@ -4,3 +4,4 @@ add_subdirectory(Launcher)
4add_subdirectory(Session)
4add_subdirectory(Session)
5add_subdirectory(DashCommunicator)
5add_subdirectory(DashCommunicator)
6add_subdirectory(InputInfo)
6add_subdirectory(InputInfo)
7+add_subdirectory(Platform)
plugins/Unity/Launcher/desktopfilehandler.cpp+3−2
@@ -111,8 +111,9 @@ QString DesktopFileHandler::displayName() const
111 settings.beginGroup(QStringLiteral("Desktop Entry"));
111 settings.beginGroup(QStringLiteral("Desktop Entry"));
112
112
113 // First try to find Name[xx_YY] and Name[xx] in .desktop file
113 // First try to find Name[xx_YY] and Name[xx] in .desktop file
114− QString locale = QLocale().name();
114+ const QString locale = QLocale().name();
115− QString shortLocale = locale.split('_').first();
115+ const QStringList splitLocale = locale.split(QLatin1Char('_'));
116+ const QString shortLocale = splitLocale.first();
116
117
117 if (locale != shortLocale && settings.contains(QStringLiteral("Name[%1]").arg(locale))) {
118 if (locale != shortLocale && settings.contains(QStringLiteral("Name[%1]").arg(locale))) {
118 return settings.value(QStringLiteral("Name[%1]").arg(locale)).toString();
119 return settings.value(QStringLiteral("Name[%1]").arg(locale)).toString();
plugins/Unity/Platform/CMakeLists.txtnew file+9
@@ -0,0 +1,9 @@
1+set(platformplugin_SRCS
2+ platform.cpp
3+ plugin.cpp)
4+
5+add_library(Platform-qml SHARED ${platformplugin_SRCS})
6+
7+qt5_use_modules(Platform-qml DBus Qml)
8+
9+add_unity8_plugin(Unity.Platform 1.0 Unity/Platform TARGETS Platform-qml)
plugins/Unity/Platform/platform.cppnew file+43
@@ -0,0 +1,43 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "platform.h"
18+
19+#include <QDBusConnection>
20+
21+Platform::Platform(QObject *parent)
22+ : QObject(parent)
23+ , m_iface("org.freedesktop.hostname1", "/org/freedesktop/hostname1", "org.freedesktop.hostname1",
24+ QDBusConnection::systemBus(), this)
25+{
26+ QMetaObject::invokeMethod(this, "init");
27+}
28+
29+void Platform::init()
30+{
31+ m_chassis = m_iface.property("Chassis").toString();
32+ m_isPC = (m_chassis == "desktop" || m_chassis == "laptop" || m_chassis == "server");
33+}
34+
35+QString Platform::chassis() const
36+{
37+ return m_chassis;
38+}
39+
40+bool Platform::isPC() const
41+{
42+ return m_isPC;
43+}
plugins/Unity/Platform/platform.hnew file+58
@@ -0,0 +1,58 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef PLATFORM_H
18+#define PLATFORM_H
19+
20+#include <QDBusInterface>
21+
22+/**
23+ * @brief The Platform class
24+ *
25+ * Wrapper around platform detection support (org.freedesktop.hostname1)
26+ */
27+class Platform: public QObject
28+{
29+ Q_OBJECT
30+ /**
31+ * The chassis property
32+ *
33+ * Supported values include: "laptop", "computer", "handset" or "tablet"
34+ * For full list see: http://www.freedesktop.org/wiki/Software/systemd/hostnamed/
35+ */
36+ Q_PROPERTY(QString chassis READ chassis CONSTANT)
37+ /**
38+ * Whether the machine is an ordinary PC (desktop, laptop or server)
39+ */
40+ Q_PROPERTY(bool isPC READ isPC CONSTANT)
41+
42+public:
43+ Platform(QObject *parent = nullptr);
44+ ~Platform() = default;
45+
46+ QString chassis() const;
47+ bool isPC() const;
48+
49+private Q_SLOTS:
50+ void init();
51+
52+private:
53+ QDBusInterface m_iface;
54+ QString m_chassis;
55+ bool m_isPC;
56+};
57+
58+#endif // PLATFORM_H
plugins/Unity/Platform/plugin.cppnew file+27
@@ -0,0 +1,27 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "plugin.h"
18+#include "platform.h"
19+
20+#include <QtQml>
21+
22+void GlobalShortcutPlugin::registerTypes(const char *uri)
23+{
24+ Q_ASSERT(uri == QLatin1String("Unity.Platform"));
25+
26+ qmlRegisterSingletonType<Platform>(uri, 1, 0, "Platform", [](QQmlEngine*, QJSEngine*) -> QObject* { return new Platform; });
27+}
plugins/Utils/relativetimeformatter.h → plugins/Unity/Platform/plugin.hrenamed+10−12
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2014 Canonical Ltd.
2+ * Copyright (C) 2015 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
@@ -12,23 +12,21 @@
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 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 */
15 */
17
16
18−#ifndef RELATIVETIMEFORMATTER_H
17+#ifndef PLATFORMPLUGIN_H
19−#define RELATIVETIMEFORMATTER_H
18+#define PLATFORMPLUGIN_H
20
19
21−#include "timeformatter.h"
20+#include <QQmlExtensionPlugin>
22
21
23−// TODO - move this to the sdk
22+class GlobalShortcutPlugin: public QQmlExtensionPlugin
24−// https://blueprints.launchpad.net/ubuntu-ui-toolkit/+spec/time-formatter
25−class RelativeTimeFormatter : public GDateTimeFormatter
26{
23{
27 Q_OBJECT
24 Q_OBJECT
28−public:
25+ Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
29− RelativeTimeFormatter(QObject *parent = 0);
30
26
31− QString format() const override;
27+public:
28+ void registerTypes(const char *uri) override;
32};
29};
33
30
34−#endif // RELATIVETIMEFORMATTER_H
31+
32+#endif // PLATFORMPLUGIN_H
plugins/Unity/Platform/qmldirnew file+2
@@ -0,0 +1,2 @@
1+module Unity.Platform
2+plugin Platform-qml
plugins/Unity/Session/dbusunitysessionservice.cpp+2−1
@@ -292,7 +292,8 @@ QString DBusUnitySessionService::RealName() const
292 if (p) {
292 if (p) {
293 const QString gecos = QString::fromLocal8Bit(p->pw_gecos);
293 const QString gecos = QString::fromLocal8Bit(p->pw_gecos);
294 if (!gecos.isEmpty()) {
294 if (!gecos.isEmpty()) {
295− return gecos.split(QLatin1Char(',')).first();
295+ const QStringList splitGecos = gecos.split(QLatin1Char(','));
296+ return splitGecos.first();
296 }
297 }
297 }
298 }
298
299
plugins/Utils/CMakeLists.txt−2
@@ -15,8 +15,6 @@ set(QMLPLUGIN_SRC
15 inputwatcher.cpp
15 inputwatcher.cpp
16 qlimitproxymodelqml.cpp
16 qlimitproxymodelqml.cpp
17 unitysortfilterproxymodelqml.cpp
17 unitysortfilterproxymodelqml.cpp
18− relativetimeformatter.cpp
19− timeformatter.cpp
20 Timer.cpp
18 Timer.cpp
21 unitymenumodelpaths.cpp
19 unitymenumodelpaths.cpp
22 windowkeysfilter.cpp
20 windowkeysfilter.cpp
plugins/Utils/plugin.cpp−5
@@ -29,8 +29,6 @@
29#include "inputwatcher.h"
29#include "inputwatcher.h"
30#include "qlimitproxymodelqml.h"
30#include "qlimitproxymodelqml.h"
31#include "unitysortfilterproxymodelqml.h"
31#include "unitysortfilterproxymodelqml.h"
32−#include "relativetimeformatter.h"
33−#include "timeformatter.h"
34#include "unitymenumodelpaths.h"
32#include "unitymenumodelpaths.h"
35#include "windowkeysfilter.h"
33#include "windowkeysfilter.h"
36#include "windowscreenshotprovider.h"
34#include "windowscreenshotprovider.h"
@@ -60,11 +58,8 @@ void UtilsPlugin::registerTypes(const char *uri)
60 qmlRegisterType<QLimitProxyModelQML>(uri, 0, 1, "LimitProxyModel");
58 qmlRegisterType<QLimitProxyModelQML>(uri, 0, 1, "LimitProxyModel");
61 qmlRegisterType<UnitySortFilterProxyModelQML>(uri, 0, 1, "UnitySortFilterProxyModel");
59 qmlRegisterType<UnitySortFilterProxyModelQML>(uri, 0, 1, "UnitySortFilterProxyModel");
62 qmlRegisterType<UnityMenuModelPaths>(uri, 0, 1, "UnityMenuModelPaths");
60 qmlRegisterType<UnityMenuModelPaths>(uri, 0, 1, "UnityMenuModelPaths");
63− qmlRegisterType<TimeFormatter>(uri, 0, 1, "TimeFormatter");
64 qmlRegisterType<WindowKeysFilter>(uri, 0, 1, "WindowKeysFilter");
61 qmlRegisterType<WindowKeysFilter>(uri, 0, 1, "WindowKeysFilter");
65− qmlRegisterType<GDateTimeFormatter>(uri, 0, 1, "GDateTimeFormatter");
66 qmlRegisterType<EasingCurve>(uri, 0, 1, "EasingCurve");
62 qmlRegisterType<EasingCurve>(uri, 0, 1, "EasingCurve");
67− qmlRegisterType<RelativeTimeFormatter>(uri, 0, 1, "RelativeTimeFormatter");
68 qmlRegisterSingletonType<WindowStateStorage>(uri, 0, 1, "WindowStateStorage", createWindowStateStorage);
63 qmlRegisterSingletonType<WindowStateStorage>(uri, 0, 1, "WindowStateStorage", createWindowStateStorage);
69 qmlRegisterType<InputWatcher>(uri, 0, 1, "InputWatcher");
64 qmlRegisterType<InputWatcher>(uri, 0, 1, "InputWatcher");
70 qmlRegisterSingletonType<Constants>(uri, 0, 1, "Constants", createConstants);
65 qmlRegisterSingletonType<Constants>(uri, 0, 1, "Constants", createConstants);
plugins/Utils/relativetimeformatter.cppdeleted−260
@@ -1,260 +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−
18−// Local
19−#include "relativetimeformatter.h"
20−
21−// Qt
22−#include <QDateTime>
23−
24−// Other
25−#include <glib.h>
26−#include <glib/gi18n.h>
27−#include <locale.h>
28−#include <langinfo.h>
29−#include <string.h>
30−
31−RelativeTimeFormatter::RelativeTimeFormatter(QObject *parent)
32− : GDateTimeFormatter(parent)
33−{
34−}
35−
36− /* Check the system locale setting to see if the format is 24-hour
37− time or 12-hour time */
38−gboolean
39−is_locale_12h(void)
40−{
41− int i;
42− static const char *formats_24h[] = {"%H", "%R", "%T", "%OH", "%k", nullptr};
43− const char* t_fmt = nl_langinfo(T_FMT);
44−
45− for (i=0; formats_24h[i]!=nullptr; i++)
46− if (strstr(t_fmt, formats_24h[i]) != nullptr)
47− return FALSE;
48−
49− return TRUE;
50−}
51−
52−typedef enum
53−{
54− DATE_PROXIMITY_YESTERDAY,
55− DATE_PROXIMITY_TODAY,
56− DATE_PROXIMITY_TOMORROW,
57− DATE_PROXIMITY_LAST_WEEK,
58− DATE_PROXIMITY_NEXT_WEEK,
59− DATE_PROXIMITY_FAR
60−} date_proximity_t;
61−
62−static date_proximity_t
63−getDateProximity(GDateTime* now, GDateTime* time)
64−{
65− date_proximity_t prox = DATE_PROXIMITY_FAR;
66− gint now_year, now_month, now_day;
67− gint time_year, time_month, time_day;
68−
69− // does it happen today?
70− g_date_time_get_ymd(now, &now_year, &now_month, &now_day);
71− g_date_time_get_ymd(time, &time_year, &time_month, &time_day);
72− if ((now_year == time_year) && (now_month == time_month) && (now_day == time_day)) {
73− return DATE_PROXIMITY_TODAY;
74− }
75−
76− // did it happen yesterday?
77− GDateTime* yesterday = g_date_time_add_days(now, -1);
78− gint tom_year, tom_month, tom_day;
79− g_date_time_get_ymd(yesterday, &tom_year, &tom_month, &tom_day);
80− g_date_time_unref(yesterday);
81− if ((tom_year == time_year) && (tom_month == time_month) && (tom_day == time_day)) {
82− return DATE_PROXIMITY_YESTERDAY;
83− }
84−
85− // does it happen tomorrow?
86− GDateTime* tomorrow = g_date_time_add_days(now, 1);
87− g_date_time_get_ymd(tomorrow, &tom_year, &tom_month, &tom_day);
88− g_date_time_unref(tomorrow);
89− if ((tom_year == time_year) && (tom_month == time_month) && (tom_day == time_day)) {
90− return DATE_PROXIMITY_TOMORROW;
91− }
92−
93− // does it happen this week?
94− if (g_date_time_compare(time, now) < 0) {
95− GDateTime* last_week = g_date_time_add_days(now, -6);
96− GDateTime* last_week_bound = g_date_time_new_local(g_date_time_get_year(last_week),
97− g_date_time_get_month(last_week),
98− g_date_time_get_day_of_month(last_week),
99− 0, 0, 0);
100− if (g_date_time_compare(time, last_week_bound) >= 0)
101− prox = DATE_PROXIMITY_LAST_WEEK;
102−
103− g_date_time_unref(last_week);
104− g_date_time_unref(last_week_bound);
105− } else {
106− GDateTime* next_week = g_date_time_add_days(now, 6);
107− GDateTime* next_week_bound = g_date_time_new_local(g_date_time_get_year(next_week),
108− g_date_time_get_month(next_week),
109− g_date_time_get_day_of_month(next_week),
110− 23, 59, 59.9);
111− if (g_date_time_compare(time, next_week_bound) <= 0)
112− prox = DATE_PROXIMITY_NEXT_WEEK;
113−
114− g_date_time_unref(next_week);
115− g_date_time_unref(next_week_bound);
116− }
117−
118− return prox;
119−}
120−
121−const char*
122−dgettext_datetime(const char *text)
123−{
124− return dgettext("unity8", text);
125−}
126−
127−/**
128− * _ a time yesterday should be shown as (e.g. “Yesterday 3:55 PM”)
129− * _ a time today should be shown as just the time (e.g. “3:55 PM”)
130− * _ a time tomorrow should be shown as(e.g. “Tomorrow 3:55 PM”)
131− * _ a time any other day this week should be shown as the short version of the
132− * day and time (e.g. “Wed 3:55 PM”)
133− * weekday (e.g. “Friday”)
134− * _ a time after this week should be shown as the short version of the day,
135− * date, and time (e.g. “Wed 21 Apr 3:55 PM”)
136− * _ in addition, when presenting the times of upcoming events, the time should
137− * be followed by the timezone if it is different from the one the computer
138− * is currently set to. For example, “Wed 3:55 PM UTC−5”.
139− *
140− * TODO - keep inline with indicator-datetime
141− */
142−char* generate_full_format_string_at_time (GDateTime* now,
143− GDateTime* then)
144−{
145− GString* ret = g_string_new (nullptr);
146−
147− if (then != nullptr) {
148− const date_proximity_t prox = getDateProximity(now, then);
149−
150− if (is_locale_12h()) {
151− switch (prox) {
152− case DATE_PROXIMITY_YESTERDAY:
153− /* Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
154− This format string is used for showing, on a 12-hour clock, times that happen yesterday.
155− (\u2003 is a unicode em space which is slightly wider than a normal space.)
156− en_US example: "Yesterday\u2003%l:%M %p" --> "Yesterday 1:00 PM" */
157− g_string_assign (ret, dgettext_datetime("Yesterday\u2003%l:%M %p"));
158− break;
159−
160− case DATE_PROXIMITY_TODAY:
161− /* Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
162− This format string is used for showing, on a 12-hour clock, times that happened today.
163− en_US example: "%l:%M %p" --> "1:00 PM" */
164− g_string_assign (ret, dgettext_datetime("%l:%M %p"));
165− break;
166−
167− case DATE_PROXIMITY_TOMORROW:
168− /* Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
169− This format string is used for showing, on a 12-hour clock, events/appointments that happen tomorrow.
170− (\u2003 is a unicode em space which is slightly wider than a normal space.)
171− en_US example: "Tomorrow\u2003%l:%M %p" --> "Tomorrow 1:00 PM" */
172− g_string_assign (ret, dgettext_datetime("Tomorrow\u2003%l:%M %p"));
173− break;
174−
175− case DATE_PROXIMITY_LAST_WEEK:
176− case DATE_PROXIMITY_NEXT_WEEK:
177− /* Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
178− This format string is used for showing, on a 12-hour clock, times that happened in the last week.
179− (\u2003 is a unicode em space which is slightly wider than a normal space.)
180− en_US example: "%a\u2003%l:%M %p" --> "Fri 1:00 PM" */
181− g_string_assign (ret, dgettext_datetime("%a\u2003%l:%M %p"));
182− break;
183−
184− case DATE_PROXIMITY_FAR:
185− /* Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
186− This format string is used for showing, on a 12-hour clock, times that happened before a week from now.
187− (\u2003 is a unicode em space which is slightly wider than a normal space.)
188− en_US example: "%a %b %d\u2003%l:%M %p" --> "Fri Oct 31 1:00 PM"
189− en_GB example: "%a %d %b\u2003%l:%M %p" --> "Fri 31 Oct 1:00 PM" */
190− g_string_assign (ret, dgettext_datetime("%a %d %b\u2003%l:%M %p"));
191− break;
192− }
193− } else {
194− switch (prox) {
195−
196− case DATE_PROXIMITY_YESTERDAY:
197− /* Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
198− This format string is used for showing, on a 24-hour clock, times that happen yesterday.
199− (\u2003 is a unicode em space which is slightly wider than a normal space.)
200− en_US example: "Yesterday\u2003%l:%M %p" --> "Yesterday 13:00" */
201− g_string_assign (ret, dgettext_datetime("Yesterday\u2003%H:%M"));
202− break;
203−
204− case DATE_PROXIMITY_TODAY:
205− /* Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
206− This format string is used for showing, on a 24-hour clock, times that happened today.
207− en_US example: "%H:%M" --> "13:00" */
208− g_string_assign (ret, dgettext_datetime("%H:%M"));
209− break;
210−
211− case DATE_PROXIMITY_TOMORROW:
212− /* Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
213− This format string is used for showing, on a 24-hour clock, events/appointments that happen tomorrow.
214− (\u2003 is a unicode em space which is slightly wider than a normal space.)
215− en_US example: "Tomorrow\u2003%l:%M %p" --> "Tomorrow 13:00" */
216− g_string_assign (ret, dgettext_datetime("Tomorrow\u2003%H:%M"));
217− break;
218−
219− case DATE_PROXIMITY_LAST_WEEK:
220− case DATE_PROXIMITY_NEXT_WEEK:
221− /* Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
222− This format string is used for showing, on a 24-hour clock, times that happened in the last week.
223− (\u2003 is a unicode em space which is slightly wider than a normal space.)
224− en_US example: "%a\u2003%H:%M" --> "Fri 13:00" */
225− g_string_assign (ret, dgettext_datetime("%a\u2003%H:%M"));
226− break;
227−
228− case DATE_PROXIMITY_FAR:
229− /* Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
230− This format string is used for showing, on a 24-hour clock, times that happened before a week from now.
231− (\u2003 is a unicode em space which is slightly wider than a normal space.)
232− en_US example: "%a %b %d\u2003%H:%M" --> "Fri Oct 31 13:00"
233− en_GB example: "%a %d %b\u2003%H:%M" --> "Fri 31 Oct 13:00" */
234− g_string_assign (ret, dgettext_datetime("%a %d %b\u2003%H:%M"));
235− break;
236− }
237− }
238− }
239−
240− return g_string_free (ret, FALSE);
241−}
242−
243−QString RelativeTimeFormatter::format() const
244−{
245− GDateTime* now = g_date_time_new_now_local();
246− if (!now) { return QString(); }
247−
248− GDateTime* then = g_date_time_new_from_unix_local(time());
249− if (!then) { return QString(); }
250−
251− char* time_format = generate_full_format_string_at_time(now, then);
252−
253− QString str(QString::fromUtf8(time_format));
254− g_free(time_format);
255−
256− g_date_time_unref(now);
257− g_date_time_unref(then);
258−
259− return str;
260−}
plugins/Utils/timeformatter.cppdeleted−206
@@ -1,206 +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− * Author: Lars Uebernickel <lars.uebernickel@canonical.com>
17− */
18−
19−#include "timeformatter.h"
20−
21−#include <gio/gio.h>
22−#include <QDateTime>
23−
24−struct TimeFormatterPrivate
25−{
26− TimeFormatter *formatter;
27−
28− QString format;
29− QString timeString;
30− qint64 time;
31−
32− GDBusConnection *system_bus;
33− guint subscription_id;
34− GCancellable *cancellable;
35−};
36−
37−static void
38−timedate1_properties_changed (GDBusConnection *connection,
39− const gchar *sender_name,
40− const gchar *object_path,
41− const gchar *interface_name,
42− const gchar *signal_name,
43− GVariant *parameters,
44− gpointer user_data)
45−{
46− Q_UNUSED(connection);
47− Q_UNUSED(sender_name);
48− Q_UNUSED(object_path);
49− Q_UNUSED(interface_name);
50− Q_UNUSED(signal_name);
51−
52− TimeFormatterPrivate *priv = (TimeFormatterPrivate *)user_data;
53− GVariant *changed;
54− GVariantIter *iter;
55− const gchar *name;
56−
57− if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sa{sv}as)")))
58− return;
59−
60− g_variant_get (parameters, "(s@a{sv}as)", nullptr, &changed, &iter);
61−
62− if (g_variant_lookup (changed, "Timezone", "s", nullptr)) {
63− priv->formatter->update();
64− }
65− else {
66− while (g_variant_iter_next (iter, "&s", &name)) {
67− if (g_str_equal (name, "Timezone")) {
68− priv->formatter->update();
69− break;
70− }
71− }
72− }
73−
74− g_variant_unref (changed);
75− g_variant_iter_free (iter);
76−}
77−
78−static void
79−got_bus(GObject *object, GAsyncResult *result, gpointer user_data)
80−{
81− Q_UNUSED(object);
82−
83− TimeFormatterPrivate *priv = (TimeFormatterPrivate *)user_data;
84− GError *error = nullptr;
85−
86− priv->system_bus = g_bus_get_finish (result, &error);
87− if (priv->system_bus == nullptr) {
88− if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
89− qWarning("TimeFormatter: cannot connect to the system bus: %s", error->message);
90− g_error_free (error);
91− return;
92− }
93−
94− /* Listen to the PropertiesChanged on the org.freedesktop.timedate1
95− * interface from any sender. In practice, this signal will only be sent
96− * from timedated (we can trust other processes on the system bus to behave
97− * nicely). That way, we don't have to watch timedated's well-known name
98− * and keep the process alive.
99− */
100− priv->subscription_id = g_dbus_connection_signal_subscribe (priv->system_bus,
101− nullptr, /* sender */
102− "org.freedesktop.DBus.Properties",
103− "PropertiesChanged",
104− nullptr,
105− "org.freedesktop.timedate1",
106− G_DBUS_SIGNAL_FLAGS_NONE,
107− timedate1_properties_changed,
108− priv, nullptr);
109−}
110−
111−TimeFormatter::TimeFormatter(QObject *parent): QObject(parent)
112−{
113− priv = new TimeFormatterPrivate;
114− priv->formatter = this;
115− priv->time = 0;
116− priv->format = QStringLiteral("yyyy-MM-dd hh:mm");
117− priv->system_bus = nullptr;
118− priv->subscription_id = 0;
119− priv->cancellable = g_cancellable_new ();
120−
121− g_bus_get (G_BUS_TYPE_SYSTEM, priv->cancellable, got_bus, priv);
122−}
123−
124−TimeFormatter::TimeFormatter(const QString &initialFormat, QObject *parent): TimeFormatter(parent)
125−{
126− priv->format = initialFormat;
127−}
128−
129−TimeFormatter::~TimeFormatter()
130−{
131− if (priv->system_bus) {
132− g_dbus_connection_signal_unsubscribe (priv->system_bus, priv->subscription_id);
133− g_object_unref (priv->system_bus);
134− }
135−
136− g_cancellable_cancel (priv->cancellable);
137− g_object_unref (priv->cancellable);
138−}
139−
140−QString TimeFormatter::format() const
141−{
142− return priv->format;
143−}
144−
145−QString TimeFormatter::timeString() const
146−{
147− return priv->timeString;
148−}
149−
150−qint64 TimeFormatter::time() const
151−{
152− return priv->time;
153−}
154−
155−void TimeFormatter::setFormat(const QString &format)
156−{
157− if (priv->format != format) {
158− priv->format = format;
159− Q_EMIT formatChanged(priv->format);
160− update();
161− }
162−}
163−
164−void TimeFormatter::setTime(qint64 time)
165−{
166− if (priv->time != time) {
167− priv->time = time;
168− Q_EMIT timeChanged(priv->time);
169− update();
170− }
171−}
172−
173−void TimeFormatter::update()
174−{
175− priv->timeString = formatTime();
176− Q_EMIT timeStringChanged(priv->timeString);
177−}
178−
179−QString TimeFormatter::formatTime() const
180−{
181− return QDateTime::fromMSecsSinceEpoch(time() / 1000).toString(format());
182−}
183−
184−GDateTimeFormatter::GDateTimeFormatter(QObject* parent)
185−: TimeFormatter(QStringLiteral("%d-%m-%Y %I:%M%p"), parent)
186−{
187−}
188−
189−QString GDateTimeFormatter::formatTime() const
190−{
191− gchar* time_string;
192− GDateTime* datetime;
193− QByteArray formatBytes = format().toUtf8();
194−
195− datetime = g_date_time_new_from_unix_local(time());
196− if (!datetime) {
197− return QLatin1String("");
198− }
199−
200− time_string = g_date_time_format(datetime, formatBytes.constData());
201− QString formattedTime(QString::fromUtf8(time_string));
202−
203− g_free(time_string);
204− g_date_time_unref(datetime);
205− return formattedTime;
206−}
plugins/Utils/timeformatter.hdeleted−68
@@ -1,68 +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− * Author: Lars Uebernickel <lars.uebernickel@canonical.com>
17− */
18−
19−#ifndef TIME_FORMATTER_H
20−#define TIME_FORMATTER_H
21−
22−#include <QObject>
23−
24−// TODO - bug #1260728
25−class TimeFormatter : public QObject
26−{
27− Q_OBJECT
28− Q_PROPERTY(QString format READ format WRITE setFormat NOTIFY formatChanged)
29− Q_PROPERTY(QString timeString READ timeString NOTIFY timeStringChanged)
30− Q_PROPERTY(qint64 time READ time WRITE setTime NOTIFY timeChanged)
31−
32−public:
33− TimeFormatter(QObject *parent = 0);
34− virtual ~TimeFormatter();
35−
36− virtual QString format() const;
37− QString timeString() const;
38− qint64 time() const;
39−
40− void setFormat(const QString &format);
41− void setTime(qint64 time);
42−
43− void update();
44−
45−Q_SIGNALS:
46− void formatChanged(const QString &format);
47− void timeStringChanged(const QString &timeString);
48− void timeChanged(qint64 time);
49−
50−protected:
51− TimeFormatter(const QString &initialFormat, QObject *parent = 0);
52−
53− virtual QString formatTime() const;
54−
55−private:
56− struct TimeFormatterPrivate *priv;
57−};
58−
59−class GDateTimeFormatter : public TimeFormatter
60−{
61−public:
62− GDateTimeFormatter(QObject *parent = 0);
63−
64−protected:
65− QString formatTime() const override;
66−};
67−
68−#endif
plugins/Utils/timezoneFormatter.cpp+1−1
@@ -29,7 +29,7 @@ QString TimezoneFormatter::currentTimeInTimezone(const QVariant &tzId) const
29 if (tz.isValid()) {
29 if (tz.isValid()) {
30 const QDateTime now = QDateTime::currentDateTime().toTimeZone(tz);
30 const QDateTime now = QDateTime::currentDateTime().toTimeZone(tz);
31 // return locale-aware string in the form "day, hh:mm", e.g. "Mon 14:30" or "Mon 1:30 pm"
31 // return locale-aware string in the form "day, hh:mm", e.g. "Mon 14:30" or "Mon 1:30 pm"
32− return QStringLiteral("%1 %2").arg(now.toString("ddd")).arg(now.time().toString(Qt::DefaultLocaleShortDate));
32+ return QStringLiteral("%1 %2").arg(now.toString(QStringLiteral("ddd"))).arg(now.time().toString(Qt::DefaultLocaleShortDate));
33 }
33 }
34 return QString();
34 return QString();
35}
35}
po/ug.po+5−5
@@ -8,14 +8,14 @@ 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: 2015-09-16 00:49+0200\n"
10"POT-Creation-Date: 2015-09-16 00:49+0200\n"
11−"PO-Revision-Date: 2015-10-02 03:54+0000\n"
11+"PO-Revision-Date: 2015-10-25 04:00+0000\n"
12"Last-Translator: Eltikin <eltikinuyghur@hotmail.com>\n"
12"Last-Translator: Eltikin <eltikinuyghur@hotmail.com>\n"
13"Language-Team: Uyghur Computer Science Association <UKIJ@yahoogroups.com>\n"
13"Language-Team: Uyghur Computer Science Association <UKIJ@yahoogroups.com>\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: 2015-10-16 05:58+0000\n"
18+"X-Launchpad-Export-Date: 2015-10-26 05:24+0000\n"
19"X-Generator: Launchpad (build 17812)\n"
19"X-Generator: Launchpad (build 17812)\n"
20
20
21#: plugins/IntegratedLightDM/Greeter.cpp:112
21#: plugins/IntegratedLightDM/Greeter.cpp:112
@@ -41,7 +41,7 @@ msgstr "تېزلەتمىنى ئېلىۋەت"
41#. en_US example: "Yesterday\u2003%l:%M %p" --> "Yesterday 1:00 PM"
41#. en_US example: "Yesterday\u2003%l:%M %p" --> "Yesterday 1:00 PM"
42#: plugins/Utils/relativetimeformatter.cpp:157
42#: plugins/Utils/relativetimeformatter.cpp:157
43msgid "Yesterday %l:%M %p"
43msgid "Yesterday %l:%M %p"
44−msgstr ""
44+msgstr "تۈنۈگۈن %l:%M %p"
45
45
46#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
46#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
47#. This format string is used for showing, on a 12-hour clock, times that happened today.
47#. This format string is used for showing, on a 12-hour clock, times that happened today.
@@ -81,7 +81,7 @@ msgstr "%a %d %b %l:%M %p"
81#. en_US example: "Yesterday\u2003%l:%M %p" --> "Yesterday 13:00"
81#. en_US example: "Yesterday\u2003%l:%M %p" --> "Yesterday 13:00"
82#: plugins/Utils/relativetimeformatter.cpp:201
82#: plugins/Utils/relativetimeformatter.cpp:201
83msgid "Yesterday %H:%M"
83msgid "Yesterday %H:%M"
84−msgstr ""
84+msgstr "تۈنۈگۈن %H:%M"
85
85
86#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
86#. Translators, please edit/rearrange these strftime(3) tokens to suit your locale!
87#. This format string is used for showing, on a 24-hour clock, times that happened today.
87#. This format string is used for showing, on a 24-hour clock, times that happened today.
@@ -502,7 +502,7 @@ msgstr ""
502
502
503#: qml/Tutorial/TutorialPage.qml:178
503#: qml/Tutorial/TutorialPage.qml:178
504msgid "You almost got it!"
504msgid "You almost got it!"
505−msgstr ""
505+msgstr "ھازىرلا پۈتۈرىدۇ!"
506
506
507#: qml/Tutorial/TutorialPage.qml:195
507#: qml/Tutorial/TutorialPage.qml:195
508msgid "Try again."
508msgid "Try again."
qml/Components/Dialogs.qml+3−2
@@ -20,6 +20,7 @@ import Unity.Application 0.1
20import Unity.Session 0.1
20import Unity.Session 0.1
21import Ubuntu.Components 1.1
21import Ubuntu.Components 1.1
22import GlobalShortcut 1.0
22import GlobalShortcut 1.0
23+import Unity.Platform 1.0
23import "../Greeter"
24import "../Greeter"
24
25
25Item {
26Item {
@@ -46,13 +47,13 @@ Item {
46
47
47 GlobalShortcut { // reboot/shutdown dialog
48 GlobalShortcut { // reboot/shutdown dialog
48 shortcut: Qt.Key_PowerDown
49 shortcut: Qt.Key_PowerDown
49− active: root.usageScenario === "desktop"
50+ active: Platform.isPC
50 onTriggered: root.unitySessionService.RequestShutdown()
51 onTriggered: root.unitySessionService.RequestShutdown()
51 }
52 }
52
53
53 GlobalShortcut { // reboot/shutdown dialog
54 GlobalShortcut { // reboot/shutdown dialog
54 shortcut: Qt.Key_PowerOff
55 shortcut: Qt.Key_PowerOff
55− active: root.usageScenario === "desktop"
56+ active: Platform.isPC
56 onTriggered: root.unitySessionService.RequestShutdown()
57 onTriggered: root.unitySessionService.RequestShutdown()
57 }
58 }
58
59
qml/Components/ScreenGrabber.qml+4−1
@@ -26,6 +26,9 @@ Rectangle {
26 anchors.fill: parent
26 anchors.fill: parent
27 opacity: 0.0
27 opacity: 0.0
28
28
29+ // to be set from outside
30+ property int rotationAngle: 0
31+
29 ScreenGrabber {
32 ScreenGrabber {
30 id: screenGrabber
33 id: screenGrabber
31 objectName: "screenGrabber"
34 objectName: "screenGrabber"
@@ -67,7 +70,7 @@ Rectangle {
67 to: 0.0
70 to: 0.0
68 onStopped: {
71 onStopped: {
69 if (visible) {
72 if (visible) {
70− screenGrabber.captureAndSave();
73+ screenGrabber.captureAndSave(root.rotationAngle);
71 visible = false;
74 visible = false;
72 }
75 }
73 }
76 }
qml/Components/WallpaperResolver.qmlnew file+63
@@ -0,0 +1,63 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.4
18+import AccountsService 0.1
19+import GSettings 1.0
20+import Ubuntu.Components 1.3
21+
22+/*
23+ Defines the background URL based on several factors, such as:
24+ - default, fallback, background
25+ - Background set in AccountSettings, if any
26+ - Background set in GSettings, if any
27+ */
28+QtObject {
29+ // Users should set their UI width here.
30+ property real width
31+
32+ property url defaultBackground: Qt.resolvedUrl(width >= units.gu(60) ? "../graphics/tablet_background.jpg"
33+ : "../graphics/phone_background.jpg")
34+
35+ // That's the property users of this component are going to consume.
36+ readonly property url background: asImageTester.status == Image.Ready ? asImageTester.source
37+ : gsImageTester.status == Image.Ready ? gsImageTester.source : defaultBackground
38+
39+ // This is a dummy image to detect if the custom AS set wallpaper loads successfully.
40+ property var _asImageTester: Image {
41+ id: asImageTester
42+ source: AccountsService.backgroundFile != undefined && AccountsService.backgroundFile.length > 0 ? AccountsService.backgroundFile : ""
43+ height: 0
44+ width: 0
45+ sourceSize.height: 0
46+ sourceSize.width: 0
47+ }
48+
49+ // This is a dummy image to detect if the custom GSettings set wallpaper loads successfully.
50+ property var _gsImageTester: Image {
51+ id: gsImageTester
52+ source: backgroundSettings.pictureUri && backgroundSettings.pictureUri.length > 0 ? backgroundSettings.pictureUri : ""
53+ height: 0
54+ width: 0
55+ sourceSize.height: 0
56+ sourceSize.width: 0
57+ }
58+
59+ property var _gsettings: GSettings {
60+ id: backgroundSettings
61+ schema.id: "org.gnome.desktop.background"
62+ }
63+}
qml/DeviceConfiguration.qml+1
@@ -86,6 +86,7 @@ StateGroup {
86 PropertyChanges {
86 PropertyChanges {
87 target: root
87 target: root
88 category: "desktop"
88 category: "desktop"
89+ supportedOrientations: root.useNativeOrientation
89 }
90 }
90 }
91 }
91 ]
92 ]
qml/DisabledScreenNotice.qmlnew file+49
@@ -0,0 +1,49 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.4
18+import Ubuntu.Components 1.3
19+import "Components"
20+
21+Image {
22+ id: root
23+
24+ WallpaperResolver {
25+ width: root.width
26+ id: wallpaperResolver
27+ }
28+
29+ source: wallpaperResolver.background
30+
31+ UbuntuShape {
32+ anchors.fill: text
33+ anchors.margins: -units.gu(2)
34+ backgroundColor: "black"
35+ opacity: 0.4
36+ }
37+
38+ Label {
39+ id: text
40+ anchors.centerIn: parent
41+ width: parent.width / 2
42+ text: i18n.tr("Your device is now connected to an external display.")
43+ color: "white"
44+ horizontalAlignment: Text.AlignHCenter
45+ verticalAlignment: Text.AlignVCenter
46+ fontSize: "x-large"
47+ wrapMode: Text.Wrap
48+ }
49+}
qml/Notifications/Notification.qml+2
@@ -50,6 +50,7 @@ Item {
50 readonly property real contentSpacing: units.gu(2)
50 readonly property real contentSpacing: units.gu(2)
51 readonly property bool canBeClosed: type === Notification.Ephemeral
51 readonly property bool canBeClosed: type === Notification.Ephemeral
52 property bool hasMouse
52 property bool hasMouse
53+ property url background: ""
53
54
54 objectName: "background"
55 objectName: "background"
55 implicitHeight: type !== Notification.PlaceHolder ? (fullscreen ? maxHeight : outterColumn.height - shapedBack.anchors.topMargin + contentSpacing * 2) : 0
56 implicitHeight: type !== Notification.PlaceHolder ? (fullscreen ? maxHeight : outterColumn.height - shapedBack.anchors.topMargin + contentSpacing * 2) : 0
@@ -401,6 +402,7 @@ Item {
401 menuData: model
402 menuData: model
402 menuIndex: index
403 menuIndex: index
403 maxHeight: notification.maxHeight
404 maxHeight: notification.maxHeight
405+ background: notification.background
404
406
405 onLoaded: {
407 onLoaded: {
406 notification.fullscreen = Qt.binding(function() { return fullscreen; });
408 notification.fullscreen = Qt.binding(function() { return fullscreen; });
qml/Notifications/NotificationMenuItemFactory.qml+2−1
@@ -30,6 +30,7 @@ Loader {
30 property int menuIndex : -1
30 property int menuIndex : -1
31 property int maxHeight
31 property int maxHeight
32 readonly property bool fullscreen: menuData.type === "com.canonical.snapdecision.pinlock"
32 readonly property bool fullscreen: menuData.type === "com.canonical.snapdecision.pinlock"
33+ property url background: ""
33
34
34 signal accepted()
35 signal accepted()
35
36
@@ -149,7 +150,7 @@ Loader {
149 infoText: notification.summary
150 infoText: notification.summary
150 errorText: errorAction.valid ? errorAction.state : ""
151 errorText: errorAction.valid ? errorAction.state : ""
151 retryText: notification.body
152 retryText: notification.body
152− background: shell.background
153+ background: menuFactory.background
153 darkenBackground: 0.4
154 darkenBackground: 0.4
154
155
155 onEntered: {
156 onEntered: {
qml/Notifications/Notifications.qml+2
@@ -29,6 +29,7 @@ ListView {
29 property real margin
29 property real margin
30 property bool useModal: snapDecisionProxyModel.count > 0
30 property bool useModal: snapDecisionProxyModel.count > 0
31 property bool hasMouse
31 property bool hasMouse
32+ property url background: ""
32
33
33 UnitySortFilterProxyModel {
34 UnitySortFilterProxyModel {
34 id: snapDecisionProxyModel
35 id: snapDecisionProxyModel
@@ -60,6 +61,7 @@ ListView {
60 maxHeight: notificationList.height
61 maxHeight: notificationList.height
61 margins: notificationList.margin
62 margins: notificationList.margin
62 hasMouse: notificationList.hasMouse
63 hasMouse: notificationList.hasMouse
64+ background: notificationList.background
63
65
64 // make sure there's no opacity-difference between the several
66 // make sure there's no opacity-difference between the several
65 // elements in a notification
67 // elements in a notification
qml/OrientedShell.qml+10−1
@@ -76,6 +76,12 @@ Rectangle {
76 oskSettings.disableHeight = shell.usageScenario == "desktop"
76 oskSettings.disableHeight = shell.usageScenario == "desktop"
77 }
77 }
78
78
79+ // we must rotate to a supported orientation regardless of shell's preference
80+ property bool orientationChangesEnabled:
81+ (orientation & supportedOrientations) === 0 ? true
82+ : shell.orientationChangesEnabled
83+
84+
79 Binding {
85 Binding {
80 target: oskSettings
86 target: oskSettings
81 property: "stayHidden"
87 property: "stayHidden"
@@ -89,7 +95,10 @@ Rectangle {
89 }
95 }
90
96
91 readonly property int supportedOrientations: shell.supportedOrientations
97 readonly property int supportedOrientations: shell.supportedOrientations
92− & deviceConfiguration.supportedOrientations
98+ & (deviceConfiguration.supportedOrientations == deviceConfiguration.useNativeOrientation
99+ ? nativeOrientation
100+ : deviceConfiguration.supportedOrientations)
101+
93 property int acceptedOrientationAngle: {
102 property int acceptedOrientationAngle: {
94 if (orientation & supportedOrientations) {
103 if (orientation & supportedOrientations) {
95 return Screen.angleBetween(nativeOrientation, orientation);
104 return Screen.angleBetween(nativeOrientation, orientation);
qml/Panel/Indicators/MenuItemFactory.qml+19−15
@@ -20,7 +20,7 @@ import Ubuntu.Settings.Components 0.1
20import QMenuModel 0.1
20import QMenuModel 0.1
21import Utils 0.1 as Utils
21import Utils 0.1 as Utils
22import Ubuntu.Components.ListItems 0.1 as ListItems
22import Ubuntu.Components.ListItems 0.1 as ListItems
23−import Ubuntu.Components 1.2
23+import Ubuntu.Components 1.3
24import Unity.Session 0.1
24import Unity.Session 0.1
25
25
26Item {
26Item {
@@ -350,20 +350,23 @@ Item {
350 id: alarmMenu;
350 id: alarmMenu;
351
351
352 Menus.EventMenu {
352 Menus.EventMenu {
353+ id: alarmItem
353 objectName: "alarmMenu"
354 objectName: "alarmMenu"
354 property QtObject menuData: null
355 property QtObject menuData: null
355 property var menuModel: menuFactory.menuModel
356 property var menuModel: menuFactory.menuModel
356 property int menuIndex: -1
357 property int menuIndex: -1
357 property var extendedData: menuData && menuData.ext || undefined
358 property var extendedData: menuData && menuData.ext || undefined
358− // TODO - bug #1260728
359+
359− property var timeFormatter: Utils.GDateTimeFormatter {
360+ property date serverTime: new Date(getExtendedProperty(extendedData, "xCanonicalTime", 0) * 1000)
360− time: getExtendedProperty(extendedData, "xCanonicalTime", 0)
361+ LiveTimer {
362+ frequency: LiveTimer.Relative
363+ relativeTime: alarmItem.serverTime
361− format: getExtendedProperty(extendedData, "xCanonicalTimeFormat", "")
364+ onTrigger: alarmItem.serverTime = new Date(getExtendedProperty(extendedData, "xCanonicalTime", 0) * 1000)
362 }
365 }
363
366
364 text: menuData && menuData.label || ""
367 text: menuData && menuData.label || ""
365 iconSource: menuData && menuData.icon || "image://theme/alarm-clock"
368 iconSource: menuData && menuData.icon || "image://theme/alarm-clock"
366− time: timeFormatter.timeString
369+ time: i18n.relativeDateTime(serverTime)
367 enabled: menuData && menuData.sensitive || false
370 enabled: menuData && menuData.sensitive || false
368 highlightWhenPressed: false
371 highlightWhenPressed: false
369
372
@@ -379,8 +382,7 @@ Item {
379
382
380 function loadAttributes() {
383 function loadAttributes() {
381 if (!menuModel || menuIndex == -1) return;
384 if (!menuModel || menuIndex == -1) return;
382− menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-time': 'int64',
385+ menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-time': 'int64'});
383− 'x-canonical-time-format': 'string'});
384 }
386 }
385 }
387 }
386 }
388 }
@@ -389,20 +391,23 @@ Item {
389 id: appointmentMenu;
391 id: appointmentMenu;
390
392
391 Menus.EventMenu {
393 Menus.EventMenu {
394+ id: appointmentItem
392 objectName: "appointmentMenu"
395 objectName: "appointmentMenu"
393 property QtObject menuData: null
396 property QtObject menuData: null
394 property var menuModel: menuFactory.menuModel
397 property var menuModel: menuFactory.menuModel
395 property int menuIndex: -1
398 property int menuIndex: -1
396 property var extendedData: menuData && menuData.ext || undefined
399 property var extendedData: menuData && menuData.ext || undefined
397− // TODO - bug #1260728
400+
398− property var timeFormatter: Utils.GDateTimeFormatter {
401+ property date serverTime: new Date(getExtendedProperty(extendedData, "xCanonicalTime", 0) * 1000)
399− time: getExtendedProperty(extendedData, "xCanonicalTime", 0)
402+ LiveTimer {
403+ frequency: LiveTimer.Relative
404+ relativeTime: appointmentItem.serverTime
400− format: getExtendedProperty(extendedData, "xCanonicalTimeFormat", "")
405+ onTrigger: appointmentItem.serverTime = new Date(getExtendedProperty(extendedData, "xCanonicalTime", 0) * 1000)
401 }
406 }
402
407
403 text: menuData && menuData.label || ""
408 text: menuData && menuData.label || ""
404 iconSource: menuData && menuData.icon || "image://theme/calendar"
409 iconSource: menuData && menuData.icon || "image://theme/calendar"
405− time: timeFormatter.timeString
410+ time: i18n.relativeDateTime(serverTime)
406 eventColor: getExtendedProperty(extendedData, "xCanonicalColor", Qt.rgba(0.0, 0.0, 0.0, 0.0))
411 eventColor: getExtendedProperty(extendedData, "xCanonicalColor", Qt.rgba(0.0, 0.0, 0.0, 0.0))
407 enabled: menuData && menuData.sensitive || false
412 enabled: menuData && menuData.sensitive || false
408 highlightWhenPressed: false
413 highlightWhenPressed: false
@@ -420,8 +425,7 @@ Item {
420 function loadAttributes() {
425 function loadAttributes() {
421 if (!menuModel || menuIndex == -1) return;
426 if (!menuModel || menuIndex == -1) return;
422 menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-color': 'string',
427 menuModel.loadExtendedAttributes(menuIndex, {'x-canonical-color': 'string',
423− 'x-canonical-time': 'int64',
428+ 'x-canonical-time': 'int64'});
424− 'x-canonical-time-format': 'string'});
425 }
429 }
426 }
430 }
427 }
431 }
qml/Panel/Indicators/MessageMenuItemFactory.qml+35−26
@@ -19,7 +19,7 @@
19 */
19 */
20
20
21import QtQuick 2.0
21import QtQuick 2.0
22−import Ubuntu.Components 0.1
22+import Ubuntu.Components 1.3
23import Ubuntu.Settings.Menus 0.1 as Menus
23import Ubuntu.Settings.Menus 0.1 as Menus
24import QMenuModel 0.1 as QMenuModel
24import QMenuModel 0.1 as QMenuModel
25import Utils 0.1 as Utils
25import Utils 0.1 as Utils
@@ -35,12 +35,17 @@ Loader {
35 signal menuSelected
35 signal menuSelected
36 signal menuDeselected
36 signal menuDeselected
37
37
38+ QtObject {
39+ id: priv
38− property var extendedData: menuData && menuData.ext || undefined
40+ property var extendedData: menuData && menuData.ext || undefined
39− property var actionsDescription: getExtendedProperty(extendedData, "xCanonicalMessageActions", undefined)
41+ property var actionsDescription: getExtendedProperty(extendedData, "xCanonicalMessageActions", undefined)
40−
41− // TODO - bug #1260728
42− property var timeFormatter: Utils.RelativeTimeFormatter {
43− time: getExtendedProperty(extendedData, "xCanonicalTime", 0) / 1000000
42+ property date time: new Date(getExtendedProperty(extendedData, "xCanonicalTime", 0) / 1000)
43+ property string timeString: i18n.relativeDateTime(time)
44+ }
45+ LiveTimer {
46+ frequency: LiveTimer.Relative
47+ relativeTime: priv.time
48+ onTrigger: priv.timeString = Qt.binding(function() { return i18n.relativeDateTime(priv.time); })
44 }
49 }
45
50
46 onMenuModelChanged: {
51 onMenuModelChanged: {
@@ -50,7 +55,7 @@ Loader {
50 loadAttributes();
55 loadAttributes();
51 }
56 }
52
57
53− sourceComponent: loadMessage(actionsDescription)
58+ sourceComponent: loadMessage(priv.actionsDescription)
54
59
55 function loadMessage(actions)
60 function loadMessage(actions)
56 {
61 {
@@ -100,11 +105,11 @@ Loader {
100 objectName: "simpleTextMessage"
105 objectName: "simpleTextMessage"
101 // text
106 // text
102 title: menuData && menuData.label || ""
107 title: menuData && menuData.label || ""
103− time: timeFormatter.timeString
108+ time: priv.timeString
104− body: getExtendedProperty(extendedData, "xCanonicalText", "")
109+ body: getExtendedProperty(priv.extendedData, "xCanonicalText", "")
105 // icons
110 // icons
106− avatar: getExtendedProperty(extendedData, "icon", "image://theme/contact")
111+ avatar: getExtendedProperty(priv.extendedData, "icon", "image://theme/contact")
107− icon: getExtendedProperty(extendedData, "xCanonicalAppIcon", "image://theme/message")
112+ icon: getExtendedProperty(priv.extendedData, "xCanonicalAppIcon", "image://theme/message")
108 // actions
113 // actions
109 enabled: menuData && menuData.sensitive || false
114 enabled: menuData && menuData.sensitive || false
110 removable: !selected
115 removable: !selected
@@ -133,7 +138,9 @@ Loader {
133 Menus.TextMessageMenu {
138 Menus.TextMessageMenu {
134 id: message
139 id: message
135 objectName: "textMessage"
140 objectName: "textMessage"
136− property var replyActionDescription: actionsDescription && actionsDescription.length > 0 ? actionsDescription[0] : undefined
141+ property var replyActionDescription: priv.actionsDescription && priv.actionsDescription.length > 0 ?
142+ priv.actionsDescription[0] :
143+ undefined
137
144
138 property var replyAction: QMenuModel.UnityMenuAction {
145 property var replyAction: QMenuModel.UnityMenuAction {
139 model: menuModel
146 model: menuModel
@@ -143,13 +150,13 @@ Loader {
143
150
144 // text
151 // text
145 title: menuData && menuData.label || ""
152 title: menuData && menuData.label || ""
146− time: timeFormatter.timeString
153+ time: priv.timeString
147− body: getExtendedProperty(extendedData, "xCanonicalText", "")
154+ body: getExtendedProperty(priv.extendedData, "xCanonicalText", "")
148− replyButtonText: getExtendedProperty(replyActionDescription, "label", i18n.tr("Send"))
155+ replyButtonText: getExtendedProperty(replyActionDescription, "label", i18n.ctr("Button: Send a reply message", "Send"))
149 replyHintText: i18n.ctr("Label: Hint in message indicator line edit", "Reply")
156 replyHintText: i18n.ctr("Label: Hint in message indicator line edit", "Reply")
150 // icons
157 // icons
151− avatar: getExtendedProperty(extendedData, "icon", "image://theme/contact")
158+ avatar: getExtendedProperty(priv.extendedData, "icon", "image://theme/contact")
152− icon: getExtendedProperty(extendedData, "xCanonicalAppIcon", "image://theme/message")
159+ icon: getExtendedProperty(priv.extendedData, "xCanonicalAppIcon", "image://theme/message")
153 // actions
160 // actions
154 replyEnabled: replyAction.valid && replyAction.enabled
161 replyEnabled: replyAction.valid && replyAction.enabled
155 enabled: menuData && menuData.sensitive || false
162 enabled: menuData && menuData.sensitive || false
@@ -183,8 +190,10 @@ Loader {
183 Menus.SnapDecisionMenu {
190 Menus.SnapDecisionMenu {
184 id: message
191 id: message
185 objectName: "snapDecision"
192 objectName: "snapDecision"
186− property var activateActionDescription: actionsDescription && actionsDescription.length > 0 ? actionsDescription[0] : undefined
193+ property var activateActionDescription: priv.actionsDescription && priv.actionsDescription.length > 0 ?
194+ priv.actionsDescription[0] : undefined
187− property var replyActionDescription: actionsDescription && actionsDescription.length > 1 ? actionsDescription[1] : undefined
195+ property var replyActionDescription: priv.actionsDescription && priv.actionsDescription.length > 1 ?
196+ priv.actionsDescription[1] : undefined
188
197
189 property var activateAction: QMenuModel.UnityMenuAction {
198 property var activateAction: QMenuModel.UnityMenuAction {
190 model: menuModel
199 model: menuModel
@@ -199,13 +208,13 @@ Loader {
199
208
200 // text
209 // text
201 title: menuData && menuData.label || ""
210 title: menuData && menuData.label || ""
202− time: timeFormatter.timeString
211+ time: priv.timeString
203− body: getExtendedProperty(extendedData, "xCanonicalText", "")
212+ body: getExtendedProperty(priv.extendedData, "xCanonicalText", "")
204− actionButtonText: getExtendedProperty(activateActionDescription, "label", i18n.tr("Call back"))
213+ actionButtonText: getExtendedProperty(activateActionDescription, "label", i18n.ctr("Button: Call back on phone", "Call back"))
205− replyButtonText: getExtendedProperty(replyActionDescription, "label", i18n.tr("Send"))
214+ replyButtonText: getExtendedProperty(replyActionDescription, "label", i18n.ctr("Button: Send a reply message", "Send"))
206 // icons
215 // icons
207− avatar: getExtendedProperty(extendedData, "icon", "image://theme/contact")
216+ avatar: getExtendedProperty(priv.extendedData, "icon", "image://theme/contact")
208− icon: getExtendedProperty(extendedData, "xCanonicalAppIcon", "image://theme/missed-call")
217+ icon: getExtendedProperty(priv.extendedData, "xCanonicalAppIcon", "image://theme/missed-call")
209 // actions
218 // actions
210 actionEnabled: activateAction.valid && activateAction.enabled
219 actionEnabled: activateAction.valid && activateAction.enabled
211 replyEnabled: replyAction.valid && replyAction.enabled
220 replyEnabled: replyAction.valid && replyAction.enabled
qml/Rotation/RotationStates.qml+2−2
@@ -83,7 +83,7 @@ StateGroup {
83 }
83 }
84
84
85 function tryUpdateState() {
85 function tryUpdateState() {
86− if (d.transitioning || (!d.startingUp && !root.shell.orientationChangesEnabled)) {
86+ if (d.transitioning || (!d.startingUp && !root.orientedShell.orientationChangesEnabled)) {
87 return;
87 return;
88 }
88 }
89
89
@@ -95,7 +95,7 @@ StateGroup {
95 }
95 }
96
96
97 property Connections shellConnections: Connections {
97 property Connections shellConnections: Connections {
98− target: root.shell
98+ target: root.orientedShell
99 onOrientationChangesEnabledChanged: {
99 onOrientationChangesEnabledChanged: {
100 d.tryUpdateState();
100 d.tryUpdateState();
101 }
101 }
qml/Shell.qml+10−33
@@ -17,7 +17,6 @@
17import QtQuick 2.0
17import QtQuick 2.0
18import QtQuick.Window 2.0
18import QtQuick.Window 2.0
19import AccountsService 0.1
19import AccountsService 0.1
20−import GSettings 1.0
21import Unity.Application 0.1
20import Unity.Application 0.1
22import Ubuntu.Components 0.1
21import Ubuntu.Components 0.1
23import Ubuntu.Components.Popups 1.0
22import Ubuntu.Components.Popups 1.0
@@ -108,9 +107,11 @@ Item {
108 enabled: greeter && !greeter.waiting
107 enabled: greeter && !greeter.waiting
109
108
110 property real edgeSize: units.gu(2)
109 property real edgeSize: units.gu(2)
111− property url defaultBackground: Qt.resolvedUrl(shell.width >= units.gu(60) ? "graphics/tablet_background.jpg" : "graphics/phone_background.jpg")
110+
112− property url background: asImageTester.status == Image.Ready ? asImageTester.source
111+ WallpaperResolver {
113− : gsImageTester.status == Image.Ready ? gsImageTester.source : defaultBackground
112+ id: wallpaperResolver
113+ width: shell.width
114+ }
114
115
115 readonly property alias greeter: greeterLoader.item
116 readonly property alias greeter: greeterLoader.item
116
117
@@ -131,31 +132,6 @@ Item {
131 shell.activateApplication(app);
132 shell.activateApplication(app);
132 }
133 }
133
134
134− // This is a dummy image to detect if the custom AS set wallpaper loads successfully.
135− Image {
136− id: asImageTester
137− source: AccountsService.backgroundFile != undefined && AccountsService.backgroundFile.length > 0 ? AccountsService.backgroundFile : ""
138− height: 0
139− width: 0
140− sourceSize.height: 0
141− sourceSize.width: 0
142− }
143−
144− GSettings {
145− id: backgroundSettings
146− schema.id: "org.gnome.desktop.background"
147− }
148−
149− // This is a dummy image to detect if the custom GSettings set wallpaper loads successfully.
150− Image {
151− id: gsImageTester
152− source: backgroundSettings.pictureUri && backgroundSettings.pictureUri.length > 0 ? backgroundSettings.pictureUri : ""
153− height: 0
154− width: 0
155− sourceSize.height: 0
156− sourceSize.width: 0
157− }
158−
159 Binding {
135 Binding {
160 target: LauncherModel
136 target: LauncherModel
161 property: "applicationManager"
137 property: "applicationManager"
@@ -323,7 +299,7 @@ Item {
323 Binding {
299 Binding {
324 target: applicationsDisplayLoader.item
300 target: applicationsDisplayLoader.item
325 property: "background"
301 property: "background"
326− value: shell.background
302+ value: wallpaperResolver.background
327 }
303 }
328 Binding {
304 Binding {
329 target: applicationsDisplayLoader.item
305 target: applicationsDisplayLoader.item
@@ -428,7 +404,7 @@ Item {
428 tabletMode: shell.usageScenario != "phone"
404 tabletMode: shell.usageScenario != "phone"
429 launcherOffset: launcher.progress
405 launcherOffset: launcher.progress
430 forcedUnlock: tutorial.running
406 forcedUnlock: tutorial.running
431− background: shell.background
407+ background: wallpaperResolver.background
432
408
433 // avoid overlapping with Launcher's edge drag area
409 // avoid overlapping with Launcher's edge drag area
434 // FIXME: Fix TouchRegistry & friends and remove this workaround
410 // FIXME: Fix TouchRegistry & friends and remove this workaround
@@ -603,7 +579,7 @@ Item {
603 id: wizard
579 id: wizard
604 objectName: "wizard"
580 objectName: "wizard"
605 anchors.fill: parent
581 anchors.fill: parent
606− background: shell.background
582+ background: wallpaperResolver.background
607
583
608 function unlockWhenDoneWithWizard() {
584 function unlockWhenDoneWithWizard() {
609 if (!active) {
585 if (!active) {
@@ -634,6 +610,7 @@ Item {
634 model: NotificationBackend.Model
610 model: NotificationBackend.Model
635 margin: units.gu(1)
611 margin: units.gu(1)
636 hasMouse: shell.hasMouse
612 hasMouse: shell.hasMouse
613+ background: wallpaperResolver.background
637
614
638 y: topmostIsFullscreen ? 0 : panel.panelHeight
615 y: topmostIsFullscreen ? 0 : panel.panelHeight
639 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
616 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
@@ -682,6 +659,7 @@ Item {
682
659
683 ScreenGrabber {
660 ScreenGrabber {
684 id: screenGrabber
661 id: screenGrabber
662+ rotationAngle: -shell.orientationAngle
685 z: dialogs.z + 10
663 z: dialogs.z + 10
686 }
664 }
687
665
@@ -710,5 +688,4 @@ Item {
710 }
688 }
711 }
689 }
712 }
690 }
713−
714}
691}
qml/Stages/ApplicationWindow.qml+2
@@ -27,6 +27,8 @@ FocusScope {
27 readonly property bool fullscreen: application ? application.fullscreen : false
27 readonly property bool fullscreen: application ? application.fullscreen : false
28 property alias interactive: sessionContainer.interactive
28 property alias interactive: sessionContainer.interactive
29 property bool orientationChangesEnabled: d.supportsSurfaceResize ? d.surfaceOldEnoughToBeResized : true
29 property bool orientationChangesEnabled: d.supportsSurfaceResize ? d.surfaceOldEnoughToBeResized : true
30+ readonly property string title: sessionContainer.surface && sessionContainer.surface.name !== "" ?
31+ sessionContainer.surface.name : d.name
30
32
31 // to be set from outside
33 // to be set from outside
32 property QtObject application
34 property QtObject application
qml/Stages/DecoratedWindow.qml+1−1
@@ -66,7 +66,7 @@ FocusScope {
66 objectName: application ? "appWindowDecoration_" + application.appId : "appWindowDecoration_null"
66 objectName: application ? "appWindowDecoration_" + application.appId : "appWindowDecoration_null"
67 anchors { left: parent.left; top: parent.top; right: parent.right }
67 anchors { left: parent.left; top: parent.top; right: parent.right }
68 height: units.gu(3)
68 height: units.gu(3)
69− title: model.name
69+ title: window.title !== "" ? window.title : model.name
70 onClose: root.close();
70 onClose: root.close();
71 onMaximize: root.maximize();
71 onMaximize: root.maximize();
72 onMinimize: root.minimize();
72 onMinimize: root.minimize();
qml/Stages/SurfaceContainer.qml+1
@@ -28,6 +28,7 @@ FocusScope {
28 property bool hadSurface: false
28 property bool hadSurface: false
29 property bool interactive
29 property bool interactive
30 property int surfaceOrientationAngle: 0
30 property int surfaceOrientationAngle: 0
31+ property string name: surface ? surface.name : ""
31 property bool resizeSurface: true
32 property bool resizeSurface: true
32
33
33 onSurfaceChanged: {
34 onSurfaceChanged: {
src/ApplicationArguments.h+10−2
@@ -26,17 +26,25 @@
26class ApplicationArguments : public QObject
26class ApplicationArguments : public QObject
27{
27{
28 Q_OBJECT
28 Q_OBJECT
29− Q_PROPERTY(QString deviceName READ deviceName CONSTANT)
29+ Q_PROPERTY(QString deviceName READ deviceName NOTIFY deviceNameChanged)
30 Q_PROPERTY(QString mode READ mode CONSTANT)
30 Q_PROPERTY(QString mode READ mode CONSTANT)
31public:
31public:
32 ApplicationArguments(QObject *parent = nullptr);
32 ApplicationArguments(QObject *parent = nullptr);
33
33
34− void setDeviceName(const QString &deviceName) { m_deviceName = deviceName; }
34+ void setDeviceName(const QString &deviceName) {
35+ if (deviceName != m_deviceName) {
36+ m_deviceName = deviceName;
37+ Q_EMIT deviceNameChanged(m_deviceName);
38+ }
39+ }
35 QString deviceName() const { return m_deviceName; }
40 QString deviceName() const { return m_deviceName; }
36
41
37 void setMode(const QString &mode) { m_mode = mode; }
42 void setMode(const QString &mode) { m_mode = mode; }
38 QString mode() const { return m_mode; }
43 QString mode() const { return m_mode; }
39
44
45+Q_SIGNALS:
46+ void deviceNameChanged(const QString&);
47+
40private:
48private:
41 QString m_deviceName;
49 QString m_deviceName;
42 QString m_mode;
50 QString m_mode;
src/CMakeLists.txt+3
@@ -21,6 +21,9 @@ add_executable(${SHELL_APP}
21 main.cpp
21 main.cpp
22 MouseTouchAdaptor.cpp
22 MouseTouchAdaptor.cpp
23 CachingNetworkManagerFactory.cpp
23 CachingNetworkManagerFactory.cpp
24+ SecondaryWindow.cpp
25+ ShellApplication.cpp
26+ ShellView.cpp
24 UnityCommandLineParser.cpp
27 UnityCommandLineParser.cpp
25 ${QML_FILES} # This is to make qml and image files appear in the IDE's project tree
28 ${QML_FILES} # This is to make qml and image files appear in the IDE's project tree
26)
29)
src/SecondaryWindow.cppnew file+31
@@ -0,0 +1,31 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "SecondaryWindow.h"
18+
19+// local
20+#include <paths.h>
21+
22+SecondaryWindow::SecondaryWindow(QQmlEngine *engine)
23+ : QQuickView(engine, nullptr)
24+{
25+ setResizeMode(QQuickView::SizeRootObjectToView);
26+ setColor("black");
27+ setTitle(QStringLiteral("Unity8 Shell - Secondary Screen"));
28+
29+ QUrl source(::qmlDirectory() + "/DisabledScreenNotice.qml");
30+ setSource(source);
31+}
src/SecondaryWindow.hnew file+30
@@ -0,0 +1,30 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef UNITY_SECONDARY_WINDOW_H
18+#define UNITY_SECONDARY_WINDOW_H
19+
20+#include <QQuickView>
21+
22+class SecondaryWindow : public QQuickView
23+{
24+ Q_OBJECT
25+
26+public:
27+ SecondaryWindow(QQmlEngine *engine);
28+};
29+
30+#endif // UNITY_SECONDARY_WINDOW_H
src/ShellApplication.cppnew file+197
@@ -0,0 +1,197 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "ShellApplication.h"
18+
19+// Qt
20+#include <QLibrary>
21+#include <QScreen>
22+
23+#include <libintl.h>
24+
25+// libandroid-properties
26+#include <hybris/properties/properties.h>
27+
28+// local
29+#include <paths.h>
30+#include "CachingNetworkManagerFactory.h"
31+#include "MouseTouchAdaptor.h"
32+#include "UnityCommandLineParser.h"
33+
34+ShellApplication::ShellApplication(int & argc, char ** argv, bool isMirServer)
35+ : QGuiApplication(argc, argv)
36+ , m_shellView(nullptr)
37+ , m_secondaryWindow(nullptr)
38+ , m_mouseTouchAdaptor(nullptr)
39+ , m_qmlEngine(nullptr)
40+{
41+
42+ setApplicationName(QStringLiteral("unity8"));
43+
44+ connect(this, &QGuiApplication::screenAdded, this, &ShellApplication::onScreenAdded);
45+
46+ setupQmlEngine(isMirServer);
47+
48+ UnityCommandLineParser parser(*this);
49+
50+ if (!parser.deviceName().isEmpty()) {
51+ m_deviceName = parser.deviceName();
52+ } else {
53+ char buffer[200];
54+ property_get("ro.product.device", buffer /* value */, "desktop" /* default_value*/);
55+ m_deviceName = QString(buffer);
56+ }
57+ m_qmlArgs.setDeviceName(m_deviceName);
58+
59+ m_qmlArgs.setMode(parser.mode());
60+
61+ // The testability driver is only loaded by QApplication but not by QGuiApplication.
62+ // However, QApplication depends on QWidget which would add some unneeded overhead => Let's load the testability driver on our own.
63+ if (parser.hasTestability() || getenv("QT_LOAD_TESTABILITY")) {
64+ QLibrary testLib(QStringLiteral("qttestability"));
65+ if (testLib.load()) {
66+ typedef void (*TasInitialize)(void);
67+ TasInitialize initFunction = (TasInitialize)testLib.resolve("qt_testability_init");
68+ if (initFunction) {
69+ initFunction();
70+ } else {
71+ qCritical("Library qttestability resolve failed!");
72+ }
73+ } else {
74+ qCritical("Library qttestability load failed!");
75+ }
76+ }
77+
78+ bindtextdomain("unity8", translationDirectory().toUtf8().data());
79+ textdomain("unity8");
80+
81+ m_shellView = new ShellView(m_qmlEngine, &m_qmlArgs);
82+
83+ if (parser.windowGeometry().isValid()) {
84+ m_shellView->setWidth(parser.windowGeometry().width());
85+ m_shellView->setHeight(parser.windowGeometry().height());
86+ }
87+
88+ if (parser.hasFrameless()) {
89+ m_shellView->setFlags(Qt::FramelessWindowHint);
90+ }
91+
92+ // You will need this if you want to interact with touch-only components using a mouse
93+ // Needed only when manually testing on a desktop.
94+ if (parser.hasMouseToTouch()) {
95+ m_mouseTouchAdaptor = MouseTouchAdaptor::instance();
96+ }
97+
98+
99+ // Some hard-coded policy for now.
100+ // NB: We don't support more than two screens at the moment
101+ //
102+ // TODO: Support an arbitrary number of screens and different policies
103+ // (eg cloned desktop, several desktops, etc)
104+ if (isMirServer && screens().count() == 2) {
105+ m_shellView->setScreen(screens().at(1));
106+ m_qmlArgs.setDeviceName("desktop");
107+
108+ m_secondaryWindow = new SecondaryWindow(m_qmlEngine);
109+ m_secondaryWindow->setScreen(screens().at(0));
110+ // QWindow::showFullScreen() also calls QWindow::requestActivate() and we don't want that!
111+ m_secondaryWindow->setWindowState(Qt::WindowFullScreen);
112+ m_secondaryWindow->setVisible(true);
113+ }
114+
115+ if (isMirServer || parser.hasFullscreen()) {
116+ m_shellView->showFullScreen();
117+ } else {
118+ m_shellView->show();
119+ }
120+}
121+
122+ShellApplication::~ShellApplication()
123+{
124+ destroyResources();
125+}
126+
127+void ShellApplication::destroyResources()
128+{
129+ // Deletion order is important. Don't use QScopedPointers and the like
130+ // Otherwise the process will hang on shutdown (bug somewhere I guess).
131+ delete m_shellView;
132+ m_shellView = nullptr;
133+
134+ delete m_secondaryWindow;
135+ m_secondaryWindow = nullptr;
136+
137+ delete m_mouseTouchAdaptor;
138+ m_mouseTouchAdaptor = nullptr;
139+
140+ delete m_qmlEngine;
141+ m_qmlEngine = nullptr;
142+}
143+
144+void ShellApplication::setupQmlEngine(bool isMirServer)
145+{
146+ m_qmlEngine = new QQmlEngine(this);
147+
148+ m_qmlEngine->setBaseUrl(QUrl::fromLocalFile(::qmlDirectory()));
149+
150+ prependImportPaths(m_qmlEngine, ::overrideImportPaths());
151+ if (!isMirServer) {
152+ prependImportPaths(m_qmlEngine, ::nonMirImportPaths());
153+ }
154+ appendImportPaths(m_qmlEngine, ::fallbackImportPaths());
155+
156+ m_qmlEngine->setNetworkAccessManagerFactory(new CachingNetworkManagerFactory);
157+
158+ QObject::connect(m_qmlEngine, &QQmlEngine::quit, this, &QGuiApplication::quit);
159+}
160+
161+void ShellApplication::onScreenAdded(QScreen * /*screen*/)
162+{
163+ // TODO: Support an arbitrary number of screens and different policies
164+ // (eg cloned desktop, several desktops, etc)
165+ if (screens().count() == 2) {
166+ m_shellView->setScreen(screens().at(1));
167+ m_qmlArgs.setDeviceName("desktop");
168+ // Changing the QScreen where a QWindow is drawn makes it also lose focus (besides having
169+ // its backing QPlatformWindow recreated). So lets refocus it.
170+ m_shellView->requestActivate();
171+
172+ m_secondaryWindow = new SecondaryWindow(m_qmlEngine);
173+ m_secondaryWindow->setScreen(screens().at(0));
174+
175+ // QWindow::showFullScreen() also calls QWindow::requestActivate() and we don't want that!
176+ m_secondaryWindow->setWindowState(Qt::WindowFullScreen);
177+ m_secondaryWindow->setVisible(true);
178+ }
179+}
180+
181+void ShellApplication::onScreenAboutToBeRemoved(QScreen *screen)
182+{
183+ // TODO: Support an arbitrary number of screens and different policies
184+ // (eg cloned desktop, several desktops, etc)
185+ if (screen == m_shellView->screen()) {
186+ Q_ASSERT(screens().count() > 1);
187+ Q_ASSERT(screens().at(0) != screen);
188+ Q_ASSERT(m_secondaryWindow);
189+ delete m_secondaryWindow;
190+ m_secondaryWindow = nullptr;
191+ m_shellView->setScreen(screens().first());
192+ m_qmlArgs.setDeviceName(m_deviceName);
193+ // Changing the QScreen where a QWindow is drawn makes it also lose focus (besides having
194+ // its backing QPlatformWindow recreated). So lets refocus it.
195+ m_shellView->requestActivate();
196+ }
197+}
src/ShellApplication.hnew file+55
@@ -0,0 +1,55 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef SHELLAPPLICATION_H
18+#define SHELLAPPLICATION_H
19+
20+#include <QGuiApplication>
21+#include <QQmlEngine>
22+#include <QQuickView>
23+#include <QScopedPointer>
24+
25+#include "ApplicationArguments.h"
26+#include "MouseTouchAdaptor.h"
27+#include "SecondaryWindow.h"
28+#include "ShellView.h"
29+
30+class ShellApplication : public QGuiApplication
31+{
32+ Q_OBJECT
33+public:
34+ ShellApplication(int & argc, char ** argv, bool isMirServer);
35+ virtual ~ShellApplication();
36+
37+ void destroyResources();
38+public Q_SLOTS:
39+ // called by qtmir
40+ void onScreenAboutToBeRemoved(QScreen *screen);
41+
42+private Q_SLOTS:
43+ void onScreenAdded(QScreen*);
44+
45+private:
46+ void setupQmlEngine(bool isMirServer);
47+ QString m_deviceName;
48+ ApplicationArguments m_qmlArgs;
49+ ShellView *m_shellView;
50+ SecondaryWindow *m_secondaryWindow;
51+ MouseTouchAdaptor *m_mouseTouchAdaptor;
52+ QQmlEngine *m_qmlEngine;
53+};
54+
55+#endif // SHELLAPPLICATION_H
src/ShellView.cppnew file+59
@@ -0,0 +1,59 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "ShellView.h"
18+
19+// Qt
20+#include <QQmlContext>
21+#include <QQuickItem>
22+
23+// local
24+#include <paths.h>
25+
26+ShellView::ShellView(QQmlEngine *engine, QObject *qmlArgs)
27+ : QQuickView(engine, nullptr)
28+{
29+ setResizeMode(QQuickView::SizeRootObjectToView);
30+ setColor("black");
31+ setTitle(QStringLiteral("Unity8"));
32+
33+ rootContext()->setContextProperty(QStringLiteral("applicationArguments"), qmlArgs);
34+
35+ QUrl source(::qmlDirectory() + "/OrientedShell.qml");
36+ setSource(source);
37+
38+ connect(this, &QWindow::widthChanged, this, &ShellView::onWidthChanged);
39+ connect(this, &QWindow::heightChanged, this, &ShellView::onHeightChanged);
40+}
41+
42+void ShellView::onWidthChanged(int w)
43+{
44+ // For good measure in case SizeRootObjectToView doesn't fulfill its promise.
45+ //
46+ // There's at least one situation that's know to leave the root object with an outdated size.
47+ // (really looks like Qt bug)
48+ // Happens when starting unity8 with an external monitor already connected.
49+ // The QResizeEvent we get still has the size of the first screen and since the resize move is triggered
50+ // from the resize event handler, the root item doesn't get resized.
51+ // TODO: Confirm the Qt bug and submit a patch upstream
52+ rootObject()->setWidth(w);
53+}
54+
55+void ShellView::onHeightChanged(int h)
56+{
57+ // See comment in ShellView::onWidthChanged()
58+ rootObject()->setHeight(h);
59+}
src/ShellView.hnew file+34
@@ -0,0 +1,34 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef UNITY_SHELL_VIEW_H
18+#define UNITY_SHELL_VIEW_H
19+
20+#include <QQuickView>
21+
22+class ShellView : public QQuickView
23+{
24+ Q_OBJECT
25+
26+public:
27+ ShellView(QQmlEngine *engine, QObject *qmlArgs);
28+
29+private Q_SLOTS:
30+ void onWidthChanged(int);
31+ void onHeightChanged(int);
32+};
33+
34+#endif // UNITY_SHELL_VIEW_H
src/main.cpp+4−101
@@ -14,26 +14,8 @@
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−// Qt
18−#include <QCommandLineParser>
19−#include <QtQuick/QQuickView>
20−#include <QtGui/QGuiApplication>
21−#include <QtQml/QQmlEngine>
22−#include <QtQml/QQmlContext>
23−#include <QLibrary>
24−#include <QDebug>
25−#include <csignal>
26−#include <libintl.h>
27−
28−// libandroid-properties
29−#include <hybris/properties/properties.h>
30−
31// local
17// local
32−#include <paths.h>
18+#include "ShellApplication.h"
33−#include "MouseTouchAdaptor.h"
34−#include "ApplicationArguments.h"
35−#include "CachingNetworkManagerFactory.h"
36−#include "UnityCommandLineParser.h"
37
19
38int main(int argc, const char *argv[])
20int main(int argc, const char *argv[])
39{
21{
@@ -43,91 +25,12 @@ int main(int argc, const char *argv[])
43 isMirServer = true;
25 isMirServer = true;
44 }
26 }
45
27
46− QGuiApplication::setApplicationName(QStringLiteral("unity8"));
28+ ShellApplication *application = new ShellApplication(argc, (char**)argv, isMirServer);
47− QGuiApplication *application;
48−
49− application = new QGuiApplication(argc, (char**)argv);
50−
51− UnityCommandLineParser parser(*application);
52−
53− ApplicationArguments qmlArgs;
54−
55− if (!parser.deviceName().isEmpty()) {
56− qmlArgs.setDeviceName(parser.deviceName());
57− } else {
58− char buffer[200];
59− property_get("ro.product.device", buffer /* value */, "desktop" /* default_value*/);
60− qmlArgs.setDeviceName(QString(buffer));
61− }
62−
63− qmlArgs.setMode(parser.mode());
64−
65− // The testability driver is only loaded by QApplication but not by QGuiApplication.
66− // However, QApplication depends on QWidget which would add some unneeded overhead => Let's load the testability driver on our own.
67− if (parser.hasTestability() || getenv("QT_LOAD_TESTABILITY")) {
68− QLibrary testLib(QStringLiteral("qttestability"));
69− if (testLib.load()) {
70− typedef void (*TasInitialize)(void);
71− TasInitialize initFunction = (TasInitialize)testLib.resolve("qt_testability_init");
72− if (initFunction) {
73− initFunction();
74− } else {
75− qCritical("Library qttestability resolve failed!");
76− }
77− } else {
78− qCritical("Library qttestability load failed!");
79− }
80− }
81−
82− bindtextdomain("unity8", translationDirectory().toUtf8().data());
83− textdomain("unity8");
84−
85− QQuickView* view = new QQuickView();
86− view->setResizeMode(QQuickView::SizeRootObjectToView);
87− view->setColor("black");
88− view->setTitle(QStringLiteral("Unity8 Shell"));
89−
90− if (parser.windowGeometry().isValid()) {
91− view->setWidth(parser.windowGeometry().width());
92− view->setHeight(parser.windowGeometry().height());
93− }
94−
95− view->engine()->setBaseUrl(QUrl::fromLocalFile(::qmlDirectory()));
96− view->rootContext()->setContextProperty(QStringLiteral("applicationArguments"), &qmlArgs);
97− if (parser.hasFrameless()) {
98− view->setFlags(Qt::FramelessWindowHint);
99− }
100−
101− // You will need this if you want to interact with touch-only components using a mouse
102− // Needed only when manually testing on a desktop.
103− MouseTouchAdaptor *mouseTouchAdaptor = 0;
104− if (parser.hasMouseToTouch()) {
105− mouseTouchAdaptor = MouseTouchAdaptor::instance();
106− }
107−
108− QUrl source(::qmlDirectory() + "/OrientedShell.qml");
109− prependImportPaths(view->engine(), ::overrideImportPaths());
110− if (!isMirServer) {
111− prependImportPaths(view->engine(), ::nonMirImportPaths());
112− }
113− appendImportPaths(view->engine(), ::fallbackImportPaths());
114−
115− CachingNetworkManagerFactory *managerFactory = new CachingNetworkManagerFactory();
116− view->engine()->setNetworkAccessManagerFactory(managerFactory);
117−
118− view->setSource(source);
119− QObject::connect(view->engine(), &QQmlEngine::quit, application, &QGuiApplication::quit);
120−
121− if (isMirServer || parser.hasFullscreen()) {
122− view->showFullScreen();
123− } else {
124− view->show();
125− }
126
29
127 int result = application->exec();
30 int result = application->exec();
128
31
129− delete view;
32+ application->destroyResources();
130− delete mouseTouchAdaptor;
33+
131 delete application;
34 delete application;
132
35
133 return result;
36 return result;
tests/autopilot/unity8/fixture_setup.py+1−1
@@ -133,7 +133,7 @@ class LaunchUnityWithFakeSensors(fixtures.Fixture):
133 args = [binary_arg] + env_args
133 args = [binary_arg] + env_args
134 self.unity_proxy = process_helpers.restart_unity_with_testability(
134 self.unity_proxy = process_helpers.restart_unity_with_testability(
135 *args)
135 *args)
136− self.main_win = self.unity_proxy.select_single(shell.QQuickView)
136+ self.main_win = self.unity_proxy.select_single(shell.ShellView)
137
137
138 def _create_sensors(self):
138 def _create_sensors(self):
139 # Wait for unity to start running.
139 # Wait for unity to start running.
tests/autopilot/unity8/greeter/tests/__init__.py+1−1
@@ -24,5 +24,5 @@ from unity8.shell.tests import UnityTestCase
24class GreeterTestCase(UnityTestCase):
24class GreeterTestCase(UnityTestCase):
25 def get_shell(self, unity_proxy):
25 def get_shell(self, unity_proxy):
26 main_window = (
26 main_window = (
27− unity_proxy.select_single(shell.QQuickView))
27+ unity_proxy.select_single(shell.ShellView))
28 return main_window.select_single('Shell')
28 return main_window.select_single('Shell')
tests/autopilot/unity8/launcher.py+1−1
@@ -42,7 +42,7 @@ class Launcher(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
42 logger.debug('The launcher is already opened.')
42 logger.debug('The launcher is already opened.')
43
43
44 def _swipe_to_show_launcher(self):
44 def _swipe_to_show_launcher(self):
45− view = self.get_root_instance().select_single('QQuickView')
45+ view = self.get_root_instance().select_single('ShellView')
46 start_y = stop_y = view.y + view.height // 2
46 start_y = stop_y = view.y + view.height // 2
47
47
48 start_x = view.x + 1
48 start_x = view.x + 1
tests/autopilot/unity8/settings_wizard/__init__.py+7−2
@@ -375,12 +375,17 @@ class WifiConnectPage(UbuntuUIToolkitCustomProxyObjectBase):
375 wizard = get_wizard(self)
375 wizard = get_wizard(self)
376 next_page = wizard.get_current_page()
376 next_page = wizard.get_current_page()
377 locationPageEnabled = True
377 locationPageEnabled = True
378+ reportingPageEnabled = True
378 if next_page.objectName == 'locationPage':
379 if next_page.objectName == 'locationPage':
379 next_page = wizard.get_location_page()
380 next_page = wizard.get_location_page()
380 else:
381 else:
381 locationPageEnabled = False
382 locationPageEnabled = False
383+ if next_page.objectName == 'reportingPage':
382− next_page = wizard.get_reporting_page()
384+ next_page = wizard.get_reporting_page()
385+ else:
386+ reportingPageEnabled = False
387+ next_page = wizard.get_finished_page()
383− return locationPageEnabled, next_page
388+ return locationPageEnabled, reportingPageEnabled, next_page
384
389
385 def _get_notification(self, unity):
390 def _get_notification(self, unity):
386 logger.info('Waiting longer for notification object')
391 logger.info('Waiting longer for notification object')
tests/autopilot/unity8/settings_wizard/tests/test_settings_wizard.py+10−5
@@ -80,14 +80,19 @@ class SkipThroughSettingsWizardTestCase(tests.UnityTestCase):
80 password_page = next_page
80 password_page = next_page
81 wifi_connect_page = self._test_password_page(password_page)
81 wifi_connect_page = self._test_password_page(password_page)
82
82
83+ reporting_page = None
83− locationPageEnabled, next_page = self._test_wifi_connect_page(
84+ locationPageEnabled, reportingPageEnabled, next_page = self._test_wifi_connect_page(wifi_connect_page)
84− wifi_connect_page)
85 if locationPageEnabled:
85 if locationPageEnabled:
86 location_page = next_page
86 location_page = next_page
87+ if reportingPageEnabled:
87− reporting_page = self._test_location_page(location_page)
88+ reporting_page = self._test_location_page(location_page)
89+ else:
90+ finish_page = next_page
91+
92+ if reporting_page is not None:
93+ finish_page = self._test_reporting_page(reporting_page)
88 else:
94 else:
89− reporting_page = next_page
95+ finish_page = next_page
90− finish_page = self._test_reporting_page(reporting_page)
91
96
92 finish_page.finish()
97 finish_page.finish()
93 self.assertFalse(
98 self.assertFalse(
tests/autopilot/unity8/shell/__init__.py+1−1
@@ -93,7 +93,7 @@ def _get_urgency(urgency):
93 return _urgency_enums.get(urgency.upper())
93 return _urgency_enums.get(urgency.upper())
94
94
95
95
96−class QQuickView(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
96+class ShellView(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase):
97 """An helper class that makes it easy to interact with the shell"""
97 """An helper class that makes it easy to interact with the shell"""
98
98
99 def get_greeter(self):
99 def get_greeter(self):
tests/autopilot/unity8/shell/tests/__init__.py+1−1
@@ -297,7 +297,7 @@ class UnityTestCase(AutopilotTestCase):
297
297
298 @property
298 @property
299 def main_window(self):
299 def main_window(self):
300− return self._proxy.select_single(shell.QQuickView)
300+ return self._proxy.select_single(shell.ShellView)
301
301
302
302
303class DashBaseTestCase(AutopilotTestCase):
303class DashBaseTestCase(AutopilotTestCase):
tests/mocks/Unity/Launcher/MockQuickListModel.cpp+1−1
@@ -32,7 +32,7 @@ QVariant MockQuickListModel::data(const QModelIndex &index, int role) const
32 switch (role)
32 switch (role)
33 {
33 {
34 case RoleLabel:
34 case RoleLabel:
35− return QLatin1String("test menu entry ") + QString::number(index.row());
35+ return QString(QLatin1String("test menu entry ") + QString::number(index.row()));
36 case RoleIcon:
36 case RoleIcon:
37 return QLatin1String("copy.png");
37 return QLatin1String("copy.png");
38 case RoleClickable:
38 case RoleClickable:
tests/mocks/Unity/fake_resultsmodel.cpp+1−1
@@ -78,7 +78,7 @@ ResultsModel::data(const QModelIndex& index, int role) const
78 case RoleTitle:
78 case RoleTitle:
79 return QString("Title.%1.%2").arg(m_categoryId).arg(index.row());
79 return QString("Title.%1.%2").arg(m_categoryId).arg(index.row());
80 case RoleArt:
80 case RoleArt:
81− return qmlDirectory() + "/graphics/applicationIcons/dash.png";
81+ return QString(qmlDirectory() + "/graphics/applicationIcons/dash.png");
82 case RoleSubtitle:
82 case RoleSubtitle:
83 return QString("Subtitle.%1.%2").arg(m_categoryId).arg(index.row());
83 return QString("Subtitle.%1.%2").arg(m_categoryId).arg(index.row());
84 case RoleMascot:
84 case RoleMascot:
tests/mocks/Unity/fake_scopesoverview.cpp+1−1
@@ -308,7 +308,7 @@ QVariant ScopesOverviewResultsModel::data(const QModelIndex& index, int role) co
308 case RoleSubtitle:
308 case RoleSubtitle:
309 return scope && scope->name() == "Videos this is long ab cd ef gh ij kl" ? "tube, movies, cinema, pictures, art, moving images, magic in a box" : QString();
309 return scope && scope->name() == "Videos this is long ab cd ef gh ij kl" ? "tube, movies, cinema, pictures, art, moving images, magic in a box" : QString();
310 case RoleArt:
310 case RoleArt:
311− return qmlDirectory() + "/graphics/applicationIcons/dash.png";
311+ return QString(qmlDirectory() + "/graphics/applicationIcons/dash.png");
312 case RoleMascot:
312 case RoleMascot:
313 case RoleEmblem:
313 case RoleEmblem:
314 case RoleSummary:
314 case RoleSummary:
tests/mocks/Utils/CMakeLists.txt−2
@@ -13,8 +13,6 @@ set(QMLPLUGIN_SRC
13 ${CMAKE_SOURCE_DIR}/plugins/Utils/activefocuslogger.cpp
13 ${CMAKE_SOURCE_DIR}/plugins/Utils/activefocuslogger.cpp
14 ${CMAKE_SOURCE_DIR}/plugins/Utils/qlimitproxymodelqml.cpp
14 ${CMAKE_SOURCE_DIR}/plugins/Utils/qlimitproxymodelqml.cpp
15 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitysortfilterproxymodelqml.cpp
15 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitysortfilterproxymodelqml.cpp
16− ${CMAKE_SOURCE_DIR}/plugins/Utils/relativetimeformatter.cpp
17− ${CMAKE_SOURCE_DIR}/plugins/Utils/timeformatter.cpp
18 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitymenumodelpaths.cpp
16 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitymenumodelpaths.cpp
19 ${CMAKE_SOURCE_DIR}/plugins/Utils/windowkeysfilter.cpp
17 ${CMAKE_SOURCE_DIR}/plugins/Utils/windowkeysfilter.cpp
20 ${CMAKE_SOURCE_DIR}/plugins/Utils/windowscreenshotprovider.cpp
18 ${CMAKE_SOURCE_DIR}/plugins/Utils/windowscreenshotprovider.cpp
tests/mocks/Utils/Utils.qmltypes+31−28
@@ -4,9 +4,18 @@ import QtQuick.tooling 1.1
4// It is used for QML tooling purposes only.
4// It is used for QML tooling purposes only.
5//
5//
6// This file was auto-generated by:
6// This file was auto-generated by:
7−// 'qmlplugindump -notrelocatable Utils 0.1 plugins'
7+// 'qmlplugindump -notrelocatable Utils 0.1 tests/mocks'
8
8
9Module {
9Module {
10+ Component {
11+ name: "Constants"
12+ prototype: "QObject"
13+ exports: ["Utils/Constants 0.1"]
14+ isCreatable: false
15+ isSingleton: true
16+ exportMetaObjectRevisions: [0]
17+ Property { name: "indicatorValueTimeout"; type: "int"; isReadonly: true }
18+ }
10 Component {
19 Component {
11 name: "EasingCurve"
20 name: "EasingCurve"
12 prototype: "QObject"
21 prototype: "QObject"
@@ -17,6 +26,22 @@ Module {
17 Property { name: "progress"; type: "double" }
26 Property { name: "progress"; type: "double" }
18 Property { name: "value"; type: "double"; isReadonly: true }
27 Property { name: "value"; type: "double"; isReadonly: true }
19 }
28 }
29+ Component {
30+ name: "InputWatcher"
31+ prototype: "QObject"
32+ exports: ["Utils/InputWatcher 0.1"]
33+ exportMetaObjectRevisions: [0]
34+ Property { name: "target"; type: "QObject"; isPointer: true }
35+ Property { name: "targetPressed"; type: "bool"; isReadonly: true }
36+ Signal {
37+ name: "targetChanged"
38+ Parameter { name: "value"; type: "QObject"; isPointer: true }
39+ }
40+ Signal {
41+ name: "targetPressedChanged"
42+ Parameter { name: "value"; type: "bool" }
43+ }
44+ }
20 Component {
45 Component {
21 name: "QAbstractProxyModel"
46 name: "QAbstractProxyModel"
22 prototype: "QAbstractItemModel"
47 prototype: "QAbstractItemModel"
@@ -59,33 +84,6 @@ Module {
59 Method { name: "clear" }
84 Method { name: "clear" }
60 Method { name: "invalidate" }
85 Method { name: "invalidate" }
61 }
86 }
62− Component {
63− name: "RelativeTimeFormatter"
64− prototype: "TimeFormatter"
65− exports: ["Utils/RelativeTimeFormatter 0.1"]
66− exportMetaObjectRevisions: [0]
67− }
68− Component {
69− name: "TimeFormatter"
70− prototype: "QObject"
71− exports: ["Utils/GDateTimeFormatter 0.1", "Utils/TimeFormatter 0.1"]
72− exportMetaObjectRevisions: [0, 0]
73− Property { name: "format"; type: "string" }
74− Property { name: "timeString"; type: "string"; isReadonly: true }
75− Property { name: "time"; type: "qlonglong" }
76− Signal {
77− name: "formatChanged"
78− Parameter { name: "format"; type: "string" }
79− }
80− Signal {
81− name: "timeStringChanged"
82− Parameter { name: "timeString"; type: "string" }
83− }
84− Signal {
85− name: "timeChanged"
86− Parameter { name: "time"; type: "qlonglong" }
87− }
88− }
89 Component {
87 Component {
90 name: "UnityMenuModelPaths"
88 name: "UnityMenuModelPaths"
91 prototype: "QObject"
89 prototype: "QObject"
@@ -160,6 +158,11 @@ Module {
160 isCreatable: false
158 isCreatable: false
161 isSingleton: true
159 isSingleton: true
162 exportMetaObjectRevisions: [0]
160 exportMetaObjectRevisions: [0]
161+ Property { name: "geometry"; type: "QVariantMap" }
162+ Signal {
163+ name: "geometryChanged"
164+ Parameter { name: "geometry"; type: "QVariantMap" }
165+ }
163 Method {
166 Method {
164 name: "saveGeometry"
167 name: "saveGeometry"
165 Parameter { name: "windowId"; type: "string" }
168 Parameter { name: "windowId"; type: "string" }
tests/mocks/Utils/plugin.cpp−13
@@ -23,14 +23,6 @@
23
23
24// local
24// local
25#include "plugin.h"
25#include "plugin.h"
26−#include "inputwatcher.h"
27−#include "qlimitproxymodelqml.h"
28−#include "unitysortfilterproxymodelqml.h"
29−#include "relativetimeformatter.h"
30−#include "timeformatter.h"
31−#include "unitymenumodelpaths.h"
32−#include "windowkeysfilter.h"
33−#include "easingcurve.h"
34#include "windowstatestorage.h"
26#include "windowstatestorage.h"
35#include "constants.h"
27#include "constants.h"
36
28
@@ -39,8 +31,6 @@
39#include <inputwatcher.h>
31#include <inputwatcher.h>
40#include <qlimitproxymodelqml.h>
32#include <qlimitproxymodelqml.h>
41#include <unitysortfilterproxymodelqml.h>
33#include <unitysortfilterproxymodelqml.h>
42−#include <relativetimeformatter.h>
43−#include <timeformatter.h>
44#include <unitymenumodelpaths.h>
34#include <unitymenumodelpaths.h>
45#include <windowkeysfilter.h>
35#include <windowkeysfilter.h>
46#include <windowscreenshotprovider.h>
36#include <windowscreenshotprovider.h>
@@ -67,11 +57,8 @@ void FakeUtilsPlugin::registerTypes(const char *uri)
67 qmlRegisterType<QLimitProxyModelQML>(uri, 0, 1, "LimitProxyModel");
57 qmlRegisterType<QLimitProxyModelQML>(uri, 0, 1, "LimitProxyModel");
68 qmlRegisterType<UnitySortFilterProxyModelQML>(uri, 0, 1, "UnitySortFilterProxyModel");
58 qmlRegisterType<UnitySortFilterProxyModelQML>(uri, 0, 1, "UnitySortFilterProxyModel");
69 qmlRegisterType<UnityMenuModelPaths>(uri, 0, 1, "UnityMenuModelPaths");
59 qmlRegisterType<UnityMenuModelPaths>(uri, 0, 1, "UnityMenuModelPaths");
70− qmlRegisterType<TimeFormatter>(uri, 0, 1, "TimeFormatter");
71 qmlRegisterType<WindowKeysFilter>(uri, 0, 1, "WindowKeysFilter");
60 qmlRegisterType<WindowKeysFilter>(uri, 0, 1, "WindowKeysFilter");
72− qmlRegisterType<GDateTimeFormatter>(uri, 0, 1, "GDateTimeFormatter");
73 qmlRegisterType<EasingCurve>(uri, 0, 1, "EasingCurve");
61 qmlRegisterType<EasingCurve>(uri, 0, 1, "EasingCurve");
74− qmlRegisterType<RelativeTimeFormatter>(uri, 0, 1, "RelativeTimeFormatter");
75 qmlRegisterSingletonType<WindowStateStorage>(uri, 0, 1, "WindowStateStorage", createWindowStateStorage);
62 qmlRegisterSingletonType<WindowStateStorage>(uri, 0, 1, "WindowStateStorage", createWindowStateStorage);
76 qmlRegisterType<InputWatcher>(uri, 0, 1, "InputWatcher");
63 qmlRegisterType<InputWatcher>(uri, 0, 1, "InputWatcher");
77 qmlRegisterSingletonType<Constants>(uri, 0, 1, "Constants", createConstants);
64 qmlRegisterSingletonType<Constants>(uri, 0, 1, "Constants", createConstants);
tests/plugins/ScreenGrabber/ScreenGrabberTest.cpp+11
@@ -55,6 +55,17 @@ private Q_SLOTS:
55 QVERIFY(!args.first().toString().isEmpty());
55 QVERIFY(!args.first().toString().isEmpty());
56 }
56 }
57
57
58+ void testRotatedScreenshot()
59+ {
60+ QSignalSpy grabberSpy(m_grabber, &ScreenGrabber::screenshotSaved);
61+ m_grabber->captureAndSave(90); // rotate by 90°
62+ const QVariantList args = grabberSpy.takeFirst();
63+ const QString filename = args.first().toString();
64+ QVERIFY(!filename.isEmpty());
65+ QImage img(filename);
66+ QVERIFY(img.height() > img.width()); // verify that the image got rotated by 90° (height > width)
67+ }
68+
58private:
69private:
59 QQuickView *m_view;
70 QQuickView *m_view;
60 ScreenGrabber *m_grabber = nullptr;
71 ScreenGrabber *m_grabber = nullptr;
tests/plugins/ScreenGrabber/grabber.qml+1−1
@@ -20,7 +20,7 @@ import ScreenGrabber 0.1
20Rectangle {
20Rectangle {
21 property var grabber: screenGrabber
21 property var grabber: screenGrabber
22
22
23− width: 100
23+ width: 101 // width intentionally bigger than height to test rotation
24 height: 100
24 height: 100
25 color: "green"
25 color: "green"
26
26
tests/plugins/Unity/Indicators/UnityMenuModelStackTest.cpp+2−9
@@ -43,6 +43,8 @@ private Q_SLOTS:
43 {
43 {
44 delete m_model;
44 delete m_model;
45 m_model = nullptr;
45 m_model = nullptr;
46+ // send deleteLaters to avoid leaks.
47+ QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
46 }
48 }
47
49
48 void testHeadOnSetHead()
50 void testHeadOnSetHead()
@@ -146,19 +148,10 @@ private Q_SLOTS:
146
148
147 m_model->removeRow(removeIndex);
149 m_model->removeRow(removeIndex);
148
150
149− waitFor([&stack, resultCount]() { return stack.count() == resultCount; }, 1000);
150 QCOMPARE(stack.count(), resultCount);
151 QCOMPARE(stack.count(), resultCount);
151 }
152 }
152
153
153private:
154private:
154− bool waitFor(std::function<bool()> functor, int ms) {
155−
156− QElapsedTimer timer;
157− timer.start();
158− while(!functor() && timer.elapsed() < ms) { QTest::qWait(10); }
159− return functor();
160− }
161−
162 QVariant recuseAddMenu(int subMenuCount, int depth_remaining)
155 QVariant recuseAddMenu(int subMenuCount, int depth_remaining)
163 {
156 {
164 QVariantList rows;
157 QVariantList rows;
tests/plugins/Utils/CMakeLists.txt−1
@@ -7,7 +7,6 @@ include_directories(
7foreach(util_test
7foreach(util_test
8 QLimitProxyModel
8 QLimitProxyModel
9 UnitySortFilterProxyModel
9 UnitySortFilterProxyModel
10− TimeFormatter
11 HomeKeyWatcher
10 HomeKeyWatcher
12)
11)
13 add_executable(${util_test}TestExec ${util_test}Test.cpp ModelTest.cpp)
12 add_executable(${util_test}TestExec ${util_test}Test.cpp ModelTest.cpp)
tests/plugins/Utils/TimeFormatterTest.cppdeleted−155
@@ -1,155 +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− * Author: Lars Uebernickel <lars.uebernickel@canonical.com>
17− */
18−
19−#include "timeformatter.h"
20−#include "relativetimeformatter.h"
21−
22−#include <locale.h>
23−
24−#include <QtTest>
25−#include <QDebug>
26−
27−class TimeFormatterTest : public QObject
28−{
29− Q_OBJECT
30−private:
31− void addTestData(QList<int> daysToAdd = QList<int>()) {
32− QTest::addColumn<QDateTime>("time");
33−
34− Q_FOREACH(int dayToAdd, daysToAdd) {
35− QDateTime time = QDateTime::currentDateTime().addDays(dayToAdd);
36− QDateTime am(time); am.setTime(QTime(1, 1, 1));
37− QDateTime midday(time); midday.setTime(QTime(12, 0, 0));
38− QDateTime pm(time); pm.setTime(QTime(17, 59, 59));
39−
40− QTest::newRow(QString("%1day-am").arg(dayToAdd).toLatin1()) << am;
41− QTest::newRow(QString("%1day-midday").arg(dayToAdd).toLatin1()) << midday;
42− QTest::newRow(QString("%1day-pm").arg(dayToAdd).toLatin1()) << pm;
43− }
44−
45− }
46−
47−private Q_SLOTS:
48− void testTimeFormatter_Format_data() { addTestData(QList<int>() << 0); }
49− void testTimeFormatter_Format()
50− {
51− QFETCH(QDateTime, time);
52− const QString format = "hh:mm dd.MM.yy";
53−
54− TimeFormatter formatter;
55− formatter.setTime(time.toMSecsSinceEpoch() * 1000);
56− formatter.setFormat(format);
57−
58− QCOMPARE(formatter.timeString(), time.toString(format));
59− }
60−
61− void tesGDatetTimeFormatter_Format_data() { addTestData(QList<int>() << 0); }
62− void tesGDatetTimeFormatter_Format()
63− {
64− QFETCH(QDateTime, time);
65− const QString format = "%d-%m-%Y %I:%M%p";
66−
67− GDateTimeFormatter formatter;
68− formatter.setTime(time.toMSecsSinceEpoch() / 1000); // strftime in seconds since epoc
69− formatter.setFormat(format);
70−
71− QCOMPARE(formatter.timeString(), time.toString("dd-MM-yyyy hh:mmAP"));
72− }
73−
74− void tesRelativeTimeFormatter_FarBack_data() { addTestData(QList<int>() << -200 << -20 << -7); }
75− void tesRelativeTimeFormatter_FarBack()
76− {
77− QFETCH(QDateTime, time);
78−
79− RelativeTimeFormatter formatter;
80− formatter.setTime(time.toMSecsSinceEpoch() / 1000); // strftime in seconds since epoc
81−
82− QCOMPARE(formatter.timeString(), time.toString("ddd dd MMM\u2003HH:mm"));
83− }
84−
85− void tesRelativeTimeFormatter_WeekBack_data() { addTestData(QList<int>() << -6 << -2); }
86− void tesRelativeTimeFormatter_WeekBack()
87− {
88− QFETCH(QDateTime, time);
89−
90− RelativeTimeFormatter formatter;
91− formatter.setTime(time.toMSecsSinceEpoch() / 1000); // strftime in seconds since epoc
92−
93− QCOMPARE(formatter.timeString(), time.toString("ddd\u2003HH:mm"));
94− }
95−
96− void tesRelativeTimeFormatter_Yesterday_data() { addTestData(QList<int>() << -1); }
97− void tesRelativeTimeFormatter_Yesterday()
98− {
99− QFETCH(QDateTime, time);
100−
101− RelativeTimeFormatter formatter;
102− formatter.setTime(time.toMSecsSinceEpoch() / 1000); // strftime in seconds since epoc
103−
104− QCOMPARE(formatter.timeString(), QString("Yesterday\u2003%1").arg(time.toString("HH:mm")));
105− }
106−
107− void tesRelativeTimeFormatter_Today_data() { addTestData(QList<int>() << 0); }
108− void tesRelativeTimeFormatter_Today()
109− {
110− QFETCH(QDateTime, time);
111−
112− RelativeTimeFormatter formatter;
113− formatter.setTime(time.toMSecsSinceEpoch() / 1000); // strftime in seconds since epoc
114−
115− QCOMPARE(formatter.timeString(), time.toString("HH:mm"));
116− }
117−
118− void tesRelativeTimeFormatter_Tomorrow_data() { addTestData(QList<int>() << 1); }
119− void tesRelativeTimeFormatter_Tomorrow()
120− {
121− QFETCH(QDateTime, time);
122−
123− RelativeTimeFormatter formatter;
124− formatter.setTime(time.toMSecsSinceEpoch() / 1000); // strftime in seconds since epoc
125−
126− QCOMPARE(formatter.timeString(), QString("Tomorrow\u2003%1").arg(time.toString("HH:mm")));
127− }
128−
129−
130− void tesRelativeTimeFormatter_WeekForward_data() { addTestData(QList<int>() << 2 << 6); }
131− void tesRelativeTimeFormatter_WeekForward()
132− {
133− QFETCH(QDateTime, time);
134−
135− RelativeTimeFormatter formatter;
136− formatter.setTime(time.toMSecsSinceEpoch() / 1000); // strftime in seconds since epoc
137−
138− QCOMPARE(formatter.timeString(), time.toString("ddd\u2003HH:mm"));
139− }
140−
141− void tesRelativeTimeFormatter_FarForward_data() { addTestData(QList<int>() << 7 << 20 << 200); }
142− void tesRelativeTimeFormatter_FarForward()
143− {
144− QFETCH(QDateTime, time);
145−
146− RelativeTimeFormatter formatter;
147− formatter.setTime(time.toMSecsSinceEpoch() / 1000); // strftime in seconds since epoc
148−
149− QCOMPARE(formatter.timeString(), time.toString("ddd dd MMM\u2003HH:mm"));
150− }
151−};
152−
153−QTEST_GUILESS_MAIN(TimeFormatterTest)
154−
155−#include "TimeFormatterTest.moc"
tests/plugins/Wizard/tst_pagelist.cpp+5−5
@@ -88,13 +88,13 @@ void PageListTest::testIterate()
88
88
89 PageList pageList;
89 PageList pageList;
90 QCOMPARE(pageList.index(), -1);
90 QCOMPARE(pageList.index(), -1);
91− QCOMPARE(pageList.next(), root.path() + "/a/" + PAGES_PATH + "/1.qml");
91+ QCOMPARE(pageList.next(), QString(root.path() + "/a/" + PAGES_PATH + "/1.qml"));
92 QCOMPARE(pageList.prev(), QString());
92 QCOMPARE(pageList.prev(), QString());
93− QCOMPARE(pageList.next(), root.path() + "/a/" + PAGES_PATH + "/2.qml");
93+ QCOMPARE(pageList.next(), QString(root.path() + "/a/" + PAGES_PATH + "/2.qml"));
94− QCOMPARE(pageList.prev(), root.path() + "/a/" + PAGES_PATH + "/1.qml");
94+ QCOMPARE(pageList.prev(), QString(root.path() + "/a/" + PAGES_PATH + "/1.qml"));
95 QCOMPARE(pageList.index(), 0);
95 QCOMPARE(pageList.index(), 0);
96− QCOMPARE(pageList.next(), root.path() + "/a/" + PAGES_PATH + "/2.qml");
96+ QCOMPARE(pageList.next(), QString(root.path() + "/a/" + PAGES_PATH + "/2.qml"));
97− QCOMPARE(pageList.next(), root.path() + "/a/" + PAGES_PATH + "/3.qml");
97+ QCOMPARE(pageList.next(), QString(root.path() + "/a/" + PAGES_PATH + "/3.qml"));
98 QCOMPARE(pageList.index(), 2);
98 QCOMPARE(pageList.index(), 2);
99 QCOMPARE(pageList.next(), QString());
99 QCOMPARE(pageList.next(), QString());
100 QCOMPARE(pageList.index(), 2);
100 QCOMPARE(pageList.index(), 2);
tests/qmltests/CMakeLists.txt+2
@@ -2,6 +2,7 @@
2add_subdirectory(Components)
2add_subdirectory(Components)
3
3
4add_unity8_qmltest(. OrientedShell LIGHTDM)
4add_unity8_qmltest(. OrientedShell LIGHTDM)
5+add_unity8_qmltest(. DisabledScreenNotice)
5add_unity8_qmltest(. Shell LIGHTDM)
6add_unity8_qmltest(. Shell LIGHTDM)
6add_unity8_qmltest(. ShellWithPin LIGHTDM)
7add_unity8_qmltest(. ShellWithPin LIGHTDM)
7add_unity8_qmltest(Components Background)
8add_unity8_qmltest(Components Background)
@@ -15,6 +16,7 @@ add_unity8_qmltest(Components Rating)
15add_unity8_qmltest(Components ResponsiveGridView)
16add_unity8_qmltest(Components ResponsiveGridView)
16add_unity8_qmltest(Components ResponsiveVerticalJournal)
17add_unity8_qmltest(Components ResponsiveVerticalJournal)
17add_unity8_qmltest(Components Showable)
18add_unity8_qmltest(Components Showable)
19+add_unity8_qmltest(Components WallpaperResolver)
18add_unity8_qmltest(Components ZoomableImage)
20add_unity8_qmltest(Components ZoomableImage)
19add_unity8_qmltest(Dash Dash)
21add_unity8_qmltest(Dash Dash)
20add_unity8_qmltest(Dash DashContent)
22add_unity8_qmltest(Dash DashContent)
tests/qmltests/Components/tst_WallpaperResolver.qmlnew file+89
@@ -0,0 +1,89 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.4
18+import QtTest 1.0
19+import Ubuntu.Components 1.3
20+import Unity.Test 0.1
21+import AccountsService 0.1
22+import GSettings 1.0
23+
24+import "../../../qml/Components"
25+
26+
27+Image {
28+ width: units.gu(70)
29+ height: units.gu(70)
30+
31+ source: wallpaperResolver.background
32+
33+ WallpaperResolver {
34+ id: wallpaperResolver
35+ width: units.gu(70)
36+ }
37+
38+ UnityTestCase {
39+ id: testCase
40+ name: "WallpaperResolver"
41+ when: windowShown
42+
43+ function test_background_data() {
44+ return [
45+ {tag: "color",
46+ accounts: Qt.resolvedUrl("data:image/svg+xml,<svg><rect width='100%' height='100%' fill='#dd4814'/></svg>"),
47+ gsettings: "",
48+ output: Qt.resolvedUrl("data:image/svg+xml,<svg><rect width='100%' height='100%' fill='#dd4814'/></svg>")},
49+
50+ {tag: "empty", accounts: "", gsettings: "", output: "defaultBackground"},
51+
52+ {tag: "as-specified",
53+ accounts: Qt.resolvedUrl("../../data/unity/backgrounds/blue.png"),
54+ gsettings: "",
55+ output: Qt.resolvedUrl("../../data/unity/backgrounds/blue.png")},
56+
57+ {tag: "gs-specified",
58+ accounts: "",
59+ gsettings: Qt.resolvedUrl("../../data/unity/backgrounds/red.png"),
60+ output: Qt.resolvedUrl("../../data/unity/backgrounds/red.png")},
61+
62+ {tag: "both-specified",
63+ accounts: Qt.resolvedUrl("../../data/unity/backgrounds/blue.png"),
64+ gsettings: Qt.resolvedUrl("../../data/unity/backgrounds/red.png"),
65+ output: Qt.resolvedUrl("../../data/unity/backgrounds/blue.png")},
66+
67+ {tag: "invalid-as",
68+ accounts: Qt.resolvedUrl("../../data/unity/backgrounds/nope.png"),
69+ gsettings: Qt.resolvedUrl("../../data/unity/backgrounds/red.png"),
70+ output: Qt.resolvedUrl("../../data/unity/backgrounds/red.png")},
71+
72+ {tag: "invalid-both",
73+ accounts: Qt.resolvedUrl("../../data/unity/backgrounds/nope.png"),
74+ gsettings: Qt.resolvedUrl("../../data/unity/backgrounds/stillnope.png"),
75+ output: "defaultBackground"},
76+ ]
77+ }
78+ function test_background(data) {
79+ AccountsService.backgroundFile = data.accounts;
80+ GSettingsController.setPictureUri(data.gsettings);
81+
82+ if (data.output === "defaultBackground") {
83+ tryCompare(wallpaperResolver, "background", wallpaperResolver.defaultBackground);
84+ } else {
85+ tryCompare(wallpaperResolver, "background", data.output);
86+ }
87+ }
88+ }
89+}
tests/qmltests/Panel/Indicators/tst_MenuItemFactory.qml+6−16
@@ -50,10 +50,6 @@ Item {
50 }
50 }
51 }
51 }
52
52
53− Utils.GDateTimeFormatter {
54− id: timeFormatter
55− }
56−
57 UT.UnityTestCase {
53 UT.UnityTestCase {
58 name: "MenuItemFactory"
54 name: "MenuItemFactory"
59 when: windowShown
55 when: windowShown
@@ -374,9 +370,9 @@ Item {
374 function test_create_alarmMenu_data() {
370 function test_create_alarmMenu_data() {
375 return [
371 return [
376 {label: "testLabel1", enabled: true, icon: "file:///testIcon1", color: Qt.rgba(0, 0, 0, 0),
372 {label: "testLabel1", enabled: true, icon: "file:///testIcon1", color: Qt.rgba(0, 0, 0, 0),
377− time: new Date(2014, 04, 14), timeFormat: "%a %d %b %l:%M %p"},
373+ time: new Date(2014, 04, 14)},
378 {label: "testLabel2", enabled: false, icon: "file:///testIcon2", color: Qt.rgba(1, 0, 0, 0),
374 {label: "testLabel2", enabled: false, icon: "file:///testIcon2", color: Qt.rgba(1, 0, 0, 0),
379− time: new Date(2015, 11, 31), timeFormat: "%A" },
375+ time: new Date(2015, 11, 31) },
380 ];
376 ];
381 }
377 }
382
378
@@ -386,11 +382,8 @@ Item {
386 menuData.sensitive = data.enabled;
382 menuData.sensitive = data.enabled;
387 menuData.icon = data.icon;
383 menuData.icon = data.icon;
388 menuData.ext = {
384 menuData.ext = {
389− 'xCanonicalTime': data.time.getTime() / 1000,
385+ 'xCanonicalTime': data.time.getTime() / 1000 // expected in seconds
390− 'xCanonicalTimeFormat': data.timeFormat
391 };
386 };
392− timeFormatter.format = data.timeFormat;
393− timeFormatter.time = data.time.getTime() / 1000;
394
387
395 loader.data = menuData;
388 loader.data = menuData;
396 loader.sourceComponent = factory.load(menuData);
389 loader.sourceComponent = factory.load(menuData);
@@ -399,7 +392,7 @@ Item {
399
392
400 compare(loader.item.text, data.label, "Label does not match data");
393 compare(loader.item.text, data.label, "Label does not match data");
401 compare(loader.item.iconSource, data.icon, "Icon does not match data");
394 compare(loader.item.iconSource, data.icon, "Icon does not match data");
402− compare(loader.item.time, timeFormatter.timeString, "Time does not match data");
395+ compare(loader.item.time, i18n.relativeDateTime(data.time), "Time does not match data");
403 compare(loader.item.enabled, data.enabled, "Enabled does not match data");
396 compare(loader.item.enabled, data.enabled, "Enabled does not match data");
404 }
397 }
405
398
@@ -419,11 +412,8 @@ Item {
419 menuData.icon = data.icon;
412 menuData.icon = data.icon;
420 menuData.ext = {
413 menuData.ext = {
421 'xCanonicalColor': data.colour,
414 'xCanonicalColor': data.colour,
422− 'xCanonicalTime': data.time.getTime() / 1000,
415+ 'xCanonicalTime': data.time.getTime() / 1000 // expected in seconds
423− 'xCanonicalTimeFormat': data.timeFormat
424 };
416 };
425− timeFormatter.format = data.timeFormat;
426− timeFormatter.time = data.time.getTime() / 1000;
427
417
428 loader.data = menuData;
418 loader.data = menuData;
429 loader.sourceComponent = factory.load(menuData);
419 loader.sourceComponent = factory.load(menuData);
@@ -432,7 +422,7 @@ Item {
432
422
433 compare(loader.item.text, data.label, "Label does not match data");
423 compare(loader.item.text, data.label, "Label does not match data");
434 compare(loader.item.iconSource, data.icon, "Icon does not match data");
424 compare(loader.item.iconSource, data.icon, "Icon does not match data");
435− compare(loader.item.time, timeFormatter.timeString, "Time does not match data");
425+ compare(loader.item.time, i18n.relativeDateTime(data.time), "Time does not match data");
436 compare(loader.item.eventColor, data.color, "Colour does not match data");
426 compare(loader.item.eventColor, data.color, "Colour does not match data");
437 compare(loader.item.enabled, data.enabled, "Enabled does not match data");
427 compare(loader.item.enabled, data.enabled, "Enabled does not match data");
438 }
428 }
tests/qmltests/Panel/Indicators/tst_MessageMenuItemFactory.qml+6−13
@@ -32,10 +32,6 @@ Item {
32 menuIndex: 0
32 menuIndex: 0
33 }
33 }
34
34
35− Utils.RelativeTimeFormatter {
36− id: timeFormatter
37− }
38−
39 UT.UnityTestCase {
35 UT.UnityTestCase {
40 name: "MessageMenuItemFactory"
36 name: "MessageMenuItemFactory"
41 when: windowShown
37 when: windowShown
@@ -82,17 +78,16 @@ Item {
82 menuData.label = data.title;
78 menuData.label = data.title;
83 menuData.sensitive = data.enabled;
79 menuData.sensitive = data.enabled;
84 menuData.ext = {
80 menuData.ext = {
85− 'xCanonicalTime': data.time.getTime()*1000,
81+ 'xCanonicalTime': data.time.getTime()*1000, // expected in microseconds
86 'xCanonicalText': data.body,
82 'xCanonicalText': data.body,
87 'icon': data.avatar,
83 'icon': data.avatar,
88 'xCanonicalAppIcon': data.icon,
84 'xCanonicalAppIcon': data.icon,
89 };
85 };
90 factory.menuData = menuData;
86 factory.menuData = menuData;
91− timeFormatter.time = data.time.getTime()/1000;
92
87
93 tryCompare(factory.item, "objectName", "simpleTextMessage");
88 tryCompare(factory.item, "objectName", "simpleTextMessage");
94 compare(factory.item.title, data.title, "Title does not match data");
89 compare(factory.item.title, data.title, "Title does not match data");
95− compare(factory.item.time, timeFormatter.timeString, "Time does not match data");
90+ compare(factory.item.time, i18n.relativeDateTime(data.time), "Time does not match data");
96 compare(factory.item.body, data.body, "Message does not match data");
91 compare(factory.item.body, data.body, "Message does not match data");
97 compare(factory.item.avatar, data.avatar, "Avatar does not match data");
92 compare(factory.item.avatar, data.avatar, "Avatar does not match data");
98 compare(factory.item.icon, data.icon, "App icon does not match data");
93 compare(factory.item.icon, data.icon, "App icon does not match data");
@@ -111,7 +106,7 @@ Item {
111 menuData.label = data.title;
106 menuData.label = data.title;
112 menuData.sensitive = data.enabled;
107 menuData.sensitive = data.enabled;
113 menuData.ext = {
108 menuData.ext = {
114− 'xCanonicalTime': data.time.getTime()*1000,
109+ 'xCanonicalTime': data.time.getTime()*1000, // expected in microseconds
115 'xCanonicalText': data.body,
110 'xCanonicalText': data.body,
116 'icon': data.avatar,
111 'icon': data.avatar,
117 'xCanonicalAppIcon': data.icon,
112 'xCanonicalAppIcon': data.icon,
@@ -123,11 +118,10 @@ Item {
123 ]
118 ]
124 };
119 };
125 factory.menuData = menuData;
120 factory.menuData = menuData;
126− timeFormatter.time = data.time.getTime()/1000;
127
121
128 tryCompare(factory.item, "objectName", "textMessage");
122 tryCompare(factory.item, "objectName", "textMessage");
129 compare(factory.item.title, data.title, "Title does not match data");
123 compare(factory.item.title, data.title, "Title does not match data");
130− compare(factory.item.time, timeFormatter.timeString, "Time does not match data");
124+ compare(factory.item.time, i18n.relativeDateTime(data.time), "Time does not match data");
131 compare(factory.item.body, data.body, "Message does not match data");
125 compare(factory.item.body, data.body, "Message does not match data");
132 compare(factory.item.avatar, data.avatar, "Avatar does not match data");
126 compare(factory.item.avatar, data.avatar, "Avatar does not match data");
133 compare(factory.item.icon, data.icon, "App icon does not match data");
127 compare(factory.item.icon, data.icon, "App icon does not match data");
@@ -147,7 +141,7 @@ Item {
147 menuData.label = data.title;
141 menuData.label = data.title;
148 menuData.sensitive = data.enabled;
142 menuData.sensitive = data.enabled;
149 menuData.ext = {
143 menuData.ext = {
150− 'xCanonicalTime': data.time.getTime()*1000,
144+ 'xCanonicalTime': data.time.getTime()*1000, // expected in microseconds
151 'xCanonicalText': data.body,
145 'xCanonicalText': data.body,
152 'icon': data.avatar,
146 'icon': data.avatar,
153 'xCanonicalAppIcon': data.icon,
147 'xCanonicalAppIcon': data.icon,
@@ -162,11 +156,10 @@ Item {
162 ]
156 ]
163 };
157 };
164 factory.menuData = menuData;
158 factory.menuData = menuData;
165− timeFormatter.time = data.time.getTime()/1000;
166
159
167 tryCompare(factory.item, "objectName", "snapDecision");
160 tryCompare(factory.item, "objectName", "snapDecision");
168 compare(factory.item.title, data.title, "Title does not match data");
161 compare(factory.item.title, data.title, "Title does not match data");
169− compare(factory.item.time, timeFormatter.timeString, "Time does not match data");
162+ compare(factory.item.time, i18n.relativeDateTime(data.time), "Time does not match data");
170 compare(factory.item.body, data.body, "Message does not match data");
163 compare(factory.item.body, data.body, "Message does not match data");
171 compare(factory.item.avatar, data.avatar, "Avatar does not match data");
164 compare(factory.item.avatar, data.avatar, "Avatar does not match data");
172 compare(factory.item.icon, data.icon, "App icon does not match data");
165 compare(factory.item.icon, data.icon, "App icon does not match data");
tests/qmltests/tst_DisabledScreenNotice.qmlnew file+37
@@ -0,0 +1,37 @@
1+/*
2+ * Copyright (C) 2015 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+import QtQuick 2.4
18+import QtTest 1.0
19+import Unity.Test 0.1
20+import "../../qml"
21+
22+
23+Item {
24+ id: root
25+ width: units.gu(70)
26+ height: units.gu(70)
27+
28+ DisabledScreenNotice {
29+ anchors.fill: parent
30+ }
31+
32+ UnityTestCase {
33+ id: testCase
34+ name: "DisabledScreenNotice"
35+ when: windowShown
36+ }
37+}
tests/qmltests/tst_OrientedShell.qml+51−1
@@ -110,6 +110,22 @@ Rectangle {
110 physicalOrientation180: Qt.LandscapeOrientation
110 physicalOrientation180: Qt.LandscapeOrientation
111 primaryOrientationAngle: 90
111 primaryOrientationAngle: 90
112 }
112 }
113+ },
114+ State {
115+ name: "desktop"
116+ PropertyChanges {
117+ target: orientedShellLoader
118+ width: units.gu(100)
119+ height: units.gu(56)
120+ }
121+ PropertyChanges {
122+ target: root
123+ physicalOrientation270: Qt.InvertedPortraitOrientation
124+ physicalOrientation0: Qt.LandscapeOrientation
125+ physicalOrientation90: Qt.PortraitOrientation
126+ physicalOrientation180: Qt.InvertedLandscapeOrientation
127+ primaryOrientationAngle: 0
128+ }
113 }
129 }
114 ]
130 ]
115
131
@@ -250,7 +266,7 @@ Rectangle {
250 anchors { left: parent.left; right: parent.right }
266 anchors { left: parent.left; right: parent.right }
251 activeFocusOnPress: false
267 activeFocusOnPress: false
252 text: "Device Name"
268 text: "Device Name"
253− model: ["mako", "manta", "flo"]
269+ model: ["mako", "manta", "flo", "desktop"]
254 onSelectedIndexChanged: {
270 onSelectedIndexChanged: {
255 testCase.tearDown();
271 testCase.tearDown();
256 applicationArguments.deviceName = model[selectedIndex];
272 applicationArguments.deviceName = model[selectedIndex];
@@ -310,12 +326,14 @@ Rectangle {
310 Row {
326 Row {
311 Button {
327 Button {
312 text: "Add mouse"
328 text: "Add mouse"
329+ activeFocusOnPress: false
313 onClicked: {
330 onClicked: {
314 UnityInputInfo.inputInfo.addMockMouse()
331 UnityInputInfo.inputInfo.addMockMouse()
315 }
332 }
316 }
333 }
317 Button {
334 Button {
318 text: "Remove mouse"
335 text: "Remove mouse"
336+ activeFocusOnPress: false
319 onClicked: {
337 onClicked: {
320 UnityInputInfo.inputInfo.removeMockMouse()
338 UnityInputInfo.inputInfo.removeMockMouse()
321 }
339 }
@@ -324,17 +342,37 @@ Rectangle {
324 Row {
342 Row {
325 Button {
343 Button {
326 text: "Add kbd"
344 text: "Add kbd"
345+ activeFocusOnPress: false
327 onClicked: {
346 onClicked: {
328 UnityInputInfo.inputInfo.addMockKeyboard()
347 UnityInputInfo.inputInfo.addMockKeyboard()
329 }
348 }
330 }
349 }
331 Button {
350 Button {
351+ activeFocusOnPress: false
332 text: "Remove kbd"
352 text: "Remove kbd"
333 onClicked: {
353 onClicked: {
334 UnityInputInfo.inputInfo.removeMockKeyboard()
354 UnityInputInfo.inputInfo.removeMockKeyboard()
335 }
355 }
336 }
356 }
337 }
357 }
358+
359+ // Simulates what happens when the shell is moved to an external monitor and back
360+ Button {
361+ id: moveToFromMonitorButton
362+ text: applicationArguments.deviceName === "desktop" ? "Move to " + prevDevName + " screen" : "Move to desktop screen"
363+ activeFocusOnPress: false
364+ property string prevDevName: "mako"
365+ onClicked: {
366+ usageModeSelector.selectedIndex = 2; // "Automatic"
367+
368+ if (applicationArguments.deviceName === "desktop") {
369+ applicationArguments.deviceName = prevDevName;
370+ } else {
371+ prevDevName = applicationArguments.deviceName;
372+ applicationArguments.deviceName = "desktop"
373+ }
374+ }
375+ }
338 }
376 }
339 }
377 }
340
378
@@ -1054,6 +1092,18 @@ Rectangle {
1054 compare(signalSpy.count, 0);
1092 compare(signalSpy.count, 0);
1055 }
1093 }
1056
1094
1095+ function test_moveToExternalMonitor() {
1096+ loadShell("flo");
1097+
1098+ compare(orientedShell.orientation, Qt.InvertedLandscapeOrientation);
1099+ compare(shell.transformRotationAngle, 90);
1100+
1101+ moveToFromMonitorButton.clicked();
1102+
1103+ tryCompare(orientedShell, "orientation", Qt.LandscapeOrientation);
1104+ tryCompare(shell, "transformRotationAngle" , 0);
1105+ }
1106+
1057 // angle - rotation angle in degrees clockwise, relative to the primary orientation.
1107 // angle - rotation angle in degrees clockwise, relative to the primary orientation.
1058 function rotateTo(angle) {
1108 function rotateTo(angle) {
1059 switch (angle) {
1109 switch (angle) {
tests/qmltests/tst_Shell.qml−24
@@ -1240,30 +1240,6 @@ Rectangle {
1240 tryCompare(launcherPanel, "x", -launcherPanel.width);
1240 tryCompare(launcherPanel, "x", -launcherPanel.width);
1241 }
1241 }
1242
1242
1243− function test_background_data() {
1244− return [
1245− {tag: "color", accounts: Qt.resolvedUrl("data:image/svg+xml,<svg><rect width='100%' height='100%' fill='#dd4814'/></svg>"), gsettings: "", output: Qt.resolvedUrl("data:image/svg+xml,<svg><rect width='100%' height='100%' fill='#dd4814'/></svg>")},
1246− {tag: "empty", accounts: "", gsettings: "", output: "defaultBackground"},
1247− {tag: "as-specified", accounts: Qt.resolvedUrl("../data/unity/backgrounds/blue.png"), gsettings: "", output: Qt.resolvedUrl("../data/unity/backgrounds/blue.png")},
1248− {tag: "gs-specified", accounts: "", gsettings: Qt.resolvedUrl("../data/unity/backgrounds/red.png"), output: Qt.resolvedUrl("../data/unity/backgrounds/red.png")},
1249− {tag: "both-specified", accounts: Qt.resolvedUrl("../data/unity/backgrounds/blue.png"), gsettings: Qt.resolvedUrl("../data/unity/backgrounds/red.png"), output: Qt.resolvedUrl("../data/unity/backgrounds/blue.png")},
1250− {tag: "invalid-as", accounts: Qt.resolvedUrl("../data/unity/backgrounds/nope.png"), gsettings: Qt.resolvedUrl("../data/unity/backgrounds/red.png"), output: Qt.resolvedUrl("../data/unity/backgrounds/red.png")},
1251− {tag: "invalid-both", accounts: Qt.resolvedUrl("../data/unity/backgrounds/nope.png"), gsettings: Qt.resolvedUrl("../data/unity/backgrounds/stillnope.png"), output: "defaultBackground"},
1252− ]
1253− }
1254− function test_background(data) {
1255− loadShell("phone");
1256− swipeAwayGreeter();
1257− AccountsService.backgroundFile = data.accounts;
1258− GSettingsController.setPictureUri(data.gsettings);
1259−
1260− if (data.output === "defaultBackground") {
1261− tryCompare(shell, "background", shell.defaultBackground);
1262− } else {
1263− tryCompare(shell, "background", data.output);
1264− }
1265− }
1266−
1267 function test_tabletLogin_data() {
1243 function test_tabletLogin_data() {
1268 return [
1244 return [
1269 {tag: "auth error", user: "auth-error", loggedIn: false, password: ""},
1245 {tag: "auth error", user: "auth-error", loggedIn: false, password: ""},