on xenial help sign in
merge trunk
770f734Lukáš Tinkl
CMakeLists.txt+1−1
@@ -57,7 +57,7 @@ find_package(Qt5DBus 5.4 REQUIRED)
57find_package(Qt5Concurrent 5.4 REQUIRED)
57find_package(Qt5Concurrent 5.4 REQUIRED)
58find_package(Qt5Sql 5.4 REQUIRED)
58find_package(Qt5Sql 5.4 REQUIRED)
59
59
60−pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=21)
60+pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=22)
61pkg_check_modules(GEONAMES REQUIRED geonames>=0.2)
61pkg_check_modules(GEONAMES REQUIRED geonames>=0.2)
62pkg_check_modules(GIO REQUIRED gio-2.0>=2.32)
62pkg_check_modules(GIO REQUIRED gio-2.0>=2.32)
63pkg_check_modules(GLIB REQUIRED glib-2.0>=2.32)
63pkg_check_modules(GLIB REQUIRED glib-2.0>=2.32)
data/unity8-greeter-wrapper+11−3
@@ -1,8 +1,7 @@
1#!/bin/sh
1#!/bin/sh
2# -*- Mode: sh; indent-tabs-mode: nil; tab-width: 4 -*-
2# -*- Mode: sh; indent-tabs-mode: nil; tab-width: 4 -*-
3#
3#
4−# Copyright (C) 2011,2013 Canonical Ltd
4+# Copyright (C) 2011,2013,2016 Canonical Ltd
5−# Author: Michael Terry <michael.terry@canonical.com>
6#
5#
7# This program is free software: you can redistribute it and/or modify it under
6# This program is free software: you can redistribute it and/or modify it under
8# the terms of the GNU General Public License as published by the Free Software
7# the terms of the GNU General Public License as published by the Free Software
@@ -42,6 +41,8 @@ set_greeter_var()
42SUB_SOCKET=$XDG_RUNTIME_DIR/mir_socket
41SUB_SOCKET=$XDG_RUNTIME_DIR/mir_socket
43rm -f $SUB_SOCKET # clear socket in case we were hard shut down
42rm -f $SUB_SOCKET # clear socket in case we were hard shut down
44
43
44+echo "DBUS_SESSION_BUS_ADDRESS=${DBUS_SESSION_BUS_ADDRESS}" >"$XDG_RUNTIME_DIR/dbus-session"
45+
45# If touch session script (which sets up grid units and mir variables) is available, use it
46# If touch session script (which sets up grid units and mir variables) is available, use it
46TOUCH_WRAPPER=
47TOUCH_WRAPPER=
47if [ -x /usr/bin/ubuntu-touch-session ]; then
48if [ -x /usr/bin/ubuntu-touch-session ]; then
@@ -65,7 +66,14 @@ set_greeter_var PA_DISABLED 1
65MIR_SOCKET=$SUB_SOCKET $TOUCH_WRAPPER /sbin/upstart --user --no-startup-event &
66MIR_SOCKET=$SUB_SOCKET $TOUCH_WRAPPER /sbin/upstart --user --no-startup-event &
66INIT_PID=$!
67INIT_PID=$!
67while [ ! -e "$XDG_RUNTIME_DIR/upstart/sessions/$INIT_PID.session" ]; do sleep 0.1; done
68while [ ! -e "$XDG_RUNTIME_DIR/upstart/sessions/$INIT_PID.session" ]; do sleep 0.1; done
68−export UPSTART_SESSION=$(/sbin/initctl list-sessions | grep "^$INIT_PID " | cut -d' ' -f2)
69+. "$XDG_RUNTIME_DIR/upstart/sessions/$INIT_PID.session"
70+export UPSTART_SESSION
71+
72+# Connect upstart to system, but do first command in a loop to avoid any
73+# race condition between the session file above being written and upstart being
74+# ready to handle requests.
75+while ! /sbin/initctl notify-dbus-address "$DBUS_SESSION_BUS_ADDRESS"; do sleep 0.1; done
76+/sbin/initctl start notify-cgmanager
69
77
70# Start any pre-greeter tasks if needed (like wizard)
78# Start any pre-greeter tasks if needed (like wizard)
71/sbin/initctl emit unity8-greeter-starting
79/sbin/initctl emit unity8-greeter-starting
debian/changelog+77
@@ -1,3 +1,80 @@
1+unity8 (8.14+16.10.20160922-0ubuntu1) yakkety; urgency=medium
2+
3+ [ Albert Astals Cid ]
4+ * LVWPH: update clipItem height when list height changes (LP:
5+ #1606935)
6+ * Put the touchdown shape inside a loader
7+ * No need for the touchdown in the card for the cardtool
8+ * Add bottom gradient to the Show Less floating button
9+ * Remove artShapeSize from non cardtool cards
10+ * LVWPH: Fix case in which header was shown incorrectly
11+ * LVWPH: Fix items cut on top in the dash (or at least some instances
12+ of it)
13+ * Reduce calls to CardCreatorCache.getCardComponent while the
14+ component is being created (LP: #1615675)
15+ * Dash::test_cardIconStyle change compare into tryCompareFunction
16+ * Make PreviewActionsTest::test_comboButton more stable
17+ * Adapt onShiftedContentXChanged to work when the content changes very
18+ abruptly
19+ * Make tryGenericScopeView show the scope correctly
20+ * Make sure the spinner does not get too close to the title text (LP:
21+ #1597392)
22+ * Show "Pull to refresh" in white when overlaid in low luminance
23+ colors (LP: #1596849)
24+ * Make test_Shell non ultra slow again (LP: #1597366)
25+ * Improve findChild calls inside tryCompareFunction.
26+
27+ [ Andrea Cimitan ]
28+ * override some Qt Components definitions so we can have sane default
29+ values for flick speeds
30+ * Elide the label text inside recent searches panel (LP: #1611796)
31+ * Add a PreviewSingleton module to store some data for previews (LP:
32+ #1595235)
33+ * Use PreviewSingleton for PreviewRatingInput and PreviewCommentInput
34+ (LP: #1595235)
35+ * rework GenericScopeView PullToRefresh test
36+ * use mouseFlick instead touchFlick for manage_dash_move_current
37+ * add a couple of waiting tricks for a flaky dash test
38+
39+ [ Daniel d'Andrada ]
40+ * Set progress bar indeterminate when processing signal received. (LP:
41+ #1249349)
42+ * Implement cursor confinement (LP: #1590099)
43+
44+ [ Josh Arenson ]
45+ * Add a frontend to the sessions model and enable a session chooser in
46+ the greeter.
47+
48+ [ Lukáš Tinkl ]
49+ * Implement edge maximizing (aka window snapping) (LP: #1602628)
50+ * On the PC platform (as opposed to running on $devices), use the
51+ "mute" action instead of silent mode
52+ * Respect Fitt's law wrt the window control buttons in panel (LP:
53+ #1611959)
54+ * Fix 2 failing color-related tests
55+
56+ [ Marco Trevisan (Treviño) ]
57+ * Indicators, mocks: add fake indicators menuitem to populate mocks
58+ with different menu types
59+ * IndicatorsClient: use PageHeader and ListItemLayout's
60+
61+ [ Michael Terry ]
62+ * Support launching apps inside a unity8 session from the greeter and
63+ support emergency dialing inside the greeter.
64+ * Fix tryShell to actually show appropriate backgrounds for each mock
65+ user.
66+ * Add an indicator to the greeter when a user is logged in (only shown
67+ if we have more than one user)
68+ * Hide the greeter OSK if indicators are open and don't show the edge
69+ tutorial during an emergency call.
70+ * Switch from lockscreen PIN pad to a passcode entry box that uses the
71+ OSK.
72+ * Use the default system wallpaper instead of our custom one.
73+ * Update look of infographic a bit
74+ * Make infographic bubbles white even on the default wallpaper.
75+
76+ -- Michał Sawicz <michal.sawicz@canonical.com> Thu, 22 Sep 2016 07:46:57 +0000
77+
1unity8 (8.14+16.10.20160831.3-0ubuntu1) yakkety; urgency=medium
78unity8 (8.14+16.10.20160831.3-0ubuntu1) yakkety; urgency=medium
2
79
3 [ Daniel d'Andrada ]
80 [ Daniel d'Andrada ]
debian/control+8−4
@@ -11,9 +11,9 @@ Build-Depends: cmake,
11# to g++ so we don't try to run arm g++
11# to g++ so we don't try to run arm g++
12# on an x86 CPU for example, when cross-compiling.
12# on an x86 CPU for example, when cross-compiling.
13 g++:native,
13 g++:native,
14− libandroid-properties-dev,
15 graphviz,
14 graphviz,
16 gsettings-ubuntu-schemas (>= 0.0.2+14.10.20140815),
15 gsettings-ubuntu-schemas (>= 0.0.2+14.10.20140815),
16+ libandroid-properties-dev,
17 libconnectivity-qt1-dev (>= 0.7.1),
17 libconnectivity-qt1-dev (>= 0.7.1),
18 libevdev-dev,
18 libevdev-dev,
19 libgeonames-dev (>= 0.2),
19 libgeonames-dev (>= 0.2),
@@ -32,10 +32,12 @@ Build-Depends: cmake,
32 libqt5svg5-dev,
32 libqt5svg5-dev,
33 libqt5xmlpatterns5-dev,
33 libqt5xmlpatterns5-dev,
34 libsystemsettings-dev,
34 libsystemsettings-dev,
35+ libubuntu-download-manager-common-dev,
35 libubuntugestures5-dev (>= 1.3.2030),
36 libubuntugestures5-dev (>= 1.3.2030),
36 libubuntugestures5-private-dev (>= 1.3.2030),
37 libubuntugestures5-private-dev (>= 1.3.2030),
37 libudev-dev,
38 libudev-dev,
39+ libudm-common-dev,
38− libunity-api-dev (>= 7.118),
40+ libunity-api-dev (>= 7.119),
39 libusermetricsoutput1-dev,
41 libusermetricsoutput1-dev,
40# Need those X11 libs touch emulation from mouse events in manual QML tests on a X11 desktop
42# Need those X11 libs touch emulation from mouse events in manual QML tests on a X11 desktop
41 libx11-dev[!arm64 !armhf],
43 libx11-dev[!arm64 !armhf],
@@ -133,6 +135,7 @@ Depends: dmz-cursor-theme,
133 ${shlibs:Depends},
135 ${shlibs:Depends},
134Recommends: indicator-keyboard,
136Recommends: indicator-keyboard,
135 indicator-session,
137 indicator-session,
138+ unity-greeter-session-broadcast,
136 unity-scope-click,
139 unity-scope-click,
137 unity-scope-mediascanner2,
140 unity-scope-mediascanner2,
138Breaks: indicator-network (<< 0.5.1+14.10.20141014),
141Breaks: indicator-network (<< 0.5.1+14.10.20141014),
@@ -153,7 +156,8 @@ Depends: qml-module-qtquick-layouts,
153 qtdeclarative5-ubuntu-settings-components (>= 0.7),
156 qtdeclarative5-ubuntu-settings-components (>= 0.7),
154 qtdeclarative5-unity-notifications-plugin (>= 0.1.2) | unity-notifications-impl,
157 qtdeclarative5-unity-notifications-plugin (>= 0.1.2) | unity-notifications-impl,
155 ubuntu-thumbnailer-impl-0,
158 ubuntu-thumbnailer-impl-0,
159+ ubuntu-wallpapers,
156− unity-application-impl-21,
160+ unity-application-impl-22,
157 unity-notifications-impl-3,
161 unity-notifications-impl-3,
158 unity-plugin-scopes | unity-scopes-impl,
162 unity-plugin-scopes | unity-scopes-impl,
159 unity-scopes-impl-12,
163 unity-scopes-impl-12,
@@ -199,7 +203,7 @@ Pre-Depends: ${misc:Pre-Depends},
199Depends: ${misc:Depends},
203Depends: ${misc:Depends},
200 ${shlibs:Depends},
204 ${shlibs:Depends},
201Provides: unity-application-impl,
205Provides: unity-application-impl,
202− unity-application-impl-21,
206+ unity-application-impl-22,
203Replaces: unity8-autopilot (<< 8.02+15.04.20150422-0ubuntu1)
207Replaces: unity8-autopilot (<< 8.02+15.04.20150422-0ubuntu1)
204Description: Fake environment for running Unity 8 shell
208Description: Fake environment for running Unity 8 shell
205 Provides fake implementations of some QML modules used by Unity 8 shell
209 Provides fake implementations of some QML modules used by Unity 8 shell
debian/rules+3−2
@@ -37,6 +37,7 @@ override_dh_installdeb:
37override_dh_makeshlibs:
37override_dh_makeshlibs:
38 dh_makeshlibs -Nunity8-private -Nunity8-fake-env
38 dh_makeshlibs -Nunity8-private -Nunity8-fake-env
39
39
40−# libMockLightDM-qml.so links against liblightdm-qt5-3.so which doesn't exist
41override_dh_shlibdeps:
40override_dh_shlibdeps:
41+ # Some mock libraries link against liblightdm-qt5-3.so which we want to
42+ # avoid, since we only really link against our mock one, not the system one.
42− dh_shlibdeps -XlibMockLightDM-qml.so
43+ dh_shlibdeps -XlibMockLightDM-qml.so -XlibMockAccountsService-qml.so
debian/unity8-private.install+1
@@ -4,6 +4,7 @@ usr/lib/*/unity8/qml/Cursor
4usr/lib/*/unity8/qml/Dash
4usr/lib/*/unity8/qml/Dash
5usr/lib/*/unity8/qml/GlobalShortcut
5usr/lib/*/unity8/qml/GlobalShortcut
6usr/lib/*/unity8/qml/Greeter
6usr/lib/*/unity8/qml/Greeter
7+usr/lib/*/unity8/qml/ImageCache
7usr/lib/*/unity8/qml/LightDM
8usr/lib/*/unity8/qml/LightDM
8usr/lib/*/unity8/qml/Lights
9usr/lib/*/unity8/qml/Lights
9usr/lib/*/unity8/qml/Powerd
10usr/lib/*/unity8/qml/Powerd
plugins/CMakeLists.txt+1
@@ -15,6 +15,7 @@ add_subdirectory(AccountsService)
15add_subdirectory(Cursor)
15add_subdirectory(Cursor)
16add_subdirectory(GlobalShortcut)
16add_subdirectory(GlobalShortcut)
17add_subdirectory(Greeter)
17add_subdirectory(Greeter)
18+add_subdirectory(ImageCache)
18add_subdirectory(LightDM)
19add_subdirectory(LightDM)
19add_subdirectory(Lights)
20add_subdirectory(Lights)
20add_subdirectory(Dash)
21add_subdirectory(Dash)
plugins/Cursor/CMakeLists.txt+2−2
@@ -28,6 +28,6 @@ target_link_libraries(Cursor-qml
28 ${QT5PLATFORM_SUPPORT_LDFLAGS}
28 ${QT5PLATFORM_SUPPORT_LDFLAGS}
29)
29)
30
30
31−qt5_use_modules(Cursor-qml Qml Quick DBus Network Gui Sql Concurrent Svg)
31+qt5_use_modules(Cursor-qml Qml Quick Svg)
32
32
33−add_unity8_plugin(Cursor 1.0 Cursor TARGETS Cursor-qml)
33+add_unity8_plugin(Cursor 1.1 Cursor TARGETS Cursor-qml)
plugins/Cursor/MousePointer.cpp+90−13
@@ -22,6 +22,7 @@
22
22
23#include <QQuickWindow>
23#include <QQuickWindow>
24#include <QGuiApplication>
24#include <QGuiApplication>
25+#include <QtMath>
25
26
26#include <qpa/qwindowsysteminterface.h>
27#include <qpa/qwindowsysteminterface.h>
27
28
@@ -49,28 +50,77 @@ void MousePointer::handleMouseEvent(ulong timestamp, QPointF movement, Qt::Mouse
49 m_accumulatedMovement -= appliedMovement;
50 m_accumulatedMovement -= appliedMovement;
50
51
51 qreal newX = x() + appliedMovement.x();
52 qreal newX = x() + appliedMovement.x();
52− if (newX < 0) {
53+ qreal newY = y() + appliedMovement.y();
54+ const qreal sceneWidth = parentItem()->width();
55+ const qreal sceneHeight = parentItem()->height();
56+
57+ if (newX <= 0 && newY < m_topBoundaryOffset) { // top left corner
58+ const auto distance = qSqrt(qPow(newX, 2) + qPow(newY-m_topBoundaryOffset, 2));
59+ Q_EMIT pushedTopLeftCorner(qAbs(distance), buttons);
60+ m_pushing = true;
61+ } else if (newX >= sceneWidth-1 && newY < m_topBoundaryOffset) { // top right corner
62+ const auto distance = qSqrt(qPow(newX-sceneWidth, 2) + qPow(newY-m_topBoundaryOffset, 2));
63+ Q_EMIT pushedTopRightCorner(qAbs(distance), buttons);
64+ m_pushing = true;
65+ } else if (newX < 0 && newY >= sceneHeight-1) { // bottom left corner
66+ const auto distance = qSqrt(qPow(newX, 2) + qPow(newY-sceneHeight, 2));
67+ Q_EMIT pushedBottomLeftCorner(qAbs(distance), buttons);
68+ m_pushing = true;
69+ } else if (newX >= sceneWidth-1 && newY >= sceneHeight-1) { // bottom right corner
70+ const auto distance = qSqrt(qPow(newX-sceneWidth, 2) + qPow(newY-sceneHeight, 2));
71+ Q_EMIT pushedBottomRightCorner(qAbs(distance), buttons);
72+ m_pushing = true;
73+ } else if (newX < 0) { // left edge
53 Q_EMIT pushedLeftBoundary(qAbs(newX), buttons);
74 Q_EMIT pushedLeftBoundary(qAbs(newX), buttons);
54− newX = 0;
75+ m_pushing = true;
55− } else if (newX >= parentItem()->width()) {
76+ } else if (newX >= sceneWidth) { // right edge
56− Q_EMIT pushedRightBoundary(newX - (parentItem()->width() - 1), buttons);
77+ Q_EMIT pushedRightBoundary(newX - (sceneWidth - 1), buttons);
57− newX = parentItem()->width() - 1;
78+ m_pushing = true;
79+ } else if (newY < m_topBoundaryOffset) { // top edge
80+ Q_EMIT pushedTopBoundary(qAbs(newY - m_topBoundaryOffset), buttons);
81+ m_pushing = true;
82+ } else if (Q_LIKELY(newX > 0 && newX < sceneWidth-1 && newY > 0 && newY < sceneHeight-1)) { // normal pos, not pushing
83+ if (m_pushing) {
84+ Q_EMIT pushStopped();
85+ m_pushing = false;
86+ }
58 }
87 }
59− setX(newX);
60
88
61− qreal newY = y() + appliedMovement.y();
89+ applyItemConfinement(newX, newY);
62− if (newY < 0) {
90+
63− newY = 0;
91+ setX(qBound(0.0, newX, sceneWidth - 1));
64− } else if (newY >= parentItem()->height()) {
65− newY = parentItem()->height() - 1;
66− }
67− setY(newY);
92+ setY(qBound(0.0, newY, sceneHeight - 1));
68
93
69 QPointF scenePosition = mapToItem(nullptr, QPointF(0, 0));
94 QPointF scenePosition = mapToItem(nullptr, QPointF(0, 0));
70 QWindowSystemInterface::handleMouseEvent(window(), timestamp, scenePosition /*local*/, scenePosition /*global*/,
95 QWindowSystemInterface::handleMouseEvent(window(), timestamp, scenePosition /*local*/, scenePosition /*global*/,
71 buttons, modifiers);
96 buttons, modifiers);
72}
97}
73
98
99+void MousePointer::applyItemConfinement(qreal &newX, qreal &newY)
100+{
101+ Q_ASSERT(parentItem() != nullptr);
102+
103+ if (m_confiningItem.isNull()) {
104+ return;
105+ }
106+
107+ QRectF confiningItemGeometry(0, 0, m_confiningItem->width(), m_confiningItem->height());
108+
109+ QRectF confiningRect = m_confiningItem->mapRectToItem(parentItem(), confiningItemGeometry);
110+
111+ if (newX < confiningRect.x()) {
112+ newX = confiningRect.x();
113+ } else if (newX > confiningRect.right()) {
114+ newX = confiningRect.right();
115+ }
116+
117+ if (newY < confiningRect.y()) {
118+ newY = confiningRect.y();
119+ } else if (newY > confiningRect.bottom()) {
120+ newY = confiningRect.bottom();
121+ }
122+}
123+
74void MousePointer::handleWheelEvent(ulong timestamp, QPoint angleDelta, Qt::KeyboardModifiers modifiers)
124void MousePointer::handleWheelEvent(ulong timestamp, QPoint angleDelta, Qt::KeyboardModifiers modifiers)
75{
125{
76 if (!parentItem()) {
126 if (!parentItem()) {
@@ -82,6 +132,20 @@ void MousePointer::handleWheelEvent(ulong timestamp, QPoint angleDelta, Qt::Keyb
82 QPoint() /* pixelDelta */, angleDelta, modifiers, Qt::ScrollUpdate);
132 QPoint() /* pixelDelta */, angleDelta, modifiers, Qt::ScrollUpdate);
83}
133}
84
134
135+int MousePointer::topBoundaryOffset() const
136+{
137+ return m_topBoundaryOffset;
138+}
139+
140+void MousePointer::setTopBoundaryOffset(int topBoundaryOffset)
141+{
142+ if (m_topBoundaryOffset == topBoundaryOffset)
143+ return;
144+
145+ m_topBoundaryOffset = topBoundaryOffset;
146+ Q_EMIT topBoundaryOffsetChanged(topBoundaryOffset);
147+}
148+
85void MousePointer::itemChange(ItemChange change, const ItemChangeData &value)
149void MousePointer::itemChange(ItemChange change, const ItemChangeData &value)
86{
150{
87 if (change == ItemSceneChange) {
151 if (change == ItemSceneChange) {
@@ -156,3 +220,16 @@ void MousePointer::setCustomCursor(const QCursor &customCursor)
156{
220{
157 CursorImageProvider::instance()->setCustomCursor(customCursor);
221 CursorImageProvider::instance()->setCustomCursor(customCursor);
158}
222}
223+
224+QQuickItem* MousePointer::confiningItem() const
225+{
226+ return m_confiningItem.data();
227+}
228+
229+void MousePointer::setConfiningItem(QQuickItem *item)
230+{
231+ if (item != m_confiningItem) {
232+ m_confiningItem = item;
233+ Q_EMIT confiningItemChanged();
234+ }
235+}
plugins/Cursor/MousePointer.h+23
@@ -27,6 +27,8 @@
27
27
28class MousePointer : public MirMousePointerInterface {
28class MousePointer : public MirMousePointerInterface {
29 Q_OBJECT
29 Q_OBJECT
30+ Q_PROPERTY(QQuickItem* confiningItem READ confiningItem WRITE setConfiningItem NOTIFY confiningItemChanged)
31+ Q_PROPERTY(int topBoundaryOffset READ topBoundaryOffset WRITE setTopBoundaryOffset NOTIFY topBoundaryOffsetChanged)
30public:
32public:
31 MousePointer(QQuickItem *parent = nullptr);
33 MousePointer(QQuickItem *parent = nullptr);
32
34
@@ -38,6 +40,12 @@ public:
38
40
39 void setCustomCursor(const QCursor &) override;
41 void setCustomCursor(const QCursor &) override;
40
42
43+ QQuickItem* confiningItem() const;
44+ void setConfiningItem(QQuickItem*);
45+
46+ int topBoundaryOffset() const;
47+ void setTopBoundaryOffset(int topBoundaryOffset);
48+
41public Q_SLOTS:
49public Q_SLOTS:
42 void handleMouseEvent(ulong timestamp, QPointF movement, Qt::MouseButtons buttons,
50 void handleMouseEvent(ulong timestamp, QPointF movement, Qt::MouseButtons buttons,
43 Qt::KeyboardModifiers modifiers) override;
51 Qt::KeyboardModifiers modifiers) override;
@@ -46,7 +54,16 @@ public Q_SLOTS:
46Q_SIGNALS:
54Q_SIGNALS:
47 void pushedLeftBoundary(qreal amount, Qt::MouseButtons buttons);
55 void pushedLeftBoundary(qreal amount, Qt::MouseButtons buttons);
48 void pushedRightBoundary(qreal amount, Qt::MouseButtons buttons);
56 void pushedRightBoundary(qreal amount, Qt::MouseButtons buttons);
57+ void pushedTopBoundary(qreal amount, Qt::MouseButtons buttons);
58+ void pushedTopLeftCorner(qreal amount, Qt::MouseButtons buttons);
59+ void pushedTopRightCorner(qreal amount, Qt::MouseButtons buttons);
60+ void pushedBottomLeftCorner(qreal amount, Qt::MouseButtons buttons);
61+ void pushedBottomRightCorner(qreal amount, Qt::MouseButtons buttons);
62+ void pushStopped();
49 void mouseMoved();
63 void mouseMoved();
64+ void confiningItemChanged();
65+
66+ void topBoundaryOffsetChanged(int topBoundaryOffset);
50
67
51protected:
68protected:
52 void itemChange(ItemChange change, const ItemChangeData &value) override;
69 void itemChange(ItemChange change, const ItemChangeData &value) override;
@@ -56,6 +73,7 @@ private Q_SLOTS:
56
73
57private:
74private:
58 void registerWindow(QWindow *window);
75 void registerWindow(QWindow *window);
76+ void applyItemConfinement(qreal &newX, qreal &newY);
59
77
60 QPointer<QWindow> m_registeredWindow;
78 QPointer<QWindow> m_registeredWindow;
61 QPointer<QScreen> m_registeredScreen;
79 QPointer<QScreen> m_registeredScreen;
@@ -64,6 +82,11 @@ private:
64
82
65 // Accumulated, unapplied, mouse movement.
83 // Accumulated, unapplied, mouse movement.
66 QPointF m_accumulatedMovement;
84 QPointF m_accumulatedMovement;
85+
86+ QPointer<QQuickItem> m_confiningItem;
87+
88+ int m_topBoundaryOffset{0};
89+ bool m_pushing{false};
67};
90};
68
91
69#endif // MOUSEPOINTER_H
92#endif // MOUSEPOINTER_H
plugins/Dash/CardCreator.js+12−10
@@ -331,13 +331,15 @@ var kEmblemIconCode = 'Icon { \n\
331 }\n';
331 }\n';
332
332
333// %1 is used as anchors of touchdown effect
333// %1 is used as anchors of touchdown effect
334−var kTouchdownCode = 'UbuntuShape { \n\
334+var kTouchdownCode = 'Loader { \n\
335− id: touchdown; \n\
335+ active: root.pressed; \n\
336− objectName: "touchdown"; \n\
337 anchors { %1 } \n\
336 anchors { %1 } \n\
338− visible: root.pressed; \n\
337+ sourceComponent: UbuntuShape { \n\
338+ objectName: "touchdown"; \n\
339+ anchors.fill: parent; \n\
339− radius: "medium"; \n\
340+ radius: "medium"; \n\
340− borderSource: "radius_pressed.sci" \n\
341+ borderSource: "radius_pressed.sci" \n\
342+ } \n\
341 }\n';
343 }\n';
342
344
343// %1 is used as anchors of subtitleLabel
345// %1 is used as anchors of subtitleLabel
@@ -506,10 +508,10 @@ function cardString(template, components, isCardTool, artShapeStyle, categoryLay
506 }
508 }
507
509
508 if (hasArt) {
510 if (hasArt) {
509− code += 'readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);\n';
510−
511 var artShapeAspect;
511 var artShapeAspect;
512 if (isCardTool) {
512 if (isCardTool) {
513+ code += 'readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);\n';
514+
513 var artShapeAspect = components["art"] && components["art"]["aspect-ratio"] || 1;
515 var artShapeAspect = components["art"] && components["art"]["aspect-ratio"] || 1;
514 if (isNaN(artShapeAspect)) {
516 if (isNaN(artShapeAspect)) {
515 artShapeAspect = 1;
517 artShapeAspect = 1;
@@ -573,7 +575,7 @@ function cardString(template, components, isCardTool, artShapeStyle, categoryLay
573 .arg(artShapeHolderShapeCode)
575 .arg(artShapeHolderShapeCode)
574 .arg(fallbackStatusCode)
576 .arg(fallbackStatusCode)
575 .arg(fallbackURICode);
577 .arg(fallbackURICode);
576− } else {
578+ } else if (isCardTool) {
577 code += 'readonly property size artShapeSize: Qt.size(-1, -1);\n'
579 code += 'readonly property size artShapeSize: Qt.size(-1, -1);\n'
578 }
580 }
579
581
@@ -923,7 +925,7 @@ function cardString(template, components, isCardTool, artShapeStyle, categoryLay
923 code += kSocialActionsRowCode.arg(socialAnchors).arg(socialColor);
925 code += kSocialActionsRowCode.arg(socialAnchors).arg(socialColor);
924 }
926 }
925
927
926− if (artShapeStyle != "shadow" && artShapeStyle != "icon") {
928+ if (artShapeStyle != "shadow" && artShapeStyle != "icon" && !isCardTool) {
927 var touchdownAnchors;
929 var touchdownAnchors;
928 if (hasBackground) {
930 if (hasBackground) {
929 touchdownAnchors = 'fill: backgroundLoader';
931 touchdownAnchors = 'fill: backgroundLoader';
plugins/Dash/listviewwithpageheader.cpp+5−4
@@ -587,6 +587,10 @@ void ListViewWithPageHeader::adjustHeader(qreal diff)
587 } else {
587 } else {
588 m_headerItem->setY(-m_minYExtent);
588 m_headerItem->setY(-m_minYExtent);
589 }
589 }
590+ } else if (m_headerItemShownHeight == 0 && m_previousContentY > m_headerItem->y() && contentY() < m_headerItem->y()) {
591+ // The header was hidden but now that we've moved up (e.g. because of item removed) it's visible
592+ // make sure it isn't
593+ m_headerItem->setY(-m_minYExtent);
590 }
594 }
591 Q_EMIT headerItemShownHeightChanged();
595 Q_EMIT headerItemShownHeightChanged();
592 } else {
596 } else {
@@ -978,6 +982,7 @@ void ListViewWithPageHeader::onContentWidthChanged()
978
982
979void ListViewWithPageHeader::onHeightChanged()
983void ListViewWithPageHeader::onHeightChanged()
980{
984{
985+ m_clipItem->setHeight(height() - m_headerItemShownHeight);
981 polish();
986 polish();
982}
987}
983
988
@@ -1388,10 +1393,6 @@ void ListViewWithPageHeader::updatePolish()
1388
1393
1389 m_contentHeightDirty = false;
1394 m_contentHeightDirty = false;
1390 adjustMinYExtent();
1395 adjustMinYExtent();
1391− if (contentHeight + m_minYExtent < height()) {
1392− // need this since in the previous call to adjustMinYExtent contentHeight is not set yet
1393− m_minYExtent = 0;
1394− }
1395 m_inContentHeightKeepHeaderShown = m_headerItem && m_headerItem->y() == contentY();
1396 m_inContentHeightKeepHeaderShown = m_headerItem && m_headerItem->y() == contentY();
1396 setContentHeight(contentHeight);
1397 setContentHeight(contentHeight);
1397 m_inContentHeightKeepHeaderShown = false;
1398 m_inContentHeightKeepHeaderShown = false;
plugins/ImageCache/CMakeLists.txtnew file+8
@@ -0,0 +1,8 @@
1+add_library(ImageCache-qml MODULE
2+ ImageCache.cpp
3+ plugin.cpp
4+ )
5+
6+qt5_use_modules(ImageCache-qml Gui Qml Quick)
7+
8+add_unity8_plugin(ImageCache 0.1 ImageCache TARGETS ImageCache-qml)
plugins/ImageCache/ImageCache.cppnew file+135
@@ -0,0 +1,135 @@
1+/*
2+ * Copyright (C) 2016 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 <QDateTime>
18+#include <QDebug>
19+#include <QDir>
20+#include <QUrl>
21+#include <QUrlQuery>
22+
23+#include "ImageCache.h"
24+
25+ImageCache::ImageCache()
26+ : QQuickImageProvider(QQmlImageProviderBase::Image,
27+ QQmlImageProviderBase::ForceAsynchronousImageLoading)
28+{
29+}
30+
31+QString ImageCache::imageCacheRoot()
32+{
33+ QString xdgCache(qgetenv("XDG_CACHE_HOME"));
34+ if (xdgCache.isEmpty()) {
35+ xdgCache = QDir::homePath() + QStringLiteral("/.cache");
36+ }
37+
38+ return QDir::cleanPath(xdgCache) + QStringLiteral("/unity8/imagecache");
39+}
40+
41+QFileInfo ImageCache::imagePath(const QUrl &image)
42+{
43+ QUrlQuery query(image);
44+
45+ auto name = query.queryItemValue(QStringLiteral("name"));
46+ if (name.isEmpty()) {
47+ name = QStringLiteral("/paths") + image.toLocalFile();
48+ } else {
49+ name = QStringLiteral("/names/") + name;
50+ }
51+
52+ return QFileInfo(imageCacheRoot() + name);
53+}
54+
55+bool ImageCache::needsUpdate(const QUrl &image, const QFileInfo &cachePath, const QSize &imageSize, const QSize &requestedSize, QSize &finalSize)
56+{
57+ if (!cachePath.exists())
58+ return true;
59+
60+ QFileInfo imageInfo(image.toLocalFile());
61+ if (imageInfo.lastModified() > cachePath.lastModified())
62+ return true;
63+
64+ QSize cacheSize(QImageReader(cachePath.filePath()).size());
65+ finalSize = calculateSize(imageSize, requestedSize);
66+ if (finalSize.isValid() && cacheSize != finalSize)
67+ return true;
68+
69+ return false;
70+}
71+
72+QSize ImageCache::calculateSize(const QSize &imageSize, const QSize &requestedSize)
73+{
74+ QSize finalSize(requestedSize);
75+
76+ if (finalSize.width() == 0) {
77+ finalSize.setWidth(imageSize.width() * (((double)finalSize.height()) / imageSize.height()));
78+ } else if (finalSize.height() == 0) {
79+ finalSize.setHeight(imageSize.height() * (((double)finalSize.width()) / imageSize.width()));
80+ }
81+
82+ return finalSize;
83+}
84+
85+QImage ImageCache::loadAndCacheImage(QImageReader &reader, const QFileInfo &cachePath, const QSize &finalSize)
86+{
87+ reader.setQuality(100);
88+ reader.setScaledSize(finalSize);
89+ auto format = reader.format(); // can't get this after reading
90+
91+ QImage loadedImage(reader.read());
92+ if (loadedImage.isNull()) {
93+ qWarning() << "ImageCache could not read image" << reader.fileName() << ":" << reader.errorString();
94+ return QImage();
95+ }
96+
97+ cachePath.dir().mkpath(QStringLiteral("."));
98+ loadedImage.save(cachePath.filePath(), format, 100);
99+
100+ return loadedImage;
101+}
102+
103+QImage ImageCache::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
104+{
105+ QUrl image(id);
106+ QImageReader imageReader(image.toLocalFile());
107+ QSize imageSize(imageReader.size());
108+ QImage result;
109+
110+ // Early exit here, with no sourceSize, scaled-up sourceSize, or bad source image
111+ if ((requestedSize.width() <= 0 && requestedSize.height() <= 0) ||
112+ imageSize.isEmpty() ||
113+ requestedSize.height() >= imageSize.height() ||
114+ requestedSize.width() >= imageSize.width()) {
115+ // We're only interested in scaling down, not up.
116+ result = imageReader.read();
117+ *size = result.size();
118+ return result;
119+ }
120+
121+ auto cachePath = imagePath(image);
122+ QSize finalSize;
123+
124+ if (needsUpdate(image, cachePath, imageSize, requestedSize, finalSize)) {
125+ if (finalSize.isEmpty()) {
126+ finalSize = calculateSize(imageSize, requestedSize);
127+ }
128+ result = loadAndCacheImage(imageReader, cachePath, finalSize);
129+ } else {
130+ result = QImage(cachePath.filePath());
131+ }
132+
133+ *size = result.size();
134+ return result;
135+}
plugins/ImageCache/ImageCache.hnew file+53
@@ -0,0 +1,53 @@
1+/*
2+ * Copyright (C) 2016 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+#pragma once
18+
19+#include <QFileInfo>
20+#include <QImageReader>
21+#include <QQuickImageProvider>
22+#include <QSize>
23+
24+/**
25+ * This class accepts an id formulated like a URL. So you'd use something like:
26+ *
27+ * Image {
28+ * source: "image://unity8imagecache/file:///usr/share/..."
29+ * }
30+ *
31+ * Right now, we only support file:/// URLs. We do accept some flags though:
32+ *
33+ * ?name=NAME
34+ * - This will use NAME as the cache lookup key instead of the provided URL.
35+ *
36+ * We don't do any cleaning of old cached files yet. So you may want to always
37+ * provide a name to avoid leaving lots of files around.
38+ */
39+
40+class ImageCache: public QQuickImageProvider
41+{
42+public:
43+ ImageCache();
44+
45+ QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize) override;
46+
47+private:
48+ static QString imageCacheRoot();
49+ static QFileInfo imagePath(const QUrl &image);
50+ static bool needsUpdate(const QUrl &image, const QFileInfo &cachePath, const QSize &imageSize, const QSize &requestedSize, QSize &finalSize);
51+ static QSize calculateSize(const QSize &imageSize, const QSize &requestedSize);
52+ static QImage loadAndCacheImage(QImageReader &reader, const QFileInfo &cachePath, const QSize &finalSize);
53+};
plugins/ImageCache/plugin.cppnew file+39
@@ -0,0 +1,39 @@
1+/*
2+ * Copyright (C) 2016 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 <QQmlEngine>
18+
19+#include "ImageCache.h"
20+#include "plugin.h"
21+
22+#include <QtQml>
23+
24+void ImageCachePlugin::registerTypes(const char *uri)
25+{
26+ Q_ASSERT(uri == QLatin1String("ImageCache"));
27+
28+ qmlRegisterTypeNotAvailable(uri, 0, 1, "__ImageCacheIgnoreMe",
29+ QStringLiteral("Ignore this: QML plugins must contain at least one type"));
30+}
31+
32+void ImageCachePlugin::initializeEngine(QQmlEngine* engine, const char* uri)
33+{
34+ Q_ASSERT(uri == QLatin1String("ImageCache"));
35+
36+ QQmlExtensionPlugin::initializeEngine(engine, uri);
37+
38+ engine->addImageProvider("unity8imagecache", new ImageCache);
39+}
plugins/ImageCache/plugin.hnew file+31
@@ -0,0 +1,31 @@
1+/*
2+ * Copyright (C) 2016 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+#pragma once
18+
19+#include <QQmlExtensionPlugin>
20+
21+class QQmlEngine;
22+
23+class ImageCachePlugin : public QQmlExtensionPlugin
24+{
25+ Q_OBJECT
26+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
27+
28+public:
29+ void registerTypes(const char* uri) override;
30+ void initializeEngine(QQmlEngine* engine, const char* uri) override;
31+};
plugins/ImageCache/qmldirnew file+2
@@ -0,0 +1,2 @@
1+module ImageCache
2+plugin ImageCache-qml
plugins/LightDM/IntegratedLightDM/liblightdm/Greeter.cpp−5
@@ -78,11 +78,6 @@ bool Greeter::showRemoteLoginHint() const
78 return true;
78 return true;
79}
79}
80
80
81−bool Greeter::lockHint () const
82−{
83− return false;
84−}
85−
86QString Greeter::selectUserHint() const
81QString Greeter::selectUserHint() const
87{
82{
88 return QLatin1String("");
83 return QLatin1String("");
plugins/LightDM/IntegratedLightDM/liblightdm/Greeter.h−2
@@ -45,7 +45,6 @@ class Q_DECL_EXPORT Greeter : public QObject
45
45
46 Q_PROPERTY(QString hostname READ hostname CONSTANT)
46 Q_PROPERTY(QString hostname READ hostname CONSTANT)
47 Q_PROPERTY(bool hasGuestAccount READ hasGuestAccountHint CONSTANT)
47 Q_PROPERTY(bool hasGuestAccount READ hasGuestAccountHint CONSTANT)
48− Q_PROPERTY(bool locked READ lockHint CONSTANT)
49
48
50 Q_ENUMS(PromptType MessageType)
49 Q_ENUMS(PromptType MessageType)
51
50
@@ -68,7 +67,6 @@ public:
68 bool hideUsersHint() const;
67 bool hideUsersHint() const;
69 bool showManualLoginHint() const;
68 bool showManualLoginHint() const;
70 bool showRemoteLoginHint() const;
69 bool showRemoteLoginHint() const;
71− bool lockHint () const;
72 bool hasGuestAccountHint() const;
70 bool hasGuestAccountHint() const;
73 QString selectUserHint() const;
71 QString selectUserHint() const;
74 bool selectGuestHint() const;
72 bool selectGuestHint() const;
plugins/LightDM/SessionsModel.cpp+22−16
@@ -47,15 +47,15 @@ void SessionsModel::setIconSearchDirectories(const QList<QUrl> searchDirectories
47 Q_EMIT iconSearchDirectoriesChanged();
47 Q_EMIT iconSearchDirectoriesChanged();
48}
48}
49
49
50−QUrl SessionsModel::iconUrl(const QString sessionName) const
50+QUrl SessionsModel::iconUrl(const QString sessionKey) const
51{
51{
52 Q_FOREACH(const QUrl& searchDirectory, m_iconSearchDirectories)
52 Q_FOREACH(const QUrl& searchDirectory, m_iconSearchDirectories)
53 {
53 {
54 // This is an established icon naming convention
54 // This is an established icon naming convention
55 QString customIconUrl = searchDirectory.toString(QUrl::StripTrailingSlash) +
55 QString customIconUrl = searchDirectory.toString(QUrl::StripTrailingSlash) +
56− "/custom_" + sessionName + "_badge.png";
56+ "/custom_" + sessionKey + "_badge.png";
57 QString iconUrl = searchDirectory.toString(QUrl::StripTrailingSlash) +
57 QString iconUrl = searchDirectory.toString(QUrl::StripTrailingSlash) +
58− "/" + sessionName + "_badge.png";
58+ "/" + sessionKey + "_badge.png";
59
59
60 QFile customIconFile(customIconUrl);
60 QFile customIconFile(customIconUrl);
61 QFile iconFile(iconUrl);
61 QFile iconFile(iconUrl);
@@ -63,29 +63,35 @@ QUrl SessionsModel::iconUrl(const QString sessionName) const
63 return QUrl(customIconUrl);
63 return QUrl(customIconUrl);
64 } else if (iconFile.exists()) {
64 } else if (iconFile.exists()) {
65 return QUrl(iconUrl);
65 return QUrl(iconUrl);
66− } else{
66+ } else {
67 // Search the legacy way
67 // Search the legacy way
68 QString path = searchDirectory.toString(QUrl::StripTrailingSlash) + "/";
68 QString path = searchDirectory.toString(QUrl::StripTrailingSlash) + "/";
69+ bool iconFound = false;
69− if (sessionName == "ubuntu" || sessionName == "ubuntu-2d") {
70+ if (sessionKey == "ubuntu" || sessionKey == "ubuntu-2d") {
70 path += "ubuntu_badge.png";
71 path += "ubuntu_badge.png";
72+ iconFound = true;
71 } else if(
73 } else if(
72− sessionName == "gnome-classic" ||
74+ sessionKey == "gnome-classic" ||
73− sessionName == "gnome-flashback-compiz" ||
75+ sessionKey == "gnome-flashback-compiz" ||
74− sessionName == "gnome-flashback-metacity" ||
76+ sessionKey == "gnome-flashback-metacity" ||
75− sessionName == "gnome-shell" ||
77+ sessionKey == "gnome-shell" ||
76− sessionName == "gnome-wayland" ||
78+ sessionKey == "gnome-wayland" ||
77− sessionName == "gnome"
79+ sessionKey == "gnome"
78 ){
80 ){
79 path += "gnome_badge.png";
81 path += "gnome_badge.png";
82+ iconFound = true;
80− } else if (sessionName == "plasma") {
83+ } else if (sessionKey == "plasma") {
81 path += "kde_badge.png";
84 path += "kde_badge.png";
85+ iconFound = true;
82− } else if (sessionName == "xterm") {
86+ } else if (sessionKey == "xterm") {
83 path += "recovery_console_badge.png";
87 path += "recovery_console_badge.png";
88+ iconFound = true;
84− } else if (sessionName == "remote-login") {
89+ } else if (sessionKey == "remote-login") {
85 path += "remote_login_help.png";
90 path += "remote_login_help.png";
91+ iconFound = true;
86 }
92 }
87
93
88− if (QFile(path).exists()) {
94+ if (QFile(path).exists() && iconFound) {
89 return path;
95 return path;
90 }
96 }
91 }
97 }
@@ -99,7 +105,7 @@ QVariant SessionsModel::data(const QModelIndex& index, int role) const
99{
105{
100 switch (role) {
106 switch (role) {
101 case SessionsModel::IconRole:
107 case SessionsModel::IconRole:
102− return iconUrl(m_model->data(index, Qt::DisplayRole).toString());
108+ return iconUrl(m_model->data(index, QLightDM::SessionsModel::KeyRole).toString());
103 default:
109 default:
104 return m_model->data(index, role);
110 return m_model->data(index, role);
105 }
111 }
plugins/LightDM/plugin.cpp+1−1
@@ -82,7 +82,7 @@ void PLUGIN_CLASSNAME::registerTypes(const char *uri)
82 qRegisterMetaType<QLightDM::Greeter::PromptType>("QLightDM::Greeter::PromptType");
82 qRegisterMetaType<QLightDM::Greeter::PromptType>("QLightDM::Greeter::PromptType");
83
83
84 qmlRegisterSingletonType<SessionsModel>(uri, 0, 1, "Sessions", sessions_provider);
84 qmlRegisterSingletonType<SessionsModel>(uri, 0, 1, "Sessions", sessions_provider);
85− qmlRegisterUncreatableType<SessionsModel>(uri, 0, 1, "SessionRoles", QStringLiteral("Type is not instantiable"));
85+ qmlRegisterUncreatableType<QLightDM::SessionsModel>(uri, 0, 1, "SessionRoles", QStringLiteral("Type is not instantiable"));
86
86
87 qmlRegisterSingletonType<UsersModel>(uri, 0, 1, "Users", users_provider);
87 qmlRegisterSingletonType<UsersModel>(uri, 0, 1, "Users", users_provider);
88 qmlRegisterUncreatableType<QLightDM::UsersModel>(uri, 0, 1, "UserRoles", QStringLiteral("Type is not instantiable"));
88 qmlRegisterUncreatableType<QLightDM::UsersModel>(uri, 0, 1, "UserRoles", QStringLiteral("Type is not instantiable"));
plugins/SessionBroadcast/SessionBroadcast.cpp+35−9
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013,2016 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,12 +12,12 @@
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− * Author: Michael Terry <michael.terry@canonical.com>
17 */
15 */
18
16
19#include "SessionBroadcast.h"
17#include "SessionBroadcast.h"
20#include <QDBusConnection>
18#include <QDBusConnection>
19+#include <QDBusConnectionInterface>
20+#include <QDBusInterface>
21
21
22#include <glib.h>
22#include <glib.h>
23
23
@@ -27,13 +27,39 @@ SessionBroadcast::SessionBroadcast(QObject* parent)
27 m_username = QString::fromUtf8(g_get_user_name());
27 m_username = QString::fromUtf8(g_get_user_name());
28
28
29 auto connection = QDBusConnection::SM_BUSNAME();
29 auto connection = QDBusConnection::SM_BUSNAME();
30+ auto interface = connection.interface();
31+ interface->startService(QStringLiteral("com.canonical.Unity.Greeter.Broadcast"));
32+
33+ m_broadcaster = new QDBusInterface(QStringLiteral("com.canonical.Unity.Greeter.Broadcast"),
34+ QStringLiteral("/com/canonical/Unity/Greeter/Broadcast"),
35+ QStringLiteral("com.canonical.Unity.Greeter.Broadcast"),
36+ connection, this);
37+
38+ connect(m_broadcaster, SIGNAL(StartUrl(const QString &, const QString &)),
39+ this, SLOT(onStartUrl(const QString &, const QString &)));
40+
41+ connect(m_broadcaster, SIGNAL(ShowHome(const QString &)),
42+ this, SLOT(onShowHome(const QString &)));
43+}
44+
45+void SessionBroadcast::requestUrlStart(const QString &username, const QString &url)
46+{
47+ m_broadcaster->asyncCall(QStringLiteral("RequestUrlStart"), username, url);
48+}
49+
50+void SessionBroadcast::requestHomeShown(const QString &username)
51+{
52+ m_broadcaster->asyncCall(QStringLiteral("RequestHomeShown"), username);
53+}
30
54
31− connection.connect(QStringLiteral("com.canonical.Unity.Greeter.Broadcast"),
55+void SessionBroadcast::onStartUrl(const QString &username, const QString &url)
32− QStringLiteral("/com/canonical/Unity/Greeter/Broadcast"),
56+{
33− QStringLiteral("com.canonical.Unity.Greeter.Broadcast"),
57+ // Since this signal is just used for testing, we don't *really* care if
34− QStringLiteral("ShowHome"),
58+ // username matches, but just in case we do eventually use the signal, we
35− this,
59+ // should only listen to our own requests.
36− SLOT(onShowHome(const QString &)));
60+ if (username == m_username) {
61+ Q_EMIT startUrl(url);
62+ }
37}
63}
38
64
39void SessionBroadcast::onShowHome(const QString &username)
65void SessionBroadcast::onShowHome(const QString &username)
plugins/SessionBroadcast/SessionBroadcast.h+10−3
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013,2016 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,8 +12,6 @@
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− * Authors: Michael Terry <michael.terry@canonical.com>
17 */
15 */
18
16
19#ifndef UNITY_SESSIONBROADCAST_H
17#ifndef UNITY_SESSIONBROADCAST_H
@@ -31,14 +29,23 @@ class SessionBroadcast: public QObject
31public:
29public:
32 explicit SessionBroadcast(QObject *parent = 0);
30 explicit SessionBroadcast(QObject *parent = 0);
33
31
32+ Q_INVOKABLE void requestUrlStart(const QString &username, const QString &url);
33+ Q_INVOKABLE void requestHomeShown(const QString &username);
34+
34Q_SIGNALS:
35Q_SIGNALS:
36+ // This signal isn't actually used by the shell
37+ // (unity-greeter-session-broadcast handles launching an app for us), but
38+ // it's useful for testing the plugin.
39+ void startUrl(const QString &url);
35 void showHome();
40 void showHome();
36
41
37private Q_SLOTS:
42private Q_SLOTS:
43+ void onStartUrl(const QString &username, const QString &url);
38 void onShowHome(const QString &username);
44 void onShowHome(const QString &username);
39
45
40private:
46private:
41 QString m_username;
47 QString m_username;
48+ QDBusInterface *m_broadcaster;
42};
49};
43
50
44#endif
51#endif
plugins/Ubuntu/DownloadDaemonListener/CMakeLists.txt+3−1
@@ -12,5 +12,7 @@ set(DOWNLOADDAEMONLISTENER_SOURCES
12add_library(DownloadDaemonListener MODULE ${DOWNLOADDAEMONLISTENER_SOURCES})
12add_library(DownloadDaemonListener MODULE ${DOWNLOADDAEMONLISTENER_SOURCES})
13
13
14qt5_use_modules(DownloadDaemonListener Qml Quick DBus Core)
14qt5_use_modules(DownloadDaemonListener Qml Quick DBus Core)
15−
15+pkg_check_modules(DDLADAPTER REQUIRED udm-common)
16+add_definitions(${DDLADAPTER_CFLAGS} ${DDLADAPTER_CFLAGS_OTHER})
17+target_link_libraries(DownloadDaemonListener ${DDLADAPTER_LIBRARIES})
16add_unity8_plugin(Ubuntu.DownloadDaemonListener 0.1 Ubuntu/DownloadDaemonListener TARGETS DownloadDaemonListener)
18add_unity8_plugin(Ubuntu.DownloadDaemonListener 0.1 Ubuntu/DownloadDaemonListener TARGETS DownloadDaemonListener)
plugins/Ubuntu/DownloadDaemonListener/DownloadTracker.cpp+2−1
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013 - Canonical Ltd.
2+ * Copyright (C) 2013-2016 Canonical Ltd.
3 *
3 *
4 * This program is free software: you can redistribute it and/or modify it
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License, as
5 * under the terms of the GNU Lesser General Public License, as
@@ -78,6 +78,7 @@ void DownloadTracker::startService()
78 connect(m_adaptor, static_cast<void (DownloadTrackerAdaptor::*)(qulonglong, qulonglong)>(&DownloadTrackerAdaptor::progress), this, &DownloadTracker::progress);
78 connect(m_adaptor, static_cast<void (DownloadTrackerAdaptor::*)(qulonglong, qulonglong)>(&DownloadTrackerAdaptor::progress), this, &DownloadTracker::progress);
79 connect(m_adaptor, &DownloadTrackerAdaptor::resumed, this, &DownloadTracker::resumed);
79 connect(m_adaptor, &DownloadTrackerAdaptor::resumed, this, &DownloadTracker::resumed);
80 connect(m_adaptor, &DownloadTrackerAdaptor::started, this, &DownloadTracker::started);
80 connect(m_adaptor, &DownloadTrackerAdaptor::started, this, &DownloadTracker::started);
81+ connect(m_adaptor, &DownloadTrackerAdaptor::processing, this, &DownloadTracker::processing);
81 }
82 }
82 // FIXME find a better way of determining if the service is ready
83 // FIXME find a better way of determining if the service is ready
83 Q_EMIT serviceReadyChanged(m_adaptor && m_adaptor->isValid());
84 Q_EMIT serviceReadyChanged(m_adaptor && m_adaptor->isValid());
plugins/Ubuntu/DownloadDaemonListener/DownloadTracker.h+2−1
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013 - Canonical Ltd.
2+ * Copyright (C) 2013-2016 Canonical Ltd.
3 *
3 *
4 * This program is free software: you can redistribute it and/or modify it
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License, as
5 * under the terms of the GNU Lesser General Public License, as
@@ -54,6 +54,7 @@ Q_SIGNALS:
54 void error(const QString &error);
54 void error(const QString &error);
55 void finished(const QString &path);
55 void finished(const QString &path);
56 void paused(bool success);
56 void paused(bool success);
57+ void processing(const QString &path);
57 void progress(qulonglong received, qulonglong total);
58 void progress(qulonglong received, qulonglong total);
58 void resumed(bool success);
59 void resumed(bool success);
59 void started(bool success);
60 void started(bool success);
plugins/Ubuntu/DownloadDaemonListener/interface/downloadtrackeradaptor.cpp+2−2
@@ -1,8 +1,8 @@
1/*
1/*
2 * This file was generated by qdbusxml2cpp version 0.8
2 * This file was generated by qdbusxml2cpp version 0.8
3− * Command line was: qdbusxml2cpp -v -c DownloadTrackerAdaptor -p downloadtrackeradaptor.h:downloadtrackeradaptor.cpp -i metatypes.h com.canonical.applications.download.xml
3+ * Command line was: qdbusxml2cpp -v -c DownloadTrackerAdaptor -p downloadtrackeradaptor.h:downloadtrackeradaptor.cpp -i metatypes.h ../../../../../../ubuntu-download-manager/trunk/docs/dbus/com.canonical.applications.download.xml
4 *
4 *
5− * qdbusxml2cpp is Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
5+ * qdbusxml2cpp is Copyright (C) 2015 The Qt Company Ltd.
6 *
6 *
7 * This is an auto-generated file.
7 * This is an auto-generated file.
8 * This file may have been hand-edited. Look for HAND-EDIT comments
8 * This file may have been hand-edited. Look for HAND-EDIT comments
plugins/Ubuntu/DownloadDaemonListener/interface/downloadtrackeradaptor.h+71−4
@@ -1,15 +1,15 @@
1/*
1/*
2 * This file was generated by qdbusxml2cpp version 0.8
2 * This file was generated by qdbusxml2cpp version 0.8
3− * Command line was: qdbusxml2cpp -v -c DownloadTrackerAdaptor -p downloadtrackeradaptor.h:downloadtrackeradaptor.cpp -i metatypes.h com.canonical.applications.download.xml
3+ * Command line was: qdbusxml2cpp -v -c DownloadTrackerAdaptor -p downloadtrackeradaptor.h:downloadtrackeradaptor.cpp -i metatypes.h ../../../../../../ubuntu-download-manager/trunk/docs/dbus/com.canonical.applications.download.xml
4 *
4 *
5− * qdbusxml2cpp is Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
5+ * qdbusxml2cpp is Copyright (C) 2015 The Qt Company Ltd.
6 *
6 *
7 * This is an auto-generated file.
7 * This is an auto-generated file.
8 * Do not edit! All changes made to it will be lost.
8 * Do not edit! All changes made to it will be lost.
9 */
9 */
10
10
11−#ifndef DOWNLOADTRACKERADAPTOR_H_1374434371
11+#ifndef DOWNLOADTRACKERADAPTOR_H
12−#define DOWNLOADTRACKERADAPTOR_H_1374434371
12+#define DOWNLOADTRACKERADAPTOR_H
13
13
14#include <QtCore/QObject>
14#include <QtCore/QObject>
15#include <QtCore/QByteArray>
15#include <QtCore/QByteArray>
@@ -36,6 +36,22 @@ public:
36
36
37 ~DownloadTrackerAdaptor();
37 ~DownloadTrackerAdaptor();
38
38
39+ Q_PROPERTY(QString ClickPackage READ clickPackage)
40+ inline QString clickPackage() const
41+ { return qvariant_cast< QString >(property("ClickPackage")); }
42+
43+ Q_PROPERTY(QString DestinationApp READ destinationApp)
44+ inline QString destinationApp() const
45+ { return qvariant_cast< QString >(property("DestinationApp")); }
46+
47+ Q_PROPERTY(bool ShowInIndicator READ showInIndicator)
48+ inline bool showInIndicator() const
49+ { return qvariant_cast< bool >(property("ShowInIndicator")); }
50+
51+ Q_PROPERTY(QString Title READ title)
52+ inline QString title() const
53+ { return qvariant_cast< QString >(property("Title")); }
54+
39public Q_SLOTS: // METHODS
55public Q_SLOTS: // METHODS
40 inline QDBusPendingReply<> allowGSMDownload(bool allowed)
56 inline QDBusPendingReply<> allowGSMDownload(bool allowed)
41 {
57 {
@@ -50,6 +66,24 @@ public Q_SLOTS: // METHODS
50 return asyncCallWithArgumentList(QStringLiteral("cancel"), argumentList);
66 return asyncCallWithArgumentList(QStringLiteral("cancel"), argumentList);
51 }
67 }
52
68
69+ inline QDBusPendingReply<> collected()
70+ {
71+ QList<QVariant> argumentList;
72+ return asyncCallWithArgumentList(QStringLiteral("collected"), argumentList);
73+ }
74+
75+ inline QDBusPendingReply<QString> filePath()
76+ {
77+ QList<QVariant> argumentList;
78+ return asyncCallWithArgumentList(QStringLiteral("filePath"), argumentList);
79+ }
80+
81+ inline QDBusPendingReply<StringMap> headers()
82+ {
83+ QList<QVariant> argumentList;
84+ return asyncCallWithArgumentList(QStringLiteral("headers"), argumentList);
85+ }
86+
53 inline QDBusPendingReply<bool> isGSMDownloadAllowed()
87 inline QDBusPendingReply<bool> isGSMDownloadAllowed()
54 {
88 {
55 QList<QVariant> argumentList;
89 QList<QVariant> argumentList;
@@ -80,6 +114,27 @@ public Q_SLOTS: // METHODS
80 return asyncCallWithArgumentList(QStringLiteral("resume"), argumentList);
114 return asyncCallWithArgumentList(QStringLiteral("resume"), argumentList);
81 }
115 }
82
116
117+ inline QDBusPendingReply<> setDestinationDir(const QString &path)
118+ {
119+ QList<QVariant> argumentList;
120+ argumentList << QVariant::fromValue(path);
121+ return asyncCallWithArgumentList(QStringLiteral("setDestinationDir"), argumentList);
122+ }
123+
124+ inline QDBusPendingReply<> setHeaders(StringMap headers)
125+ {
126+ QList<QVariant> argumentList;
127+ argumentList << QVariant::fromValue(headers);
128+ return asyncCallWithArgumentList(QStringLiteral("setHeaders"), argumentList);
129+ }
130+
131+ inline QDBusPendingReply<> setMetadata(const QVariantMap &data)
132+ {
133+ QList<QVariant> argumentList;
134+ argumentList << QVariant::fromValue(data);
135+ return asyncCallWithArgumentList(QStringLiteral("setMetadata"), argumentList);
136+ }
137+
83 inline QDBusPendingReply<> setThrottle(qulonglong speed)
138 inline QDBusPendingReply<> setThrottle(qulonglong speed)
84 {
139 {
85 QList<QVariant> argumentList;
140 QList<QVariant> argumentList;
@@ -93,6 +148,12 @@ public Q_SLOTS: // METHODS
93 return asyncCallWithArgumentList(QStringLiteral("start"), argumentList);
148 return asyncCallWithArgumentList(QStringLiteral("start"), argumentList);
94 }
149 }
95
150
151+ inline QDBusPendingReply<int> state()
152+ {
153+ QList<QVariant> argumentList;
154+ return asyncCallWithArgumentList(QStringLiteral("state"), argumentList);
155+ }
156+
96 inline QDBusPendingReply<qulonglong> throttle()
157 inline QDBusPendingReply<qulonglong> throttle()
97 {
158 {
98 QList<QVariant> argumentList;
159 QList<QVariant> argumentList;
@@ -106,10 +167,16 @@ public Q_SLOTS: // METHODS
106 }
167 }
107
168
108Q_SIGNALS: // SIGNALS
169Q_SIGNALS: // SIGNALS
170+ void authError(AuthErrorStruct error);
109 void canceled(bool success);
171 void canceled(bool success);
110 void error(const QString &error);
172 void error(const QString &error);
111 void finished(const QString &path);
173 void finished(const QString &path);
174+ void hashError(HashErrorStruct error);
175+ void httpError(HttpErrorStruct error);
176+ void networkError(NetworkErrorStruct error);
112 void paused(bool success);
177 void paused(bool success);
178+ void processError(ProcessErrorStruct error);
179+ void processing(const QString &path);
113 void progress(qulonglong received, qulonglong total);
180 void progress(qulonglong received, qulonglong total);
114 void resumed(bool success);
181 void resumed(bool success);
115 void started(bool success);
182 void started(bool success);
plugins/Ubuntu/DownloadDaemonListener/interface/metatypes.h+3−6
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2013 2013 Canonical Ltd.
2+ * Copyright 2013-2016 Canonical Ltd.
3 *
3 *
4 * This library is free software; you can redistribute it and/or
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of version 3 of the GNU Lesser General Public
5 * modify it under the terms of version 3 of the GNU Lesser General Public
@@ -19,10 +19,7 @@
19#ifndef DOWNLOADER_LIB_APP_METATYPES_H
19#ifndef DOWNLOADER_LIB_APP_METATYPES_H
20#define DOWNLOADER_LIB_APP_METATYPES_H
20#define DOWNLOADER_LIB_APP_METATYPES_H
21
21
22−#include <QMap>
22+// Just include the system file so we're always up to date.
23−
23+#include <ubuntu/download_manager/metatypes.h>
24−typedef QMap<QString, QString> StringMap;
25−
26−Q_DECLARE_METATYPE(StringMap)
27
24
28#endif // METATYPES_H
25#endif // METATYPES_H
plugins/Ubuntu/Gestures/TouchGestureArea.h+2−2
@@ -76,7 +76,7 @@ public:
76 return *this;
76 return *this;
77 }
77 }
78
78
79− bool operator=(const GestureTouchPoint& rhs) const {
79+ bool operator==(const GestureTouchPoint& rhs) const {
80 if (&rhs == this) return true;
80 if (&rhs == this) return true;
81 return m_id == rhs.m_id &&
81 return m_id == rhs.m_id &&
82 m_pressed == rhs.m_pressed &&
82 m_pressed == rhs.m_pressed &&
@@ -84,7 +84,7 @@ public:
84 m_y == rhs.m_y &&
84 m_y == rhs.m_y &&
85 m_dragging == rhs.m_dragging;
85 m_dragging == rhs.m_dragging;
86 }
86 }
87− bool operator!=(const GestureTouchPoint& rhs) const { return !operator=(rhs); }
87+ bool operator!=(const GestureTouchPoint& rhs) const { return !operator==(rhs); }
88
88
89 void setPos(const QPointF &pos);
89 void setPos(const QPointF &pos);
90
90
plugins/Utils/CMakeLists.txt+1
@@ -29,6 +29,7 @@ set(QMLPLUGIN_SRC
29 inputeventgenerator.cpp
29 inputeventgenerator.cpp
30 deviceconfigparser.cpp
30 deviceconfigparser.cpp
31 globalfunctions.cpp
31 globalfunctions.cpp
32+ URLDispatcher.cpp
32 plugin.cpp
33 plugin.cpp
33 )
34 )
34
35
plugins/Utils/URLDispatcher.cppnew file+77
@@ -0,0 +1,77 @@
1+/*
2+ * Copyright (C) 2016 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 "URLDispatcher.h"
18+
19+#include <QDBusConnection>
20+
21+class URLDispatcherInterface : public QObject
22+{
23+ Q_OBJECT
24+ Q_CLASSINFO("D-Bus Interface", "com.canonical.URLDispatcher")
25+
26+public:
27+ explicit URLDispatcherInterface(URLDispatcher *parent);
28+
29+ Q_SCRIPTABLE void DispatchURL(const QString &url, const QString &package);
30+};
31+
32+URLDispatcherInterface::URLDispatcherInterface(URLDispatcher *parent)
33+ : QObject(parent)
34+{
35+}
36+
37+void URLDispatcherInterface::DispatchURL(const QString &url, const QString &package)
38+{
39+ Q_UNUSED(package);
40+ Q_EMIT static_cast<URLDispatcher *>(parent())->urlRequested(url);
41+}
42+
43+URLDispatcher::URLDispatcher(QObject *parent)
44+ : QObject(parent)
45+ , m_dispatcher(nullptr)
46+{
47+}
48+
49+bool URLDispatcher::active() const
50+{
51+ return m_dispatcher != nullptr;
52+}
53+
54+void URLDispatcher::setActive(bool value)
55+{
56+ if (value == active())
57+ return;
58+
59+ QDBusConnection connection = QDBusConnection::sessionBus();
60+
61+ if (value) {
62+ URLDispatcherInterface *dispatcher = new URLDispatcherInterface(this);
63+ connection.registerObject(QStringLiteral("/com/canonical/URLDispatcher"),
64+ dispatcher,
65+ QDBusConnection::ExportScriptableContents);
66+ connection.registerService(QStringLiteral("com.canonical.URLDispatcher"));
67+ m_dispatcher = dispatcher;
68+ } else {
69+ connection.unregisterService(QStringLiteral("com.canonical.URLDispatcher"));
70+ delete m_dispatcher;
71+ m_dispatcher = nullptr;
72+ }
73+
74+ Q_EMIT activeChanged();
75+}
76+
77+#include "URLDispatcher.moc"
plugins/Utils/URLDispatcher.hnew file+47
@@ -0,0 +1,47 @@
1+/*
2+ * Copyright (C) 2016 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_URLDISPATCHER_H
18+#define UNITY_URLDISPATCHER_H
19+
20+#include <QObject>
21+#include <QString>
22+
23+// This class manages our url-dispatcher interception. We intercept
24+// url-dispatcher because rather than spawning the handler for the URL
25+// in our own session, we want to notify the user session to do it for us
26+// (and start an unlock in the process).
27+
28+class URLDispatcher : public QObject
29+{
30+ Q_OBJECT
31+ Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged)
32+
33+public:
34+ explicit URLDispatcher(QObject *parent=0);
35+
36+ bool active() const;
37+ void setActive(bool active);
38+
39+Q_SIGNALS:
40+ void urlRequested(const QString &url);
41+ void activeChanged();
42+
43+private:
44+ QObject *m_dispatcher;
45+};
46+
47+#endif
plugins/Utils/plugin.cpp+2
@@ -38,6 +38,7 @@
38#include "inputeventgenerator.h"
38#include "inputeventgenerator.h"
39#include "deviceconfigparser.h"
39#include "deviceconfigparser.h"
40#include "globalfunctions.h"
40#include "globalfunctions.h"
41+#include "URLDispatcher.h"
41
42
42static QObject *createWindowStateStorage(QQmlEngine *engine, QJSEngine *scriptEngine)
43static QObject *createWindowStateStorage(QQmlEngine *engine, QJSEngine *scriptEngine)
43{
44{
@@ -80,4 +81,5 @@ void UtilsPlugin::registerTypes(const char *uri)
80 qmlRegisterType<InputEventGenerator>(uri, 0, 1, "InputEventGenerator");
81 qmlRegisterType<InputEventGenerator>(uri, 0, 1, "InputEventGenerator");
81 qmlRegisterType<DeviceConfigParser>(uri, 0, 1, "DeviceConfigParser");
82 qmlRegisterType<DeviceConfigParser>(uri, 0, 1, "DeviceConfigParser");
82 qmlRegisterSingletonType<GlobalFunctions>(uri, 0, 1, "Functions", createGlobalFunctions);
83 qmlRegisterSingletonType<GlobalFunctions>(uri, 0, 1, "Functions", createGlobalFunctions);
84+ qmlRegisterType<URLDispatcher>(uri, 0, 1, "URLDispatcher");
83}
85}
plugins/Utils/windowstatestorage.cpp+5−4
@@ -76,7 +76,7 @@ WindowStateStorage::WindowState WindowStateStorage::getState(const QString &wind
76 return (WindowState)query.value(QStringLiteral("state")).toInt();
76 return (WindowState)query.value(QStringLiteral("state")).toInt();
77}
77}
78
78
79−void WindowStateStorage::saveGeometry(const QString &windowId, const QRect rect)
79+void WindowStateStorage::saveGeometry(const QString &windowId, const QRect &rect)
80{
80{
81 const QString queryString = QStringLiteral("INSERT OR REPLACE INTO geometry (windowId, x, y, width, height) values ('%1', '%2', '%3', '%4', '%5');")
81 const QString queryString = QStringLiteral("INSERT OR REPLACE INTO geometry (windowId, x, y, width, height) values ('%1', '%2', '%3', '%4', '%5');")
82 .arg(sanitiseString(windowId))
82 .arg(sanitiseString(windowId))
@@ -92,7 +92,7 @@ void WindowStateStorage::saveStage(const QString &appId, int stage)
92{
92{
93 const QString queryString = QStringLiteral("INSERT OR REPLACE INTO stage (appId, stage) values ('%1', '%2');")
93 const QString queryString = QStringLiteral("INSERT OR REPLACE INTO stage (appId, stage) values ('%1', '%2');")
94 .arg(sanitiseString(appId))
94 .arg(sanitiseString(appId))
95− .arg((int)stage);
95+ .arg(stage);
96
96
97 saveValue(queryString);
97 saveValue(queryString);
98}
98}
@@ -123,7 +123,7 @@ void WindowStateStorage::executeAsyncQuery(const QString &queryString)
123 }
123 }
124}
124}
125
125
126−QRect WindowStateStorage::getGeometry(const QString &windowId, const QRect defaultValue) const
126+QRect WindowStateStorage::getGeometry(const QString &windowId, const QRect &defaultValue) const
127{
127{
128 QString queryString = QStringLiteral("SELECT * FROM geometry WHERE windowId = '%1';")
128 QString queryString = QStringLiteral("SELECT * FROM geometry WHERE windowId = '%1';")
129 .arg(sanitiseString(windowId));
129 .arg(sanitiseString(windowId));
@@ -133,7 +133,8 @@ QRect WindowStateStorage::getGeometry(const QString &windowId, const QRect defau
133 if (!query.first()) {
133 if (!query.first()) {
134 return defaultValue;
134 return defaultValue;
135 }
135 }
136− return QRect(query.value(QStringLiteral("x")).toInt(), query.value(QStringLiteral("y")).toInt(), query.value(QStringLiteral("width")).toInt(), query.value(QStringLiteral("height")).toInt());
136+ return QRect(query.value(QStringLiteral("x")).toInt(), query.value(QStringLiteral("y")).toInt(),
137+ query.value(QStringLiteral("width")).toInt(), query.value(QStringLiteral("height")).toInt());
137}
138}
138
139
139void WindowStateStorage::initdb()
140void WindowStateStorage::initdb()
plugins/Utils/windowstatestorage.h+8−3
@@ -32,7 +32,12 @@ public:
32 WindowStateMaximizedLeft = 1 << 4,
32 WindowStateMaximizedLeft = 1 << 4,
33 WindowStateMaximizedRight = 1 << 5,
33 WindowStateMaximizedRight = 1 << 5,
34 WindowStateMaximizedHorizontally = 1 << 6,
34 WindowStateMaximizedHorizontally = 1 << 6,
35− WindowStateMaximizedVertically = 1 << 7
35+ WindowStateMaximizedVertically = 1 << 7,
36+ WindowStateMaximizedTopLeft = 1 << 8,
37+ WindowStateMaximizedTopRight = 1 << 9,
38+ WindowStateMaximizedBottomLeft = 1 << 10,
39+ WindowStateMaximizedBottomRight = 1 << 11,
40+ WindowStateRestored = 1 << 12
36 };
41 };
37 Q_DECLARE_FLAGS(WindowStates, WindowState)
42 Q_DECLARE_FLAGS(WindowStates, WindowState)
38#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0))
43#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0))
@@ -45,8 +50,8 @@ public:
45 Q_INVOKABLE void saveState(const QString &windowId, WindowState state);
50 Q_INVOKABLE void saveState(const QString &windowId, WindowState state);
46 Q_INVOKABLE WindowState getState(const QString &windowId, WindowState defaultValue) const;
51 Q_INVOKABLE WindowState getState(const QString &windowId, WindowState defaultValue) const;
47
52
48− Q_INVOKABLE void saveGeometry(const QString &windowId, const QRect rect);
53+ Q_INVOKABLE void saveGeometry(const QString &windowId, const QRect &rect);
49− Q_INVOKABLE QRect getGeometry(const QString &windowId, const QRect defaultValue) const;
54+ Q_INVOKABLE QRect getGeometry(const QString &windowId, const QRect &defaultValue) const;
50
55
51 Q_INVOKABLE void saveStage(const QString &appId, int stage);
56 Q_INVOKABLE void saveStage(const QString &appId, int stage);
52 Q_INVOKABLE int getStage(const QString &appId, int defaultValue) const;
57 Q_INVOKABLE int getStage(const QString &appId, int defaultValue) const;
po/ast.po+143−138
@@ -8,17 +8,17 @@ msgstr ""
8"Project-Id-Version: unity8\n"
8"Project-Id-Version: unity8\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: 2016-08-31 11:56+0000\n"
10"POT-Creation-Date: 2016-08-31 11:56+0000\n"
11−"PO-Revision-Date: 2016-05-16 14:07+0000\n"
11+"PO-Revision-Date: 2016-09-25 14:54+0000\n"
12"Last-Translator: Xuacu Saturio <xuacusk8@gmail.com>\n"
12"Last-Translator: Xuacu Saturio <xuacusk8@gmail.com>\n"
13"Language-Team: Asturian <ast@li.org>\n"
13"Language-Team: Asturian <ast@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-09-03 06:53+0000\n"
18+"X-Launchpad-Export-Date: 2016-09-26 06:16+0000\n"
19−"X-Generator: Launchpad (build 18179)\n"
19+"X-Generator: Launchpad (build 18204)\n"
20
20
21−#: plugins/IntegratedLightDM/Greeter.cpp:112
21+#: plugins/LightDM/Greeter.cpp:123
22msgid "Password: "
22msgid "Password: "
23msgstr "Contraseña: "
23msgstr "Contraseña: "
24
24
@@ -35,74 +35,75 @@ msgstr "Colar"
35msgid "Unpin shortcut"
35msgid "Unpin shortcut"
36msgstr "Desanclar atayu"
36msgstr "Desanclar atayu"
37
37
38−#: qml/Components/DelayedLockscreen.qml:50
38+#: qml/Greeter/DelayedLockscreen.qml:43
39msgid "Device Locked"
39msgid "Device Locked"
40msgstr "Preséu bloquiáu"
40msgstr "Preséu bloquiáu"
41
41
42−#: qml/Components/DelayedLockscreen.qml:65
42+#: qml/Greeter/DelayedLockscreen.qml:58
43msgid "You have been locked out due to too many failed passphrase attempts."
43msgid "You have been locked out due to too many failed passphrase attempts."
44msgstr "Quedasti bloquiáu polos escesivos intentos fallíos de fras de pasu."
44msgstr "Quedasti bloquiáu polos escesivos intentos fallíos de fras de pasu."
45
45
46−#: qml/Components/DelayedLockscreen.qml:66
46+#: qml/Greeter/DelayedLockscreen.qml:59
47msgid "You have been locked out due to too many failed passcode attempts."
47msgid "You have been locked out due to too many failed passcode attempts."
48msgstr ""
48msgstr ""
49"Quedasti bloquiáu polos escesivos intentos fallíos de códigu de pasu."
49"Quedasti bloquiáu polos escesivos intentos fallíos de códigu de pasu."
50
50
51−#: qml/Components/DelayedLockscreen.qml:75
51+#: qml/Greeter/DelayedLockscreen.qml:68
52#, qt-format
52#, qt-format
53msgid "Please wait %1 minute and then try again…"
53msgid "Please wait %1 minute and then try again…"
54msgid_plural "Please wait %1 minutes and then try again…"
54msgid_plural "Please wait %1 minutes and then try again…"
55msgstr[0] "Por favor, espera %1 minutu y vuelvi a intentalo…"
55msgstr[0] "Por favor, espera %1 minutu y vuelvi a intentalo…"
56msgstr[1] "Por favor, espera %1 minutos y vuelvi a intentalo…"
56msgstr[1] "Por favor, espera %1 minutos y vuelvi a intentalo…"
57
57
58−#: qml/Components/Dialogs.qml:146
58+#: qml/Components/Dialogs.qml:149
59msgctxt "Title: Lock/Log out dialog"
59msgctxt "Title: Lock/Log out dialog"
60msgid "Log out"
60msgid "Log out"
61msgstr "Zarrar sesión"
61msgstr "Zarrar sesión"
62
62
63−#: qml/Components/Dialogs.qml:147
63+#: qml/Components/Dialogs.qml:150
64msgid "Are you sure you want to log out?"
64msgid "Are you sure you want to log out?"
65msgstr "¿De xuru que quies zarrar la sesión?"
65msgstr "¿De xuru que quies zarrar la sesión?"
66
66
67−#: qml/Components/Dialogs.qml:149
67+#: qml/Components/Dialogs.qml:152
68msgctxt "Button: Lock the system"
68msgctxt "Button: Lock the system"
69msgid "Lock"
69msgid "Lock"
70msgstr "Bloquiar"
70msgstr "Bloquiar"
71
71
72−#: qml/Components/Dialogs.qml:156
72+#: qml/Components/Dialogs.qml:159
73msgctxt "Button: Log out from the system"
73msgctxt "Button: Log out from the system"
74msgid "Log Out"
74msgid "Log Out"
75msgstr "Zarrar sesión"
75msgstr "Zarrar sesión"
76
76
77−#: qml/Components/Dialogs.qml:163 qml/Components/Dialogs.qml:219
77+#: qml/Components/Dialogs.qml:166 qml/Components/Dialogs.qml:222
78+#: qml/Dash/DashPageHeader.qml:324 qml/Greeter/NarrowView.qml:232
78−#: qml/Dash/DashPageHeader.qml:295 qml/Wizard/Pages/passcode-confirm.qml:32
79+#: qml/Wizard/Pages/passcode-confirm.qml:32
79#: qml/Wizard/Pages/passcode-set.qml:32
80#: qml/Wizard/Pages/passcode-set.qml:32
80msgid "Cancel"
81msgid "Cancel"
81msgstr "Encaboxar"
82msgstr "Encaboxar"
82
83
83−#: qml/Components/Dialogs.qml:175
84+#: qml/Components/Dialogs.qml:178
84msgctxt "Title: Reboot dialog"
85msgctxt "Title: Reboot dialog"
85msgid "Reboot"
86msgid "Reboot"
86msgstr "Reaniciu"
87msgstr "Reaniciu"
87
88
88−#: qml/Components/Dialogs.qml:176
89+#: qml/Components/Dialogs.qml:179
89msgid "Are you sure you want to reboot?"
90msgid "Are you sure you want to reboot?"
90msgstr "¿De xuru que quies reaniciar?"
91msgstr "¿De xuru que quies reaniciar?"
91
92
92−#: qml/Components/Dialogs.qml:178
93+#: qml/Components/Dialogs.qml:181
93msgid "No"
94msgid "No"
94msgstr "Non"
95msgstr "Non"
95
96
96−#: qml/Components/Dialogs.qml:184
97+#: qml/Components/Dialogs.qml:187
97msgid "Yes"
98msgid "Yes"
98msgstr "Sí"
99msgstr "Sí"
99
100
100−#: qml/Components/Dialogs.qml:199
101+#: qml/Components/Dialogs.qml:202
101msgctxt "Title: Power off/Restart dialog"
102msgctxt "Title: Power off/Restart dialog"
102msgid "Power"
103msgid "Power"
103msgstr "Apagáu"
104msgstr "Apagáu"
104
105
105−#: qml/Components/Dialogs.qml:200
106+#: qml/Components/Dialogs.qml:203
106msgid ""
107msgid ""
107"Are you sure you would like\n"
108"Are you sure you would like\n"
108"to power off?"
109"to power off?"
@@ -110,99 +111,99 @@ msgstr ""
110"¿De xuru que te prestaría\n"
111"¿De xuru que te prestaría\n"
111"apagar?"
112"apagar?"
112
113
113−#: qml/Components/Dialogs.qml:202
114+#: qml/Components/Dialogs.qml:205
114msgctxt "Button: Power off the system"
115msgctxt "Button: Power off the system"
115msgid "Power off"
116msgid "Power off"
116msgstr "Apagar"
117msgstr "Apagar"
117
118
118−#: qml/Components/Dialogs.qml:211
119+#: qml/Components/Dialogs.qml:214
119msgctxt "Button: Restart the system"
120msgctxt "Button: Restart the system"
120msgid "Restart"
121msgid "Restart"
121msgstr "Reaniciar"
122msgstr "Reaniciar"
122
123
123#: qml/Components/KeyboardShortcutsOverlay.qml:41
124#: qml/Components/KeyboardShortcutsOverlay.qml:41
124msgid "Keyboard Shortcuts"
125msgid "Keyboard Shortcuts"
125−msgstr ""
126+msgstr "Atayos de tecláu"
126
127
127#: qml/Components/KeyboardShortcutsOverlay.qml:55
128#: qml/Components/KeyboardShortcutsOverlay.qml:55
128msgid "Unity 8"
129msgid "Unity 8"
129−msgstr ""
130+msgstr "Unity 8"
130
131
131#: qml/Components/KeyboardShortcutsOverlay.qml:62
132#: qml/Components/KeyboardShortcutsOverlay.qml:62
132msgid "PrtScr"
133msgid "PrtScr"
133−msgstr ""
134+msgstr "ImprPant"
134
135
135#: qml/Components/KeyboardShortcutsOverlay.qml:67
136#: qml/Components/KeyboardShortcutsOverlay.qml:67
136msgid "Takes a screenshot."
137msgid "Takes a screenshot."
137−msgstr ""
138+msgstr "Saca una imaxe de la pantalla."
138
139
139#: qml/Components/KeyboardShortcutsOverlay.qml:75
140#: qml/Components/KeyboardShortcutsOverlay.qml:75
140msgid "Alt + PrtScr"
141msgid "Alt + PrtScr"
141−msgstr ""
142+msgstr "Alt + ImprPant"
142
143
143#: qml/Components/KeyboardShortcutsOverlay.qml:80
144#: qml/Components/KeyboardShortcutsOverlay.qml:80
144msgid "Takes a screenshot of a window."
145msgid "Takes a screenshot of a window."
145−msgstr ""
146+msgstr "Saca una imaxe d'una ventana."
146
147
147#: qml/Components/KeyboardShortcutsOverlay.qml:88
148#: qml/Components/KeyboardShortcutsOverlay.qml:88
148msgid "Super + Space"
149msgid "Super + Space"
149−msgstr ""
150+msgstr "Super + Espaciu"
150
151
151#: qml/Components/KeyboardShortcutsOverlay.qml:93
152#: qml/Components/KeyboardShortcutsOverlay.qml:93
152msgid "Switches to next keyboard layout."
153msgid "Switches to next keyboard layout."
153−msgstr ""
154+msgstr "Salta al siguiente diseñu de tecláu."
154
155
155#: qml/Components/KeyboardShortcutsOverlay.qml:101
156#: qml/Components/KeyboardShortcutsOverlay.qml:101
156msgid "Super + Shift + Space"
157msgid "Super + Shift + Space"
157−msgstr ""
158+msgstr "Super +Mayús + Espaciu"
158
159
159#: qml/Components/KeyboardShortcutsOverlay.qml:106
160#: qml/Components/KeyboardShortcutsOverlay.qml:106
160msgid "Switches to previous keyboard layout."
161msgid "Switches to previous keyboard layout."
161−msgstr ""
162+msgstr "Salta al diseñu de tecláu anterior."
162
163
163#: qml/Components/KeyboardShortcutsOverlay.qml:118
164#: qml/Components/KeyboardShortcutsOverlay.qml:118
164msgid "Launcher"
165msgid "Launcher"
165−msgstr ""
166+msgstr "Llanzador"
166
167
167#: qml/Components/KeyboardShortcutsOverlay.qml:125
168#: qml/Components/KeyboardShortcutsOverlay.qml:125
168msgid "Super (Hold)"
169msgid "Super (Hold)"
169−msgstr ""
170+msgstr "Super (Caltener)"
170
171
171#: qml/Components/KeyboardShortcutsOverlay.qml:130
172#: qml/Components/KeyboardShortcutsOverlay.qml:130
172msgid "Opens the launcher, displays shortcuts."
173msgid "Opens the launcher, displays shortcuts."
173−msgstr ""
174+msgstr "Abre'l llanzador, amuesa atayos."
174
175
175#: qml/Components/KeyboardShortcutsOverlay.qml:138
176#: qml/Components/KeyboardShortcutsOverlay.qml:138
176msgid "Alt + F1"
177msgid "Alt + F1"
177−msgstr ""
178+msgstr "Alt + F1"
178
179
179#: qml/Components/KeyboardShortcutsOverlay.qml:143
180#: qml/Components/KeyboardShortcutsOverlay.qml:143
180msgid "Opens launcher keyboard navigation mode."
181msgid "Opens launcher keyboard navigation mode."
181−msgstr ""
182+msgstr "Abre'l mou de navegación con tecláu del llanzador."
182
183
183#: qml/Components/KeyboardShortcutsOverlay.qml:151
184#: qml/Components/KeyboardShortcutsOverlay.qml:151
184msgid "Super + Tab"
185msgid "Super + Tab"
185−msgstr ""
186+msgstr "Super + Tab"
186
187
187#: qml/Components/KeyboardShortcutsOverlay.qml:156
188#: qml/Components/KeyboardShortcutsOverlay.qml:156
188msgid "Switches applications via the launcher."
189msgid "Switches applications via the launcher."
189−msgstr ""
190+msgstr "Cambia d'aplicaciones col llanzador."
190
191
191#: qml/Components/KeyboardShortcutsOverlay.qml:164
192#: qml/Components/KeyboardShortcutsOverlay.qml:164
192msgid "Super + 0 to 9"
193msgid "Super + 0 to 9"
193−msgstr ""
194+msgstr "Super + 0 a 9"
194
195
195#: qml/Components/KeyboardShortcutsOverlay.qml:169
196#: qml/Components/KeyboardShortcutsOverlay.qml:169
196msgid "Same as clicking on a launcher icon."
197msgid "Same as clicking on a launcher icon."
197−msgstr ""
198+msgstr "Igual que facer click nel iconu del llanzador."
198
199
199#: qml/Components/KeyboardShortcutsOverlay.qml:181
200#: qml/Components/KeyboardShortcutsOverlay.qml:181
200msgid "Scopes"
201msgid "Scopes"
201−msgstr ""
202+msgstr "Ámbitos"
202
203
203#: qml/Components/KeyboardShortcutsOverlay.qml:188
204#: qml/Components/KeyboardShortcutsOverlay.qml:188
204msgid "Super (Tap)"
205msgid "Super (Tap)"
205−msgstr ""
206+msgstr "Super (Tocar)"
206
207
207#: qml/Components/KeyboardShortcutsOverlay.qml:193
208#: qml/Components/KeyboardShortcutsOverlay.qml:193
208msgid "Opens the Scopes home."
209msgid "Opens the Scopes home."
@@ -210,59 +211,59 @@ msgstr ""
210
211
211#: qml/Components/KeyboardShortcutsOverlay.qml:209
212#: qml/Components/KeyboardShortcutsOverlay.qml:209
212msgid "Switching"
213msgid "Switching"
213−msgstr ""
214+msgstr "Conmutación"
214
215
215#: qml/Components/KeyboardShortcutsOverlay.qml:216
216#: qml/Components/KeyboardShortcutsOverlay.qml:216
216msgid "Alt + Tab"
217msgid "Alt + Tab"
217−msgstr ""
218+msgstr "Alt + Tab"
218
219
219#: qml/Components/KeyboardShortcutsOverlay.qml:221
220#: qml/Components/KeyboardShortcutsOverlay.qml:221
220msgid "Switches between applications."
221msgid "Switches between applications."
221−msgstr ""
222+msgstr "Conmuta ente aplicaciones."
222
223
223#: qml/Components/KeyboardShortcutsOverlay.qml:229
224#: qml/Components/KeyboardShortcutsOverlay.qml:229
224msgid "Super + W"
225msgid "Super + W"
225−msgstr ""
226+msgstr "Super + W"
226
227
227#: qml/Components/KeyboardShortcutsOverlay.qml:234
228#: qml/Components/KeyboardShortcutsOverlay.qml:234
228msgid "Opens the desktop spread."
229msgid "Opens the desktop spread."
229−msgstr ""
230+msgstr "Abre esparder l'escritoriu."
230
231
231#: qml/Components/KeyboardShortcutsOverlay.qml:242
232#: qml/Components/KeyboardShortcutsOverlay.qml:242
232msgid "Cursor Left or Right"
233msgid "Cursor Left or Right"
233−msgstr ""
234+msgstr "Flecha izq. o dre."
234
235
235#: qml/Components/KeyboardShortcutsOverlay.qml:247
236#: qml/Components/KeyboardShortcutsOverlay.qml:247
236msgid "Moves the focus."
237msgid "Moves the focus."
237−msgstr ""
238+msgstr "Mueve'l focu"
238
239
239#: qml/Components/KeyboardShortcutsOverlay.qml:259
240#: qml/Components/KeyboardShortcutsOverlay.qml:259
240msgid "Windows"
241msgid "Windows"
241−msgstr ""
242+msgstr "Ventanes"
242
243
243#: qml/Components/KeyboardShortcutsOverlay.qml:266
244#: qml/Components/KeyboardShortcutsOverlay.qml:266
244msgid "Ctrl + Super + D"
245msgid "Ctrl + Super + D"
245−msgstr ""
246+msgstr "Ctrl + Super + D"
246
247
247#: qml/Components/KeyboardShortcutsOverlay.qml:271
248#: qml/Components/KeyboardShortcutsOverlay.qml:271
248msgid "Minimizes all windows."
249msgid "Minimizes all windows."
249−msgstr ""
250+msgstr "Amenorga toles ventanes."
250
251
251#: qml/Components/KeyboardShortcutsOverlay.qml:279
252#: qml/Components/KeyboardShortcutsOverlay.qml:279
252msgid "Ctrl + Super + Up"
253msgid "Ctrl + Super + Up"
253−msgstr ""
254+msgstr "Ctrl + Super + Arriba"
254
255
255#: qml/Components/KeyboardShortcutsOverlay.qml:284
256#: qml/Components/KeyboardShortcutsOverlay.qml:284
256msgid "Maximizes the current window."
257msgid "Maximizes the current window."
257−msgstr ""
258+msgstr "Maximiza la ventana actual."
258
259
259#: qml/Components/KeyboardShortcutsOverlay.qml:292
260#: qml/Components/KeyboardShortcutsOverlay.qml:292
260msgid "Ctrl + Super + Down"
261msgid "Ctrl + Super + Down"
261−msgstr ""
262+msgstr "Ctrl + Super + Abaxo"
262
263
263#: qml/Components/KeyboardShortcutsOverlay.qml:297
264#: qml/Components/KeyboardShortcutsOverlay.qml:297
264msgid "Minimizes or restores the current window."
265msgid "Minimizes or restores the current window."
265−msgstr ""
266+msgstr "Amenorga o restaura la ventana actual."
266
267
267#: qml/Components/KeyboardShortcutsOverlay.qml:305
268#: qml/Components/KeyboardShortcutsOverlay.qml:305
268msgid "Ctrl + Super + Left or Right"
269msgid "Ctrl + Super + Left or Right"
@@ -274,33 +275,33 @@ msgstr ""
274
275
275#: qml/Components/KeyboardShortcutsOverlay.qml:318
276#: qml/Components/KeyboardShortcutsOverlay.qml:318
276msgid "Alt + F4"
277msgid "Alt + F4"
277−msgstr ""
278+msgstr "Alt + F4"
278
279
279#: qml/Components/KeyboardShortcutsOverlay.qml:323
280#: qml/Components/KeyboardShortcutsOverlay.qml:323
280msgid "Closes the current window."
281msgid "Closes the current window."
281−msgstr ""
282+msgstr "Zarra la ventana actual."
282
283
283−#: qml/Components/Lockscreen.qml:239
284+#: qml/Components/Lockscreen.qml:212 qml/Greeter/NarrowView.qml:252
284msgid "Return to Call"
285msgid "Return to Call"
285msgstr "Tornar a la llamada"
286msgstr "Tornar a la llamada"
286
287
287−#: qml/Components/Lockscreen.qml:239
288+#: qml/Components/Lockscreen.qml:212
288msgid "Emergency Call"
289msgid "Emergency Call"
289msgstr "Llamada d'emerxencia"
290msgstr "Llamada d'emerxencia"
290
291
291−#: qml/Components/Lockscreen.qml:271
292+#: qml/Components/Lockscreen.qml:244
292msgid "OK"
293msgid "OK"
293msgstr "Aceutar"
294msgstr "Aceutar"
294
295
295#: qml/Components/MediaServices/VideoPlayerControls.qml:44
296#: qml/Components/MediaServices/VideoPlayerControls.qml:44
296#, qt-format
297#, qt-format
297msgid "%1:%2:%3"
298msgid "%1:%2:%3"
298−msgstr ""
299+msgstr "%1:%2:%3"
299
300
300#: qml/Components/MediaServices/VideoPlayerControls.qml:49
301#: qml/Components/MediaServices/VideoPlayerControls.qml:49
301#, qt-format
302#, qt-format
302msgid "%1:%2"
303msgid "%1:%2"
303−msgstr ""
304+msgstr "%1:%2"
304
305
305#: qml/Components/ModeSwitchWarningDialog.qml:32
306#: qml/Components/ModeSwitchWarningDialog.qml:32
306msgid "Apps may have unsaved data:"
307msgid "Apps may have unsaved data:"
@@ -332,55 +333,55 @@ msgstr "Zarrar too"
332msgid "Preview Share Item"
333msgid "Preview Share Item"
333msgstr "Entever l'elementu compartíu"
334msgstr "Entever l'elementu compartíu"
334
335
335−#: qml/Dash/DashPageHeader.qml:333
336+#: qml/Dash/DashPageHeader.qml:362
336msgctxt "Button: Open the Ubuntu Store"
337msgctxt "Button: Open the Ubuntu Store"
337msgid "Store"
338msgid "Store"
338msgstr "Tienda"
339msgstr "Tienda"
339
340
340−#: qml/Dash/DashPageHeader.qml:340
341+#: qml/Dash/DashPageHeader.qml:369
341msgctxt "Button: Start a search in the current dash scope"
342msgctxt "Button: Start a search in the current dash scope"
342msgid "Search"
343msgid "Search"
343msgstr "Guetar"
344msgstr "Guetar"
344
345
345−#: qml/Dash/DashPageHeader.qml:350
346+#: qml/Dash/DashPageHeader.qml:379
346msgctxt "Button: Show the current dash scope settings"
347msgctxt "Button: Show the current dash scope settings"
347msgid "Settings"
348msgid "Settings"
348msgstr "Axustes"
349msgstr "Axustes"
349
350
350−#: qml/Dash/DashPageHeader.qml:357
351+#: qml/Dash/DashPageHeader.qml:386
351msgid "Remove from Favorites"
352msgid "Remove from Favorites"
352msgstr "Desaniciar de favoritos"
353msgstr "Desaniciar de favoritos"
353
354
354−#: qml/Dash/DashPageHeader.qml:357
355+#: qml/Dash/DashPageHeader.qml:386
355msgid "Add to Favorites"
356msgid "Add to Favorites"
356msgstr "Amestar a favoritos"
357msgstr "Amestar a favoritos"
357
358
358−#: qml/Dash/FiltersPopover.qml:60
359+#: qml/Dash/FiltersPopover.qml:61
359msgid "Refine your results"
360msgid "Refine your results"
360msgstr "Refina los resultaos"
361msgstr "Refina los resultaos"
361
362
362−#: qml/Dash/FiltersPopover.qml:69
363+#: qml/Dash/FiltersPopover.qml:70
363msgid "Reset"
364msgid "Reset"
364msgstr "Reafitar"
365msgstr "Reafitar"
365
366
366−#: qml/Dash/GenericScopeView.qml:580 qml/Dash/GenericScopeView.qml:775
367+#: qml/Dash/GenericScopeView.qml:586 qml/Dash/GenericScopeView.qml:782
367msgid "Show less"
368msgid "Show less"
368msgstr "Ver menos"
369msgstr "Ver menos"
369
370
370−#: qml/Dash/GenericScopeView.qml:580
371+#: qml/Dash/GenericScopeView.qml:586
371msgid "Show all"
372msgid "Show all"
372msgstr "Ver too"
373msgstr "Ver too"
373
374
374−#: qml/Dash/GenericScopeView.qml:637
375+#: qml/Dash/GenericScopeView.qml:643
375msgctxt "Label: Hint for dash search line edit"
376msgctxt "Label: Hint for dash search line edit"
376msgid "Search"
377msgid "Search"
377msgstr "Guetar"
378msgstr "Guetar"
378
379
379−#: qml/Dash/PageHeaderExtraPanel.qml:56
380+#: qml/Dash/PageHeaderExtraPanel.qml:57
380msgid "Recent Searches"
381msgid "Recent Searches"
381msgstr "Guetes recién"
382msgstr "Guetes recién"
382
383
383−#: qml/Dash/PageHeaderExtraPanel.qml:67
384+#: qml/Dash/PageHeaderExtraPanel.qml:68
384msgid "Clear All"
385msgid "Clear All"
385msgstr "Llimpiar too"
386msgstr "Llimpiar too"
386
387
@@ -392,24 +393,24 @@ msgstr "Más…"
392msgid "Less..."
393msgid "Less..."
393msgstr "Menos…"
394msgstr "Menos…"
394
395
395−#: qml/Dash/Previews/PreviewCommentInput.qml:68
396+#: qml/Dash/Previews/PreviewCommentInput.qml:83
396−#: qml/Dash/Previews/PreviewRatingInput.qml:175
397+#: qml/Dash/Previews/PreviewRatingInput.qml:201
397msgid "Send"
398msgid "Send"
398msgstr "Unviar"
399msgstr "Unviar"
399
400
400−#: qml/Dash/Previews/PreviewRatingInput.qml:82
401+#: qml/Dash/Previews/PreviewRatingInput.qml:108
401msgid "Rate this"
402msgid "Rate this"
402msgstr "Valorar esto"
403msgstr "Valorar esto"
403
404
404−#: qml/Dash/Previews/PreviewRatingInput.qml:157
405+#: qml/Dash/Previews/PreviewRatingInput.qml:183
405msgid "Add a review"
406msgid "Add a review"
406msgstr "Amestar una reseña"
407msgstr "Amestar una reseña"
407
408
408−#: qml/Dash/PullToRefreshScopeStyle.qml:55
409+#: qml/Dash/PullToRefreshScopeStyle.qml:56
409msgid "Pull to refresh…"
410msgid "Pull to refresh…"
410msgstr "Tira p'anovar..."
411msgstr "Tira p'anovar..."
411
412
412−#: qml/Dash/PullToRefreshScopeStyle.qml:60
413+#: qml/Dash/PullToRefreshScopeStyle.qml:61
413msgid "Release to refresh…"
414msgid "Release to refresh…"
414msgstr "Sueltar p'anovar..."
415msgstr "Sueltar p'anovar..."
415
416
@@ -417,15 +418,15 @@ msgstr "Sueltar p'anovar..."
417msgid "Enable location data"
418msgid "Enable location data"
418msgstr "Habilitar datos d'allugamientu"
419msgstr "Habilitar datos d'allugamientu"
419
420
420−#: qml/Dash/ScopesList.qml:55
421+#: qml/Dash/ScopesList.qml:57
421msgid "Manage"
422msgid "Manage"
422msgstr "Xestionar"
423msgstr "Xestionar"
423
424
424−#: qml/Dash/ScopesList.qml:102
425+#: qml/Dash/ScopesList.qml:104
425msgid "Home"
426msgid "Home"
426msgstr "Aniciu"
427msgstr "Aniciu"
427
428
428−#: qml/Dash/ScopesList.qml:103
429+#: qml/Dash/ScopesList.qml:105
429msgid "Also installed"
430msgid "Also installed"
430msgstr "Tamién instalaos"
431msgstr "Tamién instalaos"
431
432
@@ -441,39 +442,39 @@ msgstr ""
441msgid "Unlock"
442msgid "Unlock"
442msgstr "Desbloquiar"
443msgstr "Desbloquiar"
443
444
444−#: qml/Greeter/Greeter.qml:539
445+#: qml/Greeter/Greeter.qml:596
445msgid "Try again"
446msgid "Try again"
446−msgstr ""
447+msgstr "Tentar otra vuelta"
447
448
448−#: qml/Greeter/LoginList.qml:63
449+#: qml/Greeter/LoginList.qml:70
449msgid "Passphrase"
450msgid "Passphrase"
450−msgstr ""
451+msgstr "Fras de pasu"
451
452
452−#: qml/Greeter/LoginList.qml:64
453+#: qml/Greeter/LoginList.qml:71
453msgid "Passcode"
454msgid "Passcode"
454−msgstr ""
455+msgstr "Códigu de pasu"
455
456
456−#: qml/Greeter/LoginList.qml:259
457+#: qml/Greeter/LoginList.qml:98
457msgid "Retry"
458msgid "Retry"
458msgstr "Reintentar"
459msgstr "Reintentar"
459
460
460−#: qml/Greeter/LoginList.qml:260
461+#: qml/Greeter/LoginList.qml:99
461msgid "Log In"
462msgid "Log In"
462−msgstr ""
463+msgstr "Aniciar sesión"
463
464
464−#: qml/Greeter/NarrowView.qml:54
465+#: qml/Greeter/NarrowView.qml:55
465msgid "Sorry, incorrect passphrase."
466msgid "Sorry, incorrect passphrase."
466msgstr "Perdón, fras de pasu incorreuta"
467msgstr "Perdón, fras de pasu incorreuta"
467
468
468−#: qml/Greeter/NarrowView.qml:55
469+#: qml/Greeter/NarrowView.qml:56
469msgid "Sorry, incorrect passcode."
470msgid "Sorry, incorrect passcode."
470msgstr "Perdón, códigu de pasu incorreutu."
471msgstr "Perdón, códigu de pasu incorreutu."
471
472
472−#: qml/Greeter/NarrowView.qml:56
473+#: qml/Greeter/NarrowView.qml:57
473msgid "This will be your last attempt."
474msgid "This will be your last attempt."
474msgstr "Esti sedra'l to caberu intentu"
475msgstr "Esti sedra'l to caberu intentu"
475
476
476−#: qml/Greeter/NarrowView.qml:58
477+#: qml/Greeter/NarrowView.qml:59
477msgid ""
478msgid ""
478"If passphrase is entered incorrectly, your phone will conduct a factory "
479"If passphrase is entered incorrectly, your phone will conduct a factory "
479"reset and all personal data will be deleted."
480"reset and all personal data will be deleted."
@@ -481,7 +482,7 @@ msgstr ""
481"Si s'inxerta la fras de pasu incorreutamente, el to teléfonu va reafitase y "
482"Si s'inxerta la fras de pasu incorreutamente, el to teléfonu va reafitase y "
482"van desaniciase tolos datos personales."
483"van desaniciase tolos datos personales."
483
484
484−#: qml/Greeter/NarrowView.qml:59
485+#: qml/Greeter/NarrowView.qml:60
485msgid ""
486msgid ""
486"If passcode is entered incorrectly, your phone will conduct a factory reset "
487"If passcode is entered incorrectly, your phone will conduct a factory reset "
487"and all personal data will be deleted."
488"and all personal data will be deleted."
@@ -636,49 +637,51 @@ msgctxt "Button: Go back one page in the Wizard"
636msgid "Back"
637msgid "Back"
637msgstr "Atrás"
638msgstr "Atrás"
638
639
639−#: qml/Wizard/Pages/10-welcome.qml:27
640+#: qml/Wizard/Pages/10-welcome.qml:28
640msgid "Language"
641msgid "Language"
641msgstr "Llingua"
642msgstr "Llingua"
642
643
643−#: qml/Wizard/Pages/10-welcome.qml:171 qml/Wizard/Pages/30-wifi.qml:206
644+#: qml/Wizard/Pages/10-welcome.qml:173 qml/Wizard/Pages/20-keyboard.qml:152
644−#: qml/Wizard/Pages/40-location.qml:271 qml/Wizard/Pages/50-timezone.qml:270
645+#: qml/Wizard/Pages/30-wifi.qml:207 qml/Wizard/Pages/40-location.qml:271
646+#: qml/Wizard/Pages/50-timezone.qml:271 qml/Wizard/Pages/60-account.qml:65
645−#: qml/Wizard/Pages/60-account.qml:65 qml/Wizard/Pages/70-passwd-type.qml:144
647+#: qml/Wizard/Pages/70-passwd-type.qml:145
646#: qml/Wizard/Pages/75-report-check.qml:84
648#: qml/Wizard/Pages/75-report-check.qml:84
647−#: qml/Wizard/Pages/passcode-desktop.qml:141
649+#: qml/Wizard/Pages/passcode-desktop.qml:142
648−#: qml/Wizard/Pages/password-set.qml:141
650+#: qml/Wizard/Pages/password-set.qml:142
649msgid "Next"
651msgid "Next"
650msgstr "Siguiente"
652msgstr "Siguiente"
651
653
652−#: qml/Wizard/Pages/20-keyboard.qml:30
654+#: qml/Wizard/Pages/20-keyboard.qml:31
653msgid "Select Keyboard"
655msgid "Select Keyboard"
654−msgstr ""
656+msgstr "Escoyer tecláu"
655
657
656−#: qml/Wizard/Pages/20-keyboard.qml:70
658+#: qml/Wizard/Pages/20-keyboard.qml:71
657msgid "Keyboard language"
659msgid "Keyboard language"
658−msgstr ""
660+msgstr "Llingua del tecláu"
659
661
660−#: qml/Wizard/Pages/20-keyboard.qml:92
662+#: qml/Wizard/Pages/20-keyboard.qml:93
661msgid "Keyboard layout"
663msgid "Keyboard layout"
662−msgstr ""
664+msgstr "Distribución del tecláu"
663
665
666+#: qml/Wizard/Pages/20-keyboard.qml:152 qml/Wizard/Pages/30-wifi.qml:207
664−#: qml/Wizard/Pages/30-wifi.qml:206 qml/Wizard/Pages/60-account.qml:65
667+#: qml/Wizard/Pages/60-account.qml:65 qml/Wizard/Pages/77-system-update.qml:152
665#: qml/Wizard/Pages/sim.qml:101
668#: qml/Wizard/Pages/sim.qml:101
666msgid "Skip"
669msgid "Skip"
667msgstr "Saltar"
670msgstr "Saltar"
668
671
669−#: qml/Wizard/Pages/30-wifi.qml:29
672+#: qml/Wizard/Pages/30-wifi.qml:31
670msgid "Connect to Wi‑Fi"
673msgid "Connect to Wi‑Fi"
671msgstr "Coneutase al WiFi"
674msgstr "Coneutase al WiFi"
672
675
673−#: qml/Wizard/Pages/30-wifi.qml:130
676+#: qml/Wizard/Pages/30-wifi.qml:132
674msgid "Connected"
677msgid "Connected"
675msgstr "Coneutáu"
678msgstr "Coneutáu"
676
679
677−#: qml/Wizard/Pages/30-wifi.qml:163
680+#: qml/Wizard/Pages/30-wifi.qml:165
678msgid "Available Wi-Fi networks"
681msgid "Available Wi-Fi networks"
679msgstr "Redes Wi-Fi disponibles"
682msgstr "Redes Wi-Fi disponibles"
680
683
681−#: qml/Wizard/Pages/30-wifi.qml:164
684+#: qml/Wizard/Pages/30-wifi.qml:166
682msgid "No available Wi-Fi networks"
685msgid "No available Wi-Fi networks"
683msgstr "Nun hai redes Wi-Fi disponibles"
686msgstr "Nun hai redes Wi-Fi disponibles"
684
687
@@ -716,11 +719,11 @@ msgstr "Nun usar l'allugamientu"
716msgid "You can change it later in System Settings."
719msgid "You can change it later in System Settings."
717msgstr "Pues camudalo sero na Configuración del Sistema."
720msgstr "Pues camudalo sero na Configuración del Sistema."
718
721
719−#: qml/Wizard/Pages/50-timezone.qml:29
722+#: qml/Wizard/Pages/50-timezone.qml:30
720msgid "Time Zone"
723msgid "Time Zone"
721msgstr "Estaya horaria"
724msgstr "Estaya horaria"
722
725
723−#: qml/Wizard/Pages/50-timezone.qml:181
726+#: qml/Wizard/Pages/50-timezone.qml:182
724msgid "Enter your city"
727msgid "Enter your city"
725msgstr "Escribe la ciudá"
728msgstr "Escribe la ciudá"
726
729
@@ -732,21 +735,21 @@ msgstr "Personaliza'l preséu"
732msgid "Preferred Name"
735msgid "Preferred Name"
733msgstr "Nome preferíu"
736msgstr "Nome preferíu"
734
737
735−#: qml/Wizard/Pages/70-passwd-type.qml:39
738+#: qml/Wizard/Pages/70-passwd-type.qml:40
736msgid "Lock Screen"
739msgid "Lock Screen"
737msgstr "Bloquiar pantalla"
740msgstr "Bloquiar pantalla"
738
741
739−#: qml/Wizard/Pages/70-passwd-type.qml:101
742+#: qml/Wizard/Pages/70-passwd-type.qml:102
740msgctxt "Label: Type of security method"
743msgctxt "Label: Type of security method"
741msgid "Create new password"
744msgid "Create new password"
742msgstr "Crear contraseña nueva"
745msgstr "Crear contraseña nueva"
743
746
744−#: qml/Wizard/Pages/70-passwd-type.qml:103
747+#: qml/Wizard/Pages/70-passwd-type.qml:104
745msgctxt "Label: Type of security method"
748msgctxt "Label: Type of security method"
746msgid "Create passcode (numbers only)"
749msgid "Create passcode (numbers only)"
747msgstr "Crear códigu d'accesu (sólo númberos)"
750msgstr "Crear códigu d'accesu (sólo númberos)"
748
751
749−#: qml/Wizard/Pages/70-passwd-type.qml:105
752+#: qml/Wizard/Pages/70-passwd-type.qml:106
750msgctxt "Label: Type of security method"
753msgctxt "Label: Type of security method"
751msgid "No lock code"
754msgid "No lock code"
752msgstr "Ensin códigu de bloquéu"
755msgstr "Ensin códigu de bloquéu"
@@ -771,32 +774,34 @@ msgstr "Política d'intimidá"
771
774
772#: qml/Wizard/Pages/77-system-update.qml:28
775#: qml/Wizard/Pages/77-system-update.qml:28
773msgid "Update Device"
776msgid "Update Device"
774−msgstr ""
777+msgstr "Anovar preséu"
775
778
776#: qml/Wizard/Pages/77-system-update.qml:55
779#: qml/Wizard/Pages/77-system-update.qml:55
777msgid ""
780msgid ""
778"There is a system update available and ready to install. Afterwards, the "
781"There is a system update available and ready to install. Afterwards, the "
779"device will automatically restart."
782"device will automatically restart."
780msgstr ""
783msgstr ""
784+"Hai un anovamientu del sistema disponible y preparáu pa instalar. Darréu, el "
785+"preséu reaniciará de mou automáticu."
781
786
782#: qml/Wizard/Pages/77-system-update.qml:76
787#: qml/Wizard/Pages/77-system-update.qml:76
783msgctxt "string identifying name of the update"
788msgctxt "string identifying name of the update"
784msgid "Ubuntu system"
789msgid "Ubuntu system"
785−msgstr ""
790+msgstr "Sistema Ubuntu"
786
791
787#: qml/Wizard/Pages/77-system-update.qml:83
792#: qml/Wizard/Pages/77-system-update.qml:83
788#, qt-format
793#, qt-format
789msgctxt "version of the system update"
794msgctxt "version of the system update"
790msgid "Version %1"
795msgid "Version %1"
791−msgstr ""
796+msgstr "Versión %1"
792
797
793#: qml/Wizard/Pages/77-system-update.qml:102
798#: qml/Wizard/Pages/77-system-update.qml:102
794msgid "This could take a few minutes..."
799msgid "This could take a few minutes..."
795−msgstr ""
800+msgstr "Esto podría tardar dellos minutos..."
796
801
797#: qml/Wizard/Pages/77-system-update.qml:116
802#: qml/Wizard/Pages/77-system-update.qml:116
798msgid "Install and restart now"
803msgid "Install and restart now"
799−msgstr ""
804+msgstr "Instalar y reaniciar agora"
800
805
801#: qml/Wizard/Pages/80-finished.qml:89
806#: qml/Wizard/Pages/80-finished.qml:89
802msgid "Welcome to Ubuntu"
807msgid "Welcome to Ubuntu"
@@ -841,7 +846,7 @@ msgid "Service Terms"
841msgstr ""
846msgstr ""
842
847
843#: qml/Wizard/Pages/passcode-confirm.qml:43
848#: qml/Wizard/Pages/passcode-confirm.qml:43
844−#: qml/Wizard/Pages/passcode-desktop.qml:96
849+#: qml/Wizard/Pages/passcode-desktop.qml:97
845msgid "Confirm passcode"
850msgid "Confirm passcode"
846msgstr "Confirmar códigu de pasu"
851msgstr "Confirmar códigu de pasu"
847
852
@@ -854,28 +859,28 @@ msgctxt "Enter the passcode again"
854msgid "Please re-enter."
859msgid "Please re-enter."
855msgstr ""
860msgstr ""
856
861
857−#: qml/Wizard/Pages/passcode-desktop.qml:30
862+#: qml/Wizard/Pages/passcode-desktop.qml:31
858msgid "Lock Screen Passcode"
863msgid "Lock Screen Passcode"
859msgstr ""
864msgstr ""
860
865
861−#: qml/Wizard/Pages/passcode-desktop.qml:60
866+#: qml/Wizard/Pages/passcode-desktop.qml:61
862msgid "Enter 4 numbers to setup your passcode"
867msgid "Enter 4 numbers to setup your passcode"
863msgstr ""
868msgstr ""
864
869
865−#: qml/Wizard/Pages/passcode-desktop.qml:76
870+#: qml/Wizard/Pages/passcode-desktop.qml:77
866#: qml/Wizard/Pages/passcode-set.qml:54
871#: qml/Wizard/Pages/passcode-set.qml:54
867msgid "Choose passcode"
872msgid "Choose passcode"
868msgstr ""
873msgstr ""
869
874
870−#: qml/Wizard/Pages/passcode-desktop.qml:126
875+#: qml/Wizard/Pages/passcode-desktop.qml:127
871msgid "Passcode too short"
876msgid "Passcode too short"
872msgstr ""
877msgstr ""
873
878
874−#: qml/Wizard/Pages/passcode-desktop.qml:128
879+#: qml/Wizard/Pages/passcode-desktop.qml:129
875msgid "Passcodes match"
880msgid "Passcodes match"
876msgstr ""
881msgstr ""
877
882
878−#: qml/Wizard/Pages/passcode-desktop.qml:130
883+#: qml/Wizard/Pages/passcode-desktop.qml:131
879msgid "Passcodes do not match"
884msgid "Passcodes do not match"
880msgstr ""
885msgstr ""
881
886
@@ -883,19 +888,19 @@ msgstr ""
883msgid "Passcode must be 4 characters long"
888msgid "Passcode must be 4 characters long"
884msgstr ""
889msgstr ""
885
890
886−#: qml/Wizard/Pages/password-set.qml:30
891+#: qml/Wizard/Pages/password-set.qml:31
887msgid "Lock Screen Password"
892msgid "Lock Screen Password"
888msgstr ""
893msgstr ""
889
894
890−#: qml/Wizard/Pages/password-set.qml:61
895+#: qml/Wizard/Pages/password-set.qml:62
891msgid "Enter at least 8 characters"
896msgid "Enter at least 8 characters"
892msgstr ""
897msgstr ""
893
898
894−#: qml/Wizard/Pages/password-set.qml:73
899+#: qml/Wizard/Pages/password-set.qml:74
895msgid "Choose password"
900msgid "Choose password"
896msgstr ""
901msgstr ""
897
902
898−#: qml/Wizard/Pages/password-set.qml:103
903+#: qml/Wizard/Pages/password-set.qml:104
899msgid "Confirm password"
904msgid "Confirm password"
900msgstr "Confirmar contraseña"
905msgstr "Confirmar contraseña"
901
906
po/it.po+66−60
@@ -8,17 +8,17 @@ 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: 2016-08-31 11:56+0000\n"
10"POT-Creation-Date: 2016-08-31 11:56+0000\n"
11−"PO-Revision-Date: 2016-04-03 16:08+0000\n"
11+"PO-Revision-Date: 2016-09-11 10:40+0000\n"
12−"Last-Translator: Milo Casagrande <milo.casagrande@gmail.com>\n"
12+"Last-Translator: Claudio Arseni <claudio.arseni@gmail.com>\n"
13"Language-Team: Italian <it@li.org>\n"
13"Language-Team: Italian <it@li.org>\n"
14"MIME-Version: 1.0\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: 8bit\n"
16"Content-Transfer-Encoding: 8bit\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
17"Plural-Forms: nplurals=2; plural=n != 1;\n"
18−"X-Launchpad-Export-Date: 2016-09-03 06:54+0000\n"
18+"X-Launchpad-Export-Date: 2016-09-12 06:06+0000\n"
19−"X-Generator: Launchpad (build 18179)\n"
19+"X-Generator: Launchpad (build 18186)\n"
20
20
21−#: plugins/IntegratedLightDM/Greeter.cpp:112
21+#: plugins/LightDM/Greeter.cpp:123
22msgid "Password: "
22msgid "Password: "
23msgstr "Password: "
23msgstr "Password: "
24
24
@@ -58,54 +58,54 @@ msgid_plural "Please wait %1 minutes and then try again…"
58msgstr[0] "Attendere %1 minuto poi riprovare…"
58msgstr[0] "Attendere %1 minuto poi riprovare…"
59msgstr[1] "Attendere %1 minuti poi riprovare…"
59msgstr[1] "Attendere %1 minuti poi riprovare…"
60
60
61−#: qml/Components/Dialogs.qml:146
61+#: qml/Components/Dialogs.qml:149
62msgctxt "Title: Lock/Log out dialog"
62msgctxt "Title: Lock/Log out dialog"
63msgid "Log out"
63msgid "Log out"
64msgstr "Termina sessione"
64msgstr "Termina sessione"
65
65
66−#: qml/Components/Dialogs.qml:147
66+#: qml/Components/Dialogs.qml:150
67msgid "Are you sure you want to log out?"
67msgid "Are you sure you want to log out?"
68msgstr "Terminare veramente la sessione?"
68msgstr "Terminare veramente la sessione?"
69
69
70−#: qml/Components/Dialogs.qml:149
70+#: qml/Components/Dialogs.qml:152
71msgctxt "Button: Lock the system"
71msgctxt "Button: Lock the system"
72msgid "Lock"
72msgid "Lock"
73msgstr "Blocca"
73msgstr "Blocca"
74
74
75−#: qml/Components/Dialogs.qml:156
75+#: qml/Components/Dialogs.qml:159
76msgctxt "Button: Log out from the system"
76msgctxt "Button: Log out from the system"
77msgid "Log Out"
77msgid "Log Out"
78msgstr "Termina sessione"
78msgstr "Termina sessione"
79
79
80−#: qml/Components/Dialogs.qml:163 qml/Components/Dialogs.qml:219
80+#: qml/Components/Dialogs.qml:166 qml/Components/Dialogs.qml:222
81−#: qml/Dash/DashPageHeader.qml:295 qml/Wizard/Pages/passcode-confirm.qml:32
81+#: qml/Dash/DashPageHeader.qml:322 qml/Wizard/Pages/passcode-confirm.qml:32
82#: qml/Wizard/Pages/passcode-set.qml:32
82#: qml/Wizard/Pages/passcode-set.qml:32
83msgid "Cancel"
83msgid "Cancel"
84msgstr "Annulla"
84msgstr "Annulla"
85
85
86−#: qml/Components/Dialogs.qml:175
86+#: qml/Components/Dialogs.qml:178
87msgctxt "Title: Reboot dialog"
87msgctxt "Title: Reboot dialog"
88msgid "Reboot"
88msgid "Reboot"
89msgstr "Riavvia"
89msgstr "Riavvia"
90
90
91−#: qml/Components/Dialogs.qml:176
91+#: qml/Components/Dialogs.qml:179
92msgid "Are you sure you want to reboot?"
92msgid "Are you sure you want to reboot?"
93msgstr "Riavviare veramente il dispositivo?"
93msgstr "Riavviare veramente il dispositivo?"
94
94
95−#: qml/Components/Dialogs.qml:178
95+#: qml/Components/Dialogs.qml:181
96msgid "No"
96msgid "No"
97msgstr "No"
97msgstr "No"
98
98
99−#: qml/Components/Dialogs.qml:184
99+#: qml/Components/Dialogs.qml:187
100msgid "Yes"
100msgid "Yes"
101msgstr "Sì"
101msgstr "Sì"
102
102
103−#: qml/Components/Dialogs.qml:199
103+#: qml/Components/Dialogs.qml:202
104msgctxt "Title: Power off/Restart dialog"
104msgctxt "Title: Power off/Restart dialog"
105msgid "Power"
105msgid "Power"
106msgstr "Alimentazione"
106msgstr "Alimentazione"
107
107
108−#: qml/Components/Dialogs.qml:200
108+#: qml/Components/Dialogs.qml:203
109msgid ""
109msgid ""
110"Are you sure you would like\n"
110"Are you sure you would like\n"
111"to power off?"
111"to power off?"
@@ -113,12 +113,12 @@ msgstr ""
113"Spegnere veramente\n"
113"Spegnere veramente\n"
114"il dispositivo?"
114"il dispositivo?"
115
115
116−#: qml/Components/Dialogs.qml:202
116+#: qml/Components/Dialogs.qml:205
117msgctxt "Button: Power off the system"
117msgctxt "Button: Power off the system"
118msgid "Power off"
118msgid "Power off"
119msgstr "Spegni"
119msgstr "Spegni"
120
120
121−#: qml/Components/Dialogs.qml:211
121+#: qml/Components/Dialogs.qml:214
122msgctxt "Button: Restart the system"
122msgctxt "Button: Restart the system"
123msgid "Restart"
123msgid "Restart"
124msgstr "Riavvia"
124msgstr "Riavvia"
@@ -169,7 +169,7 @@ msgstr "Launcher"
169
169
170#: qml/Components/KeyboardShortcutsOverlay.qml:125
170#: qml/Components/KeyboardShortcutsOverlay.qml:125
171msgid "Super (Hold)"
171msgid "Super (Hold)"
172−msgstr ""
172+msgstr "Super (tenere premuto)"
173
173
174#: qml/Components/KeyboardShortcutsOverlay.qml:130
174#: qml/Components/KeyboardShortcutsOverlay.qml:130
175msgid "Opens the launcher, displays shortcuts."
175msgid "Opens the launcher, displays shortcuts."
@@ -181,7 +181,7 @@ msgstr "Alt + F1"
181
181
182#: qml/Components/KeyboardShortcutsOverlay.qml:143
182#: qml/Components/KeyboardShortcutsOverlay.qml:143
183msgid "Opens launcher keyboard navigation mode."
183msgid "Opens launcher keyboard navigation mode."
184−msgstr ""
184+msgstr "Avvia la navigazione da tastiera del Launcher"
185
185
186#: qml/Components/KeyboardShortcutsOverlay.qml:151
186#: qml/Components/KeyboardShortcutsOverlay.qml:151
187msgid "Super + Tab"
187msgid "Super + Tab"
@@ -189,7 +189,7 @@ msgstr "Super + Tab"
189
189
190#: qml/Components/KeyboardShortcutsOverlay.qml:156
190#: qml/Components/KeyboardShortcutsOverlay.qml:156
191msgid "Switches applications via the launcher."
191msgid "Switches applications via the launcher."
192−msgstr ""
192+msgstr "Passa a un'altra applicazione attraverso il Launcher"
193
193
194#: qml/Components/KeyboardShortcutsOverlay.qml:164
194#: qml/Components/KeyboardShortcutsOverlay.qml:164
195msgid "Super + 0 to 9"
195msgid "Super + 0 to 9"
@@ -213,7 +213,7 @@ msgstr "Apre le Scope"
213
213
214#: qml/Components/KeyboardShortcutsOverlay.qml:209
214#: qml/Components/KeyboardShortcutsOverlay.qml:209
215msgid "Switching"
215msgid "Switching"
216−msgstr ""
216+msgstr "Cambio applicazione"
217
217
218#: qml/Components/KeyboardShortcutsOverlay.qml:216
218#: qml/Components/KeyboardShortcutsOverlay.qml:216
219msgid "Alt + Tab"
219msgid "Alt + Tab"
@@ -229,11 +229,11 @@ msgstr "Super + W"
229
229
230#: qml/Components/KeyboardShortcutsOverlay.qml:234
230#: qml/Components/KeyboardShortcutsOverlay.qml:234
231msgid "Opens the desktop spread."
231msgid "Opens the desktop spread."
232−msgstr ""
232+msgstr "Apre la visualizzazione della scrivania"
233
233
234#: qml/Components/KeyboardShortcutsOverlay.qml:242
234#: qml/Components/KeyboardShortcutsOverlay.qml:242
235msgid "Cursor Left or Right"
235msgid "Cursor Left or Right"
236−msgstr ""
236+msgstr "Freccia destra o sinistra"
237
237
238#: qml/Components/KeyboardShortcutsOverlay.qml:247
238#: qml/Components/KeyboardShortcutsOverlay.qml:247
239msgid "Moves the focus."
239msgid "Moves the focus."
@@ -241,7 +241,7 @@ msgstr "Sposta il focus"
241
241
242#: qml/Components/KeyboardShortcutsOverlay.qml:259
242#: qml/Components/KeyboardShortcutsOverlay.qml:259
243msgid "Windows"
243msgid "Windows"
244−msgstr ""
244+msgstr "Finestre"
245
245
246#: qml/Components/KeyboardShortcutsOverlay.qml:266
246#: qml/Components/KeyboardShortcutsOverlay.qml:266
247msgid "Ctrl + Super + D"
247msgid "Ctrl + Super + D"
@@ -257,7 +257,7 @@ msgstr "Ctrl + Super + Su"
257
257
258#: qml/Components/KeyboardShortcutsOverlay.qml:284
258#: qml/Components/KeyboardShortcutsOverlay.qml:284
259msgid "Maximizes the current window."
259msgid "Maximizes the current window."
260−msgstr ""
260+msgstr "Massimizza la finestra in uso"
261
261
262#: qml/Components/KeyboardShortcutsOverlay.qml:292
262#: qml/Components/KeyboardShortcutsOverlay.qml:292
263msgid "Ctrl + Super + Down"
263msgid "Ctrl + Super + Down"
@@ -337,26 +337,26 @@ msgstr "Chiudi tutto"
337msgid "Preview Share Item"
337msgid "Preview Share Item"
338msgstr "Anteprima elementi da condividere"
338msgstr "Anteprima elementi da condividere"
339
339
340−#: qml/Dash/DashPageHeader.qml:333
340+#: qml/Dash/DashPageHeader.qml:360
341msgctxt "Button: Open the Ubuntu Store"
341msgctxt "Button: Open the Ubuntu Store"
342msgid "Store"
342msgid "Store"
343msgstr "Store"
343msgstr "Store"
344
344
345−#: qml/Dash/DashPageHeader.qml:340
345+#: qml/Dash/DashPageHeader.qml:367
346msgctxt "Button: Start a search in the current dash scope"
346msgctxt "Button: Start a search in the current dash scope"
347msgid "Search"
347msgid "Search"
348msgstr "Cerca"
348msgstr "Cerca"
349
349
350−#: qml/Dash/DashPageHeader.qml:350
350+#: qml/Dash/DashPageHeader.qml:377
351msgctxt "Button: Show the current dash scope settings"
351msgctxt "Button: Show the current dash scope settings"
352msgid "Settings"
352msgid "Settings"
353msgstr "Impostazioni"
353msgstr "Impostazioni"
354
354
355−#: qml/Dash/DashPageHeader.qml:357
355+#: qml/Dash/DashPageHeader.qml:384
356msgid "Remove from Favorites"
356msgid "Remove from Favorites"
357msgstr "Rimuovi dai preferiti"
357msgstr "Rimuovi dai preferiti"
358
358
359−#: qml/Dash/DashPageHeader.qml:357
359+#: qml/Dash/DashPageHeader.qml:384
360msgid "Add to Favorites"
360msgid "Add to Favorites"
361msgstr "Aggiungi ai preferiti"
361msgstr "Aggiungi ai preferiti"
362
362
@@ -368,15 +368,15 @@ msgstr "Affina la ricerca"
368msgid "Reset"
368msgid "Reset"
369msgstr "Azzera"
369msgstr "Azzera"
370
370
371−#: qml/Dash/GenericScopeView.qml:580 qml/Dash/GenericScopeView.qml:775
371+#: qml/Dash/GenericScopeView.qml:585 qml/Dash/GenericScopeView.qml:780
372msgid "Show less"
372msgid "Show less"
373msgstr "Mostra di meno"
373msgstr "Mostra di meno"
374
374
375−#: qml/Dash/GenericScopeView.qml:580
375+#: qml/Dash/GenericScopeView.qml:585
376msgid "Show all"
376msgid "Show all"
377msgstr "Mostra tutto"
377msgstr "Mostra tutto"
378
378
379−#: qml/Dash/GenericScopeView.qml:637
379+#: qml/Dash/GenericScopeView.qml:642
380msgctxt "Label: Hint for dash search line edit"
380msgctxt "Label: Hint for dash search line edit"
381msgid "Search"
381msgid "Search"
382msgstr "Cerca"
382msgstr "Cerca"
@@ -446,7 +446,7 @@ msgstr ""
446msgid "Unlock"
446msgid "Unlock"
447msgstr "Sblocca"
447msgstr "Sblocca"
448
448
449−#: qml/Greeter/Greeter.qml:539
449+#: qml/Greeter/Greeter.qml:561
450msgid "Try again"
450msgid "Try again"
451msgstr "Riprova"
451msgstr "Riprova"
452
452
@@ -458,27 +458,27 @@ msgstr "Passphrase"
458msgid "Passcode"
458msgid "Passcode"
459msgstr "Codice"
459msgstr "Codice"
460
460
461−#: qml/Greeter/LoginList.qml:259
461+#: qml/Greeter/LoginList.qml:87
462msgid "Retry"
462msgid "Retry"
463msgstr "Riprova"
463msgstr "Riprova"
464
464
465−#: qml/Greeter/LoginList.qml:260
465+#: qml/Greeter/LoginList.qml:88
466msgid "Log In"
466msgid "Log In"
467msgstr "Accedi"
467msgstr "Accedi"
468
468
469−#: qml/Greeter/NarrowView.qml:54
469+#: qml/Greeter/NarrowView.qml:55
470msgid "Sorry, incorrect passphrase."
470msgid "Sorry, incorrect passphrase."
471msgstr "Passphrase non corretta"
471msgstr "Passphrase non corretta"
472
472
473−#: qml/Greeter/NarrowView.qml:55
473+#: qml/Greeter/NarrowView.qml:56
474msgid "Sorry, incorrect passcode."
474msgid "Sorry, incorrect passcode."
475msgstr "Passcode non corretto"
475msgstr "Passcode non corretto"
476
476
477−#: qml/Greeter/NarrowView.qml:56
477+#: qml/Greeter/NarrowView.qml:57
478msgid "This will be your last attempt."
478msgid "This will be your last attempt."
479msgstr "Questo sarà l'ultimo tentativo."
479msgstr "Questo sarà l'ultimo tentativo."
480
480
481−#: qml/Greeter/NarrowView.qml:58
481+#: qml/Greeter/NarrowView.qml:59
482msgid ""
482msgid ""
483"If passphrase is entered incorrectly, your phone will conduct a factory "
483"If passphrase is entered incorrectly, your phone will conduct a factory "
484"reset and all personal data will be deleted."
484"reset and all personal data will be deleted."
@@ -486,7 +486,7 @@ msgstr ""
486"Se verrà inserita una passphrase errata, il telefono eseguirà un ripristino "
486"Se verrà inserita una passphrase errata, il telefono eseguirà un ripristino "
487"delle impostazioni di fabbrica e tutti i dati personali verranno eliminati."
487"delle impostazioni di fabbrica e tutti i dati personali verranno eliminati."
488
488
489−#: qml/Greeter/NarrowView.qml:59
489+#: qml/Greeter/NarrowView.qml:60
490msgid ""
490msgid ""
491"If passcode is entered incorrectly, your phone will conduct a factory reset "
491"If passcode is entered incorrectly, your phone will conduct a factory reset "
492"and all personal data will be deleted."
492"and all personal data will be deleted."
@@ -623,7 +623,7 @@ msgstr "Scorrere lentamente dal bordo sinistro per aprire la scope Oggi"
623
623
624#: qml/Tutorial/TutorialLeft.qml:47
624#: qml/Tutorial/TutorialLeft.qml:47
625msgid "Short swipe from the left edge to open the launcher"
625msgid "Short swipe from the left edge to open the launcher"
626−msgstr ""
626+msgstr "Scorrere brevemente dal bordo sinistro per aprire il launcher"
627
627
628#: qml/Tutorial/TutorialRight.qml:55
628#: qml/Tutorial/TutorialRight.qml:55
629msgid "Push your mouse against the right edge to view your open apps"
629msgid "Push your mouse against the right edge to view your open apps"
@@ -631,11 +631,13 @@ msgstr "Spostare il mouse sul bordo destro per vedere le app aperte"
631
631
632#: qml/Tutorial/TutorialRight.qml:56
632#: qml/Tutorial/TutorialRight.qml:56
633msgid "Swipe from the right edge to view your open apps"
633msgid "Swipe from the right edge to view your open apps"
634−msgstr ""
634+msgstr "Scorrere dal bordo destro per visualizzare le app aperte"
635
635
636#: qml/Tutorial/TutorialTop.qml:53
636#: qml/Tutorial/TutorialTop.qml:53
637msgid "Swipe from the top edge to access notifications and quick settings"
637msgid "Swipe from the top edge to access notifications and quick settings"
638msgstr ""
638msgstr ""
639+"Scorrere dal bordo superiore per accedere alle notifiche e alle impostazioni "
640+"rapide"
639
641
640#: qml/Wizard/Page.qml:54
642#: qml/Wizard/Page.qml:54
641msgctxt "Button: Go back one page in the Wizard"
643msgctxt "Button: Go back one page in the Wizard"
@@ -646,9 +648,10 @@ msgstr "Indietro"
646msgid "Language"
648msgid "Language"
647msgstr "Lingua"
649msgstr "Lingua"
648
650
649−#: qml/Wizard/Pages/10-welcome.qml:171 qml/Wizard/Pages/30-wifi.qml:206
651+#: qml/Wizard/Pages/10-welcome.qml:172 qml/Wizard/Pages/20-keyboard.qml:151
650−#: qml/Wizard/Pages/40-location.qml:271 qml/Wizard/Pages/50-timezone.qml:270
652+#: qml/Wizard/Pages/30-wifi.qml:206 qml/Wizard/Pages/40-location.qml:271
653+#: qml/Wizard/Pages/50-timezone.qml:270 qml/Wizard/Pages/60-account.qml:65
651−#: qml/Wizard/Pages/60-account.qml:65 qml/Wizard/Pages/70-passwd-type.qml:144
654+#: qml/Wizard/Pages/70-passwd-type.qml:144
652#: qml/Wizard/Pages/75-report-check.qml:84
655#: qml/Wizard/Pages/75-report-check.qml:84
653#: qml/Wizard/Pages/passcode-desktop.qml:141
656#: qml/Wizard/Pages/passcode-desktop.qml:141
654#: qml/Wizard/Pages/password-set.qml:141
657#: qml/Wizard/Pages/password-set.qml:141
@@ -657,7 +660,7 @@ msgstr "Avanti"
657
660
658#: qml/Wizard/Pages/20-keyboard.qml:30
661#: qml/Wizard/Pages/20-keyboard.qml:30
659msgid "Select Keyboard"
662msgid "Select Keyboard"
660−msgstr ""
663+msgstr "Seleziona la tastiera"
661
664
662#: qml/Wizard/Pages/20-keyboard.qml:70
665#: qml/Wizard/Pages/20-keyboard.qml:70
663msgid "Keyboard language"
666msgid "Keyboard language"
@@ -665,26 +668,27 @@ msgstr "Lingua della tastiera"
665
668
666#: qml/Wizard/Pages/20-keyboard.qml:92
669#: qml/Wizard/Pages/20-keyboard.qml:92
667msgid "Keyboard layout"
670msgid "Keyboard layout"
668−msgstr ""
671+msgstr "Disposizione della tastiera"
669
672
673+#: qml/Wizard/Pages/20-keyboard.qml:151 qml/Wizard/Pages/30-wifi.qml:206
670−#: qml/Wizard/Pages/30-wifi.qml:206 qml/Wizard/Pages/60-account.qml:65
674+#: qml/Wizard/Pages/60-account.qml:65
671−#: qml/Wizard/Pages/sim.qml:101
675+#: qml/Wizard/Pages/77-system-update.qml:152 qml/Wizard/Pages/sim.qml:101
672msgid "Skip"
676msgid "Skip"
673msgstr "Salta"
677msgstr "Salta"
674
678
675−#: qml/Wizard/Pages/30-wifi.qml:29
679+#: qml/Wizard/Pages/30-wifi.qml:30
676msgid "Connect to Wi‑Fi"
680msgid "Connect to Wi‑Fi"
677msgstr "Connetti al Wi-Fi"
681msgstr "Connetti al Wi-Fi"
678
682
679−#: qml/Wizard/Pages/30-wifi.qml:130
683+#: qml/Wizard/Pages/30-wifi.qml:131
680msgid "Connected"
684msgid "Connected"
681msgstr "Connessi"
685msgstr "Connessi"
682
686
683−#: qml/Wizard/Pages/30-wifi.qml:163
687+#: qml/Wizard/Pages/30-wifi.qml:164
684msgid "Available Wi-Fi networks"
688msgid "Available Wi-Fi networks"
685msgstr "Reti Wi-Fi disponibili"
689msgstr "Reti Wi-Fi disponibili"
686
690
687−#: qml/Wizard/Pages/30-wifi.qml:164
691+#: qml/Wizard/Pages/30-wifi.qml:165
688msgid "No available Wi-Fi networks"
692msgid "No available Wi-Fi networks"
689msgstr "Nessuna rete Wi-Fi disponibile"
693msgstr "Nessuna rete Wi-Fi disponibile"
690
694
@@ -777,13 +781,15 @@ msgstr "Informativa sulla privacy"
777
781
778#: qml/Wizard/Pages/77-system-update.qml:28
782#: qml/Wizard/Pages/77-system-update.qml:28
779msgid "Update Device"
783msgid "Update Device"
780−msgstr ""
784+msgstr "Aggiorna il dispositivo"
781
785
782#: qml/Wizard/Pages/77-system-update.qml:55
786#: qml/Wizard/Pages/77-system-update.qml:55
783msgid ""
787msgid ""
784"There is a system update available and ready to install. Afterwards, the "
788"There is a system update available and ready to install. Afterwards, the "
785"device will automatically restart."
789"device will automatically restart."
786msgstr ""
790msgstr ""
791+"È disponibile un aggiornamento di sistema ed è pronto per l'installazione. "
792+"Il sistema si riavvierà automaticamente al termine dell'operazione."
787
793
788#: qml/Wizard/Pages/77-system-update.qml:76
794#: qml/Wizard/Pages/77-system-update.qml:76
789msgctxt "string identifying name of the update"
795msgctxt "string identifying name of the update"
@@ -794,7 +800,7 @@ msgstr "Sistema Ubuntu"
794#, qt-format
800#, qt-format
795msgctxt "version of the system update"
801msgctxt "version of the system update"
796msgid "Version %1"
802msgid "Version %1"
797−msgstr ""
803+msgstr "Versione %1"
798
804
799#: qml/Wizard/Pages/77-system-update.qml:102
805#: qml/Wizard/Pages/77-system-update.qml:102
800msgid "This could take a few minutes..."
806msgid "This could take a few minutes..."
@@ -802,7 +808,7 @@ msgstr "Potrebbe impiegare alcuni minuti..."
802
808
803#: qml/Wizard/Pages/77-system-update.qml:116
809#: qml/Wizard/Pages/77-system-update.qml:116
804msgid "Install and restart now"
810msgid "Install and restart now"
805−msgstr ""
811+msgstr "Installa e riavvia adesso"
806
812
807#: qml/Wizard/Pages/80-finished.qml:89
813#: qml/Wizard/Pages/80-finished.qml:89
808msgid "Welcome to Ubuntu"
814msgid "Welcome to Ubuntu"
@@ -936,7 +942,7 @@ msgstr "Per continuare senza SIM, premere Salta."
936
942
937#: qml/Wizard/PasswordMeter.qml:87
943#: qml/Wizard/PasswordMeter.qml:87
938msgid "Password too short"
944msgid "Password too short"
939−msgstr ""
945+msgstr "Password troppo corta"
940
946
941#: qml/Wizard/PasswordMeter.qml:89
947#: qml/Wizard/PasswordMeter.qml:89
942msgid "Passwords match"
948msgid "Passwords match"
po/unity8.pot+88−134
@@ -1,6 +1,6 @@
1# SOME DESCRIPTIVE TITLE.
1# SOME DESCRIPTIVE TITLE.
2# Copyright (C) YEAR Canonical Ltd.
2# Copyright (C) YEAR Canonical Ltd.
3−# This file is distributed under the same license as the PACKAGE package.
3+# This file is distributed under the same license as the unity8 package.
4# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
4# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
5#
5#
6#, fuzzy
6#, fuzzy
@@ -8,7 +8,7 @@ msgid ""
8msgstr ""
8msgstr ""
9"Project-Id-Version: unity8\n"
9"Project-Id-Version: unity8\n"
10"Report-Msgid-Bugs-To: \n"
10"Report-Msgid-Bugs-To: \n"
11−"POT-Creation-Date: 2016-08-31 11:56+0000\n"
11+"POT-Creation-Date: 2016-09-22 07:46+0000\n"
12"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
12"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14"Language-Team: LANGUAGE <LL@li.org>\n"
14"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -35,25 +35,6 @@ msgstr ""
35msgid "Unpin shortcut"
35msgid "Unpin shortcut"
36msgstr ""
36msgstr ""
37
37
38−#: qml/Components/DelayedLockscreen.qml:50
39−msgid "Device Locked"
40−msgstr ""
41−
42−#: qml/Components/DelayedLockscreen.qml:65
43−msgid "You have been locked out due to too many failed passphrase attempts."
44−msgstr ""
45−
46−#: qml/Components/DelayedLockscreen.qml:66
47−msgid "You have been locked out due to too many failed passcode attempts."
48−msgstr ""
49−
50−#: qml/Components/DelayedLockscreen.qml:75
51−#, qt-format
52−msgid "Please wait %1 minute and then try again…"
53−msgid_plural "Please wait %1 minutes and then try again…"
54−msgstr[0] ""
55−msgstr[1] ""
56−
57#: qml/Components/Dialogs.qml:149
38#: qml/Components/Dialogs.qml:149
58msgctxt "Title: Lock/Log out dialog"
39msgctxt "Title: Lock/Log out dialog"
59msgid "Log out"
40msgid "Log out"
@@ -74,7 +55,8 @@ msgid "Log Out"
74msgstr ""
55msgstr ""
75
56
76#: qml/Components/Dialogs.qml:166 qml/Components/Dialogs.qml:222
57#: qml/Components/Dialogs.qml:166 qml/Components/Dialogs.qml:222
58+#: qml/Dash/DashPageHeader.qml:324 qml/Greeter/NarrowView.qml:232
77−#: qml/Dash/DashPageHeader.qml:322 qml/Wizard/Pages/passcode-confirm.qml:32
59+#: qml/Wizard/Pages/passcode-confirm.qml:32
78#: qml/Wizard/Pages/passcode-set.qml:32
60#: qml/Wizard/Pages/passcode-set.qml:32
79msgid "Cancel"
61msgid "Cancel"
80msgstr ""
62msgstr ""
@@ -277,15 +259,15 @@ msgstr ""
277msgid "Closes the current window."
259msgid "Closes the current window."
278msgstr ""
260msgstr ""
279
261
280−#: qml/Components/Lockscreen.qml:239
262+#: qml/Components/Lockscreen.qml:212 qml/Greeter/NarrowView.qml:252
281msgid "Return to Call"
263msgid "Return to Call"
282msgstr ""
264msgstr ""
283
265
284−#: qml/Components/Lockscreen.qml:239
266+#: qml/Components/Lockscreen.qml:212
285msgid "Emergency Call"
267msgid "Emergency Call"
286msgstr ""
268msgstr ""
287
269
288−#: qml/Components/Lockscreen.qml:271
270+#: qml/Components/Lockscreen.qml:244
289msgid "OK"
271msgid "OK"
290msgstr ""
272msgstr ""
291
273
@@ -329,55 +311,55 @@ msgstr ""
329msgid "Preview Share Item"
311msgid "Preview Share Item"
330msgstr ""
312msgstr ""
331
313
332−#: qml/Dash/DashPageHeader.qml:360
314+#: qml/Dash/DashPageHeader.qml:362
333msgctxt "Button: Open the Ubuntu Store"
315msgctxt "Button: Open the Ubuntu Store"
334msgid "Store"
316msgid "Store"
335msgstr ""
317msgstr ""
336
318
337−#: qml/Dash/DashPageHeader.qml:367
319+#: qml/Dash/DashPageHeader.qml:369
338msgctxt "Button: Start a search in the current dash scope"
320msgctxt "Button: Start a search in the current dash scope"
339msgid "Search"
321msgid "Search"
340msgstr ""
322msgstr ""
341
323
342−#: qml/Dash/DashPageHeader.qml:377
324+#: qml/Dash/DashPageHeader.qml:379
343msgctxt "Button: Show the current dash scope settings"
325msgctxt "Button: Show the current dash scope settings"
344msgid "Settings"
326msgid "Settings"
345msgstr ""
327msgstr ""
346
328
347−#: qml/Dash/DashPageHeader.qml:384
329+#: qml/Dash/DashPageHeader.qml:386
348msgid "Remove from Favorites"
330msgid "Remove from Favorites"
349msgstr ""
331msgstr ""
350
332
351−#: qml/Dash/DashPageHeader.qml:384
333+#: qml/Dash/DashPageHeader.qml:386
352msgid "Add to Favorites"
334msgid "Add to Favorites"
353msgstr ""
335msgstr ""
354
336
355−#: qml/Dash/FiltersPopover.qml:60
337+#: qml/Dash/FiltersPopover.qml:61
356msgid "Refine your results"
338msgid "Refine your results"
357msgstr ""
339msgstr ""
358
340
359−#: qml/Dash/FiltersPopover.qml:69
341+#: qml/Dash/FiltersPopover.qml:70
360msgid "Reset"
342msgid "Reset"
361msgstr ""
343msgstr ""
362
344
363−#: qml/Dash/GenericScopeView.qml:585 qml/Dash/GenericScopeView.qml:780
345+#: qml/Dash/GenericScopeView.qml:586 qml/Dash/GenericScopeView.qml:782
364msgid "Show less"
346msgid "Show less"
365msgstr ""
347msgstr ""
366
348
367−#: qml/Dash/GenericScopeView.qml:585
349+#: qml/Dash/GenericScopeView.qml:586
368msgid "Show all"
350msgid "Show all"
369msgstr ""
351msgstr ""
370
352
371−#: qml/Dash/GenericScopeView.qml:642
353+#: qml/Dash/GenericScopeView.qml:643
372msgctxt "Label: Hint for dash search line edit"
354msgctxt "Label: Hint for dash search line edit"
373msgid "Search"
355msgid "Search"
374msgstr ""
356msgstr ""
375
357
376−#: qml/Dash/PageHeaderExtraPanel.qml:56
358+#: qml/Dash/PageHeaderExtraPanel.qml:57
377msgid "Recent Searches"
359msgid "Recent Searches"
378msgstr ""
360msgstr ""
379
361
380−#: qml/Dash/PageHeaderExtraPanel.qml:67
362+#: qml/Dash/PageHeaderExtraPanel.qml:68
381msgid "Clear All"
363msgid "Clear All"
382msgstr ""
364msgstr ""
383
365
@@ -389,24 +371,24 @@ msgstr ""
389msgid "Less..."
371msgid "Less..."
390msgstr ""
372msgstr ""
391
373
392−#: qml/Dash/Previews/PreviewCommentInput.qml:68
374+#: qml/Dash/Previews/PreviewCommentInput.qml:83
393−#: qml/Dash/Previews/PreviewRatingInput.qml:175
375+#: qml/Dash/Previews/PreviewRatingInput.qml:201
394msgid "Send"
376msgid "Send"
395msgstr ""
377msgstr ""
396
378
397−#: qml/Dash/Previews/PreviewRatingInput.qml:82
379+#: qml/Dash/Previews/PreviewRatingInput.qml:108
398msgid "Rate this"
380msgid "Rate this"
399msgstr ""
381msgstr ""
400
382
401−#: qml/Dash/Previews/PreviewRatingInput.qml:157
383+#: qml/Dash/Previews/PreviewRatingInput.qml:183
402msgid "Add a review"
384msgid "Add a review"
403msgstr ""
385msgstr ""
404
386
405−#: qml/Dash/PullToRefreshScopeStyle.qml:55
387+#: qml/Dash/PullToRefreshScopeStyle.qml:56
406msgid "Pull to refresh…"
388msgid "Pull to refresh…"
407msgstr ""
389msgstr ""
408
390
409−#: qml/Dash/PullToRefreshScopeStyle.qml:60
391+#: qml/Dash/PullToRefreshScopeStyle.qml:61
410msgid "Release to refresh…"
392msgid "Release to refresh…"
411msgstr ""
393msgstr ""
412
394
@@ -414,15 +396,15 @@ msgstr ""
414msgid "Enable location data"
396msgid "Enable location data"
415msgstr ""
397msgstr ""
416
398
417−#: qml/Dash/ScopesList.qml:55
399+#: qml/Dash/ScopesList.qml:57
418msgid "Manage"
400msgid "Manage"
419msgstr ""
401msgstr ""
420
402
421−#: qml/Dash/ScopesList.qml:102
403+#: qml/Dash/ScopesList.qml:104
422msgid "Home"
404msgid "Home"
423msgstr ""
405msgstr ""
424
406
425−#: qml/Dash/ScopesList.qml:103
407+#: qml/Dash/ScopesList.qml:105
426msgid "Also installed"
408msgid "Also installed"
427msgstr ""
409msgstr ""
428
410
@@ -436,79 +418,51 @@ msgstr ""
436msgid "Unlock"
418msgid "Unlock"
437msgstr ""
419msgstr ""
438
420
439−#: qml/Greeter/Greeter.qml:561
421+#: qml/Greeter/DelayedLockscreen.qml:43
440−msgid "Try again"
422+msgid "Device Locked"
441−msgstr ""
442−
443−#: qml/Greeter/LoginList.qml:63
444−msgid "Passphrase"
445−msgstr ""
446−
447−#: qml/Greeter/LoginList.qml:64
448−msgid "Passcode"
449−msgstr ""
450−
451−#: qml/Greeter/LoginList.qml:87
452−msgid "Retry"
453−msgstr ""
454−
455−#: qml/Greeter/LoginList.qml:88
456−msgid "Log In"
457−msgstr ""
458−
459−#: qml/Greeter/NarrowView.qml:55
460−msgid "Sorry, incorrect passphrase."
461−msgstr ""
462−
463−#: qml/Greeter/NarrowView.qml:56
464−msgid "Sorry, incorrect passcode."
465−msgstr ""
466−
467−#: qml/Greeter/NarrowView.qml:57
468−msgid "This will be your last attempt."
469msgstr ""
423msgstr ""
470
424
471−#: qml/Greeter/NarrowView.qml:59
425+#: qml/Greeter/DelayedLockscreen.qml:58
472−msgid ""
426+msgid "You have been locked out due to too many failed passphrase attempts."
473−"If passphrase is entered incorrectly, your phone will conduct a factory "
474−"reset and all personal data will be deleted."
475msgstr ""
427msgstr ""
476
428
477−#: qml/Greeter/NarrowView.qml:60
429+#: qml/Greeter/DelayedLockscreen.qml:59
478−msgid ""
430+msgid "You have been locked out due to too many failed passcode attempts."
479−"If passcode is entered incorrectly, your phone will conduct a factory reset "
480−"and all personal data will be deleted."
481msgstr ""
431msgstr ""
482
432
483−#: qml/Greeter/NarrowView.qml:129
433+#: qml/Greeter/DelayedLockscreen.qml:68
484#, qt-format
434#, qt-format
485−msgid "Enter %1"
435+msgid "Please wait %1 minute and then try again…"
436+msgid_plural "Please wait %1 minutes and then try again…"
437+msgstr[0] ""
438+msgstr[1] ""
439+
440+#: qml/Greeter/Greeter.qml:596
441+msgid "Try again"
486msgstr ""
442msgstr ""
487
443
488−#: qml/Greeter/NarrowView.qml:130
444+#: qml/Greeter/LoginList.qml:70
489−msgid "Enter passphrase"
445+msgid "Passphrase"
490msgstr ""
446msgstr ""
491
447
492−#: qml/Greeter/NarrowView.qml:131
448+#: qml/Greeter/LoginList.qml:71
493−msgid "Enter passcode"
449+msgid "Passcode"
494msgstr ""
450msgstr ""
495
451
496−#: qml/Greeter/NarrowView.qml:135
452+#: qml/Greeter/LoginList.qml:98
497−#, qt-format
453+msgid "Retry"
498−msgid "Sorry, incorrect %1"
499msgstr ""
454msgstr ""
500
455
501−#: qml/Greeter/NarrowView.qml:136
456+#: qml/Greeter/LoginList.qml:99
502−msgid "Sorry, incorrect passphrase"
457+msgid "Log In"
503msgstr ""
458msgstr ""
504
459
505−#: qml/Greeter/NarrowView.qml:137
460+#: qml/Greeter/NarrowView.qml:252
506−msgctxt "passphrase"
461+msgid "Emergency"
507−msgid "Please re-enter"
508msgstr ""
462msgstr ""
509
463
510−#: qml/Greeter/NarrowView.qml:138
464+#: qml/Greeter/SessionsList.qml:122
511−msgid "Sorry, incorrect passcode"
465+msgid "Select desktop environment"
512msgstr ""
466msgstr ""
513
467
514#: qml/Notifications/NotificationMenuItemFactory.qml:124
468#: qml/Notifications/NotificationMenuItemFactory.qml:124
@@ -627,51 +581,51 @@ msgctxt "Button: Go back one page in the Wizard"
627msgid "Back"
581msgid "Back"
628msgstr ""
582msgstr ""
629
583
630−#: qml/Wizard/Pages/10-welcome.qml:27
584+#: qml/Wizard/Pages/10-welcome.qml:28
631msgid "Language"
585msgid "Language"
632msgstr ""
586msgstr ""
633
587
634−#: qml/Wizard/Pages/10-welcome.qml:172 qml/Wizard/Pages/20-keyboard.qml:151
588+#: qml/Wizard/Pages/10-welcome.qml:173 qml/Wizard/Pages/20-keyboard.qml:152
635−#: qml/Wizard/Pages/30-wifi.qml:206 qml/Wizard/Pages/40-location.qml:271
589+#: qml/Wizard/Pages/30-wifi.qml:207 qml/Wizard/Pages/40-location.qml:271
636−#: qml/Wizard/Pages/50-timezone.qml:270 qml/Wizard/Pages/60-account.qml:65
590+#: qml/Wizard/Pages/50-timezone.qml:271 qml/Wizard/Pages/60-account.qml:65
637−#: qml/Wizard/Pages/70-passwd-type.qml:144
591+#: qml/Wizard/Pages/70-passwd-type.qml:145
638#: qml/Wizard/Pages/75-report-check.qml:84
592#: qml/Wizard/Pages/75-report-check.qml:84
639−#: qml/Wizard/Pages/passcode-desktop.qml:141
593+#: qml/Wizard/Pages/passcode-desktop.qml:142
640−#: qml/Wizard/Pages/password-set.qml:141
594+#: qml/Wizard/Pages/password-set.qml:142
641msgid "Next"
595msgid "Next"
642msgstr ""
596msgstr ""
643
597
644−#: qml/Wizard/Pages/20-keyboard.qml:30
598+#: qml/Wizard/Pages/20-keyboard.qml:31
645msgid "Select Keyboard"
599msgid "Select Keyboard"
646msgstr ""
600msgstr ""
647
601
648−#: qml/Wizard/Pages/20-keyboard.qml:70
602+#: qml/Wizard/Pages/20-keyboard.qml:71
649msgid "Keyboard language"
603msgid "Keyboard language"
650msgstr ""
604msgstr ""
651
605
652−#: qml/Wizard/Pages/20-keyboard.qml:92
606+#: qml/Wizard/Pages/20-keyboard.qml:93
653msgid "Keyboard layout"
607msgid "Keyboard layout"
654msgstr ""
608msgstr ""
655
609
656−#: qml/Wizard/Pages/20-keyboard.qml:151 qml/Wizard/Pages/30-wifi.qml:206
610+#: qml/Wizard/Pages/20-keyboard.qml:152 qml/Wizard/Pages/30-wifi.qml:207
657−#: qml/Wizard/Pages/60-account.qml:65
611+#: qml/Wizard/Pages/60-account.qml:65 qml/Wizard/Pages/77-system-update.qml:152
658−#: qml/Wizard/Pages/77-system-update.qml:152 qml/Wizard/Pages/sim.qml:101
612+#: qml/Wizard/Pages/sim.qml:101
659msgid "Skip"
613msgid "Skip"
660msgstr ""
614msgstr ""
661
615
662−#: qml/Wizard/Pages/30-wifi.qml:30
616+#: qml/Wizard/Pages/30-wifi.qml:31
663msgid "Connect to Wi‑Fi"
617msgid "Connect to Wi‑Fi"
664msgstr ""
618msgstr ""
665
619
666−#: qml/Wizard/Pages/30-wifi.qml:131
620+#: qml/Wizard/Pages/30-wifi.qml:132
667msgid "Connected"
621msgid "Connected"
668msgstr ""
622msgstr ""
669
623
670−#: qml/Wizard/Pages/30-wifi.qml:164
624+#: qml/Wizard/Pages/30-wifi.qml:165
671msgid "Available Wi-Fi networks"
625msgid "Available Wi-Fi networks"
672msgstr ""
626msgstr ""
673
627
674−#: qml/Wizard/Pages/30-wifi.qml:165
628+#: qml/Wizard/Pages/30-wifi.qml:166
675msgid "No available Wi-Fi networks"
629msgid "No available Wi-Fi networks"
676msgstr ""
630msgstr ""
677
631
@@ -707,11 +661,11 @@ msgstr ""
707msgid "You can change it later in System Settings."
661msgid "You can change it later in System Settings."
708msgstr ""
662msgstr ""
709
663
710−#: qml/Wizard/Pages/50-timezone.qml:29
664+#: qml/Wizard/Pages/50-timezone.qml:30
711msgid "Time Zone"
665msgid "Time Zone"
712msgstr ""
666msgstr ""
713
667
714−#: qml/Wizard/Pages/50-timezone.qml:181
668+#: qml/Wizard/Pages/50-timezone.qml:182
715msgid "Enter your city"
669msgid "Enter your city"
716msgstr ""
670msgstr ""
717
671
@@ -723,21 +677,21 @@ msgstr ""
723msgid "Preferred Name"
677msgid "Preferred Name"
724msgstr ""
678msgstr ""
725
679
726−#: qml/Wizard/Pages/70-passwd-type.qml:39
680+#: qml/Wizard/Pages/70-passwd-type.qml:40
727msgid "Lock Screen"
681msgid "Lock Screen"
728msgstr ""
682msgstr ""
729
683
730−#: qml/Wizard/Pages/70-passwd-type.qml:101
684+#: qml/Wizard/Pages/70-passwd-type.qml:102
731msgctxt "Label: Type of security method"
685msgctxt "Label: Type of security method"
732msgid "Create new password"
686msgid "Create new password"
733msgstr ""
687msgstr ""
734
688
735−#: qml/Wizard/Pages/70-passwd-type.qml:103
689+#: qml/Wizard/Pages/70-passwd-type.qml:104
736msgctxt "Label: Type of security method"
690msgctxt "Label: Type of security method"
737msgid "Create passcode (numbers only)"
691msgid "Create passcode (numbers only)"
738msgstr ""
692msgstr ""
739
693
740−#: qml/Wizard/Pages/70-passwd-type.qml:105
694+#: qml/Wizard/Pages/70-passwd-type.qml:106
741msgctxt "Label: Type of security method"
695msgctxt "Label: Type of security method"
742msgid "No lock code"
696msgid "No lock code"
743msgstr ""
697msgstr ""
@@ -830,7 +784,7 @@ msgid "Service Terms"
830msgstr ""
784msgstr ""
831
785
832#: qml/Wizard/Pages/passcode-confirm.qml:43
786#: qml/Wizard/Pages/passcode-confirm.qml:43
833−#: qml/Wizard/Pages/passcode-desktop.qml:96
787+#: qml/Wizard/Pages/passcode-desktop.qml:97
834msgid "Confirm passcode"
788msgid "Confirm passcode"
835msgstr ""
789msgstr ""
836
790
@@ -843,28 +797,28 @@ msgctxt "Enter the passcode again"
843msgid "Please re-enter."
797msgid "Please re-enter."
844msgstr ""
798msgstr ""
845
799
846−#: qml/Wizard/Pages/passcode-desktop.qml:30
800+#: qml/Wizard/Pages/passcode-desktop.qml:31
847msgid "Lock Screen Passcode"
801msgid "Lock Screen Passcode"
848msgstr ""
802msgstr ""
849
803
850−#: qml/Wizard/Pages/passcode-desktop.qml:60
804+#: qml/Wizard/Pages/passcode-desktop.qml:61
851msgid "Enter 4 numbers to setup your passcode"
805msgid "Enter 4 numbers to setup your passcode"
852msgstr ""
806msgstr ""
853
807
854−#: qml/Wizard/Pages/passcode-desktop.qml:76
808+#: qml/Wizard/Pages/passcode-desktop.qml:77
855#: qml/Wizard/Pages/passcode-set.qml:54
809#: qml/Wizard/Pages/passcode-set.qml:54
856msgid "Choose passcode"
810msgid "Choose passcode"
857msgstr ""
811msgstr ""
858
812
859−#: qml/Wizard/Pages/passcode-desktop.qml:126
813+#: qml/Wizard/Pages/passcode-desktop.qml:127
860msgid "Passcode too short"
814msgid "Passcode too short"
861msgstr ""
815msgstr ""
862
816
863−#: qml/Wizard/Pages/passcode-desktop.qml:128
817+#: qml/Wizard/Pages/passcode-desktop.qml:129
864msgid "Passcodes match"
818msgid "Passcodes match"
865msgstr ""
819msgstr ""
866
820
867−#: qml/Wizard/Pages/passcode-desktop.qml:130
821+#: qml/Wizard/Pages/passcode-desktop.qml:131
868msgid "Passcodes do not match"
822msgid "Passcodes do not match"
869msgstr ""
823msgstr ""
870
824
@@ -872,19 +826,19 @@ msgstr ""
872msgid "Passcode must be 4 characters long"
826msgid "Passcode must be 4 characters long"
873msgstr ""
827msgstr ""
874
828
875−#: qml/Wizard/Pages/password-set.qml:30
829+#: qml/Wizard/Pages/password-set.qml:31
876msgid "Lock Screen Password"
830msgid "Lock Screen Password"
877msgstr ""
831msgstr ""
878
832
879−#: qml/Wizard/Pages/password-set.qml:61
833+#: qml/Wizard/Pages/password-set.qml:62
880msgid "Enter at least 8 characters"
834msgid "Enter at least 8 characters"
881msgstr ""
835msgstr ""
882
836
883−#: qml/Wizard/Pages/password-set.qml:73
837+#: qml/Wizard/Pages/password-set.qml:74
884msgid "Choose password"
838msgid "Choose password"
885msgstr ""
839msgstr ""
886
840
887−#: qml/Wizard/Pages/password-set.qml:103
841+#: qml/Wizard/Pages/password-set.qml:104
888msgid "Confirm password"
842msgid "Confirm password"
889msgstr ""
843msgstr ""
890
844
qml/Components/Carousel.qml+1−2
@@ -17,6 +17,7 @@
17import QtQuick 2.4
17import QtQuick 2.4
18import Ubuntu.Components 1.3
18import Ubuntu.Components 1.3
19import "carousel.js" as CarouselJS
19import "carousel.js" as CarouselJS
20+import "."
20
21
21/*! The Carousel component presents the items of a model in a carousel view. It's similar to a
22/*! The Carousel component presents the items of a model in a carousel view. It's similar to a
22 cover flow. But it stops at it's boundaries (therefore no PathView is used).
23 cover flow. But it stops at it's boundaries (therefore no PathView is used).
@@ -141,8 +142,6 @@ Item {
141
142
142 boundsBehavior: Flickable.DragOverBounds
143 boundsBehavior: Flickable.DragOverBounds
143 cacheBuffer: carousel.cacheBuffer
144 cacheBuffer: carousel.cacheBuffer
144− flickDeceleration: Math.max(1500 * Math.pow(realWidth / referenceWidth, 1.5), 1500) // 1500 is platform default
145− maximumFlickVelocity: Math.max(2500 * Math.pow(realWidth / referenceWidth, 1.5), 2500) // 2500 is platform default
146 orientation: ListView.Horizontal
145 orientation: ListView.Horizontal
147
146
148 function getXFromContinuousIndex(index) {
147 function getXFromContinuousIndex(index) {
qml/Components/Flickable.qmlnew file+24
@@ -0,0 +1,24 @@
1+/*
2+ * Copyright (C) 2016 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 as QtQuick
18+import Ubuntu.Components 1.3
19+import "flickableUtils.js" as FlickableUtilsJS
20+
21+QtQuick.Flickable {
22+ flickDeceleration: FlickableUtilsJS.getFlickDeceleration(units.gridUnit)
23+ maximumFlickVelocity: FlickableUtilsJS.getMaximumFlickVelocity(units.gridUnit)
24+}
qml/Components/FloatingFlickable.qml+1
@@ -17,6 +17,7 @@
17import QtQuick 2.4
17import QtQuick 2.4
18import Ubuntu.Components 1.3
18import Ubuntu.Components 1.3
19import Ubuntu.Gestures 0.1
19import Ubuntu.Gestures 0.1
20+import "."
20
21
21/*
22/*
22 A Flickable that can be put in front of the item to be flicked and
23 A Flickable that can be put in front of the item to be flicked and
qml/Components/GridView.qmlnew file+24
@@ -0,0 +1,24 @@
1+/*
2+ * Copyright (C) 2016 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 as QtQuick
18+import Ubuntu.Components 1.3
19+import "flickableUtils.js" as FlickableUtilsJS
20+
21+QtQuick.GridView {
22+ flickDeceleration: FlickableUtilsJS.getFlickDeceleration(units.gridUnit)
23+ maximumFlickVelocity: FlickableUtilsJS.getMaximumFlickVelocity(units.gridUnit)
24+}
qml/Components/ListView.qmlnew file+24
@@ -0,0 +1,24 @@
1+/*
2+ * Copyright (C) 2016 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 as QtQuick
18+import Ubuntu.Components 1.3
19+import "flickableUtils.js" as FlickableUtilsJS
20+
21+QtQuick.ListView {
22+ flickDeceleration: FlickableUtilsJS.getFlickDeceleration(units.gridUnit)
23+ maximumFlickVelocity: FlickableUtilsJS.getMaximumFlickVelocity(units.gridUnit)
24+}
qml/Components/Lockscreen.qml+1−28
@@ -41,9 +41,6 @@ Showable {
41 // The text to be displayed in case the login failed
41 // The text to be displayed in case the login failed
42 property string errorText: ""
42 property string errorText: ""
43
43
44− // If > 0, a forced delay is happening
45− property int delayMinutes: 0
46−
47 // Set those to a value greater 0 to restrict the pin length.
44 // Set those to a value greater 0 to restrict the pin length.
48 // If both are unset, the Lockscreen will show a confirm button and allow typing any length of pin before
45 // If both are unset, the Lockscreen will show a confirm button and allow typing any length of pin before
49 // confirming. If minPinLength is set to a value > 0, the confirm button will only become active when the
46 // confirming. If minPinLength is set to a value > 0, the confirm button will only become active when the
@@ -91,14 +88,6 @@ Showable {
91 popup.z = Number.MAX_VALUE
88 popup.z = Number.MAX_VALUE
92 }
89 }
93
90
94− function showText(text) {
95− // Programmatically enters the given text into the lockscreen
96− if (pinPadLoader.item) {
97− pinPadLoader.item.showText(text);
98− pinPadLoader.waiting = true;
99− }
100− }
101−
102 Rectangle {
91 Rectangle {
103 // In case background fails to load
92 // In case background fails to load
104 id: backgroundBackup
93 id: backgroundBackup
@@ -107,17 +96,13 @@ Showable {
107 visible: root.background.toString() !== ""
96 visible: root.background.toString() !== ""
108 }
97 }
109
98
110− Image {
99+ Wallpaper {
111 id: backgroundImage
100 id: backgroundImage
112 objectName: "lockscreenBackground"
101 objectName: "lockscreenBackground"
113 anchors {
102 anchors {
114 fill: parent
103 fill: parent
115 }
104 }
116− // Limit how much memory we'll reserve for this image
117− sourceSize.height: height
118− sourceSize.width: width
119 source: root.required ? root.background : ""
105 source: root.required ? root.background : ""
120− fillMode: Image.PreserveAspectCrop
121 }
106 }
122
107
123 // This is to
108 // This is to
@@ -141,8 +126,6 @@ Showable {
141 source: {
126 source: {
142 if (resetting || !root.required) {
127 if (resetting || !root.required) {
143 return ""
128 return ""
144− } else if (root.delayMinutes > 0) {
145− return "DelayedLockscreen.qml"
146 } else if (root.alphaNumeric) {
129 } else if (root.alphaNumeric) {
147 return "PassphraseLockscreen.qml"
130 return "PassphraseLockscreen.qml"
148 } else {
131 } else {
@@ -197,16 +180,6 @@ Showable {
197 property: "entryEnabled"
180 property: "entryEnabled"
198 value: !pinPadLoader.waiting
181 value: !pinPadLoader.waiting
199 }
182 }
200− Binding {
201− target: pinPadLoader.item
202− property: "alphaNumeric"
203− value: root.alphaNumeric
204− }
205− Binding {
206− target: pinPadLoader.item
207− property: "delayMinutes"
208− value: root.delayMinutes
209− }
210 Binding {
183 Binding {
211 target: pinPadLoader.item
184 target: pinPadLoader.item
212 property: "showCancelButton"
185 property: "showCancelButton"
qml/Components/PanelState/PanelState.qml−1
@@ -30,5 +30,4 @@ QtObject {
30 signal closeClicked()
30 signal closeClicked()
31 signal minimizeClicked()
31 signal minimizeClicked()
32 signal restoreClicked()
32 signal restoreClicked()
33− signal focusMaximizedApp()
34}
33}
qml/Components/PassphraseLockscreen.qml−4
@@ -43,10 +43,6 @@ FocusScope {
43 }
43 }
44 }
44 }
45
45
46− function showText(text) {
47− pinentryField.text = text;
48− }
49−
50 Column {
46 Column {
51 id: shakeContainer
47 id: shakeContainer
52 anchors.horizontalCenter: parent.horizontalCenter
48 anchors.horizontalCenter: parent.horizontalCenter
qml/Components/PinLockscreen.qml−4
@@ -46,10 +46,6 @@ FocusScope {
46 }
46 }
47 }
47 }
48
48
49− function showText(text) {
50− pinentryField.text = text;
51− }
52−
53 Keys.onPressed: {
49 Keys.onPressed: {
54 if (pinentryField.text.length == root.maxPinLength)
50 if (pinentryField.text.length == root.maxPinLength)
55 return;
51 return;
qml/Components/ResponsiveGridView.qml+1
@@ -16,6 +16,7 @@
16
16
17import QtQuick 2.4
17import QtQuick 2.4
18import Ubuntu.Components 1.3
18import Ubuntu.Components 1.3
19+import "."
19
20
20/*
21/*
21 Essentially a GridView
22 Essentially a GridView
qml/Components/VolumeControl.qml+3−3
@@ -17,6 +17,7 @@
17import QtQuick 2.4
17import QtQuick 2.4
18import QMenuModel 0.1 as QMenuModel
18import QMenuModel 0.1 as QMenuModel
19import GlobalShortcut 1.0
19import GlobalShortcut 1.0
20+import Unity.Platform 1.0
20
21
21Item {
22Item {
22 id: root
23 id: root
@@ -27,8 +28,6 @@ Item {
27 readonly property int stepUp: 1
28 readonly property int stepUp: 1
28 readonly property int stepDown: -1
29 readonly property int stepDown: -1
29
30
30− property var indicators // passed from Shell.qml
31−
32 GlobalShortcut {
31 GlobalShortcut {
33 id: muteShortcut
32 id: muteShortcut
34 shortcut: Qt.Key_VolumeMute
33 shortcut: Qt.Key_VolumeMute
@@ -42,7 +41,8 @@ Item {
42 objectPath: "/com/canonical/indicator/sound"
41 objectPath: "/com/canonical/indicator/sound"
43
42
44 property variant actionObject: action("volume")
43 property variant actionObject: action("volume")
44+ // temporary workaround for lp:1623166
45− property variant muteActionObject: indicators.indicatorsModel.profile === "desktop" ? action("mute") : action("silent-mode")
45+ property variant muteActionObject: Platform.isPC ? action("mute") : action("silent-mode")
46 }
46 }
47
47
48 function volumeUp() {
48 function volumeUp() {
qml/Components/Wallpaper.qmlnew file+44
@@ -0,0 +1,44 @@
1+/*
2+ * Copyright (C) 2013-2016 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 QtQuick.Window 2.2
19+import Ubuntu.Components 1.3
20+
21+Item {
22+ id: root
23+ property url source
24+
25+ CrossFadeImage {
26+ anchors.fill: parent
27+ fillMode: Image.PreserveAspectCrop
28+
29+ // Limit how much memory we reserve and avoid reloading when item size
30+ // changes or is rotated by specifying sourceSize.
31+ //
32+ // FIXME: If the source image has a portrait aspect ratio, we should swap
33+ // sourceSize.width and sourceSize.height to prevent blurriness from double
34+ // scaling. We could easily do that with a tiny image loader to check
35+ // the aspect ratio first, but when we change sourceSize, we lose all
36+ // the benefits of CrossFadeImage. So we need to fix that component
37+ // first to gracefully handle sourceSize changes (LP: #1599203).
38+ readonly property int maxSize: Math.max(Screen.width, Screen.height)
39+ sourceSize.width: 0
40+ sourceSize.height: maxSize
41+
42+ source: root.source
43+ }
44+}
qml/Components/WallpaperResolver.qml+24−38
@@ -15,49 +15,35 @@
15 */
15 */
16
16
17import QtQuick 2.4
17import QtQuick 2.4
18−import AccountsService 0.1
19−import GSettings 1.0
20import Ubuntu.Components 1.3
18import Ubuntu.Components 1.3
21
19
22−/*
20+Item {
23− Defines the background URL based on several factors, such as:
21+ id: root
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
22
35− // That's the property users of this component are going to consume.
23+ // Provide a list of wallpapers to resolve here, preferred ones first
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 {
24+ property var candidates: []
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
25
49− // This is a dummy image to detect if the custom GSettings set wallpaper loads successfully.
26+ readonly property url background: {
50− property var _gsImageTester: Image {
27+ for (var i = 0; i < repeater.count; i++) {
51− id: gsImageTester
28+ if (repeater.itemAt(i).status === Image.Ready)
52− source: backgroundSettings.pictureUri && backgroundSettings.pictureUri.length > 0 ? backgroundSettings.pictureUri : ""
29+ return candidates[i];
53− height: 0
30+ }
54− width: 0
31+ if (i > 0) {
55− sourceSize.height: 0
32+ return candidates[i - 1]; // last item is last resort
56− sourceSize.width: 0
33+ } else {
34+ return "";
35+ }
57 }
36 }
58
37
59− property var _gsettings: GSettings {
38+ Repeater {
60− id: backgroundSettings
39+ id: repeater
61− schema.id: "org.gnome.desktop.background"
40+ model: root.candidates
41+ delegate: Image {
42+ source: modelData
43+ height: 0
44+ width: 0
45+ sourceSize.height: 0
46+ sourceSize.width: 0
47+ }
62 }
48 }
63}
49}
qml/Components/WindowControlButtons.qml+7−5
@@ -19,13 +19,12 @@ import Ubuntu.Components 1.3
19
19
20Row {
20Row {
21 id: root
21 id: root
22− spacing: overlayShown ? units.gu(2) : units.gu(1)
22+ spacing: overlayShown ? units.gu(2) : windowIsMaximized ? 0 : units.gu(1)
23 Behavior on spacing {
23 Behavior on spacing {
24 UbuntuNumberAnimation {}
24 UbuntuNumberAnimation {}
25 }
25 }
26
26
27 // to be set from outside
27 // to be set from outside
28− property Item target
29 property bool active: false
28 property bool active: false
30 property bool windowIsMaximized: false
29 property bool windowIsMaximized: false
31 property bool closeButtonShown: true
30 property bool closeButtonShown: true
@@ -49,13 +48,14 @@ Row {
49
48
50 Rectangle {
49 Rectangle {
51 anchors.fill: parent
50 anchors.fill: parent
51+ anchors.margins: windowIsMaximized ? units.dp(3) : 0
52 radius: height / 2
52 radius: height / 2
53 color: theme.palette.normal.negative
53 color: theme.palette.normal.negative
54 visible: parent.containsMouse && !overlayShown
54 visible: parent.containsMouse && !overlayShown
55 }
55 }
56 Icon {
56 Icon {
57 anchors.fill: parent
57 anchors.fill: parent
58− anchors.margins: units.dp(3)
58+ anchors.margins: windowIsMaximized ? units.dp(6) : units.dp(3)
59 source: "graphics/window-close.svg"
59 source: "graphics/window-close.svg"
60 color: root.active ? "white" : UbuntuColors.slate
60 color: root.active ? "white" : UbuntuColors.slate
61 }
61 }
@@ -71,13 +71,14 @@ Row {
71
71
72 Rectangle {
72 Rectangle {
73 anchors.fill: parent
73 anchors.fill: parent
74+ anchors.margins: windowIsMaximized ? units.dp(3) : 0
74 radius: height / 2
75 radius: height / 2
75 color: root.active ? UbuntuColors.graphite : UbuntuColors.ash
76 color: root.active ? UbuntuColors.graphite : UbuntuColors.ash
76 visible: parent.containsMouse && !overlayShown
77 visible: parent.containsMouse && !overlayShown
77 }
78 }
78 Icon {
79 Icon {
79 anchors.fill: parent
80 anchors.fill: parent
80− anchors.margins: units.dp(3)
81+ anchors.margins: windowIsMaximized ? units.dp(6) : units.dp(3)
81 source: "graphics/window-minimize.svg"
82 source: "graphics/window-minimize.svg"
82 color: root.active ? "white" : UbuntuColors.slate
83 color: root.active ? "white" : UbuntuColors.slate
83 }
84 }
@@ -103,13 +104,14 @@ Row {
103
104
104 Rectangle {
105 Rectangle {
105 anchors.fill: parent
106 anchors.fill: parent
107+ anchors.margins: windowIsMaximized ? units.dp(3) : 0
106 radius: height / 2
108 radius: height / 2
107 color: root.active ? UbuntuColors.graphite : UbuntuColors.ash
109 color: root.active ? UbuntuColors.graphite : UbuntuColors.ash
108 visible: parent.containsMouse && !overlayShown
110 visible: parent.containsMouse && !overlayShown
109 }
111 }
110 Icon {
112 Icon {
111 anchors.fill: parent
113 anchors.fill: parent
112− anchors.margins: units.dp(3)
114+ anchors.margins: windowIsMaximized ? units.dp(6) : units.dp(3)
113 source: root.windowIsMaximized ? "graphics/window-window.svg" : "graphics/window-maximize.svg"
115 source: root.windowIsMaximized ? "graphics/window-window.svg" : "graphics/window-maximize.svg"
114 color: root.active ? "white" : UbuntuColors.slate
116 color: root.active ? "white" : UbuntuColors.slate
115 }
117 }
qml/Components/ZoomableImage.qml+1−1
@@ -16,7 +16,7 @@
16
16
17import QtQuick 2.4
17import QtQuick 2.4
18import Ubuntu.Components 1.3
18import Ubuntu.Components 1.3
19−import "../Components"
19+import "."
20
20
21/*! \brief Zoomable for image.
21/*! \brief Zoomable for image.
22
22
qml/Components/flickableUtils.jsnew file+28
@@ -0,0 +1,28 @@
1+/*
2+ * Copyright (C) 2016 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+.pragma library
18+
19+/* TODO the "8" is is DEFAULT_GRID_UNIT_PX, we use it to normalize the values with the current grid unit size.
20+ Add a way to fetch this default grid unit px value from UITK. */
21+
22+function getFlickDeceleration(gridUnit) {
23+ return 1500 * gridUnit / 8;
24+}
25+
26+function getMaximumFlickVelocity(gridUnit) {
27+ return 2500 * gridUnit / 8;
28+}
qml/Dash/CardTool.qml+14−2
@@ -71,7 +71,17 @@ Item {
71 // Not readonly because gets overwritten from GenericScopeView in some cases
71 // Not readonly because gets overwritten from GenericScopeView in some cases
72 property string artShapeStyle: categoryLayout === "carousel" ? "shadow" : "inset"
72 property string artShapeStyle: categoryLayout === "carousel" ? "shadow" : "inset"
73
73
74+ // FIXME ? This seems like it should not be needed, but on Qt 5.4 + phone
75+ // we are doing unneeded calls to getCardComponent with artShapeStyle and categoryLayout being empty
76+ // Check when we move to newer Qts on the phone if we still need this
77+ readonly property bool askForCardComponent: cardTool.template !== undefined &&
78+ cardTool.components !== undefined &&
79+ cardTool.artShapeStyle !== "" &&
80+ cardTool.categoryLayout !== ""
81+
82+ property var cardComponent: askForCardComponent
74− property var cardComponent: CardCreatorCache.getCardComponent(cardTool.template, cardTool.components, false, cardTool.artShapeStyle, cardTool.categoryLayout);
83+ ? CardCreatorCache.getCardComponent(cardTool.template, cardTool.components, false, cardTool.artShapeStyle, cardTool.categoryLayout)
84+ : undefined
75
85
76 // FIXME: Saviq
86 // FIXME: Saviq
77 // Only way for the card below to actually be laid out completely.
87 // Only way for the card below to actually be laid out completely.
@@ -213,7 +223,9 @@ Item {
213 "attributes": attributesModel.model,
223 "attributes": attributesModel.model,
214 "socialActions": socialActionsModel.model
224 "socialActions": socialActionsModel.model
215 }
225 }
226+ sourceComponent: askForCardComponent
216− sourceComponent: CardCreatorCache.getCardComponent(cardTool.template, cardTool.components, true, cardTool.artShapeStyle, cardTool.categoryLayout);
227+ ? CardCreatorCache.getCardComponent(cardTool.template, cardTool.components, true, cardTool.artShapeStyle, cardTool.categoryLayout)
228+ : undefined
217 onLoaded: {
229 onLoaded: {
218 item.objectName = "cardToolCard";
230 item.objectName = "cardToolCard";
219 item.width = Qt.binding(function() { return cardTool.cardWidth !== -1 ? cardTool.cardWidth : item.implicitWidth; });
231 item.width = Qt.binding(function() { return cardTool.cardWidth !== -1 ? cardTool.cardWidth : item.implicitWidth; });
qml/Dash/DashBackground.qml+2−2
@@ -16,10 +16,10 @@
16
16
17import QtQuick 2.4
17import QtQuick 2.4
18import QtQuick.Window 2.2
18import QtQuick.Window 2.2
19−import Ubuntu.Thumbnailer 0.1
19+import ImageCache 0.1
20
20
21Image {
21Image {
22− source: "image://thumbnailer/" + Qt.resolvedUrl("graphics/paper.png")
22+ source: "image://unity8imagecache/" + Qt.resolvedUrl("graphics/paper.png")
23 fillMode: Image.PreserveAspectCrop
23 fillMode: Image.PreserveAspectCrop
24 horizontalAlignment: Image.AlignRight
24 horizontalAlignment: Image.AlignRight
25 verticalAlignment: Image.AlignTop
25 verticalAlignment: Image.AlignTop
qml/Dash/DashContent.qml−2
@@ -112,8 +112,6 @@ Item {
112 anchors.fill: parent
112 anchors.fill: parent
113 orientation: ListView.Horizontal
113 orientation: ListView.Horizontal
114 boundsBehavior: Flickable.DragAndOvershootBounds
114 boundsBehavior: Flickable.DragAndOvershootBounds
115− flickDeceleration: units.gu(625)
116− maximumFlickVelocity: width * 5
117 snapMode: ListView.SnapOneItem
115 snapMode: ListView.SnapOneItem
118 highlightMoveDuration: 250
116 highlightMoveDuration: 250
119 highlightRangeMode: ListView.StrictlyEnforceRange
117 highlightRangeMode: ListView.StrictlyEnforceRange
qml/Dash/DashNavigation.qml+1
@@ -17,6 +17,7 @@
17import QtQuick 2.4
17import QtQuick 2.4
18import Ubuntu.Components 1.3
18import Ubuntu.Components 1.3
19import Dash 0.1
19import Dash 0.1
20+import "../Components"
20
21
21Item {
22Item {
22 id: root
23 id: root
qml/Dash/DashPageHeader.qml+2
@@ -18,6 +18,7 @@ import QtQuick 2.4
18import Ubuntu.Components 1.3
18import Ubuntu.Components 1.3
19import Ubuntu.Components.Popups 1.3
19import Ubuntu.Components.Popups 1.3
20import Ubuntu.Components.ListItems 1.3
20import Ubuntu.Components.ListItems 1.3
21+import Utils 0.1
21import "../Components"
22import "../Components"
22
23
23Item {
24Item {
@@ -25,6 +26,7 @@ Item {
25 objectName: "pageHeader"
26 objectName: "pageHeader"
26 implicitHeight: headerContainer.height + signatureLineHeight
27 implicitHeight: headerContainer.height + signatureLineHeight
27 readonly property real signatureLineHeight: showSignatureLine ? units.gu(2) : 0
28 readonly property real signatureLineHeight: showSignatureLine ? units.gu(2) : 0
29+ readonly property real headerDividerLuminance: Style.luminance(bottomBorder.color)
28
30
29 property int activeFiltersCount: 0
31 property int activeFiltersCount: 0
30 property bool scopeHasFilters: false
32 property bool scopeHasFilters: false
qml/Dash/FiltersPopover.qml+1
@@ -19,6 +19,7 @@ import Ubuntu.Components 1.3
19import Ubuntu.Components.Popups 1.3
19import Ubuntu.Components.Popups 1.3
20import Ubuntu.Components.ListItems 1.3 as ListItems
20import Ubuntu.Components.ListItems 1.3 as ListItems
21import "Filters" as Filters
21import "Filters" as Filters
22+import "../Components"
22
23
23Popover {
24Popover {
24 id: root
25 id: root
qml/Dash/GenericScopeView.qml+16
@@ -23,6 +23,7 @@ import Unity 0.2
23import Dash 0.1
23import Dash 0.1
24import "../Components"
24import "../Components"
25import "../Components/ListItems" as ListItems
25import "../Components/ListItems" as ListItems
26+import "Previews/PreviewSingleton"
26
27
27FocusScope {
28FocusScope {
28 id: scopeView
29 id: scopeView
@@ -722,6 +723,7 @@ FocusScope {
722 target: categoryView
723 target: categoryView
723
724
724 readonly property real contentY: categoryView.contentY - categoryView.originY
725 readonly property real contentY: categoryView.contentY - categoryView.originY
726+ readonly property real headerDividerLuminance: categoryView.pageHeader.headerDividerLuminance
725 y: -contentY - units.gu(5)
727 y: -contentY - units.gu(5)
726
728
727 onRefresh: {
729 onRefresh: {
@@ -787,6 +789,17 @@ FocusScope {
787 color: scopeStyle ? scopeStyle.foreground : theme.palette.normal.baseText
789 color: scopeStyle ? scopeStyle.foreground : theme.palette.normal.baseText
788 }
790 }
789
791
792+ Image {
793+ anchors {
794+ bottom: parent.bottom
795+ left: parent.left
796+ right: parent.right
797+ }
798+ fillMode: Image.Stretch
799+ source: "graphics/dash_divider_top_darkgrad.png"
800+ z: -1
801+ }
802+
790 Connections {
803 Connections {
791 target: floatingSeeLess.companionTo ? categoryView : null
804 target: floatingSeeLess.companionTo ? categoryView : null
792 onContentYChanged: floatingSeeLess.updateVisibility();
805 onContentYChanged: floatingSeeLess.updateVisibility();
@@ -824,6 +837,9 @@ FocusScope {
824 }
837 }
825
838
826 function closeSubPage() {
839 function closeSubPage() {
840+ if (subPage == "preview") {
841+ PreviewSingleton.widgetExtraData = new Object();
842+ }
827 open = false;
843 open = false;
828 }
844 }
829
845
qml/Dash/PageHeaderExtraPanel.qml+2
@@ -18,6 +18,7 @@ import QtQuick 2.4
18import Ubuntu.Components 1.3
18import Ubuntu.Components 1.3
19import Ubuntu.Components.ListItems 1.3 as ListItems
19import Ubuntu.Components.ListItems 1.3 as ListItems
20import "Filters" as Filters
20import "Filters" as Filters
21+import "../Components"
21
22
22Item {
23Item {
23 id: root
24 id: root
@@ -123,6 +124,7 @@ Item {
123 }
124 }
124 text: query
125 text: query
125 color: "#888888"
126 color: "#888888"
127+ elide: Text.ElideRight
126 }
128 }
127
129
128 divider.visible: index != recentSearchesRepeater.count - 1 || (scope && scope.hasNavigation) || primaryFilter.active
130 divider.visible: index != recentSearchesRepeater.count - 1 || (scope && scope.hasNavigation) || primaryFilter.active
qml/Dash/Previews/PreviewCommentInput.qml+15
@@ -17,6 +17,7 @@
17import QtQuick 2.4
17import QtQuick 2.4
18import Ubuntu.Components 1.3
18import Ubuntu.Components 1.3
19import "../../Components"
19import "../../Components"
20+import "PreviewSingleton"
20
21
21/*! \brief Preview widget for commenting.
22/*! \brief Preview widget for commenting.
22
23
@@ -35,6 +36,20 @@ PreviewWidget {
35 triggered(root.widgetId, "commented", data);
36 triggered(root.widgetId, "commented", data);
36 }
37 }
37
38
39+ property alias commentText: commentTextArea.text
40+
41+ onCommentTextChanged: storeCommentState()
42+ onWidgetIdChanged: restoreCommentState()
43+
44+ function storeCommentState() {
45+ PreviewSingleton.widgetExtraData[widgetId] = commentText;
46+ }
47+
48+ function restoreCommentState() {
49+ if (!PreviewSingleton.widgetExtraData[widgetId]) return;
50+ if (PreviewSingleton.widgetExtraData[widgetId] != "") commentText = PreviewSingleton.widgetExtraData[widgetId];
51+ }
52+
38 TextArea {
53 TextArea {
39 id: commentTextArea
54 id: commentTextArea
40 objectName: "commentTextArea"
55 objectName: "commentTextArea"
qml/Dash/Previews/PreviewInlineVideo.qml+47
@@ -18,6 +18,7 @@ import QtQuick 2.4
18import QtMultimedia 5.0
18import QtMultimedia 5.0
19import Ubuntu.Components 1.3
19import Ubuntu.Components 1.3
20import Ubuntu.Thumbnailer 0.1
20import Ubuntu.Thumbnailer 0.1
21+import "PreviewSingleton"
21import "../../Components"
22import "../../Components"
22import "../../Components/MediaServices"
23import "../../Components/MediaServices"
23
24
@@ -36,9 +37,35 @@ PreviewWidget {
36 orientationLock: services.fullscreen
37 orientationLock: services.fullscreen
37
38
38 property alias rootItem: services.rootItem
39 property alias rootItem: services.rootItem
40+ readonly property string widgetExtraDataKey: services.source.toString()
41+
42+ onWidgetExtraDataKeyChanged: {
43+ root.restorePlaybackState();
44+ }
45+
46+ function seek() {
47+ services.mediaPlayer.seek(services.initialPosition);
48+ services.initialPosition = -1;
49+ }
50+
51+ function storePlaybackState() {
52+ if ((services.mediaPlayer.duration - services.mediaPlayer.position) < 1000) {
53+ // we're at the end of the video
54+ PreviewSingleton.widgetExtraData[widgetExtraDataKey] = 0;
55+ } else {
56+ PreviewSingleton.widgetExtraData[widgetExtraDataKey] = services.mediaPlayer.position;
57+ }
58+ }
59+
60+ function restorePlaybackState() {
61+ if (PreviewSingleton.widgetExtraData[widgetExtraDataKey] > 0) {
62+ services.initialPosition = PreviewSingleton.widgetExtraData[widgetExtraDataKey];
63+ }
64+ }
39
65
40 MediaServices {
66 MediaServices {
41 id: services
67 id: services
68+ objectName: "services"
42 width: parent.width
69 width: parent.width
43
70
44 actions: sharingAction
71 actions: sharingAction
@@ -47,8 +74,28 @@ PreviewWidget {
47 fullscreen: false
74 fullscreen: false
48 maximumEmbeddedHeight: rootItem.height / 2
75 maximumEmbeddedHeight: rootItem.height / 2
49
76
77+ property int initialPosition: -1
78+
79+ readonly property var mediaPlayer: footer.mediaPlayer
80+ readonly property url source: mediaPlayer.source
81+ readonly property int position: mediaPlayer.position
82+
50 onClose: fullscreen = false
83 onClose: fullscreen = false
51
84
85+ onPositionChanged: {
86+ if (mediaPlayer.playbackState === MediaPlayer.StoppedState) return;
87+ root.storePlaybackState();
88+ }
89+
90+ Connections {
91+ target: services.mediaPlayer
92+ ignoreUnknownSignals: true
93+ onPlaying: {
94+ // at the first playback of the file, do a seek()
95+ if (services.initialPosition > 0) root.seek();
96+ }
97+ }
98+
52 Action {
99 Action {
53 id: sharingAction
100 id: sharingAction
54 iconName: "share"
101 iconName: "share"
qml/Dash/Previews/PreviewProgress.qml+5−1
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2014 Canonical, Ltd.
2+ * Copyright (C) 2014-2016 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
@@ -59,6 +59,10 @@ PreviewWidget {
59 }
59 }
60 }
60 }
61
61
62+ onProcessing: {
63+ progressBar.indeterminate = true;
64+ }
65+
62 onFinished: {
66 onFinished: {
63 root.triggered(widgetId, "finished", widgetData)
67 root.triggered(widgetId, "finished", widgetData)
64 }
68 }
qml/Dash/Previews/PreviewRatingInput.qml+27−1
@@ -17,6 +17,7 @@
17import QtQuick 2.4
17import QtQuick 2.4
18import Ubuntu.Components 1.3
18import Ubuntu.Components 1.3
19import "../../Components"
19import "../../Components"
20+import "PreviewSingleton"
20
21
21/*! \brief Preview widget for rating.
22/*! \brief Preview widget for rating.
22
23
@@ -53,6 +54,31 @@ PreviewWidget {
53 property alias ratingValue: rating.value
54 property alias ratingValue: rating.value
54 property alias reviewText: reviewTextArea.text
55 property alias reviewText: reviewTextArea.text
55
56
57+ onRatingValueChanged: storeRatingState()
58+ onReviewTextChanged: storeTextState()
59+ onWidgetIdChanged: restoreReviewState()
60+
61+ function initializeWidgetExtraData() {
62+ if (typeof(PreviewSingleton.widgetExtraData[widgetId]) == "undefined") PreviewSingleton.widgetExtraData[widgetId] = [];
63+ }
64+
65+ function storeRatingState() {
66+ initializeWidgetExtraData();
67+ PreviewSingleton.widgetExtraData[widgetId][0] = ratingValue;
68+ }
69+
70+ function storeTextState() {
71+ initializeWidgetExtraData();
72+ PreviewSingleton.widgetExtraData[widgetId][1] = reviewText;
73+ }
74+
75+ function restoreReviewState() {
76+ if (!PreviewSingleton.widgetExtraData[widgetId]) return;
77+ if (PreviewSingleton.widgetExtraData[widgetId][0] > 0) ratingValue = PreviewSingleton.widgetExtraData[widgetId][0];
78+ if (typeof(PreviewSingleton.widgetExtraData[widgetId][1]) != "undefined" &&
79+ PreviewSingleton.widgetExtraData[widgetId][1] != "") reviewText = PreviewSingleton.widgetExtraData[widgetId][1];
80+ }
81+
56 function submit() {
82 function submit() {
57 // checks rating-input requirements
83 // checks rating-input requirements
58 if (((widgetData["required"] === "both" ||
84 if (((widgetData["required"] === "both" ||
@@ -89,7 +115,7 @@ PreviewWidget {
89 size: 5
115 size: 5
90 height: units.gu(4)
116 height: units.gu(4)
91 onValueChanged: {
117 onValueChanged: {
92− if (widgetData["visible"] === "rating") root.submit();
118+ if (widgetData && widgetData["visible"] === "rating") root.submit();
93 }
119 }
94
120
95 property var urlIconEmpty: widgetData["rating-icon-empty"] || "image://theme/non-starred"
121 property var urlIconEmpty: widgetData["rating-icon-empty"] || "image://theme/non-starred"
qml/Dash/Previews/PreviewSingleton/PreviewSingleton.qmlnew file+22
@@ -0,0 +1,22 @@
1+/*
2+ * Copyright (C) 2016 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+pragma Singleton
18+import QtQuick 2.4
19+
20+QtObject {
21+ property var widgetExtraData: new Object();
22+}
qml/Dash/Previews/PreviewSingleton/qmldirnew file+1
@@ -0,0 +1 @@
1+singleton PreviewSingleton 1.0 PreviewSingleton.qml
qml/Dash/PullToRefreshScopeStyle.qml+1
@@ -48,6 +48,7 @@ PullToRefreshStyle {
48 anchors.horizontalCenter: parent.horizontalCenter
48 anchors.horizontalCenter: parent.horizontalCenter
49 horizontalAlignment: Text.AlignHCenter
49 horizontalAlignment: Text.AlignHCenter
50 verticalAlignment: Text.AlignVCenter
50 verticalAlignment: Text.AlignVCenter
51+ color: styledItem.headerDividerLuminance < 0.3 ? UbuntuColors.porcelain : UbuntuColors.jet
51 states: [
52 states: [
52 State {
53 State {
53 name: "pulling"
54 name: "pulling"
qml/Dash/ScopeListView.qml+4−2
@@ -15,11 +15,13 @@
15 */
15 */
16
16
17import QtQuick 2.4
17import QtQuick 2.4
18+import Ubuntu.Components 1.3
18import Dash 0.1
19import Dash 0.1
20+import "../Components/flickableUtils.js" as FlickableUtilsJS
19
21
20ListViewWithPageHeader {
22ListViewWithPageHeader {
23+ flickDeceleration: FlickableUtilsJS.getFlickDeceleration(units.gridUnit)
21− maximumFlickVelocity: height * 10
24+ maximumFlickVelocity: FlickableUtilsJS.getMaximumFlickVelocity(units.gridUnit)
22− flickDeceleration: height * 2
23 // 1073741823 is s^30 -1. A quite big number so that you have "infinite" cache, but not so
25 // 1073741823 is s^30 -1. A quite big number so that you have "infinite" cache, but not so
24 // big so that if you add if with itself you're outside the 2^31 int range
26 // big so that if you add if with itself you're outside the 2^31 int range
25 cacheBuffer: 1073741823
27 cacheBuffer: 1073741823
qml/Dash/ScopesList.qml+2
@@ -15,7 +15,9 @@
15 */
15 */
16
16
17import QtQuick 2.4
17import QtQuick 2.4
18+import Ubuntu.Components 1.3
18import Dash 0.1
19import Dash 0.1
20+import "../Components"
19
21
20Item {
22Item {
21 id: root
23 id: root
qml/Dash/ScopesListCategory.qml+1
@@ -17,6 +17,7 @@
17import QtQuick 2.4
17import QtQuick 2.4
18import Ubuntu.Components 1.3
18import Ubuntu.Components 1.3
19import Dash 0.1
19import Dash 0.1
20+import "../Components"
20import "../Components/ListItems" as ListItems
21import "../Components/ListItems" as ListItems
21
22
22Item {
23Item {
qml/Greeter/Circle.qmlnew file+57
@@ -0,0 +1,57 @@
1+/*
2+ * Copyright (C) 2013,2016 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+
19+Canvas {
20+ id: root
21+ property color color
22+ property real circleScale
23+ property var centerCircle
24+
25+ onColorChanged: requestPaint()
26+ onCircleScaleChanged: requestPaint()
27+ onCenterCircleChanged: requestPaint()
28+
29+ onPaint: {
30+ if (circleScale <= 0) {
31+ return;
32+ }
33+
34+ var ctx = getContext("2d");
35+
36+ // draw big circle
37+ ctx.save();
38+ ctx.fillStyle = color;
39+ ctx.beginPath();
40+ ctx.arc(width / 2, height / 2, circleScale * width / 2, 0, 2 * Math.PI, false);
41+ ctx.fill();
42+ ctx.restore();
43+
44+ // chop out inner infographics circle
45+ if (centerCircle) {
46+ var circleMiddle = mapFromItem(centerCircle,
47+ centerCircle.width / 2,
48+ centerCircle.height / 2);
49+ ctx.save();
50+ ctx.globalCompositeOperation = "destination-out";
51+ ctx.beginPath();
52+ ctx.arc(circleMiddle.x, circleMiddle.y, centerCircle.width / 2, 0, 2 * Math.PI, false);
53+ ctx.fill();
54+ ctx.restore();
55+ }
56+ }
57+}
qml/Greeter/Clock.qml−2
@@ -73,7 +73,6 @@ Item {
73 anchors.horizontalCenter: parent.horizontalCenter
73 anchors.horizontalCenter: parent.horizontalCenter
74 font.pixelSize: units.gu(7.5)
74 font.pixelSize: units.gu(7.5)
75 color: "white"
75 color: "white"
76− opacity: 0.5
77 text: Qt.formatTime(clock.currentDate)
76 text: Qt.formatTime(clock.currentDate)
78 font.weight: Font.Light
77 font.weight: Font.Light
79 }
78 }
@@ -85,7 +84,6 @@ Item {
85 anchors.horizontalCenter: parent.horizontalCenter
84 anchors.horizontalCenter: parent.horizontalCenter
86 fontSize: "medium"
85 fontSize: "medium"
87 color: "white"
86 color: "white"
88− opacity: 0.5
89 text: Qt.formatDate(clock.currentDate, Qt.DefaultLocaleLongDate)
87 text: Qt.formatDate(clock.currentDate, Qt.DefaultLocaleLongDate)
90 font.weight: Font.Light
88 font.weight: Font.Light
91 }
89 }
qml/Greeter/CoverPage.qml+6−6
@@ -25,13 +25,14 @@ Showable {
25 property real dragHandleLeftMargin
25 property real dragHandleLeftMargin
26 property real launcherOffset
26 property real launcherOffset
27 property alias background: greeterBackground.source
27 property alias background: greeterBackground.source
28+ property alias hasCustomBackground: backgroundShade.visible
28 property real backgroundTopMargin
29 property real backgroundTopMargin
29 property var infographicModel
30 property var infographicModel
30 property bool draggable: true
31 property bool draggable: true
31
32
32 property alias infographics: infographics
33 property alias infographics: infographics
33
34
34− readonly property real showProgress: MathUtils.clamp((width - Math.abs(x)) / width, 0, 1)
35+ readonly property real showProgress: MathUtils.clamp((width - Math.abs(x + launcherOffset)) / width, 0, 1)
35
36
36 signal tease()
37 signal tease()
37 signal clicked()
38 signal clicked()
@@ -82,24 +83,23 @@ Showable {
82 color: "black"
83 color: "black"
83 }
84 }
84
85
85− CrossFadeImage {
86+ Wallpaper {
86 id: greeterBackground
87 id: greeterBackground
87 objectName: "greeterBackground"
88 objectName: "greeterBackground"
88 anchors {
89 anchors {
89 fill: parent
90 fill: parent
90 topMargin: root.backgroundTopMargin
91 topMargin: root.backgroundTopMargin
91 }
92 }
92− fillMode: Image.PreserveAspectCrop
93− // Limit how much memory we'll reserve for this image
94− sourceSize.height: height
95− sourceSize.width: width
96 }
93 }
97
94
98 // Darkens wallpaper so that we can read text on it and see infographic
95 // Darkens wallpaper so that we can read text on it and see infographic
99 Rectangle {
96 Rectangle {
97+ id: backgroundShade
98+ objectName: "backgroundShade"
100 anchors.fill: parent
99 anchors.fill: parent
101 color: "black"
100 color: "black"
102 opacity: 0.4
101 opacity: 0.4
102+ visible: false
103 }
103 }
104
104
105 Infographics {
105 Infographics {
qml/Components/DelayedLockscreen.qml → qml/Greeter/DelayedLockscreen.qmlrenamed+6−15
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013,2014 Canonical, Ltd.
2+ * Copyright (C) 2013-2016 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
@@ -16,7 +16,6 @@
16
16
17import QtQuick 2.4
17import QtQuick 2.4
18import Ubuntu.Components 1.3
18import Ubuntu.Components 1.3
19−import "../Components"
20
19
21Item {
20Item {
22 id: root
21 id: root
@@ -24,12 +23,6 @@ Item {
24 property int delayMinutes
23 property int delayMinutes
25 property bool alphaNumeric
24 property bool alphaNumeric
26
25
27− signal entered(string passphrase) // unused
28− signal cancel() // unused
29−
30− function clear(playAnimation) {}
31− function showText(text) {}
32−
33 Column {
26 Column {
34 anchors.left: parent.left
27 anchors.left: parent.left
35 anchors.right: parent.right
28 anchors.right: parent.right
@@ -46,7 +39,7 @@ Item {
46 horizontalAlignment: Text.AlignHCenter
39 horizontalAlignment: Text.AlignHCenter
47 wrapMode: Text.Wrap
40 wrapMode: Text.Wrap
48 fontSize: "x-large"
41 fontSize: "x-large"
49− color: "#f3f3e7"
42+ color: "white"
50 text: i18n.tr("Device Locked")
43 text: i18n.tr("Device Locked")
51 }
44 }
52
45
@@ -60,7 +53,7 @@ Item {
60 anchors.right: parent.right
53 anchors.right: parent.right
61 horizontalAlignment: Text.AlignHCenter
54 horizontalAlignment: Text.AlignHCenter
62 wrapMode: Text.Wrap
55 wrapMode: Text.Wrap
63− color: "#f3f3e7"
56+ color: "white"
64 text: alphaNumeric ?
57 text: alphaNumeric ?
65 i18n.tr("You have been locked out due to too many failed passphrase attempts.") :
58 i18n.tr("You have been locked out due to too many failed passphrase attempts.") :
66 i18n.tr("You have been locked out due to too many failed passcode attempts.")
59 i18n.tr("You have been locked out due to too many failed passcode attempts.")
@@ -71,7 +64,7 @@ Item {
71 anchors.right: parent.right
64 anchors.right: parent.right
72 horizontalAlignment: Text.AlignHCenter
65 horizontalAlignment: Text.AlignHCenter
73 wrapMode: Text.Wrap
66 wrapMode: Text.Wrap
74− color: "#f3f3e7"
67+ color: "white"
75 text: i18n.tr("Please wait %1 minute and then try again…",
68 text: i18n.tr("Please wait %1 minute and then try again…",
76 "Please wait %1 minutes and then try again…",
69 "Please wait %1 minutes and then try again…",
77 root.delayMinutes).arg(root.delayMinutes)
70 root.delayMinutes).arg(root.delayMinutes)
@@ -83,10 +76,8 @@ Item {
83 }
76 }
84
77
85 Icon {
78 Icon {
86− // It would be nice to use a less network-specific name,
79+ name: "lock"
87− // but this is the only lock icon we have.
88− name: "network-secure"
89− color: "#f3f3e7"
80+ color: "white"
90 height: units.gu(4)
81 height: units.gu(4)
91 width: units.gu(4)
82 width: units.gu(4)
92 anchors.horizontalCenter: parent.horizontalCenter
83 anchors.horizontalCenter: parent.horizontalCenter
qml/Greeter/FullLightDMImpl.qml+2
@@ -22,6 +22,8 @@ Item{
22
22
23 property var greeter: LightDM.Greeter
23 property var greeter: LightDM.Greeter
24 property var infographic: LightDM.Infographic
24 property var infographic: LightDM.Infographic
25+ property var sessions: LightDM.Sessions
26+ property var sessionRoles: LightDM.SessionRoles
25 property var users: LightDM.Users
27 property var users: LightDM.Users
26 property var userRoles: LightDM.UserRoles
28 property var userRoles: LightDM.UserRoles
27
29
qml/Greeter/Greeter.qml+60−24
@@ -34,6 +34,7 @@ Showable {
34 property real dragHandleLeftMargin: 0
34 property real dragHandleLeftMargin: 0
35
35
36 property url background
36 property url background
37+ property bool hasCustomBackground
37
38
38 // How far to offset the top greeter layer during a launcher left-drag
39 // How far to offset the top greeter layer during a launcher left-drag
39 property real launcherOffset
40 property real launcherOffset
@@ -72,9 +73,12 @@ Showable {
72 }
73 }
73 forcedUnlock = false;
74 forcedUnlock = false;
74 if (required) {
75 if (required) {
76+ if (loader.item) {
77+ loader.item.reset(true /* forceShow */);
78+ }
75 // Normally loader.onLoaded will select a user, but if we're
79 // Normally loader.onLoaded will select a user, but if we're
76 // already shown, do it manually.
80 // already shown, do it manually.
77− d.selectUser(d.currentIndex, true);
81+ d.selectUser(d.currentIndex, false);
78 }
82 }
79
83
80 // Even though we may already be shown, we want to call show() for its
84 // Even though we may already be shown, we want to call show() for its
@@ -105,8 +109,8 @@ Showable {
105 }
109 }
106 }
110 }
107
111
108− // Notify that the user has explicitly requested the given app through unity8 GUI.
112+ // Notify that the user has explicitly requested an app
109− function notifyUserRequestedApp(appId) {
113+ function notifyUserRequestedApp() {
110 if (!active) {
114 if (!active) {
111 return;
115 return;
112 }
116 }
@@ -132,6 +136,22 @@ Showable {
132 return d.startUnlock(true /* toTheRight */);
136 return d.startUnlock(true /* toTheRight */);
133 }
137 }
134
138
139+ function sessionToStart() {
140+ for (var i = 0; i < LightDMService.sessions.count; i++) {
141+ var session = LightDMService.sessions.data(i,
142+ LightDMService.sessionRoles.KeyRole);
143+ if (loader.item.sessionToStart === session) {
144+ return session;
145+ }
146+ }
147+
148+ if (loader.item.sessionToStart === LightDMService.greeter.defaultSession) {
149+ return LightDMService.greeter.defaultSession;
150+ } else {
151+ return "ubuntu"; // The default / fallback
152+ }
153+ }
154+
135 QtObject {
155 QtObject {
136 id: d
156 id: d
137
157
@@ -178,7 +198,7 @@ Showable {
178 return;
198 return;
179 d.waiting = true;
199 d.waiting = true;
180 if (reset) {
200 if (reset) {
181− loader.item.reset();
201+ loader.item.reset(false /* forceShow */);
182 }
202 }
183 currentIndex = index;
203 currentIndex = index;
184 var user = LightDMService.users.data(index, LightDMService.userRoles.NameRole);
204 var user = LightDMService.users.data(index, LightDMService.userRoles.NameRole);
@@ -197,7 +217,7 @@ Showable {
197
217
198 function login() {
218 function login() {
199 d.waiting = true;
219 d.waiting = true;
200− if (LightDMService.greeter.startSessionSync()) {
220+ if (LightDMService.greeter.startSessionSync(root.sessionToStart())) {
201 sessionStarted();
221 sessionStarted();
202 hideView();
222 hideView();
203 } else if (loader.item) {
223 } else if (loader.item) {
@@ -222,6 +242,29 @@ Showable {
222 }
242 }
223 }
243 }
224 }
244 }
245+
246+ function showPromptMessage(text, isError) {
247+ // inefficient, but we only rarely deal with messages
248+ var html = text.replace(/&/g, "&amp;")
249+ .replace(/</g, "&lt;")
250+ .replace(/>/g, "&gt;")
251+ .replace(/\n/g, "<br>");
252+ if (isError) {
253+ html = "<font color=\"#df382c\">" + html + "</font>";
254+ }
255+
256+ if (loader.item) {
257+ loader.item.showMessage(html);
258+ }
259+ }
260+
261+ function showFingerprintMessage(msg) {
262+ if (loader.item) {
263+ loader.item.reset(false /* forceShow */);
264+ loader.item.showErrorMessage(msg);
265+ }
266+ showPromptMessage(msg, true);
267+ }
225 }
268 }
226
269
227 onLauncherOffsetChanged: {
270 onLauncherOffsetChanged: {
@@ -393,6 +436,12 @@ Showable {
393 value: root.background
436 value: root.background
394 }
437 }
395
438
439+ Binding {
440+ target: loader.item
441+ property: "hasCustomBackground"
442+ value: root.hasCustomBackground
443+ }
444+
396 Binding {
445 Binding {
397 target: loader.item
446 target: loader.item
398 property: "locked"
447 property: "locked"
@@ -436,20 +485,7 @@ Showable {
436 onShowGreeter: root.forceShow()
485 onShowGreeter: root.forceShow()
437 onHideGreeter: root.forcedUnlock = true
486 onHideGreeter: root.forcedUnlock = true
438
487
439− onShowMessage: {
488+ onShowMessage: d.showPromptMessage(text, isError)
440− // inefficient, but we only rarely deal with messages
441− var html = text.replace(/&/g, "&amp;")
442− .replace(/</g, "&lt;")
443− .replace(/>/g, "&gt;")
444− .replace(/\n/g, "<br>");
445− if (isError) {
446− html = "<font color=\"#df382c\">" + html + "</font>";
447− }
448−
449− if (loader.item) {
450− loader.item.showMessage(html);
451− }
452− }
453
489
454 onShowPrompt: {
490 onShowPrompt: {
455 if (loader.item) {
491 if (loader.item) {
@@ -557,10 +593,8 @@ Showable {
557 if (!d.secureFingerprint) {
593 if (!d.secureFingerprint) {
558 d.startUnlock(false /* toTheRight */); // use normal login instead
594 d.startUnlock(false /* toTheRight */); // use normal login instead
559 }
595 }
560− if (loader.item) {
561− var msg = d.secureFingerprint ? i18n.tr("Try again") : "";
596+ var msg = d.secureFingerprint ? i18n.tr("Try again") : "";
562− loader.item.showErrorMessage(msg);
597+ d.showFingerprintMessage(msg);
563− }
564 }
598 }
565
599
566 Component.onCompleted: restartOperation()
600 Component.onCompleted: restartOperation()
@@ -578,8 +612,10 @@ Showable {
578 return;
612 return;
579 }
613 }
580 console.log("Identified user by fingerprint:", result);
614 console.log("Identified user by fingerprint:", result);
581− if (loader.item)
615+ if (loader.item) {
616+ loader.item.enabled = false;
582 loader.item.notifyAuthenticationSucceeded(true /* showFakePassword */);
617 loader.item.notifyAuthenticationSucceeded(true /* showFakePassword */);
618+ }
583 if (root.active)
619 if (root.active)
584 root.forcedUnlock = true;
620 root.forcedUnlock = true;
585 }
621 }
qml/Greeter/GreeterPrompt.qml+40−9
@@ -38,6 +38,16 @@ FocusScope {
38 d.enabled = true;
38 d.enabled = true;
39 }
39 }
40
40
41+ function showFakePassword() {
42+ // Just a silly hack for looking like 4 pin numbers got entered, if
43+ // a fingerprint was used and we happen to be using a pin. This was
44+ // a request from Design.
45+ if (isSecret && isPrompt && !isAlphanumeric) {
46+ d.enabled = false;
47+ text = "...."; // actual text doesn't matter
48+ }
49+ }
50+
41 StyledItem {
51 StyledItem {
42 id: d
52 id: d
43
53
@@ -50,6 +60,12 @@ FocusScope {
50 : theme.palette.disabled.raisedSecondaryText
60 : theme.palette.disabled.raisedSecondaryText
51 readonly property color errorColor: passwordInput.enabled ? theme.palette.normal.negative
61 readonly property color errorColor: passwordInput.enabled ? theme.palette.normal.negative
52 : theme.palette.disabled.negative
62 : theme.palette.disabled.negative
63+
64+ onEnabledChanged: {
65+ if (!enabled) {
66+ fakeLabel.text = passwordInput.displayText;
67+ }
68+ }
53 }
69 }
54
70
55 Rectangle {
71 Rectangle {
@@ -70,19 +86,36 @@ FocusScope {
70 }
86 }
71 }
87 }
72
88
73− AbstractButton {
89+ Rectangle {
74 id: promptButton
90 id: promptButton
75 objectName: "promptButton"
91 objectName: "promptButton"
76 anchors.fill: parent
92 anchors.fill: parent
77 visible: !root.isPrompt
93 visible: !root.isPrompt
78
94
79− onClicked: {
95+ function triggered() {
80 if (d.enabled) {
96 if (d.enabled) {
81 d.enabled = false;
97 d.enabled = false;
82 root.clicked();
98 root.clicked();
83 }
99 }
84 }
100 }
85
101
102+ Rectangle {
103+ height: parent.height;
104+ width: parent.width
105+ color: "transparent"
106+ border {
107+ color: d.textColor
108+ width: units.dp(1)
109+ }
110+ }
111+
112+ Keys.onReturnPressed: triggered();
113+ Keys.onEnterPressed: triggered();
114+ MouseArea {
115+ anchors.fill: parent
116+ onClicked: parent.triggered();
117+ }
118+
86 Label {
119 Label {
87 anchors.centerIn: parent
120 anchors.centerIn: parent
88 color: d.textColor
121 color: d.textColor
@@ -142,15 +175,13 @@ FocusScope {
142 }
175 }
143 }
176 }
144
177
145− onAccepted: {
178+ onAccepted: respond()
146− if (d.enabled)
147− respond();
148− }
149
179
150 function respond() {
180 function respond() {
181+ if (d.enabled) {
151− d.enabled = false;
182+ d.enabled = false;
152− fakeLabel.text = displayText;
153− root.responded(text);
183+ root.responded(text);
184+ }
154 }
185 }
155
186
156 Keys.onEscapePressed: {
187 Keys.onEscapePressed: {
qml/Greeter/Infographics.qml+19−20
@@ -14,7 +14,6 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
15 */
16
16
17−import "../Components"
18import "Gradient.js" as Gradient
17import "Gradient.js" as Gradient
19import QtQuick 2.4
18import QtQuick 2.4
20import Ubuntu.Components 1.3
19import Ubuntu.Components 1.3
@@ -36,6 +35,13 @@ Item {
36 || circleChangeAnimTimer.running
35 || circleChangeAnimTimer.running
37 }
36 }
38
37
38+ QtObject {
39+ id: whiteTheme
40+ property color main: "white"
41+ property color start: "white"
42+ property color end: "white"
43+ }
44+
39 Connections {
45 Connections {
40 target: model
46 target: model
41 ignoreUnknownSignals: model === undefined
47 ignoreUnknownSignals: model === undefined
@@ -142,9 +148,10 @@ Item {
142 width: dataCircle.width / divisor
148 width: dataCircle.width / divisor
143 height: dataCircle.height / divisor
149 height: dataCircle.height / divisor
144 opacity: 0.0
150 opacity: 0.0
145− scale: 0.0
151+ circleScale: 0.0
146 visible: modelData !== undefined
152 visible: modelData !== undefined
147 color: "transparent"
153 color: "transparent"
154+ centerCircle: dataCircle
148
155
149 SequentialAnimation {
156 SequentialAnimation {
150 id: pastCircleChangeAnim
157 id: pastCircleChangeAnim
@@ -160,7 +167,7 @@ Item {
160 }
167 }
161 PropertyAnimation {
168 PropertyAnimation {
162 target: pastCircle
169 target: pastCircle
163− property: "scale"
170+ property: "circleScale"
164 to: modelData
171 to: modelData
165 easing.type: Easing.OutCurve
172 easing.type: Easing.OutCurve
166 duration: circleChangeAnimTimer.interval * d.circleModifier
173 duration: circleChangeAnimTimer.interval * d.circleModifier
@@ -168,7 +175,7 @@ Item {
168 ColorAnimation {
175 ColorAnimation {
169 target: pastCircle
176 target: pastCircle
170 property: "color"
177 property: "color"
171− to: Gradient.threeColorByIndex(index, count, infographic.model.secondColor)
178+ to: Gradient.threeColorByIndex(index, count, whiteTheme)
172 easing.type: Easing.OutCurve
179 easing.type: Easing.OutCurve
173 duration: circleChangeAnimTimer.interval * d.circleModifier
180 duration: circleChangeAnimTimer.interval * d.circleModifier
174 }
181 }
@@ -202,9 +209,10 @@ Item {
202 width: dataCircle.width / divisor
209 width: dataCircle.width / divisor
203 height: dataCircle.height / divisor
210 height: dataCircle.height / divisor
204 opacity: 0.0
211 opacity: 0.0
205− scale: 0.0
212+ circleScale: 0.0
206 visible: modelData !== undefined
213 visible: modelData !== undefined
207 color: "transparent"
214 color: "transparent"
215+ centerCircle: dataCircle
208
216
209 SequentialAnimation {
217 SequentialAnimation {
210 id: presentCircleChangeAnim
218 id: presentCircleChangeAnim
@@ -221,7 +229,7 @@ Item {
221 }
229 }
222 PropertyAnimation {
230 PropertyAnimation {
223 target: presentCircle
231 target: presentCircle
224− property: "scale"
232+ property: "circleScale"
225 to: modelData
233 to: modelData
226 easing.type: Easing.OutCurve
234 easing.type: Easing.OutCurve
227 duration: circleChangeAnimTimer.interval * d.circleModifier
235 duration: circleChangeAnimTimer.interval * d.circleModifier
@@ -229,7 +237,7 @@ Item {
229 ColorAnimation {
237 ColorAnimation {
230 target: presentCircle
238 target: presentCircle
231 property: "color"
239 property: "color"
232− to: Gradient.threeColorByIndex(index, infographic.model.currentDay, infographic.model.firstColor)
240+ to: Gradient.threeColorByIndex(index, infographic.model.currentDay, whiteTheme)
233 easing.type: Easing.OutCurve
241 easing.type: Easing.OutCurve
234 duration: circleChangeAnimTimer.interval * d.circleModifier
242 duration: circleChangeAnimTimer.interval * d.circleModifier
235 }
243 }
@@ -239,15 +247,6 @@ Item {
239 }
247 }
240 }
248 }
241
249
242− Image {
243− id: backgroundCircle
244− objectName: "backgroundCircle"
245−
246− anchors.fill: parent
247−
248− source: "graphics/infographic_circle_back.png"
249− }
250−
251 Timer {
250 Timer {
252 id: dotShowAnimTimer
251 id: dotShowAnimTimer
253
252
@@ -318,7 +317,7 @@ Item {
318
317
319 index: model.index
318 index: model.index
320 count: dots.count
319 count: dots.count
321− radius: backgroundCircle.width / 2
320+ radius: dataCircle.width / 2
322 halfSize: dot.width / 2
321 halfSize: dot.width / 2
323 posOffset: radius / dot.width / 3
322 posOffset: radius / dot.width / 3
324 state: dot.state
323 state: dot.state
@@ -327,7 +326,7 @@ Item {
327 id: dot
326 id: dot
328 objectName: "dot" + index
327 objectName: "dot" + index
329
328
330− property real baseOpacity: 0.4
329+ property real baseOpacity: 1
331
330
332 width: units.dp(5) * parent.radius / 200
331 width: units.dp(5) * parent.radius / 200
333 height: units.dp(5) * parent.radius / 200
332 height: units.dp(5) * parent.radius / 200
@@ -363,10 +362,10 @@ Item {
363 property alias hideAnim: decreaseOpacity
362 property alias hideAnim: decreaseOpacity
364 property alias showAnim: increaseOpacity
363 property alias showAnim: increaseOpacity
365
364
366− property real baseOpacity: 0.6
365+ property real baseOpacity: 1
367 property real duration: dotShowAnimTimer.interval * 5
366 property real duration: dotShowAnimTimer.interval * 5
368
367
369− height: 0.7 * backgroundCircle.width
368+ height: 0.7 * dataCircle.width
370 width: notification.height
369 width: notification.height
371 anchors.centerIn: parent
370 anchors.centerIn: parent
372
371
qml/Greeter/IntegratedLightDMImpl.qml+2−1
@@ -22,7 +22,8 @@ Item{
22
22
23 property var greeter: LightDM.Greeter
23 property var greeter: LightDM.Greeter
24 property var infographic: LightDM.Infographic
24 property var infographic: LightDM.Infographic
25+ property var sessions: LightDM.Sessions
26+ property var sessionRoles: LightDM.SessionRoles
25 property var users: LightDM.Users
27 property var users: LightDM.Users
26 property var userRoles: LightDM.UserRoles
28 property var userRoles: LightDM.UserRoles
27−
28}
29}
qml/Greeter/LightDMService.qml+2
@@ -27,6 +27,8 @@ Loader {
27
27
28 property var greeter: d.valid ? loader.item.greeter : null
28 property var greeter: d.valid ? loader.item.greeter : null
29 property var infographic: d.valid ? loader.item.infographic : null
29 property var infographic: d.valid ? loader.item.infographic : null
30+ property var sessions: d.valid ? loader.item.sessions : null
31+ property var sessionRoles: d.valid ? loader.item.sessionRoles : null
30 property var users: d.valid ? loader.item.users : null
32 property var users: d.valid ? loader.item.users : null
31 property var userRoles: d.valid ? loader.item.userRoles : null
33 property var userRoles: d.valid ? loader.item.userRoles : null
32
34
qml/Greeter/LoginAreaContainer.qmlnew file+38
@@ -0,0 +1,38 @@
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+
20+Item {
21+ BorderImage {
22+ anchors {
23+ fill: parent
24+ topMargin: -units.gu(1)
25+ leftMargin: -units.gu(1.5)
26+ rightMargin: -units.gu(1.5)
27+ bottomMargin: -units.gu(1.5)
28+ }
29+ source: "../Stages/graphics/dropshadow2gu.sci"
30+ opacity: 0.35
31+ }
32+
33+ UbuntuShape {
34+ anchors.fill: parent
35+ aspect: UbuntuShape.Flat
36+ backgroundColor: theme.palette.normal.raised
37+ }
38+}
qml/Greeter/LoginList.qml+96−22
@@ -18,6 +18,7 @@ import QtQuick 2.4
18import QtGraphicalEffects 1.0
18import QtGraphicalEffects 1.0
19import Ubuntu.Components 1.3
19import Ubuntu.Components 1.3
20import "../Components"
20import "../Components"
21+import "." 0.1
21
22
22StyledItem {
23StyledItem {
23 id: root
24 id: root
@@ -28,16 +29,22 @@ StyledItem {
28 property int currentIndex
29 property int currentIndex
29 property bool locked
30 property bool locked
30 property bool waiting
31 property bool waiting
32+ property alias boxVerticalOffset: highlightItem.y
31
33
34+ readonly property alias passwordInput: passwordInput
32 readonly property int numAboveBelow: 4
35 readonly property int numAboveBelow: 4
33 readonly property int cellHeight: units.gu(5)
36 readonly property int cellHeight: units.gu(5)
34 readonly property int highlightedHeight: units.gu(15)
37 readonly property int highlightedHeight: units.gu(15)
35− readonly property int moveDuration: 200
38+ readonly property int moveDuration: UbuntuAnimation.FastDuration
39+ property string selectedSession
40+ property string currentSession
36 readonly property string currentUser: userList.currentItem.username
41 readonly property string currentUser: userList.currentItem.username
37 property bool wasPrompted: false
42 property bool wasPrompted: false
38
43
39− signal selected(int index)
44+ signal loginListSessionChanged(string session)
40 signal responded(string response)
45 signal responded(string response)
46+ signal selected(int index)
47+ signal sessionChooserButtonClicked()
41
48
42 function tryToUnlock() {
49 function tryToUnlock() {
43 if (wasPrompted) {
50 if (wasPrompted) {
@@ -78,6 +85,10 @@ StyledItem {
78 root.resetAuthentication();
85 root.resetAuthentication();
79 }
86 }
80
87
88+ function showFakePassword() {
89+ passwordInput.showFakePassword();
90+ }
91+
81 QtObject {
92 QtObject {
82 id: d
93 id: d
83
94
@@ -114,30 +125,17 @@ StyledItem {
114 userList.currentIndex = currentIndex;
125 userList.currentIndex = currentIndex;
115 }
126 }
116
127
117− BorderImage {
128+ LoginAreaContainer {
118− anchors {
119− fill: highlightItem
120− topMargin: -units.gu(1)
121− leftMargin: -units.gu(1.5)
122− rightMargin: -units.gu(1.5)
123− bottomMargin: -units.gu(1.5)
124− }
125− source: "../Stages/graphics/dropshadow2gu.sci"
126− opacity: 0.35
127− }
128−
129− UbuntuShape {
130 id: highlightItem
129 id: highlightItem
130+ objectName: "highlightItem"
131 anchors {
131 anchors {
132 left: parent.left
132 left: parent.left
133 leftMargin: units.gu(2)
133 leftMargin: units.gu(2)
134 right: parent.right
134 right: parent.right
135 rightMargin: units.gu(2)
135 rightMargin: units.gu(2)
136− verticalCenter: parent.verticalCenter
137 }
136 }
137+
138 height: root.highlightedHeight
138 height: root.highlightedHeight
139− aspect: UbuntuShape.Flat
140− backgroundColor: theme.palette.normal.raised
141 }
139 }
142
140
143 ListView {
141 ListView {
@@ -148,11 +146,10 @@ StyledItem {
148 anchors.leftMargin: units.gu(2)
146 anchors.leftMargin: units.gu(2)
149 anchors.rightMargin: units.gu(2)
147 anchors.rightMargin: units.gu(2)
150
148
151− preferredHighlightBegin: userList.height / 2 - root.highlightedHeight / 2
149+ preferredHighlightBegin: highlightItem.y
152− preferredHighlightEnd: userList.height / 2 - root.highlightedHeight / 2
150+ preferredHighlightEnd: highlightItem.y
153 highlightRangeMode: ListView.StrictlyEnforceRange
151 highlightRangeMode: ListView.StrictlyEnforceRange
154 highlightMoveDuration: root.moveDuration
152 highlightMoveDuration: root.moveDuration
155− flickDeceleration: 10000
156 interactive: count > 1
153 interactive: count > 1
157
154
158 readonly property bool movingInternally: moveTimer.running || userList.moving
155 readonly property bool movingInternally: moveTimer.running || userList.moving
@@ -173,6 +170,7 @@ StyledItem {
173
170
174 readonly property bool belowHighlight: (userList.currentIndex < 0 && index > 0) || (userList.currentIndex >= 0 && index > userList.currentIndex)
171 readonly property bool belowHighlight: (userList.currentIndex < 0 && index > 0) || (userList.currentIndex >= 0 && index > userList.currentIndex)
175 readonly property int belowOffset: root.highlightedHeight - root.cellHeight
172 readonly property int belowOffset: root.highlightedHeight - root.cellHeight
173+ readonly property string userSession: session
176 readonly property string username: name
174 readonly property string username: name
177
175
178 opacity: {
176 opacity: {
@@ -206,9 +204,22 @@ StyledItem {
206 bottomMargin: -(units.gu(4) + (parent.belowHighlight ? parent.belowOffset : 0))
204 bottomMargin: -(units.gu(4) + (parent.belowHighlight ? parent.belowOffset : 0))
207 }
205 }
208 text: realName
206 text: realName
209− color: userList.currentIndex !== index ? theme.palette.normal.raised : theme.palette.normal.raisedText
207+ color: userList.currentIndex !== index ? theme.palette.normal.raised
208+ : theme.palette.normal.raisedText
210
209
211 Behavior on anchors.topMargin { NumberAnimation { duration: root.moveDuration; easing.type: Easing.InOutQuad; } }
210 Behavior on anchors.topMargin { NumberAnimation { duration: root.moveDuration; easing.type: Easing.InOutQuad; } }
211+
212+ Rectangle {
213+ id: activeIndicator
214+ anchors.horizontalCenter: parent.left
215+ anchors.horizontalCenterOffset: -units.gu(1)
216+ anchors.verticalCenter: parent.verticalCenter
217+ color: userList.currentIndex !== index ? theme.palette.normal.raised
218+ : theme.palette.normal.focus
219+ visible: userList.count > 1 && loggedIn
220+ height: units.gu(0.5)
221+ width: height
222+ }
212 }
223 }
213
224
214 MouseArea {
225 MouseArea {
@@ -237,6 +248,67 @@ StyledItem {
237 }
248 }
238 }
249 }
239
250
251+ // Use an AbstractButton due to icon limitations with Button
252+ AbstractButton {
253+ id: sessionChooser
254+ objectName: "sessionChooserButton"
255+
256+ readonly property alias icon: badge.source
257+
258+ visible: LightDMService.sessions.count > 1 &&
259+ !LightDMService.users.data(userList.currentIndex, LightDMService.userRoles.LoggedInRole)
260+
261+ height: units.gu(3.5)
262+ width: units.gu(3.5)
263+
264+ activeFocusOnTab: true
265+ anchors {
266+ right: highlightItem.right
267+ rightMargin: units.gu(2)
268+
269+ top: highlightItem.top
270+ topMargin: units.gu(1.5)
271+ }
272+
273+ Rectangle {
274+ id: badgeHighlight
275+
276+ anchors.fill: parent
277+ visible: parent.activeFocus
278+ color: "transparent"
279+ border.color: theme.palette.normal.focus
280+ border.width: units.dp(1)
281+ radius: 3
282+ }
283+
284+ Icon {
285+ id: badge
286+ anchors.fill: parent
287+ anchors.margins: units.dp(3)
288+ keyColor: "#ffffff" // icon providers give us white icons
289+ color: theme.palette.normal.raisedSecondaryText
290+ source: LightDMService.sessions.iconUrl(root.currentSession)
291+ }
292+
293+ Keys.onReturnPressed: {
294+ sessionChooserButtonClicked();
295+ event.accepted = true;
296+ }
297+
298+ onClicked: {
299+ sessionChooserButtonClicked();
300+ }
301+
302+ // Refresh the icon path if looking at different places at runtime
303+ // this is mainly for testing
304+ Connections {
305+ target: LightDMService.sessions
306+ onIconSearchDirectoriesChanged: {
307+ badge.source = LightDMService.sessions.iconUrl(root.currentSession)
308+ }
309+ }
310+ }
311+
240 FadingLabel {
312 FadingLabel {
241 id: infoLabel
313 id: infoLabel
242 objectName: "infoLabel"
314 objectName: "infoLabel"
@@ -271,6 +343,7 @@ StyledItem {
271 width: highlightItem.width - anchors.margins * 2
343 width: highlightItem.width - anchors.margins * 2
272 opacity: userList.movingInternally ? 0 : 1
344 opacity: userList.movingInternally ? 0 : 1
273
345
346+ activeFocusOnTab: true
274 isAlphanumeric: root.alphanumeric
347 isAlphanumeric: root.alphanumeric
275
348
276 onClicked: root.tryToUnlock()
349 onClicked: root.tryToUnlock()
@@ -283,6 +356,7 @@ StyledItem {
283
356
284 WrongPasswordAnimation {
357 WrongPasswordAnimation {
285 id: wrongPasswordAnimation
358 id: wrongPasswordAnimation
359+ objectName: "wrongPasswordAnimation"
286 target: passwordInput
360 target: passwordInput
287 }
361 }
288 }
362 }
qml/Greeter/NarrowView.qml+147−61
@@ -15,7 +15,9 @@
15 */
15 */
16
16
17import QtQuick 2.4
17import QtQuick 2.4
18+import QtQuick.Window 2.2
18import Ubuntu.Components 1.3
19import Ubuntu.Components 1.3
20+import Ubuntu.Telephony 0.1 as Telephony
19import "../Components"
21import "../Components"
20
22
21FocusScope {
23FocusScope {
@@ -23,34 +25,40 @@ FocusScope {
23
25
24 property alias dragHandleLeftMargin: coverPage.dragHandleLeftMargin
26 property alias dragHandleLeftMargin: coverPage.dragHandleLeftMargin
25 property alias launcherOffset: coverPage.launcherOffset
27 property alias launcherOffset: coverPage.launcherOffset
26− property int currentIndex // unused
28+ property alias currentIndex: loginList.currentIndex
27− property alias delayMinutes: lockscreen.delayMinutes
29+ property alias delayMinutes: delayedLockscreen.delayMinutes
28 property alias backgroundTopMargin: coverPage.backgroundTopMargin
30 property alias backgroundTopMargin: coverPage.backgroundTopMargin
29 property url background
31 property url background
32+ property bool hasCustomBackground
30 property bool locked
33 property bool locked
31− property bool alphanumeric
34+ property alias alphanumeric: loginList.alphanumeric
32− property var userModel // unused
35+ property alias userModel: loginList.model
33 property alias infographicModel: coverPage.infographicModel
36 property alias infographicModel: coverPage.infographicModel
37+ property string sessionToStart
34 property bool waiting
38 property bool waiting
35 readonly property bool fullyShown: coverPage.showProgress === 1 || lockscreen.shown
39 readonly property bool fullyShown: coverPage.showProgress === 1 || lockscreen.shown
36 readonly property bool required: coverPage.required || lockscreen.required
40 readonly property bool required: coverPage.required || lockscreen.required
37 readonly property bool animating: coverPage.showAnimation.running || coverPage.hideAnimation.running
41 readonly property bool animating: coverPage.showAnimation.running || coverPage.hideAnimation.running
38
42
43+ // so that it can be replaced in tests with a mock object
44+ property var inputMethod: Qt.inputMethod
45+
39− signal selected(int index) // unused
46+ signal selected(int index)
40 signal responded(string response)
47 signal responded(string response)
41 signal tease()
48 signal tease()
42 signal emergencyCall()
49 signal emergencyCall()
43
50
44 function showMessage(html) {
51 function showMessage(html) {
45− // TODO
52+ loginList.showMessage(html);
46 }
53 }
47
54
48 function showPrompt(text, isSecret, isDefaultPrompt) {
55 function showPrompt(text, isSecret, isDefaultPrompt) {
49− lockscreen.promptText = isDefaultPrompt ? "" : text.toLowerCase();
56+ loginList.showPrompt(text, isSecret, isDefaultPrompt);
50− lockscreen.maybeShow();
51 }
57 }
52
58
53 function showLastChance() {
59 function showLastChance() {
60+ /* TODO: when we finish support for resetting device after too many
61+ failed logins, we should re-add this popup.
54 var title = lockscreen.alphaNumeric ?
62 var title = lockscreen.alphaNumeric ?
55 i18n.tr("Sorry, incorrect passphrase.") :
63 i18n.tr("Sorry, incorrect passphrase.") :
56 i18n.tr("Sorry, incorrect passcode.");
64 i18n.tr("Sorry, incorrect passcode.");
@@ -59,6 +67,7 @@ FocusScope {
59 i18n.tr("If passphrase is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted.") :
67 i18n.tr("If passphrase is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted.") :
60 i18n.tr("If passcode is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted."));
68 i18n.tr("If passcode is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted."));
61 lockscreen.showInfoPopup(title, text);
69 lockscreen.showInfoPopup(title, text);
70+ */
62 }
71 }
63
72
64 function hide() {
73 function hide() {
@@ -67,27 +76,24 @@ FocusScope {
67 }
76 }
68
77
69 function notifyAuthenticationSucceeded(showFakePassword) {
78 function notifyAuthenticationSucceeded(showFakePassword) {
70− // When using an alternate log in mechanism like fingerprints, the
71− // design is it looks like the user entered a passcode.
72− if (!alphanumeric && showFakePassword) {
79+ if (showFakePassword) {
73− lockscreen.showText("...."); // actual text doesn't matter, we show bullets
80+ loginList.showFakePassword();
74 }
81 }
75 }
82 }
76
83
77 function notifyAuthenticationFailed() {
84 function notifyAuthenticationFailed() {
78− lockscreen.customError = "";
85+ loginList.showError();
79− lockscreen.clear(true);
80 }
86 }
81
87
82 function showErrorMessage(msg) {
88 function showErrorMessage(msg) {
83 coverPage.showErrorMessage(msg);
89 coverPage.showErrorMessage(msg);
84− lockscreen.customError = msg ? msg : " "; // avoid default message
85− lockscreen.clear(true);
86 }
90 }
87
91
88− function reset() {
92+ function reset(forceShow) {
89− lockscreen.customError = "";
93+ loginList.reset();
94+ if (forceShow) {
90− coverPage.show();
95+ coverPage.show();
96+ }
91 }
97 }
92
98
93 function tryToUnlock(toTheRight) {
99 function tryToUnlock(toTheRight) {
@@ -109,48 +115,59 @@ FocusScope {
109 }
115 }
110 }
116 }
111
117
112− Lockscreen {
118+ Showable {
113 id: lockscreen
119 id: lockscreen
114 objectName: "lockscreen"
120 objectName: "lockscreen"
115−
121+ anchors.fill: parent
116 shown: false
122 shown: false
123+
117 showAnimation: StandardAnimation { property: "opacity"; to: 1 }
124 showAnimation: StandardAnimation { property: "opacity"; to: 1 }
118 hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
125 hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
126+
127+ Wallpaper {
128+ id: lockscreenBackground
129+ objectName: "lockscreenBackground"
130+ anchors {
119− anchors.fill: parent
131+ fill: parent
120− visible: required
132+ topMargin: root.backgroundTopMargin
121− enabled: !coverPage.shown
122− background: root.background
123− darkenBackground: 0.4
124− alphaNumeric: root.alphanumeric
125− minPinLength: 4
126− maxPinLength: 4
127−
128− property string promptText
129− infoText: promptText !== "" ? i18n.tr("Enter %1").arg(promptText) :
130− alphaNumeric ? i18n.tr("Enter passphrase") :
131− i18n.tr("Enter passcode")
132−
133− property string customError
134− errorText: customError !== "" ? customError :
135− promptText !== "" ? i18n.tr("Sorry, incorrect %1").arg(promptText) :
136− alphaNumeric ? i18n.tr("Sorry, incorrect passphrase") + "\n" +
137− i18n.ctr("passphrase", "Please re-enter") :
138− i18n.tr("Sorry, incorrect passcode")
139−
140− onEntered: root.responded(passphrase)
141− onCancel: coverPage.show()
142− onEmergencyCall: root.emergencyCall()
143−
144− onEnabledChanged: {
145− if (enabled) {
146− lockscreen.forceActiveFocus();
147 }
133 }
134+ source: root.background
148 }
135 }
149
136
150− onVisibleChanged: {
137+ // Darken background to match CoverPage
151− if (visible) {
138+ Rectangle {
152− lockscreen.forceActiveFocus();
139+ objectName: "lockscreenShade"
140+ anchors.fill: parent
141+ color: "black"
142+ opacity: root.hasCustomBackground ? 0.4 : 0
143+ }
144+
145+ LoginList {
146+ id: loginList
147+ objectName: "loginList"
148+
149+ anchors {
150+ horizontalCenter: parent.horizontalCenter
151+ top: parent.top
152+ bottom: parent.bottom
153 }
153 }
154+ width: units.gu(40)
155+ boxVerticalOffset: units.gu(14)
156+ enabled: !coverPage.shown && visible
157+ visible: !delayedLockscreen.visible
158+
159+ locked: root.locked
160+
161+ onSelected: if (enabled) root.selected(index)
162+ onResponded: root.responded(response)
163+ }
164+
165+ DelayedLockscreen {
166+ id: delayedLockscreen
167+ objectName: "delayedLockscreen"
168+ anchors.fill: parent
169+ visible: delayMinutes > 0
170+ alphaNumeric: loginList.alphanumeric
154 }
171 }
155
172
156 function maybeShow() {
173 function maybeShow() {
@@ -172,21 +189,16 @@ FocusScope {
172 height: parent.height
189 height: parent.height
173 width: parent.width
190 width: parent.width
174 background: root.background
191 background: root.background
192+ hasCustomBackground: root.hasCustomBackground
175 draggable: !root.waiting
193 draggable: !root.waiting
176 onTease: root.tease()
194 onTease: root.tease()
177 onClicked: hide()
195 onClicked: hide()
178
196
179 onShowProgressChanged: {
197 onShowProgressChanged: {
180 if (showProgress === 1) {
198 if (showProgress === 1) {
181− lockscreen.reset();
199+ loginList.reset();
182− }
183−
184− if (showProgress === 0) {
200+ } else if (showProgress === 0) {
185− if (root.locked) {
201+ loginList.tryToUnlock();
186− lockscreen.clear(false); // to reset focus if necessary
187− } else {
188− root.responded("");
189− }
190 }
202 }
191 }
203 }
192
204
@@ -198,4 +210,78 @@ FocusScope {
198 }
210 }
199 }
211 }
200 }
212 }
213+
214+ StyledItem {
215+ id: bottomBar
216+ visible: lockscreen.shown
217+ height: units.gu(4)
218+
219+ anchors.left: parent.left
220+ anchors.right: parent.right
221+ anchors.top: parent.bottom
222+ anchors.topMargin: - height * (1 - coverPage.showProgress)
223+ - (inputMethod && inputMethod.visible ?
224+ inputMethod.keyboardRectangle.height : 0)
225+
226+ Rectangle {
227+ color: UbuntuColors.porcelain // matches OSK background
228+ anchors.fill: parent
229+ }
230+
231+ Label {
232+ text: i18n.tr("Cancel")
233+ anchors.left: parent.left
234+ anchors.leftMargin: units.gu(2)
235+ anchors.top: parent.top
236+ anchors.bottom: parent.bottom
237+ verticalAlignment: Text.AlignVCenter
238+ font.weight: Font.Light
239+ fontSize: "small"
240+ color: UbuntuColors.slate
241+
242+ AbstractButton {
243+ anchors.fill: parent
244+ anchors.leftMargin: -units.gu(2)
245+ anchors.rightMargin: -units.gu(2)
246+ onClicked: coverPage.show()
247+ }
248+ }
249+
250+ Label {
251+ objectName: "emergencyCallLabel"
252+ text: callManager.hasCalls ? i18n.tr("Return to Call") : i18n.tr("Emergency")
253+ anchors.right: parent.right
254+ anchors.rightMargin: units.gu(2)
255+ anchors.top: parent.top
256+ anchors.bottom: parent.bottom
257+ verticalAlignment: Text.AlignVCenter
258+ font.weight: Font.Light
259+ fontSize: "small"
260+ color: UbuntuColors.slate
261+ // TODO: uncomment once bug 1616538 is fixed
262+ // visible: telepathyHelper.ready && telepathyHelper.emergencyCallsAvailable
263+ enabled: visible
264+
265+ AbstractButton {
266+ anchors.fill: parent
267+ anchors.leftMargin: -units.gu(2)
268+ anchors.rightMargin: -units.gu(2)
269+ onClicked: root.emergencyCall()
270+ }
271+ }
272+ }
273+
274+ // FIXME: It's difficult to keep something tied closely to the OSK (bug
275+ // 1616163). But as a hack to avoid the background peeking out,
276+ // we add an extra Rectangle that just serves to hide the background
277+ // during OSK animations.
278+ Rectangle {
279+ visible: bottomBar.visible
280+ height: inputMethod && inputMethod.visible ?
281+ inputMethod.keyboardRectangle.height : 0
282+ anchors.bottom: parent.bottom
283+ anchors.left: parent.left
284+ anchors.right: parent.right
285+ color: UbuntuColors.porcelain
286+ }
201}
287}
qml/Greeter/SessionIcon.qmlnew file+47
@@ -0,0 +1,47 @@
1+/*
2+ * Copyright (C) 2016 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+
19+Image {
20+ id: root
21+
22+ property color color
23+ readonly property string fragColorShader:
24+ "
25+ varying highp vec2 qt_TexCoord0;
26+ uniform sampler2D source;
27+ uniform lowp vec4 color;
28+ uniform lowp float qt_Opacity;
29+
30+ void main()
31+ {
32+ lowp vec4 sourceColor = texture2D(source, qt_TexCoord0);
33+ gl_FragColor = color * sourceColor.a * qt_Opacity;
34+ }
35+ "
36+
37+ fillMode: Image.PreserveAspectFit
38+ height: sourceSize.height
39+
40+ ShaderEffect {
41+ readonly property color color: root.color
42+ readonly property Image source: parent
43+
44+ height: source.height; width: source.width
45+ fragmentShader: fragColorShader
46+ }
47+}
qml/Greeter/SessionsList.qmlnew file+187
@@ -0,0 +1,187 @@
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 Ubuntu.Components.ListItems 1.3
20+import "." 0.1
21+import "../Components"
22+
23+Item {
24+ id: root
25+ objectName: "sessionsList"
26+
27+ property string initiallySelectedSession
28+ signal sessionSelected(string sessionKey)
29+ signal showLoginList()
30+
31+ onInitiallySelectedSessionChanged: {
32+ sessionsList.currentIndex = getSelectedIndex();
33+ sessionsList.positionViewAtIndex(sessionsList.currentIndex, ListView.Contain);
34+ }
35+
36+ function getSelectedIndex() {
37+ for (var i = 0; i < sessionsList.model.count; i++) {
38+ var key = sessionsList.model.get(i).key
39+ if (key === initiallySelectedSession) {
40+ return i;
41+ }
42+ }
43+ }
44+
45+ function currentKey() {
46+ var session = LightDMService.sessions.data(
47+ sessionsList.currentIndex, LightDMService.sessionRoles.KeyRole)
48+ return session;
49+ }
50+
51+ Keys.onEnterPressed: {
52+ showLoginList(); // Session is already selected
53+ event.accepted = true;
54+ }
55+
56+ Keys.onEscapePressed: {
57+ showLoginList();
58+ event.accepted = true;
59+ }
60+
61+ Keys.onReturnPressed: {
62+ showLoginList();
63+ event.accepted = true;
64+ }
65+
66+ Keys.onDownPressed: {
67+ if (sessionsList.currentIndex < sessionsList.model.count - 1)
68+ sessionsList.currentIndex++;
69+ sessionSelected(currentKey());
70+ event.accepted = true;
71+ }
72+
73+ Keys.onUpPressed: {
74+ if (sessionsList.currentIndex > 0)
75+ sessionsList.currentIndex--;
76+ sessionSelected(currentKey());
77+ event.accepted = true;
78+ }
79+
80+ LoginAreaContainer {
81+ readonly property real margins: sessionsList.anchors.margins
82+ readonly property real prefferedHeight: {
83+ if (sessionsList.currentItem) {
84+ return (sessionsList.currentItem.height *
85+ (1 + sessionsList.model.count)) + 2 * margins
86+ } else {
87+ return sessionsList.headerItem.height + 2 * margins
88+ }
89+ }
90+
91+ height: prefferedHeight < parent.height ? prefferedHeight : parent.height - units.gu(4)
92+ width: parent.width
93+
94+ anchors {
95+ left: parent.left
96+ right: parent.right
97+ verticalCenter: parent.verticalCenter
98+ }
99+
100+ UbuntuListView {
101+ id: sessionsList
102+
103+ anchors {
104+ top: parent.top
105+ left: parent.left
106+ right: parent.right
107+ margins: units.gu(2)
108+ }
109+
110+ height: parent.height - headerItem.height
111+
112+ boundsBehavior: Flickable.StopAtBounds
113+
114+ model: LightDMService.sessions
115+ header: ListItemLayout {
116+ id: header
117+
118+ padding.leading: 0 // handled by parent's margins
119+
120+ title.color: theme.palette.normal.raisedText
121+ title.font.pixelSize: units.gu(2.1)
122+ title.text: i18n.tr("Select desktop environment")
123+
124+ Icon {
125+ id: icon
126+ width: units.gu(3)
127+ SlotsLayout.position: SlotsLayout.Leading
128+ name: "go-previous"
129+
130+ MouseArea {
131+ anchors.fill: parent
132+ onClicked: showLoginList()
133+ }
134+ }
135+ }
136+
137+ headerPositioning: ListView.OverlayHeader
138+ highlightFollowsCurrentItem: false
139+
140+ delegate: ListItem {
141+ id: delegate
142+ objectName: "sessionDelegate" + index
143+
144+ divider.visible: false
145+
146+ visible: y > sessionsList.headerItem.y
147+ + sessionsList.headerItem.height
148+ - sessionsList.anchors.margins
149+
150+ MouseArea {
151+ anchors.fill: parent
152+ onClicked: {
153+ sessionsList.currentIndex = index
154+ sessionSelected(key)
155+ showLoginList()
156+ }
157+ }
158+
159+ Rectangle {
160+ height: parent.height
161+ width: parent.width
162+ color: "transparent"
163+ border {
164+ color: theme.palette.normal.focus
165+ width: units.gu(0.2)
166+ }
167+
168+ visible: index === sessionsList.currentIndex
169+ }
170+
171+ ListItemLayout {
172+ id: layout
173+
174+ SessionIcon {
175+ id: sessionIcon
176+ source: icon_url
177+ SlotsLayout.position: SlotsLayout.Leading
178+ color: theme.palette.normal.raisedSecondaryText
179+ }
180+
181+ title.text: display
182+ title.color: theme.palette.normal.raisedText
183+ }
184+ }
185+ }
186+ }
187+}
qml/Greeter/WideView.qml+109−38
@@ -16,25 +16,29 @@
16
16
17import QtQuick 2.4
17import QtQuick 2.4
18import Ubuntu.Components 1.3
18import Ubuntu.Components 1.3
19+import "." 0.1
19
20
20FocusScope {
21FocusScope {
21 id: root
22 id: root
22 focus: true
23 focus: true
23
24
25+ property alias background: coverPage.background
26+ property alias backgroundTopMargin: coverPage.backgroundTopMargin
27+ property alias hasCustomBackground: coverPage.hasCustomBackground
24 property alias dragHandleLeftMargin: coverPage.dragHandleLeftMargin
28 property alias dragHandleLeftMargin: coverPage.dragHandleLeftMargin
29+ property alias infographicModel: coverPage.infographicModel
25 property alias launcherOffset: coverPage.launcherOffset
30 property alias launcherOffset: coverPage.launcherOffset
26 property alias currentIndex: loginList.currentIndex
31 property alias currentIndex: loginList.currentIndex
27 property int delayMinutes // TODO
32 property int delayMinutes // TODO
28− property alias backgroundTopMargin: coverPage.backgroundTopMargin
29− property alias background: coverPage.background
30− property bool locked
31 property alias alphanumeric: loginList.alphanumeric
33 property alias alphanumeric: loginList.alphanumeric
32− property alias userModel: loginList.model
34+ property alias locked: loginList.locked
33− property alias infographicModel: coverPage.infographicModel
35+ property alias sessionToStart: loginList.currentSession
34− property bool waiting
36+ property alias waiting: loginList.waiting
37+ property var userModel // Set from outside
38+
39+ readonly property bool animating: coverPage.showAnimation.running || coverPage.hideAnimation.running
35 readonly property bool fullyShown: coverPage.showProgress === 1
40 readonly property bool fullyShown: coverPage.showProgress === 1
36 readonly property bool required: coverPage.required
41 readonly property bool required: coverPage.required
37− readonly property bool animating: coverPage.showAnimation.running || coverPage.hideAnimation.running
38
42
39 // so that it can be replaced in tests with a mock object
43 // so that it can be replaced in tests with a mock object
40 property var inputMethod: Qt.inputMethod
44 property var inputMethod: Qt.inputMethod
@@ -44,36 +48,20 @@ FocusScope {
44 signal tease()
48 signal tease()
45 signal emergencyCall() // unused
49 signal emergencyCall() // unused
46
50
47− function showMessage(html) {
48− loginList.showMessage(html);
49− }
50−
51− function showPrompt(text, isSecret, isDefaultPrompt) {
52− loginList.showPrompt(text, isSecret, isDefaultPrompt);
53− }
54−
55− function showLastChance() {
56− // TODO
57− }
58−
59− function hide() {
60− coverPage.hide();
61− }
62−
63− function notifyAuthenticationSucceeded(showFakePassword) {
64− // Nothing needed
65− }
66−
67 function notifyAuthenticationFailed() {
51 function notifyAuthenticationFailed() {
68 loginList.showError();
52 loginList.showError();
69 }
53 }
70
54
71− function showErrorMessage(msg) {
55+ function reset(forceShow) {
72− coverPage.showErrorMessage(msg);
56+ loginList.reset();
73 }
57 }
74
58
75− function reset() {
59+ function showMessage(html) {
76− loginList.reset();
60+ loginList.showMessage(html);
61+ }
62+
63+ function showPrompt(text, isSecret, isDefaultPrompt) {
64+ loginList.showPrompt(text, isSecret, isDefaultPrompt);
77 }
65 }
78
66
79 function tryToUnlock(toTheRight) {
67 function tryToUnlock(toTheRight) {
@@ -92,6 +80,20 @@ FocusScope {
92 }
80 }
93 }
81 }
94
82
83+ function hide() {
84+ coverPage.hide();
85+ }
86+
87+ function notifyAuthenticationSucceeded(showFakePassword) {
88+ if (showFakePassword) {
89+ loginList.showFakePassword();
90+ }
91+ }
92+
93+ function showLastChance() {
94+ // TODO
95+ }
96+
95 Rectangle {
97 Rectangle {
96 anchors.fill: parent
98 anchors.fill: parent
97 color: "black"
99 color: "black"
@@ -104,6 +106,7 @@ FocusScope {
104 height: parent.height
106 height: parent.height
105 width: parent.width
107 width: parent.width
106 draggable: !root.locked && !root.waiting
108 draggable: !root.locked && !root.waiting
109+ state: "LoginList"
107
110
108 infographics {
111 infographics {
109 height: 0.75 * parent.height
112 height: 0.75 * parent.height
@@ -122,21 +125,89 @@ FocusScope {
122 id: loginList
125 id: loginList
123 objectName: "loginList"
126 objectName: "loginList"
124
127
128+ width: units.gu(40)
125 anchors {
129 anchors {
126 left: parent.left
130 left: parent.left
127 leftMargin: Math.min(parent.width * 0.16, units.gu(20))
131 leftMargin: Math.min(parent.width * 0.16, units.gu(20))
128 top: parent.top
132 top: parent.top
133+ bottom: parent.bottom
129 }
134 }
130− width: units.gu(40)
131− height: inputMethod && inputMethod.visible ? parent.height - inputMethod.keyboardRectangle.height
132− : parent.height
133− Behavior on height { UbuntuNumberAnimation {} }
134
135
135− locked: root.locked
136+ boxVerticalOffset: (height - highlightedHeight -
136− waiting: root.waiting
137+ (inputMethod && inputMethod.visible ?
138+ inputMethod.keyboardRectangle.height : 0)) / 2
139+ Behavior on boxVerticalOffset { UbuntuNumberAnimation {} }
137
140
138− onSelected: root.selected(index)
141+ model: root.userModel
142+ currentSession: LightDMService.greeter.defaultSession
139 onResponded: root.responded(response)
143 onResponded: root.responded(response)
144+ onSelected: root.selected(index)
145+ onSessionChooserButtonClicked: parent.state = "SessionsList"
146+
147+ Keys.forwardTo: [sessionChooserLoader.item]
140 }
148 }
149+
150+ Loader {
151+ id: sessionChooserLoader
152+
153+ height: loginList.height
154+ width: loginList.width
155+ anchors {
156+ left: parent.left
157+ leftMargin: Math.min(parent.width * 0.16, units.gu(20))
158+ top: parent.top
159+ }
160+
161+ active: false
162+
163+ onLoaded: sessionChooserLoader.item.forceActiveFocus();
164+ Binding {
165+ target: sessionChooserLoader.item
166+ property: "initiallySelectedSession"
167+ value: loginList.currentSession
168+ }
169+
170+ Connections {
171+ target: sessionChooserLoader.item
172+ onSessionSelected: loginList.currentSession = sessionKey
173+ onShowLoginList: {
174+ coverPage.state = "LoginList"
175+ loginList.passwordInput.forceActiveFocus();
176+ }
177+ ignoreUnknownSignals: true
178+ }
179+ }
180+
181+ states: [
182+ State {
183+ name: "SessionsList"
184+ PropertyChanges { target: loginList; opacity: 0 }
185+ PropertyChanges { target: sessionChooserLoader;
186+ active: true;
187+ opacity: 1
188+ source: "SessionsList.qml"
189+ }
190+ },
191+
192+ State {
193+ name: "LoginList"
194+ PropertyChanges { target: loginList; opacity: 1 }
195+ PropertyChanges { target: sessionChooserLoader;
196+ active: false;
197+ opacity: 0
198+ source: "";
199+ }
200+ }
201+ ]
202+
203+ transitions: [
204+ Transition {
205+ from: "*"
206+ to: "*"
207+ UbuntuNumberAnimation {
208+ property: "opacity";
209+ }
210+ }
211+ ]
141 }
212 }
142}
213}
qml/Greeter/graphics/infographic_circle_back.pngdeleted

Binary file not shown.

qml/Greeter/graphics/session_icons/gnome_badge.pngnew file

Binary file not shown.

qml/Greeter/graphics/session_icons/kde_badge.pngnew file

Binary file not shown.

qml/Greeter/graphics/session_icons/recovery_console_badge.pngnew file

Binary file not shown.

qml/Greeter/graphics/session_icons/ubuntu_badge.pngnew file

Binary file not shown.

qml/Greeter/graphics/session_icons/unknown_badge.pngnew file

Binary file not shown.

qml/Launcher/LauncherPanel.qml+1−1
@@ -19,7 +19,7 @@ import Ubuntu.Components 1.3
19import Unity.Launcher 0.1
19import Unity.Launcher 0.1
20import Ubuntu.Components.Popups 1.3
20import Ubuntu.Components.Popups 1.3
21import "../Components/ListItems"
21import "../Components/ListItems"
22−import "../Components/"
22+import "../Components"
23
23
24Rectangle {
24Rectangle {
25 id: root
25 id: root
qml/Panel/IndicatorPage.qml+1
@@ -17,6 +17,7 @@
17import QtQuick 2.4
17import QtQuick 2.4
18import Ubuntu.Components 1.3 as Components
18import Ubuntu.Components 1.3 as Components
19import Unity.Indicators 0.1 as Indicators
19import Unity.Indicators 0.1 as Indicators
20+import "../Components"
20import "Indicators"
21import "Indicators"
21
22
22IndicatorBase {
23IndicatorBase {
qml/Panel/Indicators/client/IndicatorRepresentation.qml+27−23
@@ -20,25 +20,42 @@
20
20
21import QtQuick 2.4
21import QtQuick 2.4
22import Ubuntu.Components 1.3
22import Ubuntu.Components 1.3
23−import Ubuntu.Components.ListItems 1.3 as ListItem
24import "../.."
23import "../.."
25
24
26Page {
25Page {
27 id: root
26 id: root
28−
29− title: indicatorProperties && indicatorProperties.title ? indicatorProperties.title :
30− indicatorProperties && indicatorProperties.accessibleName ? indicatorProperties.accessibleName
31− : ""
32 property variant indicatorProperties
27 property variant indicatorProperties
33
28
29+ header: PageHeader {
30+ title: indicatorProperties && indicatorProperties.title ? indicatorProperties.title :
31+ indicatorProperties && indicatorProperties.accessibleName ? indicatorProperties.accessibleName
32+ : identifier
33+ }
34+
34 anchors.fill: parent
35 anchors.fill: parent
35
36
36− ListItem.Standard {
37+ ListItem {
38+ color: theme.palette.highlighted.background
37 id: visualCheckItem
39 id: visualCheckItem
40+
41+ anchors {
42+ top: header.bottom
43+ left: parent.left
44+ right: parent.right
45+ }
46+
47+ height: selectorLayout.height
48+ onClicked: visualCheck.checked = !visualCheck.checked
49+
50+ ListItemLayout {
51+ id: selectorLayout
38− text: "Enable Visual Representation"
52+ title.text: "Enable Visual Representation"
53+
39− control: Switch {
54+ Switch {
40− id: visualCheck
55+ id: visualCheck
41− checked: true
56+ checked: true
57+ SlotsLayout.position: SlotsLayout.Trailing
58+ }
42 }
59 }
43 }
60 }
44
61
@@ -48,11 +65,6 @@ Page {
48 clip:true
65 clip:true
49 asynchronous: true
66 asynchronous: true
50
67
51− Rectangle {
52− anchors.fill: pageLoader
53− color: "#221e1c" // FIXME not in palette yet
54− }
55−
56 anchors {
68 anchors {
57 top: visualCheckItem.bottom
69 top: visualCheckItem.bottom
58 left: parent.left
70 left: parent.left
@@ -92,13 +104,5 @@ Page {
92 margins: units.gu(1)
104 margins: units.gu(1)
93 }
105 }
94 height: childrenRect.height
106 height: childrenRect.height
95−
96− Button {
97− anchors {
98− left: parent.left
99− }
100− text: "Back"
101− onClicked: root.pageStack.reset()
102− }
103 }
107 }
104}
108}
qml/Panel/Indicators/client/IndicatorsClient.qml+1−1
@@ -21,8 +21,8 @@ import QtQuick 2.4
21import Ubuntu.Components 1.3
21import Ubuntu.Components 1.3
22
22
23Rectangle {
23Rectangle {
24− color: theme.palette.normal.background
25 id: root
24 id: root
25+ color: theme.palette.normal.background
26
26
27 Component.onCompleted: {
27 Component.onCompleted: {
28 theme.name = "Ubuntu.Components.Themes.SuruDark"
28 theme.name = "Ubuntu.Components.Themes.SuruDark"
qml/Panel/Indicators/client/IndicatorsList.qml+19−24
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2013 Canonical Ltd.
2+ * Copyright 2013-2016 Canonical Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
5 * it under the terms of the GNU Lesser General Public License as published by
@@ -15,19 +15,22 @@
15 *
15 *
16 * Authors:
16 * Authors:
17 * Renato Araujo Oliveira Filho <renato@canonical.com>
17 * Renato Araujo Oliveira Filho <renato@canonical.com>
18+ * Marco Trevisan <marco.trevisan@canonical.com>
18 */
19 */
19
20
20import QtQuick 2.4
21import QtQuick 2.4
21import Ubuntu.Components 1.3
22import Ubuntu.Components 1.3
22−import Ubuntu.Components.ListItems 1.3 as ListItem
23import Unity.Indicators 0.1 as Indicators
23import Unity.Indicators 0.1 as Indicators
24import "../.."
24import "../.."
25+import "../../../Components"
25
26
26Page {
27Page {
27 id: page
28 id: page
28 anchors.fill: parent
29 anchors.fill: parent
29 property string profile: ""
30 property string profile: ""
30− title: "Plugin list"
31+ header: PageHeader {
32+ title: "Indicators List"
33+ }
31
34
32 Indicators.IndicatorsModel {
35 Indicators.IndicatorsModel {
33 id: indicatorsModel
36 id: indicatorsModel
@@ -41,32 +44,24 @@ Page {
41 anchors.fill: parent
44 anchors.fill: parent
42 model: indicatorsModel
45 model: indicatorsModel
43
46
44− delegate: Rectangle {
47+ delegate: ListItem {
45− color: "#221e1c"
48+ objectName: identifier
46−
47− anchors.left: parent.left
48− anchors.right: parent.right
49− height: menuItem.height
49+ height: menuItem.height + (divider.visible ? divider.height : 0)
50
50
51− ListItem.Standard {
51+ ListItemLayout {
52 id: menuItem
52 id: menuItem
53− anchors.left: parent.left
54− anchors.right: parent.right
55− objectName: identifier
56−
57− text: identifier
53+ title.text: identifier
58−
54+ }
59− onClicked: {
60− var new_page = Qt.createComponent("IndicatorRepresentation.qml");
61− if (new_page.status !== Component.Ready) {
62− if (new_page.status === Component.Error)
63− console.error("Error: " + new_page.errorString());
64
55
65− return;
56+ onClicked: {
66− }
57+ var new_page = Qt.createComponent("IndicatorRepresentation.qml");
58+ if (new_page.status !== Component.Ready) {
59+ if (new_page.status === Component.Error)
60+ console.error("Error: " + new_page.errorString());
67
61
68− page.pageStack.push(new_page.createObject(pages), {"indicatorProperties" : model.indicatorProperties });
62+ return;
69 }
63 }
64+ page.pageStack.push(new_page.createObject(pages), {"indicatorProperties" : model.indicatorProperties });
70 }
65 }
71 }
66 }
72 }
67 }
qml/Panel/Indicators/client/IndicatorsTree.qml+5−8
@@ -21,6 +21,7 @@ import QtQuick 2.4
21import Ubuntu.Components 1.3
21import Ubuntu.Components 1.3
22import Unity.Indicators 0.1 as Indicators
22import Unity.Indicators 0.1 as Indicators
23import ".."
23import ".."
24+import "../../../Components"
24
25
25IndicatorBase {
26IndicatorBase {
26 id: root
27 id: root
@@ -30,14 +31,10 @@ IndicatorBase {
30 model: root.menuModel
31 model: root.menuModel
31 }
32 }
32
33
33− Flickable {
34+ TextArea {
34 anchors.fill: parent
35 anchors.fill: parent
35− contentHeight: all_data.height
36+ anchors.margins: units.gu(0.5)
36− clip:true
37− Text {
38− id: all_data
37+ id: all_data
39− color: "white"
40− text: printer.text
38+ text: printer.text
41− }
42 }
39 }
43}
40}
qml/Panel/Panel.qml+11−15
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013-2015 Canonical, Ltd.
2+ * Copyright (C) 2013-2016 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
@@ -100,15 +100,12 @@ Item {
100 }
100 }
101 height: indicators.minimizedPanelHeight
101 height: indicators.minimizedPanelHeight
102 hoverEnabled: true
102 hoverEnabled: true
103− onClicked: callHint.visible ? callHint.showLiveCall() : PanelState.focusMaximizedApp()
103+ onClicked: if (callHint.visible) { callHint.showLiveCall(); }
104− onDoubleClicked: PanelState.restoreClicked()
104+
105−
105+ onPressed: {
106− property bool mouseWasPressed: false
106+ if (!callHint.visible) {
107− onPressed: mouseWasPressed = containsPress
107+ // let it fall through to the window decoration of the maximized window behind, if any
108− onMouseYChanged: {
108+ mouse.accepted = false;
109− if (mouseWasPressed && mouseY > panelHeight) {
110− PanelState.restoreClicked(); // restore the window when "dragging" the panel down
111− mouseWasPressed = false;
112 }
109 }
113 }
110 }
114
111
@@ -120,11 +117,8 @@ Item {
120 anchors {
117 anchors {
121 left: parent.left
118 left: parent.left
122 top: parent.top
119 top: parent.top
123− leftMargin: units.gu(1)
124− topMargin: units.gu(0.5)
125− bottomMargin: units.gu(0.5)
126 }
120 }
127− height: indicators.minimizedPanelHeight - anchors.topMargin - anchors.bottomMargin
121+ height: indicators.minimizedPanelHeight
128
122
129 visible: ((PanelState.buttonsVisible && parent.containsMouse) || PanelState.buttonsAlwaysVisible)
123 visible: ((PanelState.buttonsVisible && parent.containsMouse) || PanelState.buttonsAlwaysVisible)
130 && !root.locked && !callHint.visible
124 && !root.locked && !callHint.visible
@@ -183,13 +177,15 @@ Item {
183 }
177 }
184 color: "white"
178 color: "white"
185 height: indicators.minimizedPanelHeight - anchors.topMargin - anchors.bottomMargin
179 height: indicators.minimizedPanelHeight - anchors.topMargin - anchors.bottomMargin
186− visible: !windowControlButtons.visible && !root.locked && !callHint.visible
180+ opacity: !windowControlButtons.visible && !root.locked && !callHint.visible ? 1 : 0
181+ visible: opacity != 0
187 verticalAlignment: Text.AlignVCenter
182 verticalAlignment: Text.AlignVCenter
188 fontSize: "medium"
183 fontSize: "medium"
189 font.weight: PanelState.buttonsVisible ? Font.Light : Font.Medium
184 font.weight: PanelState.buttonsVisible ? Font.Light : Font.Medium
190 text: PanelState.title
185 text: PanelState.title
191 elide: Text.ElideRight
186 elide: Text.ElideRight
192 maximumLineCount: 1
187 maximumLineCount: 1
188+ Behavior on opacity { UbuntuNumberAnimation {} }
193 }
189 }
194
190
195 // TODO here would the Locally integrated menus come
191 // TODO here would the Locally integrated menus come
qml/Shell.qml+107−16
@@ -26,6 +26,7 @@ import Unity.Connectivity 0.1
26import Unity.Launcher 0.1
26import Unity.Launcher 0.1
27import GlobalShortcut 1.0 // has to be before Utils, because of WindowInputFilter
27import GlobalShortcut 1.0 // has to be before Utils, because of WindowInputFilter
28import GSettings 1.0
28import GSettings 1.0
29+import ImageCache 0.1
29import Utils 0.1
30import Utils 0.1
30import Powerd 0.1
31import Powerd 0.1
31import SessionBroadcast 0.1
32import SessionBroadcast 0.1
@@ -145,12 +146,49 @@ StyledItem {
145
146
146 WallpaperResolver {
147 WallpaperResolver {
147 id: wallpaperResolver
148 id: wallpaperResolver
148− width: shell.width
149+ objectName: "wallpaperResolver"
150+
151+ readonly property url defaultBackground: "file:///usr/share/backgrounds/warty-final-ubuntu.png"
152+ readonly property bool hasCustomBackground: background != defaultBackground
153+
154+ // Use a cached version of the scaled-down wallpaper (as sometimes the
155+ // image can be quite big compared to the device size, including for
156+ // our default wallpaper). We use a name=wallpaper argument here to
157+ // make sure we don't litter our cache with lots of scaled images. We
158+ // only need to bother caching one at a time.
159+ readonly property url cachedBackground: background.toString().indexOf("file:///") === 0 ? "image://unity8imagecache/" + background + "?name=wallpaper" : background
160+
161+ GSettings {
162+ id: backgroundSettings
163+ schema.id: "org.gnome.desktop.background"
164+ }
165+
166+ candidates: [
167+ AccountsService.backgroundFile,
168+ backgroundSettings.pictureUri,
169+ defaultBackground
170+ ]
149 }
171 }
150
172
151 readonly property alias greeter: greeterLoader.item
173 readonly property alias greeter: greeterLoader.item
152
174
153 function activateApplication(appId) {
175 function activateApplication(appId) {
176+ // Either open the app in our own session, or -- if we're acting as a
177+ // greeter -- ask the user's session to open it for us.
178+ if (shell.mode === "greeter") {
179+ activateURL("application:///" + appId + ".desktop");
180+ } else {
181+ startApp(appId);
182+ }
183+ }
184+
185+ function activateURL(url) {
186+ SessionBroadcast.requestUrlStart(AccountsService.user, url);
187+ greeter.notifyUserRequestedApp();
188+ panel.indicators.hide();
189+ }
190+
191+ function startApp(appId) {
154 if (ApplicationManager.findApplication(appId)) {
192 if (ApplicationManager.findApplication(appId)) {
155 ApplicationManager.requestFocusApplication(appId);
193 ApplicationManager.requestFocusApplication(appId);
156 } else {
194 } else {
@@ -162,7 +200,7 @@ StyledItem {
162 if (greeter.locked) {
200 if (greeter.locked) {
163 greeter.lockedApp = app;
201 greeter.lockedApp = app;
164 }
202 }
165− shell.activateApplication(app);
203+ startApp(app); // locked apps are always in our same session
166 }
204 }
167
205
168 Binding {
206 Binding {
@@ -177,7 +215,6 @@ StyledItem {
177
215
178 VolumeControl {
216 VolumeControl {
179 id: volumeControl
217 id: volumeControl
180− indicators: panel.indicators
181 }
218 }
182
219
183 DashCommunicator {
220 DashCommunicator {
@@ -259,9 +296,7 @@ StyledItem {
259 ? "phone"
296 ? "phone"
260 : shell.usageScenario
297 : shell.usageScenario
261 readonly property string qmlComponent: {
298 readonly property string qmlComponent: {
262− if (shell.mode === "greeter") {
263− return "Stages/AbstractStage.qml"
264− } else if (applicationsDisplayLoader.usageScenario === "phone") {
299+ if (applicationsDisplayLoader.usageScenario === "phone") {
265 return "Stages/PhoneStage.qml";
300 return "Stages/PhoneStage.qml";
266 } else if (applicationsDisplayLoader.usageScenario === "tablet") {
301 } else if (applicationsDisplayLoader.usageScenario === "tablet") {
267 return "Stages/TabletStage.qml";
302 return "Stages/TabletStage.qml";
@@ -339,7 +374,7 @@ StyledItem {
339 Binding {
374 Binding {
340 target: applicationsDisplayLoader.item
375 target: applicationsDisplayLoader.item
341 property: "background"
376 property: "background"
342− value: wallpaperResolver.background
377+ value: wallpaperResolver.cachedBackground
343 }
378 }
344 Binding {
379 Binding {
345 target: applicationsDisplayLoader.item
380 target: applicationsDisplayLoader.item
@@ -420,11 +455,13 @@ StyledItem {
420 id: integratedGreeter
455 id: integratedGreeter
421 Greeter {
456 Greeter {
422
457
458+ enabled: panel.indicators.fullyClosed // hides OSK when panel is open
423 hides: [launcher, panel.indicators]
459 hides: [launcher, panel.indicators]
424 tabletMode: shell.usageScenario != "phone"
460 tabletMode: shell.usageScenario != "phone"
425 launcherOffset: launcher.progress
461 launcherOffset: launcher.progress
426 forcedUnlock: wizard.active || shell.mode === "full-shell"
462 forcedUnlock: wizard.active || shell.mode === "full-shell"
427− background: wallpaperResolver.background
463+ background: wallpaperResolver.cachedBackground
464+ hasCustomBackground: wallpaperResolver.hasCustomBackground
428 allowFingerprint: !dialogs.hasActiveDialog &&
465 allowFingerprint: !dialogs.hasActiveDialog &&
429 !notifications.topmostIsFullscreen &&
466 !notifications.topmostIsFullscreen &&
430 !panel.indicators.shown
467 !panel.indicators.shown
@@ -499,11 +536,15 @@ StyledItem {
499 }
536 }
500
537
501 function showHome() {
538 function showHome() {
502− greeter.notifyUserRequestedApp("unity8-dash");
539+ greeter.notifyUserRequestedApp();
503
540
541+ if (shell.mode === "greeter") {
542+ SessionBroadcast.requestHomeShown(AccountsService.user);
543+ } else {
504− var animate = !LightDMService.greeter.active && !stages.shown
544+ var animate = !LightDMService.greeter.active && !stages.shown;
505− dash.setCurrentScope(0, animate, false)
545+ dash.setCurrentScope(0, animate, false);
506− ApplicationManager.requestFocusApplication("unity8-dash")
546+ ApplicationManager.requestFocusApplication("unity8-dash");
547+ }
507 }
548 }
508
549
509 function showDash() {
550 function showDash() {
@@ -599,7 +640,7 @@ StyledItem {
599 }
640 }
600 }
641 }
601 onLauncherApplicationSelected: {
642 onLauncherApplicationSelected: {
602− greeter.notifyUserRequestedApp(appId);
643+ greeter.notifyUserRequestedApp();
603 shell.activateApplication(appId);
644 shell.activateApplication(appId);
604 }
645 }
605 onShownChanged: {
646 onShownChanged: {
@@ -660,7 +701,7 @@ StyledItem {
660 objectName: "tutorial"
701 objectName: "tutorial"
661 anchors.fill: parent
702 anchors.fill: parent
662
703
663− paused: callManager.hasCalls || !greeter || greeter.shown ||
704+ paused: callManager.hasCalls || !greeter || greeter.active ||
664 wizard.active
705 wizard.active
665 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
706 delayed: dialogs.hasActiveDialog || notifications.hasNotification ||
666 inputMethod.visible ||
707 inputMethod.visible ||
@@ -701,7 +742,7 @@ StyledItem {
701 model: NotificationBackend.Model
742 model: NotificationBackend.Model
702 margin: units.gu(1)
743 margin: units.gu(1)
703 hasMouse: shell.hasMouse
744 hasMouse: shell.hasMouse
704− background: wallpaperResolver.background
745+ background: wallpaperResolver.cachedBackground
705
746
706 y: topmostIsFullscreen ? 0 : panel.panelHeight
747 y: topmostIsFullscreen ? 0 : panel.panelHeight
707 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
748 height: parent.height - (topmostIsFullscreen ? 0 : panel.panelHeight)
@@ -745,7 +786,14 @@ StyledItem {
745
786
746 Connections {
787 Connections {
747 target: SessionBroadcast
788 target: SessionBroadcast
748− onShowHome: showHome()
789+ onShowHome: if (shell.mode !== "greeter") showHome()
790+ }
791+
792+ URLDispatcher {
793+ id: urlDispatcher
794+ objectName: "urlDispatcher"
795+ active: shell.mode === "greeter"
796+ onUrlRequested: shell.activateURL(url)
749 }
797 }
750
798
751 ItemGrabber {
799 ItemGrabber {
@@ -764,6 +812,9 @@ StyledItem {
764 id: cursor
812 id: cursor
765 visible: shell.hasMouse
813 visible: shell.hasMouse
766 z: itemGrabber.z + 1
814 z: itemGrabber.z + 1
815+ topBoundaryOffset: panel.panelHeight
816+
817+ confiningItem: applicationsDisplayLoader.item ? applicationsDisplayLoader.item.itemConfiningMouseCursor : null
767
818
768 property bool mouseNeverMoved: true
819 property bool mouseNeverMoved: true
769 Binding {
820 Binding {
@@ -777,9 +828,16 @@ StyledItem {
777
828
778 height: units.gu(3)
829 height: units.gu(3)
779
830
831+ readonly property var previewRectangle: applicationsDisplayLoader.item && applicationsDisplayLoader.item.previewRectangle &&
832+ applicationsDisplayLoader.item.previewRectangle.target &&
833+ applicationsDisplayLoader.item.previewRectangle.target.dragging ?
834+ applicationsDisplayLoader.item.previewRectangle : null
835+
780 onPushedLeftBoundary: {
836 onPushedLeftBoundary: {
781 if (buttons === Qt.NoButton) {
837 if (buttons === Qt.NoButton) {
782 launcher.pushEdge(amount);
838 launcher.pushEdge(amount);
839+ } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
840+ previewRectangle.maximizeLeft(amount);
783 }
841 }
784 }
842 }
785
843
@@ -787,6 +845,39 @@ StyledItem {
787 if (buttons === Qt.NoButton && applicationsDisplayLoader.item
845 if (buttons === Qt.NoButton && applicationsDisplayLoader.item
788 && applicationsDisplayLoader.item.pushRightEdge) {
846 && applicationsDisplayLoader.item.pushRightEdge) {
789 applicationsDisplayLoader.item.pushRightEdge(amount);
847 applicationsDisplayLoader.item.pushRightEdge(amount);
848+ } else if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximizedLeftRight) {
849+ previewRectangle.maximizeRight(amount);
850+ }
851+ }
852+
853+ onPushedTopBoundary: {
854+ if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeMaximized) {
855+ previewRectangle.maximize(amount);
856+ }
857+ }
858+ onPushedTopLeftCorner: {
859+ if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
860+ previewRectangle.maximizeTopLeft(amount);
861+ }
862+ }
863+ onPushedTopRightCorner: {
864+ if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
865+ previewRectangle.maximizeTopRight(amount);
866+ }
867+ }
868+ onPushedBottomLeftCorner: {
869+ if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
870+ previewRectangle.maximizeBottomLeft(amount);
871+ }
872+ }
873+ onPushedBottomRightCorner: {
874+ if (buttons === Qt.LeftButton && previewRectangle && previewRectangle.target.canBeCornerMaximized) {
875+ previewRectangle.maximizeBottomRight(amount);
876+ }
877+ }
878+ onPushStopped: {
879+ if (previewRectangle) {
880+ previewRectangle.stop();
790 }
881 }
791 }
882 }
792
883
qml/Stages/AbstractStage.qml+3
@@ -55,6 +55,9 @@ FocusScope {
55 | Qt.InvertedPortraitOrientation
55 | Qt.InvertedPortraitOrientation
56 | Qt.InvertedLandscapeOrientation
56 | Qt.InvertedLandscapeOrientation
57
57
58+ property Item itemConfiningMouseCursor: null
59+
60+
58 signal stageAboutToBeUnloaded
61 signal stageAboutToBeUnloaded
59 signal itemSnapshotRequested(Item item)
62 signal itemSnapshotRequested(Item item)
60
63
qml/Stages/DecoratedWindow.qml+20
@@ -52,6 +52,11 @@ FocusScope {
52 readonly property int heightIncrement: !counterRotate ? applicationWindow.heightIncrement : applicationWindow.widthIncrement
52 readonly property int heightIncrement: !counterRotate ? applicationWindow.heightIncrement : applicationWindow.widthIncrement
53
53
54 property alias overlayShown: decoration.overlayShown
54 property alias overlayShown: decoration.overlayShown
55+ property alias stageWidth: moveHandler.stageWidth
56+ property alias stageHeight: moveHandler.stageHeight
57+ readonly property alias dragging: moveHandler.dragging
58+
59+ readonly property Item clientAreaItem: applicationWindow
55
60
56 signal closeClicked()
61 signal closeClicked()
57 signal maximizeClicked()
62 signal maximizeClicked()
@@ -59,6 +64,7 @@ FocusScope {
59 signal maximizeVerticallyClicked()
64 signal maximizeVerticallyClicked()
60 signal minimizeClicked()
65 signal minimizeClicked()
61 signal decorationPressed()
66 signal decorationPressed()
67+ signal decorationReleased()
62
68
63 Rectangle {
69 Rectangle {
64 id: selectionHighlight
70 id: selectionHighlight
@@ -101,6 +107,20 @@ FocusScope {
101 onMaximizeVerticallyClicked: { root.decorationPressed(); root.maximizeVerticallyClicked(); }
107 onMaximizeVerticallyClicked: { root.decorationPressed(); root.maximizeVerticallyClicked(); }
102 onMinimizeClicked: root.minimizeClicked();
108 onMinimizeClicked: root.minimizeClicked();
103 onPressed: root.decorationPressed();
109 onPressed: root.decorationPressed();
110+
111+ onPressedChanged: moveHandler.handlePressedChanged(pressed, pressedButtons, mouseX, mouseY)
112+ onPositionChanged: moveHandler.handlePositionChanged(mouse)
113+ onReleased: {
114+ root.decorationReleased();
115+ moveHandler.handleReleased();
116+ }
117+ }
118+
119+ MoveHandler {
120+ id: moveHandler
121+ objectName: "moveHandler"
122+ target: root.parent
123+ buttonsWidth: decoration.buttonsWidth
104 }
124 }
105
125
106 ApplicationWindow {
126 ApplicationWindow {
qml/Stages/DesktopStage.qml+146−72
@@ -45,6 +45,9 @@ AbstractStage {
45 // Used by TutorialRight
45 // Used by TutorialRight
46 property bool spreadShown: spread.state == "altTab"
46 property bool spreadShown: spread.state == "altTab"
47
47
48+ // used by the snap windows (edge maximize) feature
49+ readonly property alias previewRectangle: fakeRectangle
50+
48 mainApp: priv.focusedAppDelegate ? priv.focusedAppDelegate.application : null
51 mainApp: priv.focusedAppDelegate ? priv.focusedAppDelegate.application : null
49
52
50 // application windows never rotate independently
53 // application windows never rotate independently
@@ -52,6 +55,11 @@ AbstractStage {
52
55
53 orientationChangesEnabled: true
56 orientationChangesEnabled: true
54
57
58+
59+ itemConfiningMouseCursor: !spreadShown && priv.focusedAppDelegate && priv.focusedAppDelegate.surface &&
60+ priv.focusedAppDelegate.surface.confinesMousePointer ?
61+ priv.focusedAppDelegate.clientAreaItem : null;
62+
55 GlobalShortcut {
63 GlobalShortcut {
56 id: showSpreadShortcut
64 id: showSpreadShortcut
57 shortcut: Qt.MetaModifier|Qt.Key_W
65 shortcut: Qt.MetaModifier|Qt.Key_W
@@ -68,28 +76,27 @@ AbstractStage {
68 id: maximizeWindowShortcut
76 id: maximizeWindowShortcut
69 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Up
77 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Up
70 onTriggered: priv.focusedAppDelegate.maximize()
78 onTriggered: priv.focusedAppDelegate.maximize()
71− active: priv.focusedAppDelegate !== null
79+ active: priv.focusedAppDelegate !== null && priv.focusedAppDelegate.canBeMaximized
72 }
80 }
73
81
74 GlobalShortcut {
82 GlobalShortcut {
75 id: maximizeWindowLeftShortcut
83 id: maximizeWindowLeftShortcut
76 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Left
84 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Left
77 onTriggered: priv.focusedAppDelegate.maximizeLeft()
85 onTriggered: priv.focusedAppDelegate.maximizeLeft()
78− active: priv.focusedAppDelegate !== null
86+ active: priv.focusedAppDelegate !== null && priv.focusedAppDelegate.canBeMaximizedLeftRight
79 }
87 }
80
88
81 GlobalShortcut {
89 GlobalShortcut {
82 id: maximizeWindowRightShortcut
90 id: maximizeWindowRightShortcut
83 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Right
91 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Right
84 onTriggered: priv.focusedAppDelegate.maximizeRight()
92 onTriggered: priv.focusedAppDelegate.maximizeRight()
85− active: priv.focusedAppDelegate !== null
93+ active: priv.focusedAppDelegate !== null && priv.focusedAppDelegate.canBeMaximizedLeftRight
86 }
94 }
87
95
88 GlobalShortcut {
96 GlobalShortcut {
89 id: minimizeRestoreShortcut
97 id: minimizeRestoreShortcut
90 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Down
98 shortcut: Qt.MetaModifier|Qt.ControlModifier|Qt.Key_Down
91− onTriggered: priv.focusedAppDelegate.maximized || priv.focusedAppDelegate.maximizedLeft || priv.focusedAppDelegate.maximizedRight ||
99+ onTriggered: priv.focusedAppDelegate.anyMaximized
92− priv.focusedAppDelegate.maximizedHorizontally || priv.focusedAppDelegate.maximizedVertically
93 ? priv.focusedAppDelegate.restoreFromMaximized() : priv.focusedAppDelegate.minimize()
100 ? priv.focusedAppDelegate.restoreFromMaximized() : priv.focusedAppDelegate.minimize()
94 active: priv.focusedAppDelegate !== null
101 active: priv.focusedAppDelegate !== null
95 }
102 }
@@ -165,11 +172,6 @@ AbstractStage {
165 onCloseClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.close(); } }
172 onCloseClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.close(); } }
166 onMinimizeClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.minimize(); } }
173 onMinimizeClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.minimize(); } }
167 onRestoreClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.restoreFromMaximized(); } }
174 onRestoreClicked: { if (priv.focusedAppDelegate) { priv.focusedAppDelegate.restoreFromMaximized(); } }
168− onFocusMaximizedApp: {
169− if (priv.foregroundMaximizedAppDelegate) {
170− priv.foregroundMaximizedAppDelegate.focus = true;
171− }
172− }
173 }
175 }
174
176
175 Binding {
177 Binding {
@@ -240,12 +242,10 @@ AbstractStage {
240 anchors.fill: parent
242 anchors.fill: parent
241 focus: spread.state !== "altTab"
243 focus: spread.state !== "altTab"
242
244
243− CrossFadeImage {
245+ Wallpaper {
244 id: wallpaper
246 id: wallpaper
245 anchors.fill: parent
247 anchors.fill: parent
246 source: root.background
248 source: root.background
247− sourceSize { height: root.height; width: root.width }
248− fillMode: Image.PreserveAspectCrop
249 }
249 }
250
250
251 TopLevelSurfaceRepeater {
251 TopLevelSurfaceRepeater {
@@ -276,7 +276,7 @@ AbstractStage {
276 value: appDelegate.requestedY -
276 value: appDelegate.requestedY -
277 Math.min(appDelegate.requestedY - PanelState.panelHeight,
277 Math.min(appDelegate.requestedY - PanelState.panelHeight,
278 Math.max(0, priv.virtualKeyboardHeight - (appContainer.height - (appDelegate.requestedY + appDelegate.height))))
278 Math.max(0, priv.virtualKeyboardHeight - (appContainer.height - (appDelegate.requestedY + appDelegate.height))))
279− when: root.oskEnabled && appDelegate.focus && appDelegate.state == "normal"
279+ when: root.oskEnabled && appDelegate.focus && (appDelegate.state == "normal" || appDelegate.state == "restored")
280 && SurfaceManager.inputMethodSurface
280 && SurfaceManager.inputMethodSurface
281 && SurfaceManager.inputMethodSurface.state != Mir.HiddenState
281 && SurfaceManager.inputMethodSurface.state != Mir.HiddenState
282 && SurfaceManager.inputMethodSurface.state != Mir.MinimizedState
282 && SurfaceManager.inputMethodSurface.state != Mir.MinimizedState
@@ -291,7 +291,7 @@ AbstractStage {
291 onShellOrientationAngleChanged: {
291 onShellOrientationAngleChanged: {
292 // at this point decoratedWindow.surfaceOrientationAngle is the old shellOrientationAngle
292 // at this point decoratedWindow.surfaceOrientationAngle is the old shellOrientationAngle
293 if (application && application.rotatesWindowContents) {
293 if (application && application.rotatesWindowContents) {
294− if (state == "normal") {
294+ if (state == "normal" || state == "restored") {
295 var angleDiff = decoratedWindow.surfaceOrientationAngle - shellOrientationAngle;
295 var angleDiff = decoratedWindow.surfaceOrientationAngle - shellOrientationAngle;
296 angleDiff = (360 + angleDiff) % 360;
296 angleDiff = (360 + angleDiff) % 360;
297 if (angleDiff === 90 || angleDiff === 270) {
297 if (angleDiff === 90 || angleDiff === 270) {
@@ -317,14 +317,29 @@ AbstractStage {
317 property int requestedWidth: -1
317 property int requestedWidth: -1
318 property int requestedHeight: -1
318 property int requestedHeight: -1
319
319
320− readonly property bool maximized: windowState & WindowStateStorage.WindowStateMaximized
320+ readonly property bool maximized: windowState === WindowStateStorage.WindowStateMaximized
321− readonly property bool maximizedLeft: windowState & WindowStateStorage.WindowStateMaximizedLeft
321+ readonly property bool maximizedLeft: windowState === WindowStateStorage.WindowStateMaximizedLeft
322− readonly property bool maximizedRight: windowState & WindowStateStorage.WindowStateMaximizedRight
322+ readonly property bool maximizedRight: windowState === WindowStateStorage.WindowStateMaximizedRight
323− readonly property bool maximizedHorizontally: windowState & WindowStateStorage.WindowStateMaximizedHorizontally
323+ readonly property bool maximizedHorizontally: windowState === WindowStateStorage.WindowStateMaximizedHorizontally
324− readonly property bool maximizedVertically: windowState & WindowStateStorage.WindowStateMaximizedVertically
324+ readonly property bool maximizedVertically: windowState === WindowStateStorage.WindowStateMaximizedVertically
325+ readonly property bool maximizedTopLeft: windowState === WindowStateStorage.WindowStateMaximizedTopLeft
326+ readonly property bool maximizedTopRight: windowState === WindowStateStorage.WindowStateMaximizedTopRight
327+ readonly property bool maximizedBottomLeft: windowState === WindowStateStorage.WindowStateMaximizedBottomLeft
328+ readonly property bool maximizedBottomRight: windowState === WindowStateStorage.WindowStateMaximizedBottomRight
329+ readonly property bool anyMaximized: maximized || maximizedLeft || maximizedRight || maximizedHorizontally || maximizedVertically ||
330+ maximizedTopLeft || maximizedTopRight || maximizedBottomLeft || maximizedBottomRight
331+
325 readonly property bool minimized: windowState & WindowStateStorage.WindowStateMinimized
332 readonly property bool minimized: windowState & WindowStateStorage.WindowStateMinimized
326 readonly property alias fullscreen: decoratedWindow.fullscreen
333 readonly property alias fullscreen: decoratedWindow.fullscreen
327
334
335+ readonly property bool canBeMaximized: canBeMaximizedHorizontally && canBeMaximizedVertically
336+ readonly property bool canBeMaximizedLeftRight: (maximumWidth == 0 || maximumWidth >= appContainer.width/2) &&
337+ (maximumHeight == 0 || maximumHeight >= appContainer.height)
338+ readonly property bool canBeCornerMaximized: (maximumWidth == 0 || maximumWidth >= appContainer.width/2) &&
339+ (maximumHeight == 0 || maximumHeight >= appContainer.height/2)
340+ readonly property bool canBeMaximizedHorizontally: maximumWidth == 0 || maximumWidth >= appContainer.width
341+ readonly property bool canBeMaximizedVertically: maximumHeight == 0 || maximumHeight >= appContainer.height
342+
328 property int windowState: WindowStateStorage.WindowStateNormal
343 property int windowState: WindowStateStorage.WindowStateNormal
329 property bool animationsEnabled: true
344 property bool animationsEnabled: true
330 property alias title: decoratedWindow.title
345 property alias title: decoratedWindow.title
@@ -335,14 +350,16 @@ AbstractStage {
335 readonly property var surface: model.surface
350 readonly property var surface: model.surface
336 readonly property alias resizeArea: resizeArea
351 readonly property alias resizeArea: resizeArea
337 readonly property alias focusedSurface: decoratedWindow.focusedSurface
352 readonly property alias focusedSurface: decoratedWindow.focusedSurface
353+ readonly property bool dragging: touchControls.overlayShown ? touchControls.dragging : decoratedWindow.dragging
338
354
339 readonly property bool isDash: model.application.appId == "unity8-dash"
355 readonly property bool isDash: model.application.appId == "unity8-dash"
356+ readonly property alias clientAreaItem: decoratedWindow.clientAreaItem
340
357
341 function claimFocus() {
358 function claimFocus() {
342 if (spread.state == "altTab") {
359 if (spread.state == "altTab") {
343 spread.cancel();
360 spread.cancel();
344 }
361 }
345− appDelegate.restore();
362+ appDelegate.restore(true /* animated */, appDelegate.windowState);
346 }
363 }
347 Connections {
364 Connections {
348 target: model.surface
365 target: model.surface
@@ -450,28 +467,34 @@ AbstractStage {
450 animationsEnabled = (animated === undefined) || animated;
467 animationsEnabled = (animated === undefined) || animated;
451 windowState = WindowStateStorage.WindowStateMaximizedVertically;
468 windowState = WindowStateStorage.WindowStateMaximizedVertically;
452 }
469 }
470+ function maximizeTopLeft(animated) {
471+ animationsEnabled = (animated === undefined) || animated;
472+ windowState = WindowStateStorage.WindowStateMaximizedTopLeft;
473+ }
474+ function maximizeTopRight(animated) {
475+ animationsEnabled = (animated === undefined) || animated;
476+ windowState = WindowStateStorage.WindowStateMaximizedTopRight;
477+ }
478+ function maximizeBottomLeft(animated) {
479+ animationsEnabled = (animated === undefined) || animated;
480+ windowState = WindowStateStorage.WindowStateMaximizedBottomLeft;
481+ }
482+ function maximizeBottomRight(animated) {
483+ animationsEnabled = (animated === undefined) || animated;
484+ windowState = WindowStateStorage.WindowStateMaximizedBottomRight;
485+ }
453 function minimize(animated) {
486 function minimize(animated) {
454 animationsEnabled = (animated === undefined) || animated;
487 animationsEnabled = (animated === undefined) || animated;
455 windowState |= WindowStateStorage.WindowStateMinimized; // add the minimized bit
488 windowState |= WindowStateStorage.WindowStateMinimized; // add the minimized bit
456 }
489 }
457 function restoreFromMaximized(animated) {
490 function restoreFromMaximized(animated) {
458 animationsEnabled = (animated === undefined) || animated;
491 animationsEnabled = (animated === undefined) || animated;
459− windowState = WindowStateStorage.WindowStateNormal;
492+ windowState = WindowStateStorage.WindowStateRestored;
460 }
493 }
461− function restore(animated) {
494+ function restore(animated,state) {
462 animationsEnabled = (animated === undefined) || animated;
495 animationsEnabled = (animated === undefined) || animated;
496+ windowState = state || WindowStateStorage.WindowStateRestored;
463 windowState &= ~WindowStateStorage.WindowStateMinimized; // clear the minimized bit
497 windowState &= ~WindowStateStorage.WindowStateMinimized; // clear the minimized bit
464− if (maximized)
465− maximize();
466− else if (maximizedLeft)
467− maximizeLeft();
468− else if (maximizedRight)
469− maximizeRight();
470− else if (maximizedHorizontally)
471− maximizeHorizontally();
472− else if (maximizedVertically)
473− maximizeVertically();
474−
475 focus = true;
498 focus = true;
476 }
499 }
477
500
@@ -488,9 +511,14 @@ AbstractStage {
488 duration: UbuntuAnimation.SnapDuration
511 duration: UbuntuAnimation.SnapDuration
489 }
512 }
490
513
514+ // unlike requestedX/Y, this is the last known grab position before being pushed against edges/corners
515+ // when restoring, the window should return to these, not to the place where it was dropped near the edge
516+ property real restoredX
517+ property real restoredY
518+
491 states: [
519 states: [
492 State {
520 State {
493− name: "fullscreen"; when: decoratedWindow.fullscreen && !appDelegate.minimized
521+ name: "fullscreen"; when: appDelegate.fullscreen && !appDelegate.minimized
494 PropertyChanges {
522 PropertyChanges {
495 target: appDelegate;
523 target: appDelegate;
496 x: rotation == 0 ? 0 : (parent.width - width) / 2 + (shellOrientationAngle == 90 ? -PanelState.panelHeight : PanelState.panelHeight)
524 x: rotation == 0 ? 0 : (parent.width - width) / 2 + (shellOrientationAngle == 90 ? -PanelState.panelHeight : PanelState.panelHeight)
@@ -509,61 +537,94 @@ AbstractStage {
509 }
537 }
510 },
538 },
511 State {
539 State {
512− name: "maximized"; when: appDelegate.maximized && !appDelegate.minimized
540+ name: "restored";
541+ when: appDelegate.windowState == WindowStateStorage.WindowStateRestored
542+ extend: "normal"
513 PropertyChanges {
543 PropertyChanges {
514 target: appDelegate;
544 target: appDelegate;
515− x: root.leftMargin;
545+ requestedX: restoredX;
516− y: 0;
546+ requestedY: restoredY;
517− visuallyMinimized: false;
518− visuallyMaximized: true
519 }
547 }
548+ },
549+ State {
550+ name: "maximized"; when: appDelegate.maximized && !appDelegate.minimized
520 PropertyChanges {
551 PropertyChanges {
521− target: decoratedWindow
552+ target: appDelegate;
553+ requestedX: root.leftMargin;
554+ requestedY: 0;
522 requestedWidth: appContainer.width - root.leftMargin;
555 requestedWidth: appContainer.width - root.leftMargin;
523 requestedHeight: appContainer.height;
556 requestedHeight: appContainer.height;
557+ visuallyMinimized: false;
558+ visuallyMaximized: true
524 }
559 }
525 },
560 },
526 State {
561 State {
527 name: "maximizedLeft"; when: appDelegate.maximizedLeft && !appDelegate.minimized
562 name: "maximizedLeft"; when: appDelegate.maximizedLeft && !appDelegate.minimized
528 PropertyChanges {
563 PropertyChanges {
529 target: appDelegate
564 target: appDelegate
530− x: root.leftMargin
565+ requestedX: root.leftMargin
531− y: PanelState.panelHeight
566+ requestedY: PanelState.panelHeight
532− }
533− PropertyChanges {
534− target: decoratedWindow
535 requestedWidth: (appContainer.width - root.leftMargin)/2
567 requestedWidth: (appContainer.width - root.leftMargin)/2
536 requestedHeight: appContainer.height - PanelState.panelHeight
568 requestedHeight: appContainer.height - PanelState.panelHeight
537 }
569 }
538 },
570 },
539 State {
571 State {
540 name: "maximizedRight"; when: appDelegate.maximizedRight && !appDelegate.minimized
572 name: "maximizedRight"; when: appDelegate.maximizedRight && !appDelegate.minimized
573+ extend: "maximizedLeft"
541 PropertyChanges {
574 PropertyChanges {
542 target: appDelegate;
575 target: appDelegate;
543− x: (appContainer.width + root.leftMargin)/2
576+ requestedX: (appContainer.width + root.leftMargin)/2
544− y: PanelState.panelHeight
545 }
577 }
578+ },
579+ State {
580+ name: "maximizedTopLeft"; when: appDelegate.maximizedTopLeft && !appDelegate.minimized
546 PropertyChanges {
581 PropertyChanges {
547− target: decoratedWindow
582+ target: appDelegate
583+ requestedX: root.leftMargin
584+ requestedY: PanelState.panelHeight
548 requestedWidth: (appContainer.width - root.leftMargin)/2
585 requestedWidth: (appContainer.width - root.leftMargin)/2
549− requestedHeight: appContainer.height - PanelState.panelHeight
586+ requestedHeight: (appContainer.height - PanelState.panelHeight)/2
587+ }
588+ },
589+ State {
590+ name: "maximizedTopRight"; when: appDelegate.maximizedTopRight && !appDelegate.minimized
591+ extend: "maximizedTopLeft"
592+ PropertyChanges {
593+ target: appDelegate
594+ requestedX: (appContainer.width + root.leftMargin)/2
595+ }
596+ },
597+ State {
598+ name: "maximizedBottomLeft"; when: appDelegate.maximizedBottomLeft && !appDelegate.minimized
599+ PropertyChanges {
600+ target: appDelegate
601+ requestedX: root.leftMargin
602+ requestedY: (appContainer.height + PanelState.panelHeight)/2
603+ requestedWidth: (appContainer.width - root.leftMargin)/2
604+ requestedHeight: appContainer.height/2
605+ }
606+ },
607+ State {
608+ name: "maximizedBottomRight"; when: appDelegate.maximizedBottomRight && !appDelegate.minimized
609+ extend: "maximizedBottomLeft"
610+ PropertyChanges {
611+ target: appDelegate
612+ requestedX: (appContainer.width + root.leftMargin)/2
550 }
613 }
551 },
614 },
552 State {
615 State {
553 name: "maximizedHorizontally"; when: appDelegate.maximizedHorizontally && !appDelegate.minimized
616 name: "maximizedHorizontally"; when: appDelegate.maximizedHorizontally && !appDelegate.minimized
554− PropertyChanges { target: appDelegate; x: root.leftMargin }
617+ PropertyChanges { target: appDelegate; requestedX: root.leftMargin; requestedY: requestedY; requestedWidth: appContainer.width - root.leftMargin }
555− PropertyChanges { target: decoratedWindow; requestedWidth: appContainer.width - root.leftMargin }
556 },
618 },
557 State {
619 State {
558 name: "maximizedVertically"; when: appDelegate.maximizedVertically && !appDelegate.minimized
620 name: "maximizedVertically"; when: appDelegate.maximizedVertically && !appDelegate.minimized
559− PropertyChanges { target: appDelegate; y: PanelState.panelHeight }
621+ PropertyChanges { target: appDelegate; requestedX: requestedX; requestedY: PanelState.panelHeight; requestedHeight: appContainer.height - PanelState.panelHeight }
560− PropertyChanges { target: decoratedWindow; requestedHeight: appContainer.height - PanelState.panelHeight }
561 },
622 },
562 State {
623 State {
563 name: "minimized"; when: appDelegate.minimized
624 name: "minimized"; when: appDelegate.minimized
564 PropertyChanges {
625 PropertyChanges {
565 target: appDelegate;
626 target: appDelegate;
566− x: -appDelegate.width / 2;
627+ requestedX: -appDelegate.width / 2;
567 scale: units.gu(5) / appDelegate.width;
628 scale: units.gu(5) / appDelegate.width;
568 opacity: 0;
629 opacity: 0;
569 visuallyMinimized: true;
630 visuallyMinimized: true;
@@ -573,21 +634,17 @@ AbstractStage {
573 ]
634 ]
574 transitions: [
635 transitions: [
575 Transition {
636 Transition {
576− to: "normal"
637+ to: "normal,restored"
577 enabled: appDelegate.animationsEnabled
638 enabled: appDelegate.animationsEnabled
578 PropertyAction { target: appDelegate; properties: "visuallyMinimized,visuallyMaximized" }
639 PropertyAction { target: appDelegate; properties: "visuallyMinimized,visuallyMaximized" }
579− UbuntuNumberAnimation { target: appDelegate; properties: "x,y,opacity,requestedWidth,requestedHeight,scale"; duration: UbuntuAnimation.FastDuration }
640+ UbuntuNumberAnimation { target: appDelegate; properties: "requestedX,requestedY,opacity,scale,requestedWidth,requestedHeight" }
580− UbuntuNumberAnimation { target: decoratedWindow; properties: "requestedWidth,requestedHeight"; duration: UbuntuAnimation.FastDuration }
581 },
641 },
582 Transition {
642 Transition {
583 to: "minimized"
643 to: "minimized"
584 enabled: appDelegate.animationsEnabled
644 enabled: appDelegate.animationsEnabled
585 PropertyAction { target: appDelegate; property: "visuallyMaximized" }
645 PropertyAction { target: appDelegate; property: "visuallyMaximized" }
586 SequentialAnimation {
646 SequentialAnimation {
587− ParallelAnimation {
588− UbuntuNumberAnimation { target: appDelegate; properties: "x,y,opacity,scale"; duration: UbuntuAnimation.FastDuration }
647+ UbuntuNumberAnimation { target: appDelegate; properties: "requestedX,requestedY,opacity,scale,requestedWidth,requestedHeight" }
589− UbuntuNumberAnimation { target: decoratedWindow; properties: "requestedWidth,requestedHeight"; duration: UbuntuAnimation.FastDuration }
590− }
591 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
648 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
592 ScriptAction {
649 ScriptAction {
593 script: {
650 script: {
@@ -604,11 +661,9 @@ AbstractStage {
604 enabled: appDelegate.animationsEnabled
661 enabled: appDelegate.animationsEnabled
605 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
662 PropertyAction { target: appDelegate; property: "visuallyMinimized" }
606 SequentialAnimation {
663 SequentialAnimation {
607− ParallelAnimation {
608− UbuntuNumberAnimation { target: appDelegate; properties: "x,y,opacity,scale"; duration: UbuntuAnimation.FastDuration }
664+ UbuntuNumberAnimation { target: appDelegate; properties: "requestedX,requestedY,opacity,scale,requestedWidth,requestedHeight" }
609− UbuntuNumberAnimation { target: decoratedWindow; properties: "requestedWidth,requestedHeight"; duration: UbuntuAnimation.FastDuration }
610− }
611 PropertyAction { target: appDelegate; property: "visuallyMaximized" }
665 PropertyAction { target: appDelegate; property: "visuallyMaximized" }
666+ ScriptAction { script: { fakeRectangle.stop(); } }
612 }
667 }
613 }
668 }
614 ]
669 ]
@@ -674,27 +729,38 @@ AbstractStage {
674 surface: model.surface
729 surface: model.surface
675 active: appDelegate.focus
730 active: appDelegate.focus
676 focus: true
731 focus: true
677− maximizeButtonShown: (maximumWidth == 0 || maximumWidth >= appContainer.width) &&
732+ maximizeButtonShown: appDelegate.canBeMaximized
678− (maximumHeight == 0 || maximumHeight >= appContainer.height)
679 overlayShown: touchControls.overlayShown
733 overlayShown: touchControls.overlayShown
734+ stageWidth: appContainer.width
735+ stageHeight: appContainer.height
680
736
681 requestedWidth: appDelegate.requestedWidth
737 requestedWidth: appDelegate.requestedWidth
682 requestedHeight: appDelegate.requestedHeight
738 requestedHeight: appDelegate.requestedHeight
683
739
684 onCloseClicked: { appDelegate.close(); }
740 onCloseClicked: { appDelegate.close(); }
685− onMaximizeClicked: appDelegate.maximized || appDelegate.maximizedLeft || appDelegate.maximizedRight
686− || appDelegate.maximizedHorizontally || appDelegate.maximizedVertically
687− ? appDelegate.restoreFromMaximized() : appDelegate.maximize()
741+ onMaximizeClicked: appDelegate.anyMaximized ? appDelegate.restoreFromMaximized() : appDelegate.maximize();
688 onMaximizeHorizontallyClicked: appDelegate.maximizedHorizontally ? appDelegate.restoreFromMaximized() : appDelegate.maximizeHorizontally()
742 onMaximizeHorizontallyClicked: appDelegate.maximizedHorizontally ? appDelegate.restoreFromMaximized() : appDelegate.maximizeHorizontally()
689 onMaximizeVerticallyClicked: appDelegate.maximizedVertically ? appDelegate.restoreFromMaximized() : appDelegate.maximizeVertically()
743 onMaximizeVerticallyClicked: appDelegate.maximizedVertically ? appDelegate.restoreFromMaximized() : appDelegate.maximizeVertically()
690 onMinimizeClicked: appDelegate.minimize()
744 onMinimizeClicked: appDelegate.minimize()
691 onDecorationPressed: { appDelegate.focus = true; }
745 onDecorationPressed: { appDelegate.focus = true; }
746+ onDecorationReleased: fakeRectangle.commit();
692 }
747 }
693
748
694 WindowControlsOverlay {
749 WindowControlsOverlay {
695 id: touchControls
750 id: touchControls
696− anchors.fill: appDelegate
697 target: appDelegate
751 target: appDelegate
752+ stageWidth: appContainer.width
753+ stageHeight: appContainer.height
754+
755+ onFakeMaximizeAnimationRequested: if (!appDelegate.maximized) fakeRectangle.maximize(amount, true)
756+ onFakeMaximizeLeftAnimationRequested: if (!appDelegate.maximizedLeft) fakeRectangle.maximizeLeft(amount, true)
757+ onFakeMaximizeRightAnimationRequested: if (!appDelegate.maximizedRight) fakeRectangle.maximizeRight(amount, true)
758+ onFakeMaximizeTopLeftAnimationRequested: if (!appDelegate.maximizedTopLeft) fakeRectangle.maximizeTopLeft(amount, true);
759+ onFakeMaximizeTopRightAnimationRequested: if (!appDelegate.maximizedTopRight) fakeRectangle.maximizeTopRight(amount, true);
760+ onFakeMaximizeBottomLeftAnimationRequested: if (!appDelegate.maximizedBottomLeft) fakeRectangle.maximizeBottomLeft(amount, true);
761+ onFakeMaximizeBottomRightAnimationRequested: if (!appDelegate.maximizedBottomRight) fakeRectangle.maximizeBottomRight(amount, true);
762+ onStopFakeAnimation: fakeRectangle.stop();
763+ onDragReleased: fakeRectangle.commit();
698 }
764 }
699
765
700 WindowedFullscreenPolicy {
766 WindowedFullscreenPolicy {
@@ -706,6 +772,14 @@ AbstractStage {
706 }
772 }
707 }
773 }
708
774
775+ FakeMaximizeDelegate {
776+ id: fakeRectangle
777+ target: priv.focusedAppDelegate
778+ leftMargin: root.leftMargin
779+ appContainerWidth: appContainer.width
780+ appContainerHeight: appContainer.height
781+ }
782+
709 EdgeBarrier {
783 EdgeBarrier {
710 id: edgeBarrier
784 id: edgeBarrier
711
785
qml/Stages/FakeMaximizeDelegate.qmlnew file+233
@@ -0,0 +1,233 @@
1+/*
2+ * Copyright (C) 2016 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 Utils 0.1 // For EdgeBarrierSettings
20+import "../Components/PanelState"
21+
22+Rectangle {
23+ id: fakeRectangle
24+ visible: opacity > 0 && target && !target.anyMaximized // we go from 0.2 to 0.5
25+ enabled: visible
26+ color: "#ffffff"
27+ border.width: units.dp(2)
28+ border.color: "#99ffffff"
29+
30+ scale: progress > 0 && progress <= hintThreshold ? MathUtils.projectValue(progress, 0.0, 1.0, 1, 2) : 1
31+ opacity: progress > 0 ? MathUtils.projectValue(progress, 0.0, 1.0, 0.2, 0.5) : 0
32+
33+ property int edge: -1 // Item.TransformOrigin
34+ property var target // appDelegate
35+ property int leftMargin
36+ property real appContainerWidth
37+ property real appContainerHeight
38+
39+ readonly property real hintThreshold: 0.1
40+
41+ // Edge push progress
42+ // Value range is [0.0, 1.0]
43+ readonly property real progress: priv.directProgress != -1 ? priv.directProgress : priv.accumulatedProgress
44+
45+ signal passed(int origin)
46+
47+ QtObject {
48+ id: priv
49+
50+ readonly property real accumulatedProgress: MathUtils.clamp(accumulatedPush / EdgeBarrierSettings.pushThreshold, 0.0, 1.0)
51+ property real directProgress: -1
52+ property real accumulatedPush: 0
53+
54+ function push(amount) {
55+ if (accumulatedPush === EdgeBarrierSettings.pushThreshold) {
56+ // NO-OP
57+ return;
58+ }
59+
60+ if (accumulatedPush + amount > EdgeBarrierSettings.pushThreshold) {
61+ accumulatedPush = EdgeBarrierSettings.pushThreshold;
62+ } else {
63+ accumulatedPush += amount;
64+ }
65+
66+ if (accumulatedPush === EdgeBarrierSettings.pushThreshold) {
67+ passed(edge);
68+ // commit(); // NB: uncomment to have automatic maximization on 100% progress
69+ }
70+ }
71+
72+ function setup(edge) {
73+ if (edge !== fakeRectangle.edge) {
74+ stop(); // a different edge, start anew
75+ }
76+ fakeRectangle.x = target.x;
77+ fakeRectangle.y = target.y;
78+ fakeRectangle.width = target.width;
79+ fakeRectangle.height = target.height;
80+ fakeRectangle.edge = edge;
81+ fakeRectangle.transformOrigin = edge;
82+ }
83+
84+ function processAnimation(amount, animation, isProgress) {
85+ if (isProgress) {
86+ priv.directProgress = amount;
87+ } else {
88+ priv.directProgress = -1;
89+ priv.push(amount);
90+ }
91+
92+ if (progress > hintThreshold) { // above 10% we start the full preview animation
93+ animation.start();
94+ }
95+ }
96+ }
97+
98+ function commit() {
99+ if (progress > hintThreshold && edge != -1) {
100+ if (edge == Item.Top) {
101+ target.maximize();
102+ } else if (edge == Item.Left) {
103+ target.maximizeLeft();
104+ } else if (edge == Item.Right) {
105+ target.maximizeRight();
106+ } else if (edge == Item.TopLeft) {
107+ target.maximizeTopLeft();
108+ } else if (edge == Item.TopRight) {
109+ target.maximizeTopRight();
110+ } else if (edge == Item.BottomLeft) {
111+ target.maximizeBottomLeft();
112+ } else if (edge == Item.BottomRight) {
113+ target.maximizeBottomRight();
114+ }
115+ } else {
116+ stop();
117+ }
118+ }
119+
120+ function stop() {
121+ priv.accumulatedPush = 0;
122+ priv.directProgress = -1;
123+ edge = -1;
124+ }
125+
126+ function maximize(amount, isProgress) {
127+ if (fakeRectangle.edge != Item.Top) {
128+ priv.setup(Item.Top);
129+ }
130+ priv.processAnimation(amount, fakeMaximizeAnimation, isProgress);
131+ }
132+
133+ function maximizeLeft(amount, isProgress) {
134+ if (fakeRectangle.edge != Item.Left) {
135+ priv.setup(Item.Left);
136+ }
137+ priv.processAnimation(amount, fakeMaximizeLeftAnimation, isProgress);
138+ }
139+
140+ function maximizeRight(amount, isProgress) {
141+ if (fakeRectangle.edge != Item.Right) {
142+ priv.setup(Item.Right);
143+ }
144+ priv.processAnimation(amount, fakeMaximizeRightAnimation, isProgress);
145+ }
146+
147+ function maximizeTopLeft(amount, isProgress) {
148+ if (fakeRectangle.edge != Item.TopLeft) {
149+ priv.setup(Item.TopLeft);
150+ }
151+ priv.processAnimation(amount, fakeMaximizeTopLeftAnimation, isProgress);
152+ }
153+
154+ function maximizeTopRight(amount, isProgress) {
155+ if (fakeRectangle.edge != Item.TopRight) {
156+ priv.setup(Item.TopRight);
157+ }
158+ priv.processAnimation(amount, fakeMaximizeTopRightAnimation, isProgress);
159+ }
160+
161+ function maximizeBottomLeft(amount, isProgress) {
162+ if (fakeRectangle.edge != Item.BottomLeft) {
163+ priv.setup(Item.BottomLeft);
164+ }
165+ priv.processAnimation(amount, fakeMaximizeBottomLeftAnimation, isProgress);
166+ }
167+
168+ function maximizeBottomRight(amount, isProgress) {
169+ if (fakeRectangle.edge != Item.BottomRight) {
170+ priv.setup(Item.BottomRight);
171+ }
172+ priv.processAnimation(amount, fakeMaximizeBottomRightAnimation, isProgress);
173+ }
174+
175+ Behavior on opacity { UbuntuNumberAnimation { duration: UbuntuAnimation.BriskDuration } }
176+ Behavior on scale { UbuntuNumberAnimation { duration: UbuntuAnimation.BriskDuration } }
177+
178+ ParallelAnimation {
179+ id: fakeMaximizeAnimation
180+ UbuntuNumberAnimation { target: fakeRectangle; properties: "x"; duration: UbuntuAnimation.BriskDuration; to: leftMargin }
181+ UbuntuNumberAnimation { target: fakeRectangle; properties: "y"; duration: UbuntuAnimation.BriskDuration; to: PanelState.panelHeight }
182+ UbuntuNumberAnimation { target: fakeRectangle; properties: "width"; duration: UbuntuAnimation.BriskDuration; to: appContainerWidth - leftMargin }
183+ UbuntuNumberAnimation { target: fakeRectangle; properties: "height"; duration: UbuntuAnimation.BriskDuration; to: appContainerHeight }
184+ }
185+
186+ ParallelAnimation {
187+ id: fakeMaximizeLeftAnimation
188+ UbuntuNumberAnimation { target: fakeRectangle; properties: "x"; duration: UbuntuAnimation.BriskDuration; to: leftMargin }
189+ UbuntuNumberAnimation { target: fakeRectangle; properties: "y"; duration: UbuntuAnimation.BriskDuration; to: PanelState.panelHeight }
190+ UbuntuNumberAnimation { target: fakeRectangle; properties: "width"; duration: UbuntuAnimation.BriskDuration; to: (appContainerWidth - leftMargin)/2 }
191+ UbuntuNumberAnimation { target: fakeRectangle; properties: "height"; duration: UbuntuAnimation.BriskDuration; to: appContainerHeight - PanelState.panelHeight }
192+ }
193+
194+ ParallelAnimation {
195+ id: fakeMaximizeRightAnimation
196+ UbuntuNumberAnimation { target: fakeRectangle; properties: "x"; duration: UbuntuAnimation.BriskDuration; to: (appContainerWidth + leftMargin)/2 }
197+ UbuntuNumberAnimation { target: fakeRectangle; properties: "y"; duration: UbuntuAnimation.BriskDuration; to: PanelState.panelHeight }
198+ UbuntuNumberAnimation { target: fakeRectangle; properties: "width"; duration: UbuntuAnimation.BriskDuration; to: (appContainerWidth - leftMargin)/2 }
199+ UbuntuNumberAnimation { target: fakeRectangle; properties: "height"; duration: UbuntuAnimation.BriskDuration; to: appContainerHeight - PanelState.panelHeight }
200+ }
201+
202+ ParallelAnimation {
203+ id: fakeMaximizeTopLeftAnimation
204+ UbuntuNumberAnimation { target: fakeRectangle; properties: "x"; duration: UbuntuAnimation.BriskDuration; to: leftMargin }
205+ UbuntuNumberAnimation { target: fakeRectangle; properties: "y"; duration: UbuntuAnimation.BriskDuration; to: PanelState.panelHeight }
206+ UbuntuNumberAnimation { target: fakeRectangle; properties: "width"; duration: UbuntuAnimation.BriskDuration; to: (appContainerWidth - leftMargin)/2 }
207+ UbuntuNumberAnimation { target: fakeRectangle; properties: "height"; duration: UbuntuAnimation.BriskDuration; to: (appContainerHeight - PanelState.panelHeight)/2 }
208+ }
209+
210+ ParallelAnimation {
211+ id: fakeMaximizeTopRightAnimation
212+ UbuntuNumberAnimation { target: fakeRectangle; properties: "x"; duration: UbuntuAnimation.BriskDuration; to: (appContainerWidth + leftMargin)/2 }
213+ UbuntuNumberAnimation { target: fakeRectangle; properties: "y"; duration: UbuntuAnimation.BriskDuration; to: PanelState.panelHeight }
214+ UbuntuNumberAnimation { target: fakeRectangle; properties: "width"; duration: UbuntuAnimation.BriskDuration; to: (appContainerWidth - leftMargin)/2 }
215+ UbuntuNumberAnimation { target: fakeRectangle; properties: "height"; duration: UbuntuAnimation.BriskDuration; to: (appContainerHeight - PanelState.panelHeight)/2 }
216+ }
217+
218+ ParallelAnimation {
219+ id: fakeMaximizeBottomLeftAnimation
220+ UbuntuNumberAnimation { target: fakeRectangle; properties: "x"; duration: UbuntuAnimation.BriskDuration; to: leftMargin }
221+ UbuntuNumberAnimation { target: fakeRectangle; properties: "y"; duration: UbuntuAnimation.BriskDuration; to: (appContainerHeight + PanelState.panelHeight)/2 }
222+ UbuntuNumberAnimation { target: fakeRectangle; properties: "width"; duration: UbuntuAnimation.BriskDuration; to: (appContainerWidth - leftMargin)/2 }
223+ UbuntuNumberAnimation { target: fakeRectangle; properties: "height"; duration: UbuntuAnimation.BriskDuration; to: appContainerHeight/2 }
224+ }
225+
226+ ParallelAnimation {
227+ id: fakeMaximizeBottomRightAnimation
228+ UbuntuNumberAnimation { target: fakeRectangle; properties: "x"; duration: UbuntuAnimation.BriskDuration; to: (appContainerWidth + leftMargin)/2 }
229+ UbuntuNumberAnimation { target: fakeRectangle; properties: "y"; duration: UbuntuAnimation.BriskDuration; to: (appContainerHeight + PanelState.panelHeight)/2 }
230+ UbuntuNumberAnimation { target: fakeRectangle; properties: "width"; duration: UbuntuAnimation.BriskDuration; to: (appContainerWidth - leftMargin)/2 }
231+ UbuntuNumberAnimation { target: fakeRectangle; properties: "height"; duration: UbuntuAnimation.BriskDuration; to: appContainerHeight/2 }
232+ }
233+}
qml/Stages/MoveHandler.qmlnew file+179
@@ -0,0 +1,179 @@
1+/*
2+ * Copyright (C) 2014-2016 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 Unity.Application 0.1 // For Mir singleton
19+import Ubuntu.Components 1.3
20+import Utils 0.1
21+import "../Components"
22+import "../Components/PanelState"
23+
24+QtObject {
25+ id: root
26+
27+ property Item target // appDelegate
28+ property int stageWidth
29+ property int stageHeight
30+ property real buttonsWidth: 0
31+
32+ readonly property bool dragging: priv.dragging
33+
34+ signal fakeMaximizeAnimationRequested(real amount)
35+ signal fakeMaximizeLeftAnimationRequested(real amount)
36+ signal fakeMaximizeRightAnimationRequested(real amount)
37+ signal fakeMaximizeTopLeftAnimationRequested(real amount)
38+ signal fakeMaximizeTopRightAnimationRequested(real amount)
39+ signal fakeMaximizeBottomLeftAnimationRequested(real amount)
40+ signal fakeMaximizeBottomRightAnimationRequested(real amount)
41+ signal stopFakeAnimation()
42+
43+ property QtObject priv: QtObject {
44+ property real distanceX
45+ property real distanceY
46+ property bool dragging
47+
48+ readonly property int triggerArea: units.gu(8)
49+ property bool nearLeftEdge: target.maximizedLeft
50+ property bool nearTopEdge: target.maximized
51+ property bool nearRightEdge: target.maximizedRight
52+ property bool nearTopLeftCorner: target.maximizedTopLeft
53+ property bool nearTopRightCorner: target.maximizedTopRight
54+ property bool nearBottomLeftCorner: target.maximizedBottomLeft
55+ property bool nearBottomRightCorner: target.maximizedBottomRight
56+
57+ function resetEdges() {
58+ nearLeftEdge = false;
59+ nearRightEdge = false;
60+ nearTopEdge = false;
61+ nearTopLeftCorner = false;
62+ nearTopRightCorner = false;
63+ nearBottomLeftCorner = false;
64+ nearBottomRightCorner = false;
65+ }
66+
67+ // return the progress of mouse pointer movement from 0 to 1 within a corner square of the screen
68+ // 0 -> before the mouse enters the square
69+ // 1 -> mouse is in the outer corner
70+ // a is the corner, b is the mouse pos
71+ function progressInCorner(ax, ay, bx, by) {
72+ // distance of two points, a and b, in pixels
73+ var distance = Math.sqrt(Math.pow(bx-ax, 2) + Math.pow(by-ay, 2));
74+ // length of the triggerArea square diagonal
75+ var diagLength = Math.sqrt(2 * priv.triggerArea * priv.triggerArea);
76+ var ratio = 1 - (distance / diagLength);
77+ return bx > 0 && bx <= stageWidth && by > 0 && by <= stageHeight ? ratio : 1; // everything "outside" of our square from the center is 1
78+ }
79+ property real progress: 0
80+ }
81+
82+ function handlePressedChanged(pressed, pressedButtons, mouseX, mouseY) {
83+ if (pressed && pressedButtons === Qt.LeftButton) {
84+ var pos = mapToItem(target, mouseX, mouseY);
85+ if (target.anyMaximized) {
86+ // keep distanceX relative to the normal window width minus the window control buttons (+spacing)
87+ // so that dragging it back doesn't make the window jump around to weird positions, away from the mouse pointer
88+ priv.distanceX = MathUtils.clampAndProject(pos.x, 0, target.width, buttonsWidth, target.resizeArea.normalWidth);
89+ priv.distanceY = MathUtils.clampAndProject(pos.y, 0, target.height, 0, target.resizeArea.normalHeight);
90+ } else {
91+ priv.distanceX = pos.x;
92+ priv.distanceY = pos.y;
93+ }
94+
95+ priv.dragging = true;
96+ } else {
97+ priv.dragging = false;
98+ Mir.cursorName = "";
99+ }
100+ }
101+
102+ function handlePositionChanged(mouse, sensingPoints) {
103+ if (priv.dragging) {
104+ Mir.cursorName = "grabbing";
105+
106+ if (target.anyMaximized) { // restore from maximized when dragging away from edges/corners
107+ priv.progress = 0;
108+ target.restore(false, WindowStateStorage.WindowStateNormal);
109+ }
110+
111+ var pos = mapToItem(target.parent, mouse.x, mouse.y);
112+ // Use integer coordinate values to ensure that target is left in a pixel-aligned
113+ // position. Mouse movement could have subpixel precision, yielding a fractional
114+ // mouse position.
115+ target.requestedX = Math.round(pos.x - priv.distanceX);
116+ target.requestedY = Math.round(Math.max(pos.y - priv.distanceY, PanelState.panelHeight));
117+
118+ if (sensingPoints) { // edge/corner detection when dragging via the touch overlay
119+ if (sensingPoints.topLeft.x < priv.triggerArea && sensingPoints.topLeft.y < PanelState.panelHeight + priv.triggerArea
120+ && target.canBeCornerMaximized) { // top left
121+ priv.progress = priv.progressInCorner(0, PanelState.panelHeight, sensingPoints.topLeft.x, sensingPoints.topLeft.y);
122+ priv.resetEdges();
123+ priv.nearTopLeftCorner = true;
124+ root.fakeMaximizeTopLeftAnimationRequested(priv.progress);
125+ } else if (sensingPoints.topRight.x > stageWidth - priv.triggerArea && sensingPoints.topRight.y < PanelState.panelHeight + priv.triggerArea
126+ && target.canBeCornerMaximized) { // top right
127+ priv.progress = priv.progressInCorner(stageWidth, PanelState.panelHeight, sensingPoints.topRight.x, sensingPoints.topRight.y);
128+ priv.resetEdges();
129+ priv.nearTopRightCorner = true;
130+ root.fakeMaximizeTopRightAnimationRequested(priv.progress);
131+ } else if (sensingPoints.bottomLeft.x < priv.triggerArea && sensingPoints.bottomLeft.y > stageHeight - priv.triggerArea
132+ && target.canBeCornerMaximized) { // bottom left
133+ priv.progress = priv.progressInCorner(0, stageHeight, sensingPoints.bottomLeft.x, sensingPoints.bottomLeft.y);
134+ priv.resetEdges();
135+ priv.nearBottomLeftCorner = true;
136+ root.fakeMaximizeBottomLeftAnimationRequested(priv.progress);
137+ } else if (sensingPoints.bottomRight.x > stageWidth - priv.triggerArea && sensingPoints.bottomRight.y > stageHeight - priv.triggerArea
138+ && target.canBeCornerMaximized) { // bottom right
139+ priv.progress = priv.progressInCorner(stageWidth, stageHeight, sensingPoints.bottomRight.x, sensingPoints.bottomRight.y);
140+ priv.resetEdges();
141+ priv.nearBottomRightCorner = true;
142+ root.fakeMaximizeBottomRightAnimationRequested(priv.progress);
143+ } else if (sensingPoints.left.x < priv.triggerArea && target.canBeMaximizedLeftRight) { // left
144+ priv.progress = MathUtils.clampAndProject(sensingPoints.left.x, priv.triggerArea, 0, 0, 1);
145+ priv.resetEdges();
146+ priv.nearLeftEdge = true;
147+ root.fakeMaximizeLeftAnimationRequested(priv.progress);
148+ } else if (sensingPoints.right.x > stageWidth - priv.triggerArea && target.canBeMaximizedLeftRight) { // right
149+ priv.progress = MathUtils.clampAndProject(sensingPoints.right.x, stageWidth - priv.triggerArea, stageWidth, 0, 1);
150+ priv.resetEdges();
151+ priv.nearRightEdge = true;
152+ root.fakeMaximizeRightAnimationRequested(priv.progress);
153+ } else if (sensingPoints.top.y < PanelState.panelHeight + priv.triggerArea && target.canBeMaximized) { // top
154+ priv.progress = MathUtils.clampAndProject(sensingPoints.top.y, PanelState.panelHeight + priv.triggerArea, 0, 0, 1);
155+ priv.resetEdges();
156+ priv.nearTopEdge = true;
157+ root.fakeMaximizeAnimationRequested(priv.progress);
158+ } else if (priv.nearLeftEdge || priv.nearRightEdge || priv.nearTopEdge || priv.nearTopLeftCorner || priv.nearTopRightCorner ||
159+ priv.nearBottomLeftCorner || priv.nearBottomRightCorner) {
160+ priv.progress = 0;
161+ priv.resetEdges();
162+ root.stopFakeAnimation();
163+ }
164+ }
165+ }
166+ }
167+
168+ function handleReleased(touchMode) {
169+ if (touchMode) {
170+ priv.progress = 0;
171+ priv.resetEdges();
172+ }
173+ if ((target.state == "normal" || target.state == "restored") && priv.progress == 0) {
174+ // save the x/y to restore to
175+ target.restoredX = target.x;
176+ target.restoredY = target.y;
177+ }
178+ }
179+}
qml/Stages/Splash.qml+3−6
@@ -119,6 +119,7 @@ Item {
119 }
119 }
120
120
121 Label {
121 Label {
122+ id: titleLabel
122 text: root.title
123 text: root.title
123 anchors.horizontalCenter: parent.horizontalCenter
124 anchors.horizontalCenter: parent.horizontalCenter
124 anchors.top: iconShape.bottom
125 anchors.top: iconShape.bottom
@@ -137,12 +138,8 @@ Item {
137
138
138 ActivityIndicator {
139 ActivityIndicator {
139 id: spinner
140 id: spinner
140− anchors.centerIn: header.visible ? parent : undefined
141+ anchors.centerIn: parent
141− anchors.verticalCenterOffset: header.visible ? header.height / 2 : 0
142+ anchors.verticalCenterOffset: header.visible ? header.height / 2 : Math.max(titleLabel.y / 2, units.gu(8))
142−
143− anchors.horizontalCenter: header.visible ? undefined : parent.horizontalCenter
144− anchors.bottom: header.visible ? undefined : parent.bottom
145− anchors.bottomMargin: header.visible ? 0 : units.gu(12)
146 }
143 }
147
144
148 MouseArea {
145 MouseArea {
qml/Stages/TabletStage.qml+7−5
@@ -485,8 +485,7 @@ AbstractStage {
485 return;
485 return;
486 }
486 }
487
487
488− switch (phase) {
488+ if (phase === 0) {
489− case 0:
490 // the "spreadEnabled" part is because when code does "phase = 0; contentX = -shift" to
489 // the "spreadEnabled" part is because when code does "phase = 0; contentX = -shift" to
491 // dismiss the spread because spreadEnabled went to false, for some reason, during tests,
490 // dismiss the spread because spreadEnabled went to false, for some reason, during tests,
492 // Flickable might jump in and change contentX value back, causing the code below to do
491 // Flickable might jump in and change contentX value back, causing the code below to do
@@ -496,14 +495,17 @@ AbstractStage {
496 if (root.spreadEnabled && shiftedContentX > width * positionMarker2) {
495 if (root.spreadEnabled && shiftedContentX > width * positionMarker2) {
497 phase = 1;
496 phase = 1;
498 }
497 }
499− break;
498+ }
500− case 1:
499+
500+ // Do not turn to else if
501+ // Sometimes the animation of shiftedContentX is very fast and we need to jump from phase 0 to 1 to 2
502+ // in the same onShiftedContentXChanged
503+ if (phase === 1) {
501 if (shiftedContentX < width * positionMarker2) {
504 if (shiftedContentX < width * positionMarker2) {
502 phase = 0;
505 phase = 0;
503 } else if (shiftedContentX >= width * positionMarker4 && !spreadDragArea.dragging) {
506 } else if (shiftedContentX >= width * positionMarker4 && !spreadDragArea.dragging) {
504 phase = 2;
507 phase = 2;
505 }
508 }
506− break;
507 }
509 }
508 }
510 }
509
511
qml/Stages/TopLevelSurfaceRepeater.qml+7−1
@@ -27,7 +27,13 @@ Repeater {
27 if (!startingUp) {
27 if (!startingUp) {
28 // the top-most surface must be the focused one.
28 // the top-most surface must be the focused one.
29 var topmostDelegate = itemAt(0);
29 var topmostDelegate = itemAt(0);
30+ if (topmostDelegate.focus) {
31+ // Delegate got focused while we were still starting up. Not good.
32+ // Force signal handler to run again
33+ topmostDelegate.onFocusChanged(true);
34+ } else {
30− topmostDelegate.focus = true;
35+ topmostDelegate.focus = true;
36+ }
31 }
37 }
32 }
38 }
33
39
qml/Stages/WindowControlsOverlay.qml+76−38
@@ -23,12 +23,26 @@ import "../Components/PanelState"
23Item {
23Item {
24 id: root
24 id: root
25 enabled: target && !target.fullscreen
25 enabled: target && !target.fullscreen
26+ anchors.fill: target
26
27
27 // to be set from outside
28 // to be set from outside
28 property Item target // appDelegate
29 property Item target // appDelegate
30+ property alias stageWidth: moveHandler.stageWidth
31+ property alias stageHeight: moveHandler.stageHeight
29
32
30 // to be read from outside
33 // to be read from outside
31 readonly property alias overlayShown: overlay.visible
34 readonly property alias overlayShown: overlay.visible
35+ readonly property alias dragging: priv.dragging
36+
37+ signal fakeMaximizeAnimationRequested(real amount)
38+ signal fakeMaximizeLeftAnimationRequested(real amount)
39+ signal fakeMaximizeRightAnimationRequested(real amount)
40+ signal fakeMaximizeTopLeftAnimationRequested(real amount)
41+ signal fakeMaximizeTopRightAnimationRequested(real amount)
42+ signal fakeMaximizeBottomLeftAnimationRequested(real amount)
43+ signal fakeMaximizeBottomRightAnimationRequested(real amount)
44+ signal stopFakeAnimation()
45+ signal dragReleased()
32
46
33 TouchGestureArea {
47 TouchGestureArea {
34 id: gestureArea
48 id: gestureArea
@@ -51,16 +65,18 @@ Item {
51 readonly property bool recognizedDrag: recognizedPress && dragging
65 readonly property bool recognizedDrag: recognizedPress && dragging
52 onRecognizedDragChanged: {
66 onRecognizedDragChanged: {
53 if (recognizedDrag) {
67 if (recognizedDrag) {
54− priv.handlePressedChanged(true, tp.x, tp.y);
68+ moveHandler.handlePressedChanged(true, Qt.LeftButton, tp.x, tp.y);
55− } else if (!moveHandler.containsPress) { // prevent interfering with the central piece drag/move
69+ } else if (!mouseArea.containsPress) { // prevent interfering with the central piece drag/move
56− priv.dragging = false;
70+ moveHandler.handlePressedChanged(false, Qt.LeftButton);
71+ root.dragReleased();
72+ moveHandler.handleReleased(true);
57 }
73 }
58 }
74 }
59
75
60 readonly property point tp: recognizedPress ? Qt.point(touchPoints[0].x, touchPoints[0].y) : Qt.point(-1, -1)
76 readonly property point tp: recognizedPress ? Qt.point(touchPoints[0].x, touchPoints[0].y) : Qt.point(-1, -1)
61 onUpdated: {
77 onUpdated: {
62 if (recognizedDrag) {
78 if (recognizedDrag) {
63− priv.handlePositionChanged(tp.x, tp.y);
79+ moveHandler.handlePositionChanged(tp, priv.getSensingPoints());
64 }
80 }
65 }
81 }
66 }
82 }
@@ -69,33 +85,37 @@ Item {
69 Timer {
85 Timer {
70 id: overlayTimer
86 id: overlayTimer
71 interval: 2000
87 interval: 2000
72− repeat: priv.dragging || (priv.resizeArea && priv.resizeArea.dragging)
88+ repeat: priv.dragging
73 }
89 }
74
90
75 QtObject {
91 QtObject {
76 id: priv
92 id: priv
77− property real distanceX
78− property real distanceY
79− property bool dragging
80−
81 readonly property var resizeArea: root.target && root.target.resizeArea ? root.target.resizeArea : null
93 readonly property var resizeArea: root.target && root.target.resizeArea ? root.target.resizeArea : null
94+ readonly property bool ensureWindow: root.target.state == "normal" || root.target.state == "restored"
95+ readonly property bool dragging: moveHandler.dragging || (resizeArea && resizeArea.dragging)
82
96
83− function handlePressedChanged(pressed, mouseX, mouseY) {
97+ function getSensingPoints() {
84− if (pressed) {
98+ var xPoints = [];
85− var pos = mapToItem(root.target, mouseX, mouseY);
99+ var yPoints = [];
86− priv.distanceX = pos.x;
100+ for (var i = 0; i < gestureArea.touchPoints.length; i++) {
87− priv.distanceY = pos.y;
101+ var pt = gestureArea.touchPoints[i];
88− priv.dragging = true;
102+ xPoints.push(pt.x);
89− } else {
103+ yPoints.push(pt.y);
90− priv.dragging = false;
91 }
104 }
92− }
93
105
94− function handlePositionChanged(mouseX, mouseY) {
106+ var leftmost = Math.min.apply(Math, xPoints);
95− if (priv.dragging) {
107+ var rightmost = Math.max.apply(Math, xPoints);
108+ var topmost = Math.min.apply(Math, yPoints);
109+ var bottommost = Math.max.apply(Math, yPoints);
110+
111+ return {
112+ left: mapToItem(target.parent, leftmost, (topmost+bottommost)/2),
96− var pos = mapToItem(root.target.parent, mouseX, mouseY);
113+ top: mapToItem(target.parent, (leftmost+rightmost)/2, topmost),
97− root.target.requestedX = Math.round(pos.x - priv.distanceX);
114+ right: mapToItem(target.parent, rightmost, (topmost+bottommost)/2),
98− root.target.requestedY = Math.round(Math.max(pos.y - priv.distanceY, PanelState.panelHeight));
115+ topLeft: mapToItem(target.parent, leftmost, topmost),
116+ topRight: mapToItem(target.parent, rightmost, topmost),
117+ bottomLeft: mapToItem(target.parent, leftmost, bottommost),
118+ bottomRight: mapToItem(target.parent, rightmost, bottommost)
99 }
119 }
100 }
120 }
101 }
121 }
@@ -113,8 +133,6 @@ Item {
113 UbuntuNumberAnimation {}
133 UbuntuNumberAnimation {}
114 }
134 }
115
135
116− readonly property bool anyMaximized: target && (target.maximized || target.maximizedLeft || target.maximizedRight)
117−
118 Image {
136 Image {
119 source: "graphics/arrows-centre.png"
137 source: "graphics/arrows-centre.png"
120 width: units.gu(10)
138 width: units.gu(10)
@@ -125,15 +143,33 @@ Item {
125
143
126 // move handler
144 // move handler
127 MouseArea {
145 MouseArea {
128− id: moveHandler
146+ id: mouseArea
129 anchors.fill: parent
147 anchors.fill: parent
130 visible: overlay.visible
148 visible: overlay.visible
131 enabled: visible
149 enabled: visible
132 hoverEnabled: true
150 hoverEnabled: true
133− cursorShape: priv.dragging ? Qt.ClosedHandCursor : (overlay.visible ? Qt.OpenHandCursor : Qt.ArrowCursor)
134
151
135− onPressedChanged: priv.handlePressedChanged(pressed, mouseX, mouseY)
152+ onPressedChanged: moveHandler.handlePressedChanged(pressed, pressedButtons, mouseX, mouseY)
136− onPositionChanged: priv.handlePositionChanged(mouseX, mouseY)
153+ onPositionChanged: moveHandler.handlePositionChanged(mouse)
154+ onReleased: {
155+ root.dragReleased();
156+ moveHandler.handleReleased();
157+ }
158+ }
159+
160+ MoveHandler {
161+ id: moveHandler
162+ objectName: "moveHandler"
163+ target: root.target
164+
165+ onFakeMaximizeAnimationRequested: root.fakeMaximizeAnimationRequested(amount)
166+ onFakeMaximizeLeftAnimationRequested: root.fakeMaximizeLeftAnimationRequested(amount)
167+ onFakeMaximizeRightAnimationRequested: root.fakeMaximizeRightAnimationRequested(amount)
168+ onFakeMaximizeTopLeftAnimationRequested: root.fakeMaximizeTopLeftAnimationRequested(amount)
169+ onFakeMaximizeTopRightAnimationRequested: root.fakeMaximizeTopRightAnimationRequested(amount)
170+ onFakeMaximizeBottomLeftAnimationRequested: root.fakeMaximizeBottomLeftAnimationRequested(amount)
171+ onFakeMaximizeBottomRightAnimationRequested: root.fakeMaximizeBottomRightAnimationRequested(amount)
172+ onStopFakeAnimation: root.stopFakeAnimation()
137 }
173 }
138
174
139 // dismiss area
175 // dismiss area
@@ -148,7 +184,7 @@ Item {
148 }
184 }
149
185
150 overlayTimer.stop();
186 overlayTimer.stop();
151− mouse.accepted = root.contains(Qt.point(mouse.x, mouse.y));
187+ mouse.accepted = root.contains(mapToItem(root.target, mouse.x, mouse.y));
152 }
188 }
153 propagateComposedEvents: true
189 propagateComposedEvents: true
154 }
190 }
@@ -157,7 +193,7 @@ Item {
157 ResizeGrip { // top left
193 ResizeGrip { // top left
158 anchors.horizontalCenter: parent.left
194 anchors.horizontalCenter: parent.left
159 anchors.verticalCenter: parent.top
195 anchors.verticalCenter: parent.top
160− visible: target && !overlay.anyMaximized && !target.maximizedHorizontally && !target.maximizedVertically
196+ visible: priv.ensureWindow || target.maximizedBottomRight
161 resizeTarget: priv.resizeArea
197 resizeTarget: priv.resizeArea
162 }
198 }
163
199
@@ -165,7 +201,7 @@ Item {
165 anchors.horizontalCenter: parent.horizontalCenter
201 anchors.horizontalCenter: parent.horizontalCenter
166 anchors.verticalCenter: parent.top
202 anchors.verticalCenter: parent.top
167 rotation: 45
203 rotation: 45
168− visible: target && !overlay.anyMaximized && !target.maximizedVertically
204+ visible: priv.ensureWindow || target.maximizedHorizontally || target.maximizedBottomLeft || target.maximizedBottomRight
169 resizeTarget: priv.resizeArea
205 resizeTarget: priv.resizeArea
170 }
206 }
171
207
@@ -173,7 +209,7 @@ Item {
173 anchors.horizontalCenter: parent.right
209 anchors.horizontalCenter: parent.right
174 anchors.verticalCenter: parent.top
210 anchors.verticalCenter: parent.top
175 rotation: 90
211 rotation: 90
176− visible: target && !overlay.anyMaximized && !target.maximizedHorizontally && !target.maximizedVertically
212+ visible: priv.ensureWindow || target.maximizedBottomLeft
177 resizeTarget: priv.resizeArea
213 resizeTarget: priv.resizeArea
178 }
214 }
179
215
@@ -181,14 +217,15 @@ Item {
181 anchors.horizontalCenter: parent.right
217 anchors.horizontalCenter: parent.right
182 anchors.verticalCenter: parent.verticalCenter
218 anchors.verticalCenter: parent.verticalCenter
183 rotation: 135
219 rotation: 135
184− visible: target && !target.maximizedRight && !target.maximized && !target.maximizedHorizontally
220+ visible: priv.ensureWindow || target.maximizedVertically || target.maximizedLeft ||
221+ target.maximizedTopLeft || target.maximizedBottomLeft
185 resizeTarget: priv.resizeArea
222 resizeTarget: priv.resizeArea
186 }
223 }
187
224
188 ResizeGrip { // bottom right
225 ResizeGrip { // bottom right
189 anchors.horizontalCenter: parent.right
226 anchors.horizontalCenter: parent.right
190 anchors.verticalCenter: parent.bottom
227 anchors.verticalCenter: parent.bottom
191− visible: target && !overlay.anyMaximized && !target.maximizedHorizontally && !target.maximizedVertically
228+ visible: priv.ensureWindow || target.maximizedTopLeft
192 resizeTarget: priv.resizeArea
229 resizeTarget: priv.resizeArea
193 }
230 }
194
231
@@ -196,7 +233,7 @@ Item {
196 anchors.horizontalCenter: parent.horizontalCenter
233 anchors.horizontalCenter: parent.horizontalCenter
197 anchors.verticalCenter: parent.bottom
234 anchors.verticalCenter: parent.bottom
198 rotation: 45
235 rotation: 45
199− visible: target && !overlay.anyMaximized && !target.maximizedVertically
236+ visible: priv.ensureWindow || target.maximizedHorizontally || target.maximizedTopLeft || target.maximizedTopRight
200 resizeTarget: priv.resizeArea
237 resizeTarget: priv.resizeArea
201 }
238 }
202
239
@@ -204,7 +241,7 @@ Item {
204 anchors.horizontalCenter: parent.left
241 anchors.horizontalCenter: parent.left
205 anchors.verticalCenter: parent.bottom
242 anchors.verticalCenter: parent.bottom
206 rotation: 90
243 rotation: 90
207− visible: target && !overlay.anyMaximized && !target.maximizedHorizontally && !target.maximizedVertically
244+ visible: priv.ensureWindow || target.maximizedTopRight
208 resizeTarget: priv.resizeArea
245 resizeTarget: priv.resizeArea
209 }
246 }
210
247
@@ -212,7 +249,8 @@ Item {
212 anchors.horizontalCenter: parent.left
249 anchors.horizontalCenter: parent.left
213 anchors.verticalCenter: parent.verticalCenter
250 anchors.verticalCenter: parent.verticalCenter
214 rotation: 135
251 rotation: 135
215− visible: target && !target.maximizedLeft && !target.maximized && !target.maximizedHorizontally
252+ visible: priv.ensureWindow || target.maximizedVertically || target.maximizedRight ||
253+ target.maximizedTopRight || target.maximizedBottomRight
216 resizeTarget: priv.resizeArea
254 resizeTarget: priv.resizeArea
217 }
255 }
218 }
256 }
qml/Stages/WindowDecoration.qml+12−43
@@ -15,22 +15,24 @@
15 */
15 */
16
16
17import QtQuick 2.4
17import QtQuick 2.4
18−import Unity.Application 0.1 // For Mir singleton
19import Ubuntu.Components 1.3
18import Ubuntu.Components 1.3
20import "../Components"
19import "../Components"
21−import "../Components/PanelState"
22
20
23MouseArea {
21MouseArea {
24 id: root
22 id: root
25 clip: true
23 clip: true
26
24
27− property Item target
25+ property Item target // appDelegate
28 property alias title: titleLabel.text
26 property alias title: titleLabel.text
29 property alias maximizeButtonShown: buttons.maximizeButtonShown
27 property alias maximizeButtonShown: buttons.maximizeButtonShown
30 property bool active: false
28 property bool active: false
31− acceptedButtons: Qt.AllButtons // prevent leaking unhandled mouse events
32 property alias overlayShown: buttons.overlayShown
29 property alias overlayShown: buttons.overlayShown
33
30
31+ readonly property real buttonsWidth: buttons.width + row.spacing
32+
33+ acceptedButtons: Qt.AllButtons // prevent leaking unhandled mouse events
34+ hoverEnabled: true
35+
34 signal closeClicked()
36 signal closeClicked()
35 signal minimizeClicked()
37 signal minimizeClicked()
36 signal maximizeClicked()
38 signal maximizeClicked()
@@ -38,42 +40,11 @@ MouseArea {
38 signal maximizeVerticallyClicked()
40 signal maximizeVerticallyClicked()
39
41
40 onDoubleClicked: {
42 onDoubleClicked: {
41− if (maximizeButtonShown && mouse.button == Qt.LeftButton) {
43+ if (target.canBeMaximized && mouse.button == Qt.LeftButton) {
42 root.maximizeClicked();
44 root.maximizeClicked();
43 }
45 }
44 }
46 }
45
47
46− QtObject {
47− id: priv
48− property real distanceX
49− property real distanceY
50− property bool dragging
51− }
52−
53− onPressedChanged: {
54− if (pressed && pressedButtons == Qt.LeftButton) {
55− var pos = mapToItem(root.target, mouseX, mouseY);
56− priv.distanceX = pos.x;
57− priv.distanceY = pos.y;
58− priv.dragging = true;
59− } else {
60− priv.dragging = false;
61− Mir.cursorName = "";
62− }
63− }
64−
65− onPositionChanged: {
66− if (priv.dragging) {
67− Mir.cursorName = "grabbing";
68− var pos = mapToItem(root.target.parent, mouseX, mouseY);
69− // Use integer coordinate values to ensure that target is left in a pixel-aligned
70− // position. Mouse movement could have subpixel precision, yielding a fractional
71− // mouse position.
72− root.target.requestedX = Math.round(pos.x - priv.distanceX);
73− root.target.requestedY = Math.round(Math.max(pos.y - priv.distanceY, PanelState.panelHeight));
74− }
75− }
76−
77 // do not let unhandled wheel event pass thru the decoration
48 // do not let unhandled wheel event pass thru the decoration
78 onWheel: wheel.accepted = true;
49 onWheel: wheel.accepted = true;
79
50
@@ -85,6 +56,7 @@ MouseArea {
85 }
56 }
86
57
87 Row {
58 Row {
59+ id: row
88 anchors {
60 anchors {
89 fill: parent
61 fill: parent
90 leftMargin: overlayShown ? units.gu(5) : units.gu(1)
62 leftMargin: overlayShown ? units.gu(5) : units.gu(1)
@@ -105,10 +77,9 @@ MouseArea {
105 onCloseClicked: root.closeClicked();
77 onCloseClicked: root.closeClicked();
106 onMinimizeClicked: root.minimizeClicked();
78 onMinimizeClicked: root.minimizeClicked();
107 onMaximizeClicked: root.maximizeClicked();
79 onMaximizeClicked: root.maximizeClicked();
108− onMaximizeHorizontallyClicked: root.maximizeHorizontallyClicked();
80+ onMaximizeHorizontallyClicked: if (root.target.canBeMaximizedHorizontally) root.maximizeHorizontallyClicked();
109− onMaximizeVerticallyClicked: root.maximizeVerticallyClicked();
81+ onMaximizeVerticallyClicked: if (root.target.canBeMaximizedVertically) root.maximizeVerticallyClicked();
110 closeButtonShown: root.target.application.appId !== "unity8-dash"
82 closeButtonShown: root.target.application.appId !== "unity8-dash"
111− target: root.target
112 }
83 }
113
84
114 Label {
85 Label {
@@ -122,10 +93,8 @@ MouseArea {
122 font.weight: root.active ? Font.Light : Font.Medium
93 font.weight: root.active ? Font.Light : Font.Medium
123 elide: Text.ElideRight
94 elide: Text.ElideRight
124 opacity: overlayShown ? 0 : 1
95 opacity: overlayShown ? 0 : 1
125− visible: opacity == 1
96+ visible: opacity != 0
126− Behavior on opacity {
97+ Behavior on opacity { UbuntuNumberAnimation {} }
127− OpacityAnimator { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing }
128− }
129 }
98 }
130 }
99 }
131}
100}
qml/Stages/WindowResizeArea.qml+13−24
@@ -30,6 +30,8 @@ MouseArea {
30
30
31 property var windowStateStorage: WindowStateStorage
31 property var windowStateStorage: WindowStateStorage
32 readonly property alias dragging: d.dragging
32 readonly property alias dragging: d.dragging
33+ readonly property alias normalWidth: priv.normalWidth
34+ readonly property alias normalHeight: priv.normalHeight
33
35
34 // The target item managed by this. Must be a parent or a sibling
36 // The target item managed by this. Must be a parent or a sibling
35 // The area will anchor to it and manage resize events
37 // The area will anchor to it and manage resize events
@@ -54,7 +56,7 @@ MouseArea {
54 property int normalHeight: 0
56 property int normalHeight: 0
55
57
56 function updateNormalGeometry() {
58 function updateNormalGeometry() {
57− if (root.target.state == "normal") {
59+ if (root.target.state == "normal" || root.target.state == "restored") {
58 normalX = root.target.requestedX
60 normalX = root.target.requestedX
59 normalY = root.target.requestedY
61 normalY = root.target.requestedY
60 normalWidth = root.target.width
62 normalWidth = root.target.width
@@ -83,34 +85,21 @@ MouseArea {
83 target.requestedY = Qt.binding(function() { return Math.max(Math.min(windowGeometry.y, root.screenHeight - target.requestedHeight), PanelState.panelHeight); });
85 target.requestedY = Qt.binding(function() { return Math.max(Math.min(windowGeometry.y, root.screenHeight - target.requestedHeight), PanelState.panelHeight); });
84
86
85 var windowState = windowStateStorage.getState(root.windowId, WindowStateStorage.WindowStateNormal)
87 var windowState = windowStateStorage.getState(root.windowId, WindowStateStorage.WindowStateNormal)
86− switch (windowState) {
88+ target.restore(false /* animated */, windowState);
87− case WindowStateStorage.WindowStateNormal:
88− break;
89− case WindowStateStorage.WindowStateMaximized:
90− target.maximize(false);
91− break;
92− case WindowStateStorage.WindowStateMaximizedLeft:
93− target.maximizeLeft(false);
94− break;
95− case WindowStateStorage.WindowStateMaximizedRight:
96− target.maximizeRight(false);
97− break;
98− case WindowStateStorage.WindowStateMaximizedHorizontally:
99− target.maximizeHorizontally(false);
100− break;
101− case WindowStateStorage.WindowStateMaximizedVertically:
102− target.maximizeVertically(false);
103− break;
104− default:
105− console.warn("Unsupported window state");
106− break;
107− }
108
89
109 priv.updateNormalGeometry();
90 priv.updateNormalGeometry();
91+
92+ // initialize the x/y to restore to
93+ target.restoredX = priv.normalX;
94+ target.restoredY = priv.normalY;
110 }
95 }
111
96
112 function saveWindowState() {
97 function saveWindowState() {
98+ var state = target.windowState;
99+ if (state === WindowStateStorage.WindowStateRestored) {
100+ state = WindowStateStorage.WindowStateNormal;
101+ }
113− windowStateStorage.saveState(root.windowId, target.windowState & ~WindowStateStorage.WindowStateMinimized); // clear the minimized bit when saving
102+ windowStateStorage.saveState(root.windowId, state & ~WindowStateStorage.WindowStateMinimized); // clear the minimized bit when saving
114 windowStateStorage.saveGeometry(root.windowId, Qt.rect(priv.normalX, priv.normalY, priv.normalWidth, priv.normalHeight));
103 windowStateStorage.saveGeometry(root.windowId, Qt.rect(priv.normalX, priv.normalY, priv.normalWidth, priv.normalHeight));
115 }
104 }
116
105
qml/Wizard/Pages/10-welcome.qml+1
@@ -20,6 +20,7 @@ import Ubuntu.Components.ListItems 1.3
20import Ubuntu.SystemSettings.LanguagePlugin 1.0
20import Ubuntu.SystemSettings.LanguagePlugin 1.0
21import Wizard 0.1
21import Wizard 0.1
22import ".." as LocalComponents
22import ".." as LocalComponents
23+import "../../Components"
23
24
24LocalComponents.Page {
25LocalComponents.Page {
25 objectName: "languagePage"
26 objectName: "languagePage"
qml/Wizard/Pages/20-keyboard.qml+1
@@ -23,6 +23,7 @@ import AccountsService 0.1
23import Unity.InputInfo 0.1
23import Unity.InputInfo 0.1
24import Unity.Application 0.1
24import Unity.Application 0.1
25import ".." as LocalComponents
25import ".." as LocalComponents
26+import "../../Components"
26
27
27LocalComponents.Page {
28LocalComponents.Page {
28 objectName: "keyboardPage"
29 objectName: "keyboardPage"
qml/Wizard/Pages/30-wifi.qml+1
@@ -22,6 +22,7 @@ import Wizard 0.1
22import Ubuntu.Connectivity 1.0
22import Ubuntu.Connectivity 1.0
23import Ubuntu.SystemImage 0.1
23import Ubuntu.SystemImage 0.1
24import ".." as LocalComponents
24import ".." as LocalComponents
25+import "../../Components"
25
26
26LocalComponents.Page {
27LocalComponents.Page {
27 id: wifiPage
28 id: wifiPage
qml/Wizard/Pages/50-timezone.qml+1
@@ -21,6 +21,7 @@ import Wizard 0.1
21import Ubuntu.SystemSettings.TimeDate 1.1
21import Ubuntu.SystemSettings.TimeDate 1.1
22import Utils 0.1 as Utils
22import Utils 0.1 as Utils
23import ".." as LocalComponents
23import ".." as LocalComponents
24+import "../../Components"
24
25
25LocalComponents.Page {
26LocalComponents.Page {
26 id: tzPage
27 id: tzPage
qml/Wizard/Pages/70-passwd-type.qml+1
@@ -19,6 +19,7 @@ import Ubuntu.Components 1.3
19import Ubuntu.Components.ListItems 1.3
19import Ubuntu.Components.ListItems 1.3
20import Ubuntu.SystemSettings.SecurityPrivacy 1.0
20import Ubuntu.SystemSettings.SecurityPrivacy 1.0
21import ".." as LocalComponents
21import ".." as LocalComponents
22+import "../../Components"
22
23
23/**
24/**
24 * One quirk with this page: we don't actually set the password. We avoid
25 * One quirk with this page: we don't actually set the password. We avoid
qml/Wizard/Pages/passcode-confirm.qml+2−2
@@ -18,7 +18,7 @@ import QtQuick 2.4
18import Ubuntu.Components 1.3
18import Ubuntu.Components 1.3
19import Ubuntu.SystemSettings.SecurityPrivacy 1.0
19import Ubuntu.SystemSettings.SecurityPrivacy 1.0
20import ".." as LocalComponents
20import ".." as LocalComponents
21−import "../../Components" as UnityComponents
21+import "../../Components"
22
22
23/**
23/**
24 * See the main passwd-type page for an explanation of why we don't actually
24 * See the main passwd-type page for an explanation of why we don't actually
@@ -34,7 +34,7 @@ LocalComponents.Page {
34 // If we are entering this page, clear any saved password and get focus
34 // If we are entering this page, clear any saved password and get focus
35 onEnabledChanged: if (enabled) lockscreen.clear(false)
35 onEnabledChanged: if (enabled) lockscreen.clear(false)
36
36
37− UnityComponents.Lockscreen {
37+ Lockscreen {
38 id: lockscreen
38 id: lockscreen
39 anchors {
39 anchors {
40 fill: content
40 fill: content
qml/Wizard/Pages/passcode-desktop.qml+1
@@ -18,6 +18,7 @@ import QtQuick 2.4
18import QtQuick.Layouts 1.1
18import QtQuick.Layouts 1.1
19import Ubuntu.Components 1.3
19import Ubuntu.Components 1.3
20import ".." as LocalComponents
20import ".." as LocalComponents
21+import "../../Components"
21
22
22/**
23/**
23 * See the main passwd-type page for an explanation of why we don't actually
24 * See the main passwd-type page for an explanation of why we don't actually
qml/Wizard/Pages/passcode-set.qml+2−2
@@ -18,7 +18,7 @@ import QtQuick 2.4
18import Ubuntu.Components 1.3
18import Ubuntu.Components 1.3
19import Ubuntu.SystemSettings.SecurityPrivacy 1.0
19import Ubuntu.SystemSettings.SecurityPrivacy 1.0
20import ".." as LocalComponents
20import ".." as LocalComponents
21−import "../../Components" as UnityComponents
21+import "../../Components"
22
22
23/**
23/**
24 * See the main passwd-type page for an explanation of why we don't actually
24 * See the main passwd-type page for an explanation of why we don't actually
@@ -45,7 +45,7 @@ LocalComponents.Page {
45 onTriggered: pageStack.load(Qt.resolvedUrl("passcode-confirm.qml"));
45 onTriggered: pageStack.load(Qt.resolvedUrl("passcode-confirm.qml"));
46 }
46 }
47
47
48− UnityComponents.Lockscreen {
48+ Lockscreen {
49 id: lockscreen
49 id: lockscreen
50 anchors {
50 anchors {
51 fill: content
51 fill: content
qml/Wizard/Pages/password-set.qml+1
@@ -18,6 +18,7 @@ import QtQuick 2.4
18import QtQuick.Layouts 1.1
18import QtQuick.Layouts 1.1
19import Ubuntu.Components 1.3
19import Ubuntu.Components 1.3
20import ".." as LocalComponents
20import ".." as LocalComponents
21+import "../../Components"
21
22
22/**
23/**
23 * See the main passwd-type page for an explanation of why we don't actually
24 * See the main passwd-type page for an explanation of why we don't actually
qml/Wizard/WizardItemSelector.qml+1
@@ -17,6 +17,7 @@
17import QtQuick 2.4
17import QtQuick 2.4
18import Ubuntu.Components 1.3
18import Ubuntu.Components 1.3
19import Ubuntu.Components.ListItems 1.3 as ListItem
19import Ubuntu.Components.ListItems 1.3 as ListItem
20+import "../Components"
20
21
21Rectangle {
22Rectangle {
22 id: optionToggle
23 id: optionToggle
qml/graphics/phone_background.jpgdeleted

Binary file not shown.

qml/graphics/tablet_background.jpgdeleted

Binary file not shown.

tests/CMakeLists.txt+8−13
@@ -37,27 +37,22 @@ set(UNITY_IMPORT_PATHS ${UNITY_IMPORT_PATHS} CACHE INTERNAL "")
37
37
38macro(unity8_parse_arguments)
38macro(unity8_parse_arguments)
39 cmake_parse_arguments(U8TEST
39 cmake_parse_arguments(U8TEST
40− "${QMLTEST_OPTIONS};LIGHTDM"
40+ "${QMLTEST_OPTIONS}"
41 "${QMLTEST_SINGLE}"
41 "${QMLTEST_SINGLE}"
42 "${QMLTEST_MULTI}"
42 "${QMLTEST_MULTI}"
43 ${ARGN}
43 ${ARGN}
44 )
44 )
45
45
46 set(ld_paths)
46 set(ld_paths)
47− if(U8TEST_LIGHTDM)
48− list(APPEND ld_paths
47+ list(APPEND ld_paths
49− ${UNITY_MOCKPATH}/libusermetrics
48+ ${UNITY_MOCKPATH}/libusermetrics
50− ${UNITY_MOCKPATH}/LightDM/IntegratedLightDM/liblightdm
49+ ${UNITY_MOCKPATH}/LightDM/IntegratedLightDM/liblightdm
51− )
50+ )
52− endif()
51+
53−
54− if(ld_paths)
55− string(REPLACE ";" ":" ld_library_path "${ld_paths}")
52+ string(REPLACE ";" ":" ld_library_path "${ld_paths}")
56− set(ld_library_path LD_LIBRARY_PATH=${ld_library_path})
53+ set(ld_library_path LD_LIBRARY_PATH=${ld_library_path})
57− endif()
58
54
59 set(U8TEST_ARGN "${ARGN}")
55 set(U8TEST_ARGN "${ARGN}")
60− list(REMOVE_ITEM U8TEST_ARGN LIGHTDM)
61
56
62 set(environment
57 set(environment
63 UNITY_TESTING=1
58 UNITY_TESTING=1
tests/imports/check_imports.py+58
@@ -81,6 +81,53 @@ def scan_for_bad_import(file_path, all_pat, good_pats):
81 print("%s: bad import version in multiple lines" % file_path)
81 print("%s: bad import version in multiple lines" % file_path)
82 return bool(errors)
82 return bool(errors)
83
83
84+# Flickable matches
85+flickable_pat = re.compile(r'.*\s*Flickable\s*{')
86+listview_pat = re.compile(r'.*\s*ListView\s*{')
87+gridview_pat = re.compile(r'.*\s*GridView\s*{')
88+flickable_pats = [flickable_pat, listview_pat, gridview_pat]
89+unity_components_pat = re.compile(r'.*import ".*Components"')
90+components_import_pat = re.compile(r'.*import "."')
91+components_path = re.compile(r'.*qml/Components.*')
92+skip_components_flickable_path = re.compile(r'.*qml/Components/Flickable.qml')
93+skip_components_listview_path = re.compile(r'.*qml/Components/ListView.qml')
94+skip_components_gridview_path = re.compile(r'.*qml/Components/GridView.qml')
95+skip_mocks_path = re.compile(r'.*tests/mocks.*')
96+
97+def scan_for_flickable_imports(file_path, component_pats, qtquick_pat, unitycomponents_pat):
98+ errors = []
99+ with open(file_path, 'rt', encoding='utf-8') as ifile, open(file_path, 'rt', encoding='utf-8') as i2file:
100+ flickable_found = False
101+ for lino, line in enumerate(ifile, start=1):
102+ for component_pat in component_pats:
103+ if component_pat.match(line):
104+ flickable_found = True
105+ if flickable_found:
106+ qtquick_found = False
107+ unitycomponents_found = False
108+ for lino, line in enumerate(i2file, start=1):
109+ if not qtquick_found and qtquick_pat.match(line):
110+ qtquick_found = True
111+ if unitycomponents_pat.match(line):
112+ unitycomponents_found = True
113+ if not qtquick_found:
114+ errors.append(lino)
115+ else:
116+ return
117+ if not unitycomponents_found:
118+ errors.append(lino)
119+ if 0 < len(errors) <= 10:
120+ if len(errors) > 1:
121+ plural = 's'
122+ else:
123+ plural = ''
124+ print(
125+ "%s: missing/wrong order of Components import in line%s %s" % (
126+ file_path, plural, ", ".join((str(i) for i in errors))))
127+ elif errors:
128+ print("%s: missing/wrong order of Components imports in multiple lines" % file_path)
129+ return bool(errors)
130+
84# Parse args
131# Parse args
85
132
86parser = argparse.ArgumentParser(
133parser = argparse.ArgumentParser(
@@ -116,6 +163,17 @@ try:
116 found_bad_import = True
163 found_bad_import = True
117 if scan_for_bad_import(path, ubuntu_components_pat, [ubuntu_good_components_pat]):
164 if scan_for_bad_import(path, ubuntu_components_pat, [ubuntu_good_components_pat]):
118 found_bad_import = True
165 found_bad_import = True
166+ if skip_mocks_path.match(path) or \
167+ skip_components_flickable_path.match(path) or \
168+ skip_components_listview_path.match(path) or \
169+ skip_components_gridview_path.match(path):
170+ break
171+ if not components_path.match(path):
172+ if scan_for_flickable_imports(path, flickable_pats, quick_good_pat, unity_components_pat):
173+ found_bad_import = True
174+ else:
175+ if scan_for_flickable_imports(path, flickable_pats, quick_good_pat, components_import_pat):
176+ found_bad_import = True
119
177
120except OSError as e:
178except OSError as e:
121 error("cannot create file list for \"" + dir + "\": " + e.strerror)
179 error("cannot create file list for \"" + dir + "\": " + e.strerror)
tests/mocks/AccountsService/AccountsService.cpp+19−3
@@ -15,7 +15,9 @@
15 */
15 */
16
16
17#include "AccountsService.h"
17#include "AccountsService.h"
18+#include "MockUsersModel.h"
18
19
20+#include <QLightDM/UsersModel>
19#include <paths.h>
21#include <paths.h>
20
22
21AccountsService::AccountsService(QObject* parent)
23AccountsService::AccountsService(QObject* parent)
@@ -23,15 +25,17 @@ AccountsService::AccountsService(QObject* parent)
23 m_enableFingerprintIdentification(true),
25 m_enableFingerprintIdentification(true),
24 m_enableLauncherWhileLocked(true),
26 m_enableLauncherWhileLocked(true),
25 m_enableIndicatorsWhileLocked(true),
27 m_enableIndicatorsWhileLocked(true),
26− m_backgroundFile(qmlDirectory() + "/graphics/phone_background.jpg"),
28+ m_backgroundFile(),
27 m_statsWelcomeScreen(true),
29 m_statsWelcomeScreen(true),
28 m_failedLogins(0),
30 m_failedLogins(0),
29 m_failedFingerprintLogins(0),
31 m_failedFingerprintLogins(0),
30 m_demoEdges(false),
32 m_demoEdges(false),
31 m_demoEdgesCompleted(),
33 m_demoEdgesCompleted(),
32 m_hereEnabled(false),
34 m_hereEnabled(false),
33− m_hereLicensePath("")
35+ m_hereLicensePath(""),
36+ m_usersModel(new MockUsersModel(this))
34{
37{
38+ m_usersModel->setMockMode("full");
35}
39}
36
40
37QString AccountsService::user() const
41QString AccountsService::user() const
@@ -44,6 +48,7 @@ void AccountsService::setUser(const QString &user)
44 m_user = user;
48 m_user = user;
45 Q_EMIT userChanged();
49 Q_EMIT userChanged();
46 Q_EMIT passwordDisplayHintChanged();
50 Q_EMIT passwordDisplayHintChanged();
51+ Q_EMIT backgroundFileChanged();
47}
52}
48
53
49bool AccountsService::demoEdges() const
54bool AccountsService::demoEdges() const
@@ -111,7 +116,18 @@ void AccountsService::setEnableIndicatorsWhileLocked(bool enableIndicatorsWhileL
111
116
112QString AccountsService::backgroundFile() const
117QString AccountsService::backgroundFile() const
113{
118{
119+ if (!m_backgroundFile.isEmpty()) {
114− return m_backgroundFile;
120+ return m_backgroundFile;
121+ }
122+
123+ // Check if our mock user has a background set in liblightdm
124+ for (int i = 0; i < m_usersModel->count(); i++) {
125+ if (m_usersModel->data(i, QLightDM::UsersModel::NameRole) == m_user) {
126+ return m_usersModel->data(i, QLightDM::UsersModel::BackgroundPathRole).toString();
127+ }
128+ }
129+
130+ return QString();
115}
131}
116
132
117void AccountsService::setBackgroundFile(const QString &backgroundFile)
133void AccountsService::setBackgroundFile(const QString &backgroundFile)
tests/mocks/AccountsService/AccountsService.h+3
@@ -22,6 +22,8 @@
22#include <QStringList>
22#include <QStringList>
23#include <QVariant>
23#include <QVariant>
24
24
25+class MockUsersModel;
26+
25class AccountsService: public QObject
27class AccountsService: public QObject
26{
28{
27 Q_OBJECT
29 Q_OBJECT
@@ -163,6 +165,7 @@ private:
163 QString m_realName;
165 QString m_realName;
164 QStringList m_kbdMap;
166 QStringList m_kbdMap;
165 QString m_email;
167 QString m_email;
168+ MockUsersModel *m_usersModel;
166};
169};
167
170
168#endif
171#endif
tests/mocks/AccountsService/CMakeLists.txt+14
@@ -1,10 +1,24 @@
1+include_directories(
2+ ${CMAKE_CURRENT_BINARY_DIR}
3+ ${CMAKE_SOURCE_DIR}/plugins/LightDM
4+ ${CMAKE_SOURCE_DIR}/plugins/Utils
5+ ${CMAKE_SOURCE_DIR}/tests/mocks/LightDM/IntegratedLightDM
6+ )
7+
1add_library(MockAccountsService-qml MODULE
8add_library(MockAccountsService-qml MODULE
9+ ${CMAKE_SOURCE_DIR}/plugins/LightDM/UsersModel.cpp
10+ ${CMAKE_SOURCE_DIR}/plugins/Utils/unitysortfilterproxymodelqml.cpp
11+ ${CMAKE_SOURCE_DIR}/tests/mocks/LightDM/IntegratedLightDM/MockUsersModel.cpp
2 AccountsService.cpp
12 AccountsService.cpp
3 plugin.cpp
13 plugin.cpp
4 )
14 )
5
15
6qt5_use_modules(MockAccountsService-qml DBus Qml)
16qt5_use_modules(MockAccountsService-qml DBus Qml)
7
17
18+target_link_libraries(MockAccountsService-qml
19+ MockLightDM
20+ )
21+
8add_unity8_mock(AccountsService 0.1 AccountsService
22add_unity8_mock(AccountsService 0.1 AccountsService
9 PREFIX mocks
23 PREFIX mocks
10 TARGETS MockAccountsService-qml
24 TARGETS MockAccountsService-qml
tests/mocks/CMakeLists.txt+1
@@ -39,6 +39,7 @@ add_subdirectory(Lights)
39add_subdirectory(MeeGo)
39add_subdirectory(MeeGo)
40add_subdirectory(Powerd)
40add_subdirectory(Powerd)
41add_subdirectory(QMenuModel)
41add_subdirectory(QMenuModel)
42+add_subdirectory(SessionBroadcast)
42add_subdirectory(Ubuntu)
43add_subdirectory(Ubuntu)
43add_subdirectory(Unity)
44add_subdirectory(Unity)
44add_subdirectory(QtMultimedia)
45add_subdirectory(QtMultimedia)
tests/mocks/Cursor/CMakeLists.txt+1−1
@@ -1 +1 @@
1−add_unity8_mock(Cursor 1.0 Cursor PREFIX mocks)
1+add_unity8_mock(Cursor 1.1 Cursor PREFIX mocks)
tests/mocks/Cursor/Cursor.qml+10−1
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2015 Canonical, Ltd.
2+ * Copyright (C) 2015-2016 Canonical, Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -17,7 +17,16 @@
17import QtQuick 2.4
17import QtQuick 2.4
18
18
19Item {
19Item {
20+ property int topBoundaryOffset // effectively panel height
21+ property Item confiningItem
22+
20 signal pushedLeftBoundary(real amount, int buttons)
23 signal pushedLeftBoundary(real amount, int buttons)
21 signal pushedRightBoundary(real amount, int buttons)
24 signal pushedRightBoundary(real amount, int buttons)
25+ signal pushedTopBoundary(real amount, int buttons)
26+ signal pushedTopLeftCorner(real amount, int buttons)
27+ signal pushedTopRightCorner(real amount, int buttons)
28+ signal pushedBottomLeftCorner(real amount, int buttons)
29+ signal pushedBottomRightCorner(real amount, int buttons)
30+ signal pushStopped()
22 signal mouseMoved()
31 signal mouseMoved()
23}
32}
tests/mocks/LightDM/IntegratedLightDM/MockSessionsModel.cpp+29
@@ -18,6 +18,23 @@
18#include "MockSessionsModel.h"
18#include "MockSessionsModel.h"
19#include <QLightDM/SessionsModel>
19#include <QLightDM/SessionsModel>
20
20
21+
22+int MockSessionsModel::numSessions() const
23+{
24+ QLightDM::SessionsModel* qSessionsModel =
25+ static_cast<QLightDM::SessionsModel*>(sourceModel());
26+
27+ return qSessionsModel->numSessions();
28+}
29+
30+int MockSessionsModel::numAvailableSessions() const
31+{
32+ QLightDM::SessionsModel* qSessionsModel =
33+ static_cast<QLightDM::SessionsModel*>(sourceModel());
34+
35+ return qSessionsModel->numAvailableSessions();
36+}
37+
21QString MockSessionsModel::testScenario() const
38QString MockSessionsModel::testScenario() const
22{
39{
23 QLightDM::SessionsModel* qSessionsModel =
40 QLightDM::SessionsModel* qSessionsModel =
@@ -26,6 +43,17 @@ QString MockSessionsModel::testScenario() const
26 return qSessionsModel->testScenario();
43 return qSessionsModel->testScenario();
27}
44}
28
45
46+void MockSessionsModel::setNumSessions(const int numSessions)
47+{
48+ QLightDM::SessionsModel* qSessionsModel =
49+ static_cast<QLightDM::SessionsModel*>(sourceModel());
50+
51+ if (qSessionsModel->numSessions() != numSessions) {
52+ qSessionsModel->setNumSessions(numSessions);
53+ Q_EMIT numSessionsChanged();
54+ }
55+}
56+
29void MockSessionsModel::setTestScenario(const QString testScenario)
57void MockSessionsModel::setTestScenario(const QString testScenario)
30{
58{
31 QLightDM::SessionsModel* qSessionsModel =
59 QLightDM::SessionsModel* qSessionsModel =
@@ -33,5 +61,6 @@ void MockSessionsModel::setTestScenario(const QString testScenario)
33
61
34 if (qSessionsModel->testScenario() != testScenario) {
62 if (qSessionsModel->testScenario() != testScenario) {
35 qSessionsModel->setTestScenario(testScenario);
63 qSessionsModel->setTestScenario(testScenario);
64+ Q_EMIT testScenarioChanged();
36 }
65 }
37}
66}
tests/mocks/LightDM/IntegratedLightDM/MockSessionsModel.h+12−2
@@ -24,11 +24,21 @@ class MockSessionsModel : public SessionsModel
24{
24{
25 Q_OBJECT
25 Q_OBJECT
26
26
27+ Q_PROPERTY(int numAvailableSessions READ numAvailableSessions CONSTANT)
28+ Q_PROPERTY(int numSessions READ numSessions WRITE setNumSessions NOTIFY numSessionsChanged)
27− Q_PROPERTY(QString testScenario READ testScenario WRITE setTestScenario)
29+ Q_PROPERTY(QString testScenario READ testScenario WRITE setTestScenario NOTIFY testScenarioChanged)
28
30
29public:
31public:
32+ int numAvailableSessions() const;
33+ int numSessions() const;
30 QString testScenario() const;
34 QString testScenario() const;
35+ void setNumSessions(const int numSessions);
31− void setTestScenario(QString testScenario);
36+ void setTestScenario(const QString testScenario);
37+
38+Q_SIGNALS:
39+ void numSessionsChanged();
40+ void testScenarioChanged();
41+
32};
42};
33
43
34#endif // MOCK_UNITY_SESSIONSMODEL_H
44#endif // MOCK_UNITY_SESSIONSMODEL_H
tests/mocks/LightDM/IntegratedLightDM/MockUsersModel.cpp+5
@@ -19,6 +19,11 @@
19#include <QLightDM/UsersModel>
19#include <QLightDM/UsersModel>
20#include <QSortFilterProxyModel>
20#include <QSortFilterProxyModel>
21
21
22+MockUsersModel::MockUsersModel(QObject* parent)
23+ : UsersModel(parent)
24+{
25+}
26+
22QString MockUsersModel::mockMode() const
27QString MockUsersModel::mockMode() const
23{
28{
24 QLightDM::UsersModel* qUsersModel =
29 QLightDM::UsersModel* qUsersModel =
tests/mocks/LightDM/IntegratedLightDM/MockUsersModel.h+2
@@ -27,6 +27,8 @@ class MockUsersModel : public UsersModel
27 Q_PROPERTY(QString mockMode READ mockMode WRITE setMockMode NOTIFY mockModeChanged)
27 Q_PROPERTY(QString mockMode READ mockMode WRITE setMockMode NOTIFY mockModeChanged)
28
28
29public:
29public:
30+ explicit MockUsersModel(QObject* parent=0);
31+
30 QString mockMode() const;
32 QString mockMode() const;
31 void setMockMode(QString mockMode);
33 void setMockMode(QString mockMode);
32
34
tests/mocks/LightDM/IntegratedLightDM/liblightdm/Greeter.cpp−5
@@ -69,11 +69,6 @@ bool Greeter::showRemoteLoginHint() const
69 return true;
69 return true;
70}
70}
71
71
72−bool Greeter::lockHint () const
73−{
74− return false;
75−}
76−
77QString Greeter::selectUserHint() const
72QString Greeter::selectUserHint() const
78{
73{
79 Q_D(const Greeter);
74 Q_D(const Greeter);
tests/mocks/LightDM/IntegratedLightDM/liblightdm/Greeter.h−2
@@ -41,7 +41,6 @@ class Q_DECL_EXPORT Greeter : public QObject
41
41
42 Q_PROPERTY(QString hostname READ hostname CONSTANT)
42 Q_PROPERTY(QString hostname READ hostname CONSTANT)
43 Q_PROPERTY(bool hasGuestAccount READ hasGuestAccountHint CONSTANT)
43 Q_PROPERTY(bool hasGuestAccount READ hasGuestAccountHint CONSTANT)
44− Q_PROPERTY(bool locked READ lockHint CONSTANT)
45
44
46 Q_ENUMS(PromptType MessageType)
45 Q_ENUMS(PromptType MessageType)
47
46
@@ -67,7 +66,6 @@ public:
67 bool hideUsersHint() const;
66 bool hideUsersHint() const;
68 bool showManualLoginHint() const;
67 bool showManualLoginHint() const;
69 bool showRemoteLoginHint() const;
68 bool showRemoteLoginHint() const;
70− bool lockHint () const;
71 bool hasGuestAccountHint() const;
69 bool hasGuestAccountHint() const;
72 QString selectUserHint() const;
70 QString selectUserHint() const;
73 void setSelectUserHint(const QString &selectUserHint); // only in mock
71 void setSelectUserHint(const QString &selectUserHint); // only in mock
tests/mocks/LightDM/IntegratedLightDM/liblightdm/SessionsModel.cpp+22
@@ -80,12 +80,34 @@ int SessionsModel::rowCount(const QModelIndex& parent) const
80 }
80 }
81}
81}
82
82
83+int SessionsModel::numSessions() const
84+{
85+ Q_D(const SessionsModel);
86+ return d->numSessions;
87+}
88+
89+int SessionsModel::numAvailableSessions() const
90+{
91+ Q_D(const SessionsModel);
92+ return d->numAvailableSessions();
93+}
94+
83QString SessionsModel::testScenario() const
95QString SessionsModel::testScenario() const
84{
96{
85 Q_D(const SessionsModel);
97 Q_D(const SessionsModel);
86 return d->testScenario;
98 return d->testScenario;
87}
99}
88
100
101+void SessionsModel::setNumSessions(int numSessions)
102+{
103+ Q_D(SessionsModel);
104+
105+ if (d->numSessions != numSessions) {
106+ d->numSessions = numSessions;
107+ d->resetEntries();
108+ }
109+}
110+
89void SessionsModel::setTestScenario(QString testScenario)
111void SessionsModel::setTestScenario(QString testScenario)
90{
112{
91 Q_D(SessionsModel);
113 Q_D(SessionsModel);
tests/mocks/LightDM/IntegratedLightDM/liblightdm/SessionsModel.h+3−3
@@ -31,9 +31,6 @@ class Q_DECL_EXPORT SessionsModel : public QAbstractListModel
31
31
32 Q_ENUMS(SessionModelRoles SessionType)
32 Q_ENUMS(SessionModelRoles SessionType)
33
33
34− // Mock-only API for testing purposes
35− Q_PROPERTY(QString testScenario READ testScenario WRITE setTestScenario)
36−
37 public:
34 public:
38
35
39 enum SessionModelRoles {
36 enum SessionModelRoles {
@@ -57,7 +54,10 @@ class Q_DECL_EXPORT SessionsModel : public QAbstractListModel
57 int rowCount(const QModelIndex& parent) const override;
54 int rowCount(const QModelIndex& parent) const override;
58 QVariant data(const QModelIndex& index, int role) const override;
55 QVariant data(const QModelIndex& index, int role) const override;
59
56
57+ int numSessions() const;
58+ int numAvailableSessions() const;
60 QString testScenario() const;
59 QString testScenario() const;
60+ void setNumSessions(int numSessions);
61 void setTestScenario(QString testScenario);
61 void setTestScenario(QString testScenario);
62
62
63 protected:
63 protected:
tests/mocks/LightDM/IntegratedLightDM/liblightdm/SessionsModelPrivate.cpp+27−17
@@ -16,24 +16,44 @@
16
16
17#include "SessionsModel.h"
17#include "SessionsModel.h"
18#include "SessionsModelPrivate.h"
18#include "SessionsModelPrivate.h"
19−
19+#include <QDebug>
20namespace QLightDM
20namespace QLightDM
21{
21{
22
22
23SessionsModelPrivate::SessionsModelPrivate(SessionsModel* parent)
23SessionsModelPrivate::SessionsModelPrivate(SessionsModel* parent)
24 : testScenario("singleSession")
24 : testScenario("singleSession")
25+ , m_availableSessions(
26+ {
27+ {"ubuntu", "", "Ubuntu", ""},
28+ {"ubuntu-2d", "", "Ubuntu 2D", ""},
29+ {"gnome", "", "GNOME", ""},
30+ {"gnome-classic", "", "GNOME Classic", ""},
31+ {"gnome-flashback-compiz", "", "GNOME Flashback (Compiz)", ""},
32+ {"gnome-flashback-metacity", "", "GNOME Flashback (Metacity)", ""},
33+ {"gnome-wayland", "", "GNOME on Wayland", ""},
34+ {"plasma", "", "Plasma", ""},
35+ {"kde", "", "KDE" , ""},
36+ {"xterm", "", "Recovery Console", ""},
37+ {"", "", "Unknown?", ""}
38+ })
25 , q_ptr(parent)
39 , q_ptr(parent)
26{
40{
41+ numSessions = numAvailableSessions();
27 resetEntries();
42 resetEntries();
28}
43}
29
44
45+int SessionsModelPrivate::numAvailableSessions() const
46+{
47+ return m_availableSessions.length();
48+}
49+
30void SessionsModelPrivate::resetEntries()
50void SessionsModelPrivate::resetEntries()
31{
51{
32 Q_Q(SessionsModel);
52 Q_Q(SessionsModel);
33
53
34 q->beginResetModel();
54 q->beginResetModel();
35 if (testScenario == "multipleSessions") {
55 if (testScenario == "multipleSessions") {
36− resetEntries_multipleSessions();
56+ resetEntries_multipleSessions(numSessions);
37 } else if (testScenario == "noSessions") {
57 } else if (testScenario == "noSessions") {
38 resetEntries_noSessions();
58 resetEntries_noSessions();
39 } else {
59 } else {
@@ -42,22 +62,12 @@ void SessionsModelPrivate::resetEntries()
42 q->endResetModel();
62 q->endResetModel();
43}
63}
44
64
45−void SessionsModelPrivate::resetEntries_multipleSessions()
65+void SessionsModelPrivate::resetEntries_multipleSessions(int numSessions)
46{
66{
47− sessionItems =
67+ sessionItems.clear();
48− {
68+ for (int i = 0; i < numSessions; i++) {
49− {"ubuntu", "", "Ubuntu", ""},
69+ sessionItems.append(m_availableSessions.value(i));
50− {"ubuntu-2d", "", "Ubuntu 2D", ""},
70+ }
51− {"gnome", "", "GNOME", ""},
52− {"gnome-classic", "", "GNOME Classic", ""},
53− {"gnome-flashback-compiz", "", "GNOME Flashback (Compiz)", ""},
54− {"gnome-flashback-metacity", "", "GNOME Flashback (Metacity)", ""},
55− {"gnome-wayland", "", "GNOME on Wayland", ""},
56− {"plasma", "", "Plasma", ""},
57− {"kde", "", "KDE" , ""},
58− {"xterm", "", "Recovery Console", ""},
59− {"", "", "Unknown?", ""}
60− };
61}
71}
62
72
63void SessionsModelPrivate::resetEntries_noSessions()
73void SessionsModelPrivate::resetEntries_noSessions()
tests/mocks/LightDM/IntegratedLightDM/liblightdm/SessionsModelPrivate.h+5−1
@@ -40,7 +40,10 @@ public:
40 explicit SessionsModelPrivate(SessionsModel* parent=0);
40 explicit SessionsModelPrivate(SessionsModel* parent=0);
41 virtual ~SessionsModelPrivate() = default;
41 virtual ~SessionsModelPrivate() = default;
42
42
43+ int numAvailableSessions() const;
44+
43 QList<SessionItem> sessionItems;
45 QList<SessionItem> sessionItems;
46+ int numSessions;
44 QString testScenario;
47 QString testScenario;
45
48
46 void resetEntries();
49 void resetEntries();
@@ -48,7 +51,8 @@ protected:
48 SessionsModel* const q_ptr;
51 SessionsModel* const q_ptr;
49
52
50private:
53private:
54+ const QList<SessionItem> m_availableSessions;
51− void resetEntries_multipleSessions();
55+ void resetEntries_multipleSessions(int numSessions);
52 void resetEntries_noSessions();
56 void resetEntries_noSessions();
53 void resetEntries_singleSession();
57 void resetEntries_singleSession();
54 Q_DECLARE_PUBLIC(SessionsModel)
58 Q_DECLARE_PUBLIC(SessionsModel)
tests/mocks/LightDM/IntegratedLightDM/liblightdm/UsersModelPrivate.cpp+15−1
@@ -17,6 +17,8 @@
17#include "UsersModelPrivate.h"
17#include "UsersModelPrivate.h"
18#include "UsersModel.h"
18#include "UsersModel.h"
19
19
20+#include <QDir>
21+
20namespace QLightDM
22namespace QLightDM
21{
23{
22
24
@@ -53,6 +55,17 @@ void UsersModelPrivate::resetEntries()
53 entries[i].uid = i + 1;
55 entries[i].uid = i + 1;
54 }
56 }
55
57
58+ // Assign backgrounds
59+ QDir backgroundDir("/usr/share/backgrounds");
60+ QStringList backgrounds = backgroundDir.entryList(QDir::Files);
61+ if (!backgrounds.empty()) {
62+ for (int i = 0; i < entries.size(); i++) {
63+ if (entries[i].background.isNull()) {
64+ entries[i].background = backgroundDir.filePath(backgrounds[i % backgrounds.size()]);
65+ }
66+ }
67+ }
68+
56 q->endResetModel();
69 q->endResetModel();
57}
70}
58
71
@@ -96,7 +109,7 @@ void UsersModelPrivate::resetEntries_full()
96 { "wide-info-prompt", "Wide Info Prompt", 0, 0, false, false, "ubuntu", 0 },
109 { "wide-info-prompt", "Wide Info Prompt", 0, 0, false, false, "ubuntu", 0 },
97 { "multi-info-prompt", "Multi Info Prompt", 0, 0, false, false, "ubuntu", 0 },
110 { "multi-info-prompt", "Multi Info Prompt", 0, 0, false, false, "ubuntu", 0 },
98 { "long-name", "Long name (far far too long to fit, seriously this would never fit on the screen, you will never see this part of the name)", 0, 0, false, false, "ubuntu", 0 },
111 { "long-name", "Long name (far far too long to fit, seriously this would never fit on the screen, you will never see this part of the name)", 0, 0, false, false, "ubuntu", 0 },
99− { "color-background", "Color Background", "#dd4814", 0, false, false, "ubuntu", 0 },
112+ { "color-background", "Color Background", "#E95420", 0, false, false, "ubuntu", 0 },
100 // white and black are a bit redundant, but useful for manually testing if UI is still readable
113 // white and black are a bit redundant, but useful for manually testing if UI is still readable
101 { "white-background", "White Background", "#ffffff", 0, false, false, "ubuntu", 0 },
114 { "white-background", "White Background", "#ffffff", 0, false, false, "ubuntu", 0 },
102 { "black-background", "Black Background", "#000000", 0, false, false, "ubuntu", 0 },
115 { "black-background", "Black Background", "#000000", 0, false, false, "ubuntu", 0 },
@@ -104,6 +117,7 @@ void UsersModelPrivate::resetEntries_full()
104 { "unicode", "가나다라마", 0, 0, false, false, "ubuntu", 0 },
117 { "unicode", "가나다라마", 0, 0, false, false, "ubuntu", 0 },
105 { "no-response", "No Response", 0, 0, false, false, "ubuntu", 0 },
118 { "no-response", "No Response", 0, 0, false, false, "ubuntu", 0 },
106 { "empty-name", "", 0, 0, false, false, "ubuntu", 0 },
119 { "empty-name", "", 0, 0, false, false, "ubuntu", 0 },
120+ { "active", "Active Account", 0, 0, true, false, "ubuntu", 0 },
107 };
121 };
108}
122}
109
123
tests/mocks/LightDM/IntegratedLightDM/plugin.cpp+1−1
@@ -72,7 +72,7 @@ void LightDMPlugin::registerTypes(const char *uri)
72 qRegisterMetaType<QLightDM::Greeter::PromptType>("QLightDM::Greeter::PromptType");
72 qRegisterMetaType<QLightDM::Greeter::PromptType>("QLightDM::Greeter::PromptType");
73 qmlRegisterSingletonType<MockGreeter>(uri, 0, 1, "Greeter", greeter_provider);
73 qmlRegisterSingletonType<MockGreeter>(uri, 0, 1, "Greeter", greeter_provider);
74 qmlRegisterSingletonType<MockSessionsModel>(uri, 0, 1, "Sessions", sessions_provider);
74 qmlRegisterSingletonType<MockSessionsModel>(uri, 0, 1, "Sessions", sessions_provider);
75− qmlRegisterUncreatableType<SessionsModel>(uri, 0, 1, "SessionRoles", "Type is not instantiable");
75+ qmlRegisterUncreatableType<QLightDM::SessionsModel>(uri, 0, 1, "SessionRoles", "Type is not instantiable");
76 qmlRegisterSingletonType<MockUsersModel>(uri, 0, 1, "Users", users_provider);
76 qmlRegisterSingletonType<MockUsersModel>(uri, 0, 1, "Users", users_provider);
77 qmlRegisterUncreatableType<QLightDM::UsersModel>(uri, 0, 1, "UserRoles", "Type is not instantiable");
77 qmlRegisterUncreatableType<QLightDM::UsersModel>(uri, 0, 1, "UserRoles", "Type is not instantiable");
78 qmlRegisterSingletonType<UserMetricsOutput::UserMetrics>(uri, 0, 1, "Infographic", infographic_provider);
78 qmlRegisterSingletonType<UserMetricsOutput::UserMetrics>(uri, 0, 1, "Infographic", infographic_provider);
tests/mocks/QtMultimedia/mediaplayer.cpp+1
@@ -161,6 +161,7 @@ void MediaPlayer::play()
161 if (m_playbackState != PlayingState) {
161 if (m_playbackState != PlayingState) {
162 m_playbackState = PlayingState;
162 m_playbackState = PlayingState;
163 Q_EMIT playbackStateChanged(m_playbackState);
163 Q_EMIT playbackStateChanged(m_playbackState);
164+ Q_EMIT playing();
164
165
165 m_timer.start();
166 m_timer.start();
166 }
167 }
tests/mocks/QtMultimedia/mediaplayer.h+1
@@ -131,6 +131,7 @@ public Q_SLOTS:
131
131
132Q_SIGNALS:
132Q_SIGNALS:
133 void playlistChanged();
133 void playlistChanged();
134+ void playing();
134 void sourceChanged(const QUrl &source);
135 void sourceChanged(const QUrl &source);
135 void playbackStateChanged(PlaybackState playbackState);
136 void playbackStateChanged(PlaybackState playbackState);
136 void positionChanged(int position);
137 void positionChanged(int position);
tests/mocks/SessionBroadcast/CMakeLists.txtnew file+8
@@ -0,0 +1,8 @@
1+add_library(MockSessionBroadcast-qml MODULE
2+ plugin.cpp
3+ SessionBroadcast.cpp
4+ )
5+
6+qt5_use_modules(MockSessionBroadcast-qml Qml)
7+
8+add_unity8_mock(SessionBroadcast 0.1 SessionBroadcast TARGETS MockSessionBroadcast-qml)
tests/mocks/SessionBroadcast/SessionBroadcast.cppnew file+34
@@ -0,0 +1,34 @@
1+/*
2+ * Copyright (C) 2016 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 "SessionBroadcast.h"
18+
19+SessionBroadcast::SessionBroadcast(QObject* parent)
20+ : QObject(parent)
21+{
22+}
23+
24+void SessionBroadcast::requestUrlStart(const QString &, const QString &url)
25+{
26+ // No user name guards, we won't worry about that aspect of the plugin here
27+ Q_EMIT startUrl(url);
28+}
29+
30+void SessionBroadcast::requestHomeShown(const QString &)
31+{
32+ // No user name guards, we won't worry about that aspect of the plugin here
33+ Q_EMIT showHome();
34+}
tests/mocks/SessionBroadcast/SessionBroadcast.hnew file+39
@@ -0,0 +1,39 @@
1+/*
2+ * Copyright (C) 2012,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+ * Authors: Michael Terry <michael.terry@canonical.com>
17+ */
18+
19+#ifndef UNITY_MOCK_SESSIONBROADCAST_H
20+#define UNITY_MOCK_SESSIONBROADCAST_H
21+
22+#include <QObject>
23+
24+class SessionBroadcast: public QObject
25+{
26+ Q_OBJECT
27+
28+public:
29+ explicit SessionBroadcast(QObject *parent = 0);
30+
31+ Q_INVOKABLE void requestUrlStart(const QString &username, const QString &url);
32+ Q_INVOKABLE void requestHomeShown(const QString &username);
33+
34+Q_SIGNALS:
35+ void startUrl(const QString &url);
36+ void showHome();
37+};
38+
39+#endif
tests/mocks/SessionBroadcast/plugin.cppnew file+31
@@ -0,0 +1,31 @@
1+/*
2+ * Copyright (C) 2016 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 "SessionBroadcast.h"
19+
20+#include <QtQml/qqml.h>
21+
22+static QObject *broadcast_provider(QQmlEngine *, QJSEngine *)
23+{
24+ return new SessionBroadcast();
25+}
26+
27+void PowerdPlugin::registerTypes(const char *uri)
28+{
29+ Q_ASSERT(uri == QLatin1String("SessionBroadcast"));
30+ qmlRegisterSingletonType<SessionBroadcast>(uri, 0, 1, "SessionBroadcast", broadcast_provider);
31+}
tests/mocks/SessionBroadcast/plugin.hnew file+32
@@ -0,0 +1,32 @@
1+/*
2+ * Copyright (C) 2016 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 MOCK_SESSIONBROADCAST_PLUGIN_H
18+#define MOCK_SESSIONBROADCAST_PLUGIN_H
19+
20+#include <QtQml/QQmlEngine>
21+#include <QtQml/QQmlExtensionPlugin>
22+
23+class PowerdPlugin : public QQmlExtensionPlugin
24+{
25+ Q_OBJECT
26+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
27+
28+public:
29+ void registerTypes(const char *uri) override;
30+};
31+
32+#endif
tests/mocks/SessionBroadcast/qmldirnew file+2
@@ -0,0 +1,2 @@
1+module SessionBroadcast
2+plugin MockSessionBroadcast-qml
tests/mocks/Ubuntu/DownloadDaemonListener/MockDownloadTracker.cpp+3−1
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013 - Canonical Ltd.
2+ * Copyright (C) 2013-2016 Canonical Ltd.
3 *
3 *
4 * This program is free software: you can redistribute it and/or modify it
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License, as
5 * under the terms of the GNU Lesser General Public License, as
@@ -67,6 +67,8 @@ void MockDownloadTracker::startService()
67 Q_EMIT finished("downloadComplete");
67 Q_EMIT finished("downloadComplete");
68 }else if(m_dbusPath == "error") {
68 }else if(m_dbusPath == "error") {
69 Q_EMIT error("DOWNLOAD ERROR");
69 Q_EMIT error("DOWNLOAD ERROR");
70+ }else if(m_dbusPath == "processing") {
71+ Q_EMIT processing(m_dbusPath);
70 }
72 }
71 }
73 }
72}
74}
tests/mocks/Ubuntu/DownloadDaemonListener/MockDownloadTracker.h+2−1
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013 - Canonical Ltd.
2+ * Copyright (C) 2013-2016 Canonical Ltd.
3 *
3 *
4 * This program is free software: you can redistribute it and/or modify it
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License, as
5 * under the terms of the GNU Lesser General Public License, as
@@ -51,6 +51,7 @@ Q_SIGNALS:
51 void error(const QString &error);
51 void error(const QString &error);
52 void finished(const QString &path);
52 void finished(const QString &path);
53 void paused(bool success);
53 void paused(bool success);
54+ void processing(const QString &path);
54 void progress(qulonglong received, qulonglong total);
55 void progress(qulonglong received, qulonglong total);
55 void resumed(bool success);
56 void resumed(bool success);
56 void started(bool success);
57 void started(bool success);
tests/mocks/Ubuntu/Telephony/MockTelepathyHelper.cpp+27
@@ -20,6 +20,8 @@
20
20
21MockTelepathyHelper::MockTelepathyHelper(QObject *parent)
21MockTelepathyHelper::MockTelepathyHelper(QObject *parent)
22 : QObject(parent)
22 : QObject(parent)
23+ , m_ready(true)
24+ , m_emergencyCallsAvailable(true)
23{
25{
24}
26}
25
27
@@ -33,3 +35,28 @@ void MockTelepathyHelper::registerChannelObserver(const QString& name)
33{
35{
34 Q_UNUSED(name);
36 Q_UNUSED(name);
35}
37}
38+
39+bool MockTelepathyHelper::ready() const
40+{
41+ return m_ready;
42+}
43+
44+void MockTelepathyHelper::setReady(bool value)
45+{
46+ if (m_ready != value) {
47+ m_ready = value;
48+ Q_EMIT readyChanged();
49+ }
50+}
51+bool MockTelepathyHelper::emergencyCallsAvailable() const
52+{
53+ return m_emergencyCallsAvailable;
54+}
55+
56+void MockTelepathyHelper::setEmergencyCallsAvailable(bool value)
57+{
58+ if (m_emergencyCallsAvailable != value) {
59+ m_emergencyCallsAvailable = value;
60+ Q_EMIT emergencyCallsAvailableChanged();
61+ }
62+}
tests/mocks/Ubuntu/Telephony/MockTelepathyHelper.h+22
@@ -24,6 +24,14 @@
24class MockTelepathyHelper : public QObject
24class MockTelepathyHelper : public QObject
25{
25{
26 Q_OBJECT
26 Q_OBJECT
27+ Q_PROPERTY(bool ready
28+ READ ready
29+ WRITE setReady // only in mock
30+ NOTIFY readyChanged)
31+ Q_PROPERTY(bool emergencyCallsAvailable
32+ READ emergencyCallsAvailable
33+ WRITE setEmergencyCallsAvailable // only in mock
34+ NOTIFY emergencyCallsAvailableChanged)
27 Q_DISABLE_COPY(MockTelepathyHelper)
35 Q_DISABLE_COPY(MockTelepathyHelper)
28public:
36public:
29 MockTelepathyHelper(QObject *parent = 0);
37 MockTelepathyHelper(QObject *parent = 0);
@@ -31,6 +39,20 @@ public:
31 static MockTelepathyHelper *instance();
39 static MockTelepathyHelper *instance();
32
40
33 Q_INVOKABLE void registerChannelObserver(const QString& name);
41 Q_INVOKABLE void registerChannelObserver(const QString& name);
42+
43+ bool ready() const;
44+ void setReady(bool value);
45+
46+ bool emergencyCallsAvailable() const;
47+ void setEmergencyCallsAvailable(bool value);
48+
49+Q_SIGNALS:
50+ void readyChanged();
51+ void emergencyCallsAvailableChanged();
52+
53+private:
54+ bool m_ready;
55+ bool m_emergencyCallsAvailable;
34};
56};
35
57
36#endif // MOCKCONTACTWATCHER_H
58#endif // MOCKCONTACTWATCHER_H
tests/mocks/Unity/Application/ApplicationInfo.cpp+1−1
@@ -138,7 +138,7 @@ void ApplicationInfo::createSurface()
138
138
139void ApplicationInfo::setIconId(const QString &iconId)
139void ApplicationInfo::setIconId(const QString &iconId)
140{
140{
141− setIcon(QString("../../tests/graphics/applicationIcons/%2@18.png")
141+ setIcon(QString("../../graphics/applicationIcons/%2@18.png")
142 .arg(iconId));
142 .arg(iconId));
143}
143}
144
144
tests/mocks/Unity/Application/MirSurface.h+2
@@ -106,6 +106,8 @@ public:
106 bool focused() const override;
106 bool focused() const override;
107 QRect inputBounds() const override;
107 QRect inputBounds() const override;
108
108
109+ bool confinesMousePointer() const override { return false; }
110+
109 Q_INVOKABLE void requestFocus() override;
111 Q_INVOKABLE void requestFocus() override;
110
112
111 Q_INVOKABLE void close() override;
113 Q_INVOKABLE void close() override;
tests/mocks/Unity/Indicators/CMakeLists.txt+1
@@ -16,6 +16,7 @@ set(IndicatorsFakeQml_SOURCES
16 fakeunitymenumodelcache.cpp
16 fakeunitymenumodelcache.cpp
17 ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/indicators.h
17 ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/indicators.h
18 ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/menucontentactivator.cpp
18 ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/menucontentactivator.cpp
19+ ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/modelprinter.cpp
19 ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/sharedunitymenumodel.cpp
20 ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/sharedunitymenumodel.cpp
20 ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/unitymenumodelcache.cpp
21 ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/unitymenumodelcache.cpp
21 ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/unitymenumodelstack.cpp
22 ${CMAKE_SOURCE_DIR}/plugins/Unity/Indicators/unitymenumodelstack.cpp
tests/mocks/Unity/Indicators/IndicatorsModel.qml+32−7
@@ -18,6 +18,7 @@ import QtQuick 2.4
18import Unity.Indicators 0.1 as Indicators
18import Unity.Indicators 0.1 as Indicators
19import Unity.InputInfo 0.1
19import Unity.InputInfo 0.1
20import AccountsService 0.1
20import AccountsService 0.1
21+import "fakeindicatorsmodeldata.js" as FakeIndicators
21
22
22Indicators.FakeIndicatorsModel {
23Indicators.FakeIndicatorsModel {
23 id: root
24 id: root
@@ -60,7 +61,7 @@ Indicators.FakeIndicatorsModel {
60 }
61 }
61 },
62 },
62 {
63 {
63− "identifier": "fake-indicator-sound",
64+ "identifier": "fake-indicator-files",
64 "indicatorProperties": {
65 "indicatorProperties": {
65 "enabled": true,
66 "enabled": true,
66 "busName": "com.canonical.indicators.fake4",
67 "busName": "com.canonical.indicators.fake4",
@@ -69,7 +70,7 @@ Indicators.FakeIndicatorsModel {
69 }
70 }
70 },
71 },
71 {
72 {
72− "identifier": "fake-indicator-power",
73+ "identifier": "fake-indicator-sound",
73 "indicatorProperties": {
74 "indicatorProperties": {
74 "enabled": true,
75 "enabled": true,
75 "busName": "com.canonical.indicators.fake5",
76 "busName": "com.canonical.indicators.fake5",
@@ -78,7 +79,7 @@ Indicators.FakeIndicatorsModel {
78 }
79 }
79 },
80 },
80 {
81 {
81− "identifier": "fake-indicator-datetime",
82+ "identifier": "fake-indicator-power",
82 "indicatorProperties": {
83 "indicatorProperties": {
83 "enabled": true,
84 "enabled": true,
84 "busName": "com.canonical.indicators.fake6",
85 "busName": "com.canonical.indicators.fake6",
@@ -87,13 +88,22 @@ Indicators.FakeIndicatorsModel {
87 }
88 }
88 },
89 },
89 {
90 {
90− "identifier": "fake-indicator-session",
91+ "identifier": "fake-indicator-datetime",
91 "indicatorProperties": {
92 "indicatorProperties": {
92 "enabled": true,
93 "enabled": true,
93 "busName": "com.canonical.indicators.fake7",
94 "busName": "com.canonical.indicators.fake7",
94 "menuObjectPath": "/com/canonical/indicators/fake7",
95 "menuObjectPath": "/com/canonical/indicators/fake7",
95 "actionsObjectPath": "/com/canonical/indicators/fake7"
96 "actionsObjectPath": "/com/canonical/indicators/fake7"
96 }
97 }
98+ },
99+ {
100+ "identifier": "fake-indicator-session",
101+ "indicatorProperties": {
102+ "enabled": true,
103+ "busName": "com.canonical.indicators.fake8",
104+ "menuObjectPath": "/com/canonical/indicators/fake8",
105+ "actionsObjectPath": "/com/canonical/indicators/fake8"
106+ }
97 }
107 }
98 ]
108 ]
99
109
@@ -133,21 +143,26 @@ Indicators.FakeIndicatorsModel {
133 "",
143 "",
134 [ "image://theme/messages-new" ]));
144 [ "image://theme/messages-new" ]));
135 Indicators.UnityMenuModelCache.setCachedModelData("/com/canonical/indicators/fake4",
145 Indicators.UnityMenuModelCache.setCachedModelData("/com/canonical/indicators/fake4",
146+ getUnityMenuModelData("fake-indicator-files",
147+ "Files (F)",
148+ "",
149+ [ "image://theme/transfer-progress" ]));
150+ Indicators.UnityMenuModelCache.setCachedModelData("/com/canonical/indicators/fake5",
136 getUnityMenuModelData("fake-indicator-sound",
151 getUnityMenuModelData("fake-indicator-sound",
137 "Sound (F)",
152 "Sound (F)",
138 "",
153 "",
139 [ "image://theme/audio-volume-high" ]));
154 [ "image://theme/audio-volume-high" ]));
140− Indicators.UnityMenuModelCache.setCachedModelData("/com/canonical/indicators/fake5",
155+ Indicators.UnityMenuModelCache.setCachedModelData("/com/canonical/indicators/fake6",
141 getUnityMenuModelData("fake-indicator-power",
156 getUnityMenuModelData("fake-indicator-power",
142 "Battery (F)",
157 "Battery (F)",
143 "",
158 "",
144 [ "image://theme/battery-020" ]));
159 [ "image://theme/battery-020" ]));
145− Indicators.UnityMenuModelCache.setCachedModelData("/com/canonical/indicators/fake6",
160+ Indicators.UnityMenuModelCache.setCachedModelData("/com/canonical/indicators/fake7",
146 getUnityMenuModelData("fake-indicator-datetime",
161 getUnityMenuModelData("fake-indicator-datetime",
147 "Upcoming Events (F)",
162 "Upcoming Events (F)",
148 "12:04",
163 "12:04",
149 []));
164 []));
150− Indicators.UnityMenuModelCache.setCachedModelData("/com/canonical/indicators/fake7",
165+ Indicators.UnityMenuModelCache.setCachedModelData("/com/canonical/indicators/fake8",
151 getUnityMenuModelData("fake-indicator-session",
166 getUnityMenuModelData("fake-indicator-session",
152 "System (F)",
167 "System (F)",
153 "",
168 "",
@@ -155,6 +170,16 @@ Indicators.FakeIndicatorsModel {
155 }
170 }
156
171
157 function getUnityMenuModelData(identifier, title, label, icons) {
172 function getUnityMenuModelData(identifier, title, label, icons) {
173+ var menudata = FakeIndicators.fakeMenuData[identifier];
174+
175+ if (menudata !== undefined) {
176+ var rootState = menudata[0]["rowData"].actionState;
177+ rootState.title = title;
178+ rootState.label = label;
179+ rootState.icons = icons;
180+ return menudata;
181+ }
182+
158 var root = [{
183 var root = [{
159 "rowData": { // 1
184 "rowData": { // 1
160 "label": "",
185 "label": "",
tests/mocks/Unity/Indicators/fakeindicatorsmodeldata.jsnew file+1106
@@ -0,0 +1,1106 @@
1+var usageScenario = (typeof shell != 'undefined') ? shell.usageScenario : "desktop";
2+
3+var fakeMenuData = {
4+ "fake-indicator-messages": [
5+ {
6+ "rowData": {
7+ "action": "indicator._header",
8+ "actionState": {
9+ "icons": [ "image://theme/messages-new" ],
10+ "label": "",
11+ "title": "Messages (F)",
12+ "visible": true
13+ },
14+ "icon": "",
15+ "isCheck": false,
16+ "isRadio": false,
17+ "isSeparator": false,
18+ "isToggled": false,
19+ "label": "",
20+ "sensitive": true,
21+ "type": "com.canonical.indicator.root"
22+ },
23+ "submenu": [
24+ {
25+ "rowData": {
26+ "label": "There's an updated system image.",
27+ "sensitive": true,
28+ "isSeparator": false,
29+ "icon": "file:///usr/share/ubuntu/settings/system/icons/settings-system-update.svg",
30+ "type": "com.canonical.indicator.messages.messageitem",
31+ "ext": {
32+ "icon": "file:///usr/share/ubuntu/settings/system/icons/settings-system-update.svg",
33+ "xCanonicalAppIcon": "file:///usr/share/ubuntu/settings/system/system-settings.png",
34+ "xCanonicalMessageActions": null,
35+ "xCanonicalText": "Tap to open the system updater.",
36+ "xCanonicalTime": ((new Date).getTime() - 350 * 1000) * 1000,
37+ },
38+ "action": "indicator.ubuntu-system-settings",
39+ "actionState": {},
40+ "isCheck": false,
41+ "isRadio": false,
42+ "isToggled": false,
43+ }
44+ },
45+ {
46+ "rowData": {
47+ "action": "indicator.telephony-service-sms.msg.MSG_ID",
48+ "actionState": {},
49+ "ext": {},
50+ "icon": "file:///usr/share/telephony-service/assets/avatar-default@18.png",
51+ "type": "com.canonical.indicator.messages.messageitem",
52+ "ext": {
53+ "icon": "file:///usr/share/telephony-service/assets/avatar-default@18.png",
54+ "xCanonicalAppIcon": "image://theme/telephony-service-message",
55+ "xCanonicalMessageActions": "",
56+ "xCanonicalText": "This is a message sent to an awesome Ubuntu phone.",
57+ "xCanonicalTime": ((new Date).getTime() - 35 * 1000) * 1000
58+ },
59+ "isCheck": false,
60+ "isRadio": false,
61+ "isSeparator": false,
62+ "isToggled": false,
63+ "label": "+44391234567 - [SIM 1]",
64+ "sensitive": true
65+ }
66+ },
67+ {
68+ "rowData": {
69+ "action": "indicator.remove-all",
70+ "actionState": {},
71+ "ext": {},
72+ "icon": "",
73+ "isCheck": false,
74+ "isRadio": false,
75+ "isSeparator": false,
76+ "isToggled": false,
77+ "label": "Clear All",
78+ "sensitive": true,
79+ "type": "com.canonical.indicator.button"
80+ }
81+ },
82+ ]
83+ }
84+ ],
85+
86+ "fake-indicator-session": [
87+ {
88+ "rowData": {
89+ "action": "indicator._header",
90+ "actionState": {
91+ "icons": [],
92+ "label": "",
93+ "title": "Session (F)",
94+ "visible": usageScenario == "desktop"
95+ },
96+ "icon": "",
97+ "isCheck": false,
98+ "isRadio": false,
99+ "isSeparator": false,
100+ "isToggled": false,
101+ "label": "",
102+ "sensitive": true,
103+ "type": "com.canonical.indicator.root"
104+ },
105+ "submenu": [
106+ {
107+ "rowData": {
108+ "action": "indicator.about",
109+ "actionState": {},
110+ "ext": {},
111+ "icon": "",
112+ "isCheck": false,
113+ "isRadio": false,
114+ "isSeparator": false,
115+ "isToggled": false,
116+ "label": "About this " + usageScenario,
117+ "sensitive": true,
118+ "type": ""
119+ }
120+ },
121+ {
122+ "rowData": {
123+ "action": "indicator.help",
124+ "actionState": {},
125+ "ext": {},
126+ "icon": "",
127+ "isCheck": false,
128+ "isRadio": false,
129+ "isSeparator": false,
130+ "isToggled": false,
131+ "label": "Ubuntu Help",
132+ "sensitive": true,
133+ "type": ""
134+ }
135+ },
136+ {
137+ "rowData": {
138+ "action": "",
139+ "actionState": {},
140+ "ext": {},
141+ "icon": "",
142+ "isCheck": false,
143+ "isRadio": false,
144+ "isSeparator": true,
145+ "isToggled": false,
146+ "label": "",
147+ "sensitive": true,
148+ "type": ""
149+ }
150+ },
151+ {
152+ "rowData": {
153+ "action": "indicator.settings",
154+ "actionState": {},
155+ "ext": {},
156+ "icon": "",
157+ "isCheck": false,
158+ "isRadio": false,
159+ "isSeparator": false,
160+ "isToggled": false,
161+ "label": "System Settings...",
162+ "sensitive": true,
163+ "type": ""
164+ }
165+ },
166+ {
167+ "rowData": {
168+ "action": "",
169+ "actionState": {},
170+ "ext": {},
171+ "icon": "",
172+ "isCheck": false,
173+ "isRadio": false,
174+ "isSeparator": true,
175+ "isToggled": false,
176+ "label": "",
177+ "sensitive": true,
178+ "type": ""
179+ }
180+ },
181+ {
182+ "rowData": {
183+ "action": "indicator.switch-to-screensaver",
184+ "actionState": {},
185+ "ext": {},
186+ "icon": "",
187+ "isCheck": false,
188+ "isRadio": false,
189+ "isSeparator": false,
190+ "isToggled": false,
191+ "label": "Lock",
192+ "sensitive": true,
193+ "type": ""
194+ }
195+ },
196+ {
197+ "rowData": {
198+ "action": "indicator.switch-to-guest",
199+ "actionState": {},
200+ "ext": {},
201+ "icon": "",
202+ "isCheck": false,
203+ "isRadio": false,
204+ "isSeparator": false,
205+ "isToggled": false,
206+ "label": "Guest Session",
207+ "sensitive": true,
208+ "type": "indicator.guest-menu-item"
209+ }
210+ },
211+ {
212+ "rowData": {
213+ "action": "indicator.switch-to-user",
214+ "actionState": {},
215+ "ext": {},
216+ "icon": "",
217+ "isCheck": false,
218+ "isRadio": true,
219+ "isSeparator": false,
220+ "isToggled": false,
221+ "label": "Marco",
222+ "sensitive": true,
223+ "type": "indicator.user-menu-item"
224+ }
225+ },
226+ {
227+ "rowData": {
228+ "action": "indicator.switch-to-user",
229+ "actionState": {},
230+ "ext": {},
231+ "icon": "",
232+ "isCheck": false,
233+ "isRadio": true,
234+ "isSeparator": false,
235+ "isToggled": false,
236+ "label": "Unity8",
237+ "sensitive": true,
238+ "type": "indicator.user-menu-item"
239+ }
240+ },
241+ {
242+ "rowData": {
243+ "action": "",
244+ "actionState": {},
245+ "ext": {},
246+ "icon": "",
247+ "isCheck": false,
248+ "isRadio": false,
249+ "isSeparator": true,
250+ "isToggled": false,
251+ "label": "",
252+ "sensitive": true,
253+ "type": ""
254+ }
255+ },
256+ {
257+ "rowData": {
258+ "action": "indicator.logout",
259+ "actionState": {},
260+ "ext": {},
261+ "icon": "",
262+ "isCheck": false,
263+ "isRadio": false,
264+ "isSeparator": false,
265+ "isToggled": false,
266+ "label": "Logout...",
267+ "sensitive": true,
268+ "type": ""
269+ }
270+ },
271+ {
272+ "rowData": {
273+ "action": "",
274+ "actionState": {},
275+ "ext": {},
276+ "icon": "",
277+ "isCheck": false,
278+ "isRadio": false,
279+ "isSeparator": true,
280+ "isToggled": false,
281+ "label": "",
282+ "sensitive": true,
283+ "type": ""
284+ }
285+ },
286+ {
287+ "rowData": {
288+ "action": "indicator.suspend",
289+ "actionState": {},
290+ "ext": {},
291+ "icon": "",
292+ "isCheck": false,
293+ "isRadio": false,
294+ "isSeparator": false,
295+ "isToggled": false,
296+ "label": "Suspend",
297+ "sensitive": true,
298+ "type": ""
299+ }
300+ },
301+ {
302+ "rowData": {
303+ "action": "indicator.power-off",
304+ "actionState": {},
305+ "ext": {},
306+ "icon": "",
307+ "isCheck": false,
308+ "isRadio": false,
309+ "isSeparator": false,
310+ "isToggled": false,
311+ "label": "Power off...",
312+ "sensitive": true,
313+ "type": ""
314+ }
315+ }
316+ ]
317+ }
318+ ],
319+
320+ "fake-indicator-power": [
321+ {
322+ "rowData": {
323+ "action": "indicator._header",
324+ "actionState": {
325+ "icons": [ "image://theme/battery-020" ],
326+ "label": "",
327+ "title": "Battery (F)",
328+ "visible": true
329+ },
330+ "icon": "",
331+ "isCheck": false,
332+ "isRadio": false,
333+ "isSeparator": false,
334+ "isToggled": false,
335+ "label": "",
336+ "sensitive": true,
337+ "type": "com.canonical.indicator.root"
338+ },
339+ "submenu": [
340+ {
341+ "rowData": {
342+ "action": "indicator.activate-statistics",
343+ "actionState": {},
344+ "ext": {},
345+ "icon": "image://theme/battery-full-charged-symbolic",
346+ "isCheck": false,
347+ "isRadio": false,
348+ "isSeparator": false,
349+ "isToggled": false,
350+ "label": "Battery (full charged)",
351+ "sensitive": true,
352+ "type": "com.canonical.indicator.basic"
353+ }
354+ },
355+ {
356+ "rowData": {
357+ "action": "indicator.activate-statistics",
358+ "actionState": {},
359+ "ext": {},
360+ "icon": "image://theme/battery-100",
361+ "isCheck": false,
362+ "isRadio": false,
363+ "isSeparator": false,
364+ "isToggled": false,
365+ "label": "Battery",
366+ "sensitive": true,
367+ "type": "com.canonical.indicator.basic"
368+ }
369+ },
370+ {
371+ "rowData": {
372+ "action": "",
373+ "actionState": {},
374+ "ext": {},
375+ "icon": "",
376+ "isCheck": false,
377+ "isRadio": false,
378+ "isSeparator": true,
379+ "isToggled": false,
380+ "label": " ",
381+ "sensitive": true,
382+ "type": ""
383+ }
384+ },
385+ {
386+ "rowData": {
387+ "action": "indicator.show-time",
388+ "actionState": false,
389+ "ext": {},
390+ "icon": "",
391+ "isCheck": true,
392+ "isRadio": false,
393+ "isSeparator": false,
394+ "isToggled": false,
395+ "label": "Show time on menubar",
396+ "sensitive": true,
397+ "type": ""
398+ }
399+ },
400+ {
401+ "rowData": {
402+ "action": "indicator.show-percentage",
403+ "actionState": false,
404+ "ext": {},
405+ "icon": "",
406+ "isCheck": true,
407+ "isRadio": false,
408+ "isSeparator": false,
409+ "isToggled": false,
410+ "label": "Show percentage on menubar",
411+ "sensitive": true,
412+ "type": ""
413+ }
414+ },
415+ {
416+ "rowData": {
417+ "action": "indicator.activate-settings",
418+ "actionState": {},
419+ "ext": {},
420+ "icon": "",
421+ "isCheck": false,
422+ "isRadio": false,
423+ "isSeparator": false,
424+ "isToggled": false,
425+ "label": "Power Settings...",
426+ "sensitive": true,
427+ "type": ""
428+ }
429+ }
430+ ]
431+ }
432+ ],
433+
434+ "fake-indicator-sound": [
435+ {
436+ "rowData": {
437+ "action": "indicator._header",
438+ "actionState": {
439+ "icons": [ "image://theme/audio-volume-high" ],
440+ "label": "",
441+ "title": "Sound (F)",
442+ "visible": true
443+ },
444+ "icon": "",
445+ "isCheck": false,
446+ "isRadio": false,
447+ "isSeparator": false,
448+ "isToggled": false,
449+ "label": "",
450+ "sensitive": true,
451+ "type": "com.canonical.indicator.root"
452+ },
453+ "submenu": [
454+ {
455+ "rowData": {
456+ "action": "indicator.silent-mode",
457+ "actionState": true,
458+ "ext": {},
459+ "icon": "",
460+ "isCheck": true,
461+ "isRadio": false,
462+ "isSeparator": false,
463+ "isToggled": false,
464+ "label": "Silent Mode",
465+ "sensitive": true,
466+ "type": "com.canonical.indicator.switch"
467+ }
468+ },
469+ {
470+ "rowData": {
471+ "action": "indicator.volume",
472+ "actionState": 0.35,
473+ "ext": {
474+ "maxIcon": "image://theme/audio-volume-high-panel",
475+ "maxValue": 1,
476+ "minIcon": "image://theme/audio-volume-low-zero-panel",
477+ "minValue": 0,
478+ "xCanonicalSyncAction": "indicator.volume-sync"
479+ },
480+ "icon": "",
481+ "isCheck": false,
482+ "isRadio": true,
483+ "isSeparator": false,
484+ "isToggled": false,
485+ "label": "Volume",
486+ "sensitive": true,
487+ "type": "com.canonical.unity.slider"
488+ }
489+ },
490+ {
491+ "rowData": {
492+ "action": "",
493+ "actionState": {},
494+ "ext": {},
495+ "icon": "",
496+ "isCheck": false,
497+ "isRadio": false,
498+ "isSeparator": true,
499+ "isToggled": false,
500+ "label": "",
501+ "sensitive": true,
502+ "type": ""
503+ }
504+ },
505+ {
506+ "rowData": {
507+ "action": "indicator.mediaplayer-app.desktop",
508+ "actionState": {
509+ "running": true,
510+ "state": "Stopped"
511+ },
512+ "ext": {},
513+ "icon": "file:///usr/share/mediaplayer-app/mediaplayer-app.png",
514+ "isCheck": false,
515+ "isRadio": false,
516+ "isSeparator": false,
517+ "isToggled": false,
518+ "label": "Media Player",
519+ "sensitive": false,
520+ "type": "com.canonical.unity.media-player"
521+ }
522+ },
523+ {
524+ "rowData": {
525+ "actionState": {},
526+ "ext": {
527+ "action": ""
528+ },
529+ "icon": "",
530+ "isCheck": false,
531+ "isRadio": false,
532+ "isSeparator": false,
533+ "isToggled": false,
534+ "label": "",
535+ "sensitive": true,
536+ "type": "com.canonical.unity.playback-item"
537+ }
538+ },
539+ {
540+ "rowData": {
541+ "action": "indicator.phone-settings",
542+ "actionState": {},
543+ "ext": {},
544+ "icon": "",
545+ "isCheck": false,
546+ "isRadio": false,
547+ "isSeparator": false,
548+ "isToggled": false,
549+ "label": "Sound Settings…",
550+ "sensitive": true,
551+ "type": ""
552+ }
553+ }
554+ ]
555+ }
556+ ],
557+
558+
559+ "fake-indicator-datetime": [
560+ {
561+ "rowData": {
562+ "action": "indicator._header",
563+ "actionState": {
564+ "icons": [],
565+ "label": "16.10",
566+ "title": "Upcoming Events (F)",
567+ "visible": true
568+ },
569+ "icon": "",
570+ "isCheck": false,
571+ "isRadio": false,
572+ "isSeparator": false,
573+ "isToggled": false,
574+ "label": "",
575+ "sensitive": true,
576+ "type": "com.canonical.indicator.root"
577+ },
578+ "submenu": [
579+ {
580+ "rowData": {
581+ "action": "indicator.phone.open-calendar-app",
582+ "actionState": {},
583+ "ext": {},
584+ "icon": "image://theme/calendar",
585+ "isCheck": false,
586+ "isRadio": false,
587+ "isSeparator": false,
588+ "isToggled": false,
589+ "label": "Saturday, 31 December 2016",
590+ "sensitive": true,
591+ "type": ""
592+ }
593+ },
594+ {
595+ "rowData": {
596+ "action": "",
597+ "actionState": {},
598+ "ext": {},
599+ "icon": "fake-indicator-datetime",
600+ "isCheck": false,
601+ "isRadio": false,
602+ "isSeparator": true,
603+ "isToggled": false,
604+ "label": "",
605+ "sensitive": true,
606+ "type": ""
607+ }
608+ },
609+ {
610+ "rowData": {
611+ "action": "indicator.phone.open-alarm-app",
612+ "actionState": {},
613+ "ext": {},
614+ "icon": "image://theme/clock",
615+ "isCheck": false,
616+ "isRadio": false,
617+ "isSeparator": false,
618+ "isToggled": false,
619+ "label": "Clock",
620+ "sensitive": true,
621+ "type": ""
622+ }
623+ },
624+ {
625+ "rowData": {
626+ "action": "indicator.phone.open-appointment",
627+ "actionState": {},
628+ "ext": {
629+ "xCanonicalColor": Qt.rgba(32,74,135,1),
630+ "xCanonicalTime": (new Date).getTime() / 1000 + 35000
631+ },
632+ "icon": "",
633+ "isCheck": false,
634+ "isRadio": false,
635+ "isSeparator": false,
636+ "isToggled": false,
637+ "label": "Finish Indicator mocks",
638+ "sensitive": true,
639+ "type": "com.canonical.indicator.appointment"
640+ }
641+ },
642+ {
643+ "rowData": {
644+ "action": "indicator.phone.open-appointment",
645+ "actionState": {},
646+ "ext": {
647+ "xCanonicalColor": Qt.rgba(32,74,135,1),
648+ "xCanonicalTime": (new Date).getTime() / 1000 + 50474
649+ },
650+ "icon": "",
651+ "isCheck": false,
652+ "isRadio": false,
653+ "isSeparator": false,
654+ "isToggled": false,
655+ "label": "Implement Desktop version",
656+ "sensitive": true,
657+ "type": "com.canonical.indicator.appointment"
658+ }
659+ },
660+ {
661+ "rowData": {
662+ "action": "indicator.phone.open-appointment",
663+ "actionState": {},
664+ "ext": {
665+ "xCanonicalColor": Qt.rgba(32,74,135,1),
666+ "xCanonicalTime": (new Date).getTime() / 1000 + 385000
667+ },
668+ "icon": "",
669+ "isCheck": false,
670+ "isRadio": false,
671+ "isSeparator": false,
672+ "isToggled": false,
673+ "label": "Ubuntu drinks event",
674+ "sensitive": true,
675+ "type": "com.canonical.indicator.appointment"
676+ }
677+ },
678+ {
679+ "rowData": {
680+ "action": "indicator.phone.open-appointment",
681+ "actionState": {},
682+ "ext": {
683+ "xCanonicalColor": Qt.rgba(32,74,135,1),
684+ "xCanonicalTime": (new Date).getTime() / 1000 + 3500050
685+ },
686+ "icon": "",
687+ "isCheck": false,
688+ "isRadio": false,
689+ "isSeparator": false,
690+ "isToggled": false,
691+ "label": "Land unity8",
692+ "sensitive": true,
693+ "type": "com.canonical.indicator.appointment"
694+ }
695+ },
696+ {
697+ "rowData": {
698+ "action": "",
699+ "actionState": {},
700+ "ext": {},
701+ "icon": "",
702+ "isCheck": false,
703+ "isRadio": false,
704+ "isSeparator": true,
705+ "isToggled": false,
706+ "label": "",
707+ "sensitive": true,
708+ "type": ""
709+ }
710+ },
711+ {
712+ "rowData": {
713+ "action": "indicator.phone.open-settings-app",
714+ "actionState": {},
715+ "ext": {},
716+ "icon": "",
717+ "isCheck": false,
718+ "isRadio": false,
719+ "isSeparator": false,
720+ "isToggled": false,
721+ "label": "Date and Time settings…",
722+ "sensitive": true,
723+ "type": ""
724+ }
725+ }
726+ ]
727+ }
728+ ],
729+
730+
731+ "fake-indicator-files": [
732+ {
733+ "rowData": {
734+ "action": "indicator._header",
735+ "actionState": {
736+ "icons": [],
737+ "label": "Files",
738+ "title": "Files",
739+ "visible": true
740+ },
741+ "icon": "",
742+ "isCheck": false,
743+ "isRadio": false,
744+ "isSeparator": false,
745+ "isToggled": false,
746+ "label": "",
747+ "sensitive": true,
748+ "type": "com.canonical.indicator.root"
749+ },
750+ "submenu": [
751+ {
752+ "rowData": {
753+ "action": "indicator.pause-all",
754+ "actionState": {},
755+ "ext": {
756+ "xCanonicalExtraLabel": "Pause all"
757+ },
758+ "icon": "",
759+ "isCheck": false,
760+ "isRadio": false,
761+ "isSeparator": false,
762+ "isToggled": false,
763+ "label": "",
764+ "sensitive": true,
765+ "type": "com.canonical.indicator.button-section"
766+ }
767+ },
768+ {
769+ "rowData": {
770+ "action": "indicator.activate-transfer",
771+ "actionState": {},
772+ "ext": {
773+ "xCanonicalUid": 1003
774+ },
775+ "icon": "file:///usr/share/webbrowser-app/webbrowser-app.png",
776+ "isCheck": false,
777+ "isRadio": false,
778+ "isSeparator": false,
779+ "isToggled": false,
780+ "label": "Ubuntu daily.iso",
781+ "sensitive": true,
782+ "type": "com.canonical.indicator.transfer"
783+ }
784+ },
785+ {
786+ "rowData": {
787+ "action": "",
788+ "actionState": {},
789+ "ext": {},
790+ "icon": "",
791+ "isCheck": false,
792+ "isRadio": false,
793+ "isSeparator": true,
794+ "isToggled": false,
795+ "label": "",
796+ "sensitive": true,
797+ "type": ""
798+ }
799+ },
800+ {
801+ "rowData": {
802+ "action": "indicator.clear-all",
803+ "actionState": {},
804+ "ext": {
805+ "xCanonicalExtraLabel": "Clear all"
806+ },
807+ "icon": "",
808+ "isCheck": false,
809+ "isRadio": false,
810+ "isSeparator": false,
811+ "isToggled": false,
812+ "label": "Successful Transfers",
813+ "sensitive": true,
814+ "type": "com.canonical.indicator.button-section"
815+ }
816+ },
817+ {
818+ "rowData": {
819+ "action": "indicator.activate-transfer",
820+ "actionState": {},
821+ "ext": {
822+ "xCanonicalUid": 1002
823+ },
824+ "icon": "file:///usr/share/webbrowser-app/webbrowser-app.png",
825+ "isCheck": false,
826+ "isRadio": false,
827+ "isSeparator": false,
828+ "isToggled": false,
829+ "label": "Ubuntu Guide.pdf",
830+ "sensitive": true,
831+ "type": "com.canonical.indicator.transfer"
832+ }
833+ },
834+ {
835+ "rowData": {
836+ "action": "indicator.activate-transfer",
837+ "actionState": {},
838+ "ext": {
839+ "xCanonicalUid": 1001
840+ },
841+ "icon": "file:///usr/share/webbrowser-app/webbrowser-app.png",
842+ "isCheck": false,
843+ "isRadio": false,
844+ "isSeparator": false,
845+ "isToggled": false,
846+ "label": "Unity8 Hacking.pdf",
847+ "sensitive": true,
848+ "type": "com.canonical.indicator.transfer"
849+ }
850+ }
851+ ]
852+ }
853+ ],
854+
855+ "fake-indicator-network": [
856+ {
857+ "rowData": {
858+ "action": "indicator._header",
859+ "actionState": {
860+ "icons": [],
861+ "label": "",
862+ "title": "Network",
863+ "visible": true
864+ },
865+ "icon": "",
866+ "isCheck": false,
867+ "isRadio": false,
868+ "isSeparator": false,
869+ "isToggled": false,
870+ "label": "",
871+ "sensitive": true,
872+ "type": "com.canonical.indicator.root"
873+ },
874+ "submenu": [
875+ {
876+ "rowData": {
877+ "action": "indicator.airplane.enabled",
878+ "actionState": false,
879+ "ext": {},
880+ "icon": "",
881+ "isCheck": true,
882+ "isRadio": false,
883+ "isSeparator": false,
884+ "isToggled": false,
885+ "label": "Flight Mode",
886+ "sensitive": true,
887+ "type": "com.canonical.indicator.switch"
888+ }
889+ },
890+ /* Anything to see here, since no fake modem actions are implemented
891+ {
892+ "rowData": {
893+ "actionState": false,
894+ "ext": {},
895+ "icon": "",
896+ "isCheck": false,
897+ "isRadio": false,
898+ "isSeparator": true,
899+ "isToggled": false,
900+ "label": "",
901+ "sensitive": true,
902+ "type": ""
903+ }
904+ },
905+ {
906+ "rowData": {
907+ "actionState": false,
908+ "ext": {
909+ "xCanonicalModemConnectivityIconAction": "indicator.modem.1::connectivity-icon",
910+ "xCanonicalModemLockedAction": "indicator.modem.1::locked",
911+ "xCanonicalModemRoamingAction": "indicator.modem.1::roaming",
912+ "xCanonicalModemSimIdentifierLabelAction": "indicator.modem.1::sim-identifier-label",
913+ "xCanonicalModemStatusIconAction": "indicator.modem.1::status-icon",
914+ "xCanonicalModemStatusLabelAction": "indicator.modem.1::status-label"
915+ },
916+ "icon": "",
917+ "isCheck": false,
918+ "isRadio": false,
919+ "isSeparator": false,
920+ "isToggled": false,
921+ "label": "",
922+ "sensitive": true,
923+ "type": "com.canonical.indicator.network.modeminfoitem",
924+ "visible": false
925+ }
926+ },
927+ */
928+ {
929+ "rowData": {
930+ "action": "indicator.wifi.enable",
931+ "actionState": true,
932+ "ext": {},
933+ "icon": "",
934+ "isCheck": true,
935+ "isRadio": false,
936+ "isSeparator": false,
937+ "isToggled": true,
938+ "label": "Wi-Fi",
939+ "sensitive": true,
940+ "type": "com.canonical.indicator.switch"
941+ }
942+ },
943+ {
944+ "rowData": {
945+ "action": "",
946+ "actionState": {},
947+ "ext": {},
948+ "icon": "",
949+ "isCheck": false,
950+ "isRadio": false,
951+ "isSeparator": true,
952+ "isToggled": false,
953+ "label": "",
954+ "sensitive": true,
955+ "type": ""
956+ }
957+ },
958+ {
959+ "rowData": {
960+ "action": "indicator.accesspoint.1",
961+ "actionState": true,
962+ "ext": {
963+ "xCanonicalWifiApIsAdhoc": 0,
964+ "xCanonicalWifiApIsSecure": 1,
965+ "xCanonicalWifiApStrengthAction": "indicator.accesspoint.1::strength"
966+ },
967+ "icon": "",
968+ "isCheck": true,
969+ "isRadio": false,
970+ "isSeparator": false,
971+ "isToggled": true,
972+ "label": "Canonical",
973+ "sensitive": true,
974+ "type": "unity.widgets.systemsettings.tablet.accesspoint"
975+ }
976+ },
977+ {
978+ "rowData": {
979+ "action": "indicator.accesspoint.2",
980+ "actionState": false,
981+ "ext": {
982+ "xCanonicalWifiApIsAdhoc": 0,
983+ "xCanonicalWifiApIsSecure": 1,
984+ "xCanonicalWifiApStrengthAction": "indicator.accesspoint.2::strength"
985+ },
986+ "icon": "",
987+ "isCheck": true,
988+ "isRadio": false,
989+ "isSeparator": false,
990+ "isToggled": false,
991+ "label": "Ubuntu",
992+ "sensitive": true,
993+ "type": "unity.widgets.systemsettings.tablet.accesspoint"
994+ }
995+ },
996+ {
997+ "rowData": {
998+ "action": "indicator.accesspoint.3",
999+ "actionState": false,
1000+ "ext": {
1001+ "xCanonicalWifiApIsAdhoc": 0,
1002+ "xCanonicalWifiApIsSecure": 0,
1003+ "xCanonicalWifiApStrengthAction": "indicator.accesspoint.3::strength"
1004+ },
1005+ "icon": "",
1006+ "isCheck": true,
1007+ "isRadio": false,
1008+ "isSeparator": false,
1009+ "isToggled": false,
1010+ "label": "Linux",
1011+ "sensitive": true,
1012+ "type": "unity.widgets.systemsettings.tablet.accesspoint"
1013+ }
1014+ },
1015+ {
1016+ "rowData": {
1017+ "action": "indicator.accesspoint.4",
1018+ "actionState": false,
1019+ "ext": {
1020+ "xCanonicalWifiApIsAdhoc": 0,
1021+ "xCanonicalWifiApIsSecure": 0,
1022+ "xCanonicalWifiApStrengthAction": "indicator.accesspoint.4::strength"
1023+ },
1024+ "icon": "",
1025+ "isCheck": true,
1026+ "isRadio": false,
1027+ "isSeparator": false,
1028+ "isToggled": false,
1029+ "label": "for Human Beings",
1030+ "sensitive": true,
1031+ "type": "unity.widgets.systemsettings.tablet.accesspoint"
1032+ }
1033+ },
1034+ {
1035+ "rowData": {
1036+ "action": "indicator.wifi.settings",
1037+ "actionState": {},
1038+ "ext": {},
1039+ "icon": "",
1040+ "isCheck": false,
1041+ "isRadio": false,
1042+ "isSeparator": false,
1043+ "isToggled": false,
1044+ "label": "Wi-Fi settings…",
1045+ "sensitive": true,
1046+ "type": ""
1047+ }
1048+ }
1049+ ]
1050+ }
1051+ ],
1052+
1053+ "fake-indicator-bluetooth": [
1054+ {
1055+ "rowData": {
1056+ "action": "indicator._header",
1057+ "actionState": {
1058+ "icons": [],
1059+ "label": "",
1060+ "title": "Network",
1061+ "visible": true
1062+ },
1063+ "icon": "",
1064+ "isCheck": false,
1065+ "isRadio": false,
1066+ "isSeparator": false,
1067+ "isToggled": false,
1068+ "label": "",
1069+ "sensitive": true,
1070+ "type": "com.canonical.indicator.root"
1071+ },
1072+ "submenu": [
1073+ {
1074+ "rowData": {
1075+ "action": "indicator.bluetooth.enable",
1076+ "actionState": true,
1077+ "ext": {},
1078+ "icon": "",
1079+ "isCheck": true,
1080+ "isRadio": false,
1081+ "isSeparator": false,
1082+ "isToggled": true,
1083+ "label": "Bluetooth",
1084+ "sensitive": true,
1085+ "type": "com.canonical.indicator.switch"
1086+ }
1087+ },
1088+ {
1089+ "rowData": {
1090+ "action": "indicator.bluetooth.settings",
1091+ "actionState": {},
1092+ "ext": {},
1093+ "icon": "",
1094+ "isCheck": false,
1095+ "isRadio": false,
1096+ "isSeparator": false,
1097+ "isToggled": false,
1098+ "label": "Bluetooth settings…",
1099+ "sensitive": true,
1100+ "type": ""
1101+ }
1102+ }
1103+ ]
1104+ }
1105+ ]
1106+}
tests/mocks/Unity/Indicators/fakeplugin.cpp+2
@@ -29,6 +29,7 @@
29#include "sharedunitymenumodel.h"
29#include "sharedunitymenumodel.h"
30#include "fakeunitymenumodelcache.h"
30#include "fakeunitymenumodelcache.h"
31#include "unitymenumodelstack.h"
31#include "unitymenumodelstack.h"
32+#include "modelprinter.h"
32
33
33#include <unitymenumodel.h>
34#include <unitymenumodel.h>
34
35
@@ -49,6 +50,7 @@ void IndicatorsFakePlugin::registerTypes(const char * uri)
49 qmlRegisterType<MenuContentActivator>(uri, 0, 1, "MenuContentActivator");
50 qmlRegisterType<MenuContentActivator>(uri, 0, 1, "MenuContentActivator");
50 qmlRegisterType<UnityMenuModelStack>(uri, 0, 1, "UnityMenuModelStack");
51 qmlRegisterType<UnityMenuModelStack>(uri, 0, 1, "UnityMenuModelStack");
51 qmlRegisterType<SharedUnityMenuModel>(uri, 0, 1, "SharedUnityMenuModel");
52 qmlRegisterType<SharedUnityMenuModel>(uri, 0, 1, "SharedUnityMenuModel");
53+ qmlRegisterType<ModelPrinter>(uri, 0, 1, "ModelPrinter");
52
54
53 qmlRegisterSingletonType<FakeUnityMenuModelCache>(uri, 0, 1, "UnityMenuModelCache", menuModelCacheSingleton);
55 qmlRegisterSingletonType<FakeUnityMenuModelCache>(uri, 0, 1, "UnityMenuModelCache", menuModelCacheSingleton);
54
56
tests/mocks/Utils/CMakeLists.txt+1
@@ -32,6 +32,7 @@ set(QMLPLUGIN_SRC
32 constants.cpp
32 constants.cpp
33 plugin.cpp
33 plugin.cpp
34 windowstatestorage.cpp
34 windowstatestorage.cpp
35+ URLDispatcher.cpp
35 )
36 )
36
37
37add_library(FakeUtils-qml SHARED
38add_library(FakeUtils-qml SHARED
tests/mocks/Utils/URLDispatcher.cppnew file+38
@@ -0,0 +1,38 @@
1+/*
2+ * Copyright (C) 2016 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 "URLDispatcher.h"
18+
19+#include <QDBusConnection>
20+
21+URLDispatcher::URLDispatcher(QObject *parent)
22+ : QObject(parent)
23+ , m_active(false)
24+{
25+}
26+
27+bool URLDispatcher::active() const
28+{
29+ return m_active;
30+}
31+
32+void URLDispatcher::setActive(bool value)
33+{
34+ if (m_active != value) {
35+ m_active = value;
36+ Q_EMIT activeChanged();
37+ }
38+}
tests/mocks/Utils/URLDispatcher.hnew file+47
@@ -0,0 +1,47 @@
1+/*
2+ * Copyright (C) 2016 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_URLDISPATCHER_H
18+#define UNITY_URLDISPATCHER_H
19+
20+#include <QObject>
21+#include <QString>
22+
23+// This class manages our url-dispatcher interception. We intercept
24+// url-dispatcher because rather than spawning the handler for the URL
25+// in our own session, we want to notify the user session to do it for us
26+// (and start an unlock in the process).
27+
28+class URLDispatcher : public QObject
29+{
30+ Q_OBJECT
31+ Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged)
32+
33+public:
34+ explicit URLDispatcher(QObject *parent=0);
35+
36+ bool active() const;
37+ void setActive(bool active);
38+
39+Q_SIGNALS:
40+ void urlRequested(const QString &url);
41+ void activeChanged();
42+
43+private:
44+ bool m_active;
45+};
46+
47+#endif
tests/mocks/Utils/plugin.cpp+2
@@ -25,6 +25,7 @@
25#include "plugin.h"
25#include "plugin.h"
26#include "windowstatestorage.h"
26#include "windowstatestorage.h"
27#include "constants.h"
27#include "constants.h"
28+#include "URLDispatcher.h"
28
29
29// plugin
30// plugin
30#include <activefocuslogger.h>
31#include <activefocuslogger.h>
@@ -80,4 +81,5 @@ void FakeUtilsPlugin::registerTypes(const char *uri)
80 qmlRegisterType<InputEventGenerator>(uri, 0, 1, "InputEventGenerator");
81 qmlRegisterType<InputEventGenerator>(uri, 0, 1, "InputEventGenerator");
81 qmlRegisterType<DeviceConfigParser>(uri, 0, 1, "DeviceConfigParser");
82 qmlRegisterType<DeviceConfigParser>(uri, 0, 1, "DeviceConfigParser");
82 qmlRegisterSingletonType<GlobalFunctions>(uri, 0, 1, "Functions", createGlobalFunctions);
83 qmlRegisterSingletonType<GlobalFunctions>(uri, 0, 1, "Functions", createGlobalFunctions);
84+ qmlRegisterType<URLDispatcher>(uri, 0, 1, "URLDispatcher");
83}
85}
tests/mocks/Utils/windowstatestorage.cpp+2−2
@@ -41,7 +41,7 @@ void WindowStateStorage::saveGeometry(const QString &windowId, const QRect &rect
41 m_geometry[windowId] = rect;
41 m_geometry[windowId] = rect;
42}
42}
43
43
44−QRect WindowStateStorage::getGeometry(const QString &windowId, const QRect &defaultValue)
44+QRect WindowStateStorage::getGeometry(const QString &windowId, const QRect &defaultValue) const
45{
45{
46 if (!m_geometry.contains(windowId)) return defaultValue;
46 if (!m_geometry.contains(windowId)) return defaultValue;
47 return m_geometry.value(windowId).toRect();
47 return m_geometry.value(windowId).toRect();
@@ -70,7 +70,7 @@ void WindowStateStorage::saveState(const QString &windowId, WindowState state)
70 m_state[windowId] = state;
70 m_state[windowId] = state;
71}
71}
72
72
73−WindowStateStorage::WindowState WindowStateStorage::getState(const QString &windowId, WindowStateStorage::WindowState defaultValue)
73+WindowStateStorage::WindowState WindowStateStorage::getState(const QString &windowId, WindowStateStorage::WindowState defaultValue) const
74{
74{
75 if (!m_state.contains(windowId)) return defaultValue;
75 if (!m_state.contains(windowId)) return defaultValue;
76 return m_state.value(windowId);
76 return m_state.value(windowId);
tests/mocks/Utils/windowstatestorage.h+8−3
@@ -32,7 +32,12 @@ public:
32 WindowStateMaximizedLeft = 1 << 4,
32 WindowStateMaximizedLeft = 1 << 4,
33 WindowStateMaximizedRight = 1 << 5,
33 WindowStateMaximizedRight = 1 << 5,
34 WindowStateMaximizedHorizontally = 1 << 6,
34 WindowStateMaximizedHorizontally = 1 << 6,
35− WindowStateMaximizedVertically = 1 << 7
35+ WindowStateMaximizedVertically = 1 << 7,
36+ WindowStateMaximizedTopLeft = 1 << 8,
37+ WindowStateMaximizedTopRight = 1 << 9,
38+ WindowStateMaximizedBottomLeft = 1 << 10,
39+ WindowStateMaximizedBottomRight = 1 << 11,
40+ WindowStateRestored = 1 << 12
36 };
41 };
37 Q_DECLARE_FLAGS(WindowStates, WindowState)
42 Q_DECLARE_FLAGS(WindowStates, WindowState)
38#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0))
43#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0))
@@ -42,10 +47,10 @@ public:
42 WindowStateStorage(QObject *parent = 0);
47 WindowStateStorage(QObject *parent = 0);
43
48
44 Q_INVOKABLE void saveState(const QString &windowId, WindowState state);
49 Q_INVOKABLE void saveState(const QString &windowId, WindowState state);
45− Q_INVOKABLE WindowState getState(const QString &windowId, WindowState defaultValue);
50+ Q_INVOKABLE WindowState getState(const QString &windowId, WindowState defaultValue) const;
46
51
47 Q_INVOKABLE void saveGeometry(const QString &windowId, const QRect &rect);
52 Q_INVOKABLE void saveGeometry(const QString &windowId, const QRect &rect);
48− Q_INVOKABLE QRect getGeometry(const QString &windowId, const QRect &defaultValue);
53+ Q_INVOKABLE QRect getGeometry(const QString &windowId, const QRect &defaultValue) const;
49
54
50 Q_INVOKABLE void saveStage(const QString &appId, int stage);
55 Q_INVOKABLE void saveStage(const QString &appId, int stage);
51 Q_INVOKABLE int getStage(const QString &appId, int defaultValue) const;
56 Q_INVOKABLE int getStage(const QString &appId, int defaultValue) const;
tests/plugins/CMakeLists.txt+2
@@ -3,7 +3,9 @@ add_subdirectory(Cursor)
3add_subdirectory(Dash)
3add_subdirectory(Dash)
4add_subdirectory(GlobalShortcut)
4add_subdirectory(GlobalShortcut)
5add_subdirectory(Greeter)
5add_subdirectory(Greeter)
6+add_subdirectory(ImageCache)
6add_subdirectory(LightDM)
7add_subdirectory(LightDM)
8+add_subdirectory(SessionBroadcast)
7add_subdirectory(Ubuntu)
9add_subdirectory(Ubuntu)
8add_subdirectory(Unity)
10add_subdirectory(Unity)
9add_subdirectory(Utils)
11add_subdirectory(Utils)
tests/plugins/Dash/cardcreator/1.res+8−7
@@ -11,7 +11,6 @@ AbstractButton {
11 property int fixedHeaderHeight: -1;
11 property int fixedHeaderHeight: -1;
12 property size fixedArtShapeSize: Qt.size(-1, -1);
12 property size fixedArtShapeSize: Qt.size(-1, -1);
13signal action(var actionId);
13signal action(var actionId);
14−readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
15Loader {
14Loader {
16 id: artShapeLoader;
15 id: artShapeLoader;
17 height: root.fixedArtShapeSize.height;
16 height: root.fixedArtShapeSize.height;
@@ -69,13 +68,15 @@ Label {
69 font.weight: cardData && cardData["subtitle"] ? Font.DemiBold : Font.Normal;
68 font.weight: cardData && cardData["subtitle"] ? Font.DemiBold : Font.Normal;
70 horizontalAlignment: Text.AlignHCenter;
69 horizontalAlignment: Text.AlignHCenter;
71 }
70 }
72−UbuntuShape {
71+Loader {
73− id: touchdown;
72+ active: root.pressed;
74− objectName: "touchdown";
75 anchors { fill: artShapeLoader }
73 anchors { fill: artShapeLoader }
76− visible: root.pressed;
74+ sourceComponent: UbuntuShape {
75+ objectName: "touchdown";
76+ anchors.fill: parent;
77− radius: "medium";
77+ radius: "medium";
78− borderSource: "radius_pressed.sci"
78+ borderSource: "radius_pressed.sci"
79+ }
79}
80}
80implicitHeight: titleLabel.y + titleLabel.height + units.gu(1);
81implicitHeight: titleLabel.y + titleLabel.height + units.gu(1);
81}
82}
tests/plugins/Dash/cardcreator/1.res.cardcreator−8
@@ -65,13 +65,5 @@ Label {
65 font.weight: cardData && cardData["subtitle"] ? Font.DemiBold : Font.Normal;
65 font.weight: cardData && cardData["subtitle"] ? Font.DemiBold : Font.Normal;
66 horizontalAlignment: Text.AlignHCenter;
66 horizontalAlignment: Text.AlignHCenter;
67 }
67 }
68−UbuntuShape {
69− id: touchdown;
70− objectName: "touchdown";
71− anchors { fill: artShapeLoader }
72− visible: root.pressed;
73− radius: "medium";
74− borderSource: "radius_pressed.sci"
75−}
76implicitHeight: titleLabel.y + titleLabel.height + units.gu(1);
68implicitHeight: titleLabel.y + titleLabel.height + units.gu(1);
77}
69}
tests/plugins/Dash/cardcreator/10.res−1
@@ -49,7 +49,6 @@ Loader {
49 }
49 }
50 }
50 }
51 }
51 }
52−readonly property size artShapeSize: Qt.size(-1, -1);
53readonly property int headerHeight: row.height;
52readonly property int headerHeight: row.height;
54Row {
53Row {
55 id: row;
54 id: row;
tests/plugins/Dash/cardcreator/11.res+10−9
@@ -49,7 +49,6 @@ Loader {
49 }
49 }
50 }
50 }
51 }
51 }
52−readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
53Loader {
52Loader {
54 id: artShapeLoader;
53 id: artShapeLoader;
55 height: root.fixedArtShapeSize.height;
54 height: root.fixedArtShapeSize.height;
@@ -163,13 +162,15 @@ top: titleLabel.bottom;
163
162
164 ]
163 ]
165 }
164 }
165+Loader {
166+ active: root.pressed;
167+ anchors { fill: backgroundLoader }
166−UbuntuShape {
168+ sourceComponent: UbuntuShape {
167− id: touchdown;
168− objectName: "touchdown";
169+ objectName: "touchdown";
169− anchors { fill: backgroundLoader }
170+ anchors.fill: parent;
170− visible: root.pressed;
171− radius: "medium";
171+ radius: "medium";
172− borderSource: "radius_pressed.sci"
172+ borderSource: "radius_pressed.sci"
173+ }
173− }
174+}
174implicitHeight: row.y + row.height + units.gu(1);
175implicitHeight: row.y + row.height + units.gu(1);
175}
176}
tests/plugins/Dash/cardcreator/11.res.cardcreator−8
@@ -156,13 +156,5 @@ top: titleLabel.bottom;
156
156
157 ]
157 ]
158 }
158 }
159−UbuntuShape {
160− id: touchdown;
161− objectName: "touchdown";
162− anchors { fill: backgroundLoader }
163− visible: root.pressed;
164− radius: "medium";
165− borderSource: "radius_pressed.sci"
166− }
167implicitHeight: row.y + row.height + units.gu(1);
159implicitHeight: row.y + row.height + units.gu(1);
168}
160}
tests/plugins/Dash/cardcreator/12.res+8−7
@@ -11,7 +11,6 @@ AbstractButton {
11 property int fixedHeaderHeight: -1;
11 property int fixedHeaderHeight: -1;
12 property size fixedArtShapeSize: Qt.size(-1, -1);
12 property size fixedArtShapeSize: Qt.size(-1, -1);
13signal action(var actionId);
13signal action(var actionId);
14−readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
15Loader {
14Loader {
16 id: artShapeLoader;
15 id: artShapeLoader;
17 height: root.fixedArtShapeSize.height;
16 height: root.fixedArtShapeSize.height;
@@ -95,13 +94,15 @@ CardSocialActions {
95 model: cardData && cardData["socialActions"];
94 model: cardData && cardData["socialActions"];
96 onClicked: root.action(actionId);
95 onClicked: root.action(actionId);
97}
96}
98−UbuntuShape {
97+Loader {
99− id: touchdown;
98+ active: root.pressed;
100− objectName: "touchdown";
101 anchors { fill: artShapeLoader }
99 anchors { fill: artShapeLoader }
102− visible: root.pressed;
100+ sourceComponent: UbuntuShape {
101+ objectName: "touchdown";
102+ anchors.fill: parent;
103− radius: "medium";
103+ radius: "medium";
104− borderSource: "radius_pressed.sci"
104+ borderSource: "radius_pressed.sci"
105+ }
105}
106}
106implicitHeight: socialActionsRow.y + socialActionsRow.height + units.gu(1);
107implicitHeight: socialActionsRow.y + socialActionsRow.height + units.gu(1);
107}
108}
tests/plugins/Dash/cardcreator/12.res.cardcreator−8
@@ -91,13 +91,5 @@ CardSocialActions {
91 model: cardData && cardData["socialActions"];
91 model: cardData && cardData["socialActions"];
92 onClicked: root.action(actionId);
92 onClicked: root.action(actionId);
93}
93}
94−UbuntuShape {
95− id: touchdown;
96− objectName: "touchdown";
97− anchors { fill: artShapeLoader }
98− visible: root.pressed;
99− radius: "medium";
100− borderSource: "radius_pressed.sci"
101−}
102implicitHeight: socialActionsRow.y + socialActionsRow.height + units.gu(1);
94implicitHeight: socialActionsRow.y + socialActionsRow.height + units.gu(1);
103}
95}
tests/plugins/Dash/cardcreator/2.res−1
@@ -49,7 +49,6 @@ Loader {
49 }
49 }
50 }
50 }
51 }
51 }
52−readonly property size artShapeSize: Qt.size(-1, -1);
53readonly property int headerHeight: row.height;
52readonly property int headerHeight: row.height;
54Row {
53Row {
55 id: row;
54 id: row;
tests/plugins/Dash/cardcreator/3.res−1
@@ -11,7 +11,6 @@ AbstractButton {
11 property int fixedHeaderHeight: -1;
11 property int fixedHeaderHeight: -1;
12 property size fixedArtShapeSize: Qt.size(-1, -1);
12 property size fixedArtShapeSize: Qt.size(-1, -1);
13signal action(var actionId);
13signal action(var actionId);
14−readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
15Loader {
14Loader {
16 id: artShapeLoader;
15 id: artShapeLoader;
17 height: root.fixedArtShapeSize.height;
16 height: root.fixedArtShapeSize.height;
tests/plugins/Dash/cardcreator/4.res+8−7
@@ -11,7 +11,6 @@ AbstractButton {
11 property int fixedHeaderHeight: -1;
11 property int fixedHeaderHeight: -1;
12 property size fixedArtShapeSize: Qt.size(-1, -1);
12 property size fixedArtShapeSize: Qt.size(-1, -1);
13signal action(var actionId);
13signal action(var actionId);
14−readonly property size artShapeSize: Qt.size(-1, -1);
15readonly property int headerHeight: row.height;
14readonly property int headerHeight: row.height;
16Row {
15Row {
17 id: row;
16 id: row;
@@ -98,13 +97,15 @@ Label {
98}
97}
99]
98]
100}
99}
101−UbuntuShape {
100+Loader {
102− id: touchdown;
101+ active: root.pressed;
103− objectName: "touchdown";
104 anchors { fill: root }
102 anchors { fill: root }
105− visible: root.pressed;
103+ sourceComponent: UbuntuShape {
104+ objectName: "touchdown";
105+ anchors.fill: parent;
106− radius: "medium";
106+ radius: "medium";
107− borderSource: "radius_pressed.sci"
107+ borderSource: "radius_pressed.sci"
108+ }
108}
109}
109implicitHeight: row.y + row.height + units.gu(1);
110implicitHeight: row.y + row.height + units.gu(1);
110}
111}
tests/plugins/Dash/cardcreator/4.res.cardcreator−8
@@ -95,13 +95,5 @@ Label {
95}
95}
96]
96]
97}
97}
98−UbuntuShape {
99− id: touchdown;
100− objectName: "touchdown";
101− anchors { fill: root }
102− visible: root.pressed;
103− radius: "medium";
104− borderSource: "radius_pressed.sci"
105−}
106implicitHeight: row.y + row.height + units.gu(1);
98implicitHeight: row.y + row.height + units.gu(1);
107}
99}
tests/plugins/Dash/cardcreator/5.res−1
@@ -11,7 +11,6 @@ AbstractButton {
11 property int fixedHeaderHeight: -1;
11 property int fixedHeaderHeight: -1;
12 property size fixedArtShapeSize: Qt.size(-1, -1);
12 property size fixedArtShapeSize: Qt.size(-1, -1);
13signal action(var actionId);
13signal action(var actionId);
14−readonly property size artShapeSize: artShapeLoader.item ? Qt.size(artShapeLoader.item.width, artShapeLoader.item.height) : Qt.size(-1, -1);
15Loader {
14Loader {
16 id: artShapeLoader;
15 id: artShapeLoader;
17 height: root.fixedArtShapeSize.height;
16 height: root.fixedArtShapeSize.height;
tests/plugins/Dash/cardcreator/6.res+8−7
@@ -49,7 +49,6 @@ Loader {
49 }
49 }
50 }
50 }
51 }
51 }
52−readonly property size artShapeSize: Qt.size(-1, -1);
53readonly property int headerHeight: titleLabel.height + subtitleLabel.height + subtitleLabel.anchors.topMargin;
52readonly property int headerHeight: titleLabel.height + subtitleLabel.height + subtitleLabel.anchors.topMargin;
54Item {
53Item {
55 id: headerTitleContainer;
54 id: headerTitleContainer;
@@ -114,13 +113,15 @@ Item {
114
113
115 ]
114 ]
116 }
115 }
117−UbuntuShape {
116+Loader {
118− id: touchdown;
117+ active: root.pressed;
119− objectName: "touchdown";
120 anchors { fill: backgroundLoader }
118 anchors { fill: backgroundLoader }
121− visible: root.pressed;
119+ sourceComponent: UbuntuShape {
120+ objectName: "touchdown";
121+ anchors.fill: parent;
122− radius: "medium";
122+ radius: "medium";
123− borderSource: "radius_pressed.sci"
123+ borderSource: "radius_pressed.sci"
124+ }
124}
125}
125implicitHeight: headerTitleContainer.y + headerTitleContainer.height + units.gu(1);
126implicitHeight: headerTitleContainer.y + headerTitleContainer.height + units.gu(1);
126}
127}
tests/plugins/Dash/cardcreator/6.res.cardcreator−8
@@ -112,13 +112,5 @@ Item {
112
112
113 ]
113 ]
114 }
114 }
115−UbuntuShape {
116− id: touchdown;
117− objectName: "touchdown";
118− anchors { fill: backgroundLoader }
119− visible: root.pressed;
120− radius: "medium";
121− borderSource: "radius_pressed.sci"
122−}
123implicitHeight: headerTitleContainer.y + headerTitleContainer.height + units.gu(1);
115implicitHeight: headerTitleContainer.y + headerTitleContainer.height + units.gu(1);
124}
116}
tests/plugins/Dash/cardcreator/7.res−1
@@ -49,7 +49,6 @@ Loader {
49 }
49 }
50 }
50 }
51 }
51 }
52−readonly property size artShapeSize: Qt.size(-1, -1);
53readonly property int headerHeight: row.height;
52readonly property int headerHeight: row.height;
54Row {
53Row {
55 id: row;
54 id: row;
tests/plugins/Dash/cardcreator/8.res+8−7
@@ -49,7 +49,6 @@ Loader {
49 }
49 }
50 }
50 }
51 }
51 }
52−readonly property size artShapeSize: Qt.size(-1, -1);
53readonly property int headerHeight: row.height;
52readonly property int headerHeight: row.height;
54Row {
53Row {
55 id: row;
54 id: row;
@@ -96,13 +95,15 @@ left: parent.left;
96
95
97 ]
96 ]
98 }
97 }
99−UbuntuShape {
98+Loader {
100− id: touchdown;
99+ active: root.pressed;
101− objectName: "touchdown";
102 anchors { fill: backgroundLoader }
100 anchors { fill: backgroundLoader }
103− visible: root.pressed;
101+ sourceComponent: UbuntuShape {
102+ objectName: "touchdown";
103+ anchors.fill: parent;
104− radius: "medium";
104+ radius: "medium";
105− borderSource: "radius_pressed.sci"
105+ borderSource: "radius_pressed.sci"
106+ }
106}
107}
107implicitHeight: row.y + row.height + units.gu(1);
108implicitHeight: row.y + row.height + units.gu(1);
108}
109}
tests/plugins/Dash/cardcreator/8.res.cardcreator−8
@@ -93,13 +93,5 @@ left: parent.left;
93
93
94 ]
94 ]
95 }
95 }
96−UbuntuShape {
97− id: touchdown;
98− objectName: "touchdown";
99− anchors { fill: backgroundLoader }
100− visible: root.pressed;
101− radius: "medium";
102− borderSource: "radius_pressed.sci"
103−}
104implicitHeight: row.y + row.height + units.gu(1);
96implicitHeight: row.y + row.height + units.gu(1);
105}
97}
tests/plugins/Dash/cardcreator/9.res+10−9
@@ -11,7 +11,6 @@ AbstractButton {
11 property int fixedHeaderHeight: -1;
11 property int fixedHeaderHeight: -1;
12 property size fixedArtShapeSize: Qt.size(-1, -1);
12 property size fixedArtShapeSize: Qt.size(-1, -1);
13signal action(var actionId);
13signal action(var actionId);
14−readonly property size artShapeSize: Qt.size(-1, -1);
15readonly property int headerHeight: titleLabel.height + subtitleLabel.height + subtitleLabel.anchors.topMargin + audioProgressBar.height + audioProgressBar.anchors.topMargin;
14readonly property int headerHeight: titleLabel.height + subtitleLabel.height + subtitleLabel.anchors.topMargin + audioProgressBar.height + audioProgressBar.anchors.topMargin;
16Label {
15Label {
17 id: titleLabel;
16 id: titleLabel;
@@ -107,13 +106,15 @@ CardAudioProgress {
107 onPressAndHold: {
106 onPressAndHold: {
108 root.pressAndHold();
107 root.pressAndHold();
109 }
108 }
109+ }Loader {
110+ active: root.pressed;
111+ anchors { fill: root }
110− }UbuntuShape {
112+ sourceComponent: UbuntuShape {
111− id: touchdown;
112− objectName: "touchdown";
113+ objectName: "touchdown";
113− anchors { fill: root }
114+ anchors.fill: parent;
114− visible: root.pressed;
115− radius: "medium";
115+ radius: "medium";
116− borderSource: "radius_pressed.sci"
116+ borderSource: "radius_pressed.sci"
117+ }
117− }
118+}
118implicitHeight: audioButton.height;
119implicitHeight: audioButton.height;
119}
120}
tests/plugins/Dash/cardcreator/9.res.cardcreator+1−9
@@ -105,13 +105,5 @@ CardAudioProgress {
105 onPressAndHold: {
105 onPressAndHold: {
106 root.pressAndHold();
106 root.pressAndHold();
107 }
107 }
108− }UbuntuShape {
109− id: touchdown;
110− objectName: "touchdown";
111− anchors { fill: root }
112− visible: root.pressed;
113− radius: "medium";
114− borderSource: "radius_pressed.sci"
115− }
116−implicitHeight: audioButton.height;
108+ }implicitHeight: audioButton.height;
117}
109}
tests/plugins/Dash/listviewwithpageheadertest.cpp+30
@@ -1994,6 +1994,36 @@ private Q_SLOTS:
1994 verifyItem(2, 400., 350., false);
1994 verifyItem(2, 400., 350., false);
1995 }
1995 }
1996
1996
1997+ void testRemoveItemNotShowHeader()
1998+ {
1999+ QMetaObject::invokeMethod(model, "removeItems", Q_ARG(QVariant, 0), Q_ARG(QVariant, 6));
2000+ QMetaObject::invokeMethod(model, "insertItem", Q_ARG(QVariant, 0), Q_ARG(QVariant, 90));
2001+ QMetaObject::invokeMethod(model, "insertItem", Q_ARG(QVariant, 1), Q_ARG(QVariant, 600));
2002+ QTRY_COMPARE(lvwph->m_visibleItems.count(), 2);
2003+ scrollToBottom();
2004+ QMetaObject::invokeMethod(model, "insertItem", Q_ARG(QVariant, 1), Q_ARG(QVariant, 2000));
2005+ QTRY_COMPARE(lvwph->m_visibleItems.count(), 3);
2006+ QMetaObject::invokeMethod(model, "removeItems", Q_ARG(QVariant, 2), Q_ARG(QVariant, 1));
2007+ QTRY_COMPARE(lvwph->m_headerItem->y(), -610.);
2008+ QTRY_COMPARE(lvwph->m_minYExtent, 610.);
2009+ }
2010+
2011+ void testRemoveItemCutContents()
2012+ {
2013+ view->rootObject()->setHeight(921);
2014+ QMetaObject::invokeMethod(model, "removeItems", Q_ARG(QVariant, 0), Q_ARG(QVariant, 6));
2015+ QMetaObject::invokeMethod(model, "insertItem", Q_ARG(QVariant, 0), Q_ARG(QVariant, 1000));
2016+ QTRY_COMPARE(lvwph->m_visibleItems.count(), 1);
2017+ QTRY_COMPARE(lvwph->contentHeight(), 1050.);
2018+ scrollToBottom();
2019+ QMetaObject::invokeMethod(model, "insertItem", Q_ARG(QVariant, 0), Q_ARG(QVariant, 530));
2020+ QTRY_COMPARE(lvwph->m_visibleItems.count(), 2);
2021+ QMetaObject::invokeMethod(model, "removeItems", Q_ARG(QVariant, 1), Q_ARG(QVariant, 1));
2022+ QTRY_COMPARE(lvwph->m_visibleItems.count(), 1);
2023+ verifyItem(0, 50., 530., false);
2024+ QTRY_COMPARE(lvwph->m_minYExtent, 530.);
2025+ }
2026+
1997private:
2027private:
1998 QQuickView *view;
2028 QQuickView *view;
1999 ListViewWithPageHeader *lvwph;
2029 ListViewWithPageHeader *lvwph;
tests/plugins/ImageCache/CMakeLists.txtnew file+20
@@ -0,0 +1,20 @@
1+include_directories(
2+ ${CMAKE_CURRENT_BINARY_DIR}
3+ )
4+
5+include_directories(
6+ SYSTEM
7+ ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
8+ )
9+
10+add_definitions(-DCURRENT_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}")
11+remove_definitions(-DQT_NO_KEYWORDS)
12+
13+add_executable(ImageCacheTestExec
14+ test.cpp
15+ )
16+qt5_use_modules(ImageCacheTestExec Core Quick Test)
17+add_unity8_uitest(ImageCache ImageCacheTestExec
18+ DEPENDS ImageCache-qml
19+ IMPORT_PATHS ${UNITY_PLUGINPATH}
20+ )
tests/plugins/ImageCache/graphics/wide.jpgnew file

Binary file not shown.

tests/plugins/ImageCache/test.cppnew file+214
@@ -0,0 +1,214 @@
1+/*
2+ * Copyright (C) 2016 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 <QCoreApplication>
18+#include <QFileInfo>
19+#include <QImageReader>
20+#include <QQuickView>
21+#include <QTemporaryDir>
22+#include <QtTestGui>
23+#include <private/qquickimage_p.h>
24+#include <time.h>
25+#include <utime.h>
26+
27+Q_DECLARE_METATYPE(QQuickImage::Status)
28+
29+class ImageCacheTest : public QObject
30+{
31+ Q_OBJECT
32+
33+private:
34+
35+ void setUpImage(const QString &name, const QSize &sourceSize = QSize())
36+ {
37+ QString url = "image://unity8imagecache/file://" + sourceFile(name);
38+ image->setProperty("sourceSize", sourceSize);
39+ image->setProperty("source", url);
40+ }
41+
42+ void waitForImage(QQuickImage::Status status = QQuickImage::Ready)
43+ {
44+ QTRY_COMPARE(image->property("status"), QVariant((int)status));
45+ }
46+
47+ QSize cachedImageSize(const QString &filename)
48+ {
49+ QImageReader reader(filename);
50+ return reader.size();
51+ }
52+
53+ QString sourceFile(const QString &name)
54+ {
55+ return CURRENT_SOURCE_DIR "/graphics/" + name;
56+ }
57+
58+ QString cachedFile(bool isPath, const QString &name)
59+ {
60+ if (isPath)
61+ return home->path() + "/.cache/unity8/imagecache/paths" + sourceFile(name);
62+ else
63+ return home->path() + "/.cache/unity8/imagecache/names/" + name;
64+ }
65+
66+ void createCachedImage(const QString &name, const QString &cachePath, const QSize &cacheSize, time_t mtime_in = 0)
67+ {
68+ QString origPath = sourceFile(name);
69+ QImageReader origImage(origPath);
70+ origImage.setScaledSize(cacheSize);
71+ auto format = origImage.format(); // can't get this after reading
72+
73+ QImage cacheImage(origImage.read());
74+ QVERIFY(!cacheImage.isNull());
75+
76+ QFileInfo(cachePath).dir().mkpath(QStringLiteral("."));
77+ QVERIFY(cacheImage.save(cachePath, format, 100));
78+
79+ // Make file a few seconds old, so we can more easily tell if it gets updated
80+ auto mtime = mtime_in == 0 ? QFileInfo(cachePath).lastModified().toTime_t() - 5 : mtime_in;
81+ struct utimbuf timebuffer;
82+ timebuffer.modtime = mtime;
83+ timebuffer.actime = timebuffer.modtime;
84+ QCOMPARE(utime(cachePath.toUtf8().data(), &timebuffer), 0);
85+ }
86+
87+private Q_SLOTS:
88+
89+ void init()
90+ {
91+ home = new QTemporaryDir();
92+ QVERIFY(home->isValid());
93+ qputenv("HOME", home->path().toUtf8());
94+
95+ view = new QQuickView();
96+ view->setSource(QUrl::fromLocalFile(CURRENT_SOURCE_DIR "/test.qml"));
97+ image = view->rootObject();
98+
99+ view->show();
100+ QTest::qWaitForWindowExposed(view);
101+
102+ waitForImage(QQuickImage::Null);
103+ }
104+
105+ void cleanup()
106+ {
107+ delete view;
108+ delete home;
109+ }
110+
111+ void testFileNotFound()
112+ {
113+ setUpImage("NOTHERE");
114+ waitForImage(QQuickImage::Error);
115+ QVERIFY(!QFile::exists(cachedFile(true, "NOTHERE")));
116+ }
117+
118+ void testNoSourceSize()
119+ {
120+ setUpImage("wide.jpg");
121+ waitForImage();
122+ QVERIFY(!QFile::exists(cachedFile(true, "wide.jpg")));
123+ }
124+
125+ void testNoScalingUp()
126+ {
127+ setUpImage("wide.jpg", QSize(1000, 1000));
128+ waitForImage();
129+ QVERIFY(!QFile::exists(cachedFile(true, "wide.jpg")));
130+ }
131+
132+ void testFullSourceSize()
133+ {
134+ setUpImage("wide.jpg", QSize(100, 100));
135+ waitForImage();
136+ QCOMPARE(cachedImageSize(cachedFile(true, "wide.jpg")), QSize(100, 100));
137+ }
138+
139+ void testWidthSourceSize()
140+ {
141+ setUpImage("wide.jpg", QSize(100, 0));
142+ waitForImage();
143+ // wide.jpg is 500x200
144+ QCOMPARE(cachedImageSize(cachedFile(true, "wide.jpg")), QSize(100, 40));
145+ }
146+
147+ void testHeightSourceSize()
148+ {
149+ setUpImage("wide.jpg", QSize(0, 100));
150+ waitForImage();
151+ // wide.jpg is 500x200
152+ QCOMPARE(cachedImageSize(cachedFile(true, "wide.jpg")), QSize(250, 100));
153+ }
154+
155+ void testNameArg()
156+ {
157+ setUpImage("wide.jpg?name=foo", QSize(0, 100));
158+ waitForImage();
159+ QVERIFY(!QFile::exists(cachedFile(true, ""))); // check for dir itself
160+ QVERIFY(QFile::exists(cachedFile(false, "foo")));
161+ }
162+
163+ void testLoadCache()
164+ {
165+ auto cacheName = cachedFile(false, "foo");
166+ createCachedImage("wide.jpg", cacheName, QSize(250, 100));
167+ auto mtime = QFileInfo(cacheName).lastModified();
168+
169+ auto now = time(NULL);
170+ QVERIFY(QFileInfo(cacheName).lastModified().toTime_t() < now); // sanity check
171+
172+ setUpImage("wide.jpg?name=foo", QSize(0, 100));
173+ waitForImage();
174+ QCOMPARE(QFileInfo(cacheName).lastModified(), mtime); // wasn't recreated
175+ }
176+
177+ void testDifferentSize()
178+ {
179+ auto cacheName = cachedFile(false, "foo");
180+ createCachedImage("wide.jpg", cacheName, QSize(250, 100));
181+
182+ auto now = time(NULL);
183+ QVERIFY(QFileInfo(cacheName).lastModified().toTime_t() < now); // sanity check
184+
185+ setUpImage("wide.jpg?name=foo", QSize(100, 0));
186+ waitForImage();
187+ QVERIFY(QFileInfo(cacheName).lastModified().toTime_t() >= now); // was recreated
188+ }
189+
190+ void testStaleCache()
191+ {
192+ auto sourceName = sourceFile("wide.jpg");
193+ auto mtime = QFileInfo(sourceName).lastModified().toTime_t() - 5;
194+
195+ auto cacheName = cachedFile(false, "foo");
196+ createCachedImage("wide.jpg", cacheName, QSize(250, 100), mtime);
197+
198+ auto now = time(NULL);
199+ QVERIFY(QFileInfo(cacheName).lastModified().toTime_t() < now); // sanity check
200+
201+ setUpImage("wide.jpg?name=foo", QSize(0, 100));
202+ waitForImage();
203+ QVERIFY(QFileInfo(cacheName).lastModified().toTime_t() >= now); // was recreated
204+ }
205+
206+private:
207+ QQuickView *view;
208+ QObject *image;
209+ QTemporaryDir *home;
210+};
211+
212+QTEST_MAIN(ImageCacheTest)
213+
214+#include "test.moc"
qml/Components/Circle.qml → tests/plugins/ImageCache/test.qmlrenamed+6−5
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright 2016 Canonical Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
5 * it under the terms of the GNU General Public License as published by
@@ -15,8 +15,9 @@
15 */
15 */
16
16
17import QtQuick 2.4
17import QtQuick 2.4
18+import ImageCache 0.1
18
19
19−Rectangle {
20+Image {
20− antialiasing: true
21− radius: width / 2
21+ width: 400
22+ height: 400
22− }
23+}
tests/plugins/LightDM/IntegratedLightDM/CMakeLists.txt−3
@@ -26,7 +26,6 @@ add_unity8_uitest(GreeterDBus dbus-test-runner
26 ARGS --task $<TARGET_FILE:GreeterDBusTestExec>
26 ARGS --task $<TARGET_FILE:GreeterDBusTestExec>
27 DEPENDS MockLightDM
27 DEPENDS MockLightDM
28 IMPORT_PATHS ${UNITY_MOCKPATH}
28 IMPORT_PATHS ${UNITY_MOCKPATH}
29− LIGHTDM
30 )
29 )
31
30
32add_executable(GreeterPamTestExec
31add_executable(GreeterPamTestExec
@@ -63,7 +62,6 @@ target_include_directories(GreeterSessionsModelTestExec PUBLIC
63add_unity8_uitest(GreeterSessions GreeterSessionsModelTestExec
62add_unity8_uitest(GreeterSessions GreeterSessionsModelTestExec
64 ENVIRONMENT LIBLIGHTDM_MOCK_MODE=full
63 ENVIRONMENT LIBLIGHTDM_MOCK_MODE=full
65 DEPENDS MockLightDM
64 DEPENDS MockLightDM
66− LIGHTDM
67 )
65 )
68
66
69# UsersModelTest
67# UsersModelTest
@@ -87,7 +85,6 @@ target_include_directories(GreeterUsersModelTestExec PUBLIC
87add_unity8_uitest(GreeterUsers GreeterUsersModelTestExec
85add_unity8_uitest(GreeterUsers GreeterUsersModelTestExec
88 ENVIRONMENT LIBLIGHTDM_MOCK_MODE=full
86 ENVIRONMENT LIBLIGHTDM_MOCK_MODE=full
89 DEPENDS MockLightDM
87 DEPENDS MockLightDM
90− LIGHTDM
91 )
88 )
92
89
93add_executable(GreeterIntegratedTestExec
90add_executable(GreeterIntegratedTestExec
tests/plugins/LightDM/IntegratedLightDM/sessionsmodel.cpp+32
@@ -59,6 +59,38 @@ private Q_SLOTS:
59 }
59 }
60 }
60 }
61
61
62+ void testIconLookupLogic()
63+ {
64+ const QString data[][2] = {
65+ //{SESSION KEY, ICON NAME}
66+ {"gnome-classic", "gnome_badge.png"},
67+ {"gnome-flashback-compiz", "gnome_badge.png"},
68+ {"gnome-flashback-metacity", "gnome_badge.png"},
69+ {"gnome-shell", "gnome_badge.png"},
70+ {"gnome-wayland", "gnome_badge.png"},
71+ {"gnome", "gnome_badge.png"},
72+ {"kde", "kde_badge.png"},
73+ {"plasma", "kde_badge.png"},
74+ {"recovery_console", "recovery_console_badge.png"},
75+ {"ubuntu","ubuntu_badge.png"},
76+ {"ubuntu-2d", "ubuntu_badge.png"},
77+ {"xterm", "recovery_console_badge.png"},
78+ {"made up session", "unknown_badge.png"},
79+ {"", "unknown_badge.png"}
80+ };
81+
82+ const short length = sizeof(data) / sizeof(data[0]);
83+ for (int i = 0; i < length; i++) {
84+ std::string key = data[i][0].toStdString();
85+ std::string icon = data[i][1].toStdString();
86+ std::string url = model->iconUrl(data[i][0]).toString().toStdString();
87+ std::string errorMessage = "Session icon url for " + key +
88+ " should contain " + icon + " but " + url + " was returned";
89+ QVERIFY2(model->iconUrl(data[i][0]).toString().contains(data[i][1]),
90+ errorMessage.c_str());
91+ }
92+ }
93+
62 void testMultipleSessionsCountIsCorrect()
94 void testMultipleSessionsCountIsCorrect()
63 {
95 {
64 sourceModel->setTestScenario("multipleSessions");
96 sourceModel->setTestScenario("multipleSessions");
tests/plugins/LightDM/IntegratedLightDM/usersmodel.cpp+2−2
@@ -59,10 +59,10 @@ private Q_SLOTS:
59 void testMangleColor()
59 void testMangleColor()
60 {
60 {
61 QString background = getStringValue(sourceModel, "color-background", QLightDM::UsersModel::BackgroundPathRole);
61 QString background = getStringValue(sourceModel, "color-background", QLightDM::UsersModel::BackgroundPathRole);
62− QVERIFY(background == "#dd4814");
62+ QVERIFY(background == "#E95420");
63
63
64 background = getStringValue(model, "color-background", QLightDM::UsersModel::BackgroundPathRole);
64 background = getStringValue(model, "color-background", QLightDM::UsersModel::BackgroundPathRole);
65− QVERIFY(background == "data:image/svg+xml,<svg><rect width='100%' height='100%' fill='#dd4814'/></svg>");
65+ QVERIFY(background == "data:image/svg+xml,<svg><rect width='100%' height='100%' fill='#E95420'/></svg>");
66 }
66 }
67
67
68 void testMangleEmptyName()
68 void testMangleEmptyName()
tests/plugins/SessionBroadcast/BroadcastServer.cppnew file+33
@@ -0,0 +1,33 @@
1+/*
2+ * Copyright 2016 Canonical Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it
5+ * under the terms of the GNU General Public License version 3, as published
6+ * by the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but
9+ * WITHOUT ANY WARRANTY; without even the implied warranties of
10+ * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
11+ * PURPOSE. See the GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * version 3 along with this program. If not, see
15+ * <http://www.gnu.org/licenses/>
16+ */
17+
18+#include "BroadcastServer.h"
19+
20+BroadcastServer::BroadcastServer(QObject *parent)
21+ : QObject(parent)
22+{
23+}
24+
25+void BroadcastServer::RequestUrlStart(const QString &username, const QString &url)
26+{
27+ Q_EMIT StartUrl(username, url);
28+}
29+
30+void BroadcastServer::RequestHomeShown(const QString &username)
31+{
32+ Q_EMIT ShowHome(username);
33+}
tests/plugins/SessionBroadcast/BroadcastServer.hnew file+40
@@ -0,0 +1,40 @@
1+/*
2+ * Copyright 2016 Canonical Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it
5+ * under the terms of the GNU General Public License version 3, as published
6+ * by the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but
9+ * WITHOUT ANY WARRANTY; without even the implied warranties of
10+ * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
11+ * PURPOSE. See the GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * version 3 along with this program. If not, see
15+ * <http://www.gnu.org/licenses/>
16+ */
17+
18+#ifndef UNITY_BROADCASTSERVER_H
19+#define UNITY_BROADCASTSERVER_H
20+
21+#include <QDBusContext>
22+#include <QObject>
23+
24+class BroadcastServer: public QObject, protected QDBusContext
25+{
26+ Q_OBJECT
27+
28+public:
29+ explicit BroadcastServer(QObject *parent = nullptr);
30+
31+public Q_SLOTS:
32+ void RequestUrlStart(const QString &username, const QString &url);
33+ void RequestHomeShown(const QString &username);
34+
35+Q_SIGNALS:
36+ void StartUrl(const QString &username, const QString &url);
37+ void ShowHome(const QString &username);
38+};
39+
40+#endif
tests/plugins/SessionBroadcast/CMakeLists.txtnew file+45
@@ -0,0 +1,45 @@
1+macro(make_dbus_class NAME INTERFACE)
2+ if(${CMAKE_CURRENT_SOURCE_DIR}/interfaces.xml IS_NEWER_THAN ${CMAKE_CURRENT_BINARY_DIR}/${NAME}Adaptor.h)
3+ execute_process(COMMAND ${QDBUSXML2CPP_EXECUTABLE} -c ${NAME}Adaptor -a ${CMAKE_CURRENT_BINARY_DIR}/${NAME}Adaptor ${CMAKE_CURRENT_SOURCE_DIR}/interfaces.xml ${INTERFACE})
4+ endif()
5+endmacro(make_dbus_class)
6+
7+make_dbus_class(Broadcast com.canonical.Unity.Greeter.Broadcast)
8+
9+add_definitions(-DSM_BUSNAME=sessionBus)
10+
11+include_directories(
12+ ${GLIB_INCLUDE_DIRS}
13+ ${CMAKE_CURRENT_BINARY_DIR}
14+ ${CMAKE_CURRENT_SOURCE_DIR}
15+ ${CMAKE_SOURCE_DIR}/plugins/SessionBroadcast
16+)
17+
18+link_libraries(
19+ ${GLIB_LIBRARIES}
20+)
21+
22+add_executable(MockBroadcastServer
23+ ${CMAKE_CURRENT_BINARY_DIR}/BroadcastAdaptor.cpp
24+ server.cpp
25+ BroadcastServer.cpp
26+)
27+qt5_use_modules(MockBroadcastServer Core DBus)
28+
29+### SessionBroadcastTest
30+add_executable(sessionbroadcasttestExec
31+ ${CMAKE_SOURCE_DIR}/plugins/SessionBroadcast/SessionBroadcast.cpp
32+ sessionbroadcasttest.cpp
33+)
34+qt5_use_modules(sessionbroadcasttestExec Test Core Qml DBus)
35+
36+add_unity8_unittest(SessionBroadcast dbus-test-runner
37+ ARG_PREFIX "--parameter"
38+ ARGS
39+ --task $<TARGET_FILE:MockBroadcastServer>
40+ --task-name server
41+ --ignore-return
42+ --task $<TARGET_FILE:sessionbroadcasttestExec>
43+ --task-name client
44+ --wait-for com.canonical.Unity.Greeter.Broadcast
45+)
tests/plugins/SessionBroadcast/interfaces.xmlnew file+18
@@ -0,0 +1,18 @@
1+<node>
2+ <interface name="com.canonical.Unity.Greeter.Broadcast">
3+ <method name="RequestUrlStart">
4+ <arg name="username" type="s" direction="in" />
5+ <arg name="url" type="s" direction="in" />
6+ </method>
7+ <method name="RequestHomeShown">
8+ <arg name="username" type="s" direction="in" />
9+ </method>
10+ <signal name="StartUrl">
11+ <arg name="username" type="s" direction="out" />
12+ <arg name="url" type="s" direction="out" />
13+ </signal>
14+ <signal name="ShowHome">
15+ <arg name="username" type="s" direction="out" />
16+ </signal>
17+ </interface>
18+</node>
tests/plugins/SessionBroadcast/server.cppnew file+37
@@ -0,0 +1,37 @@
1+/*
2+ * Copyright 2016 Canonical Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it
5+ * under the terms of the GNU General Public License version 3, as published
6+ * by the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but
9+ * WITHOUT ANY WARRANTY; without even the implied warranties of
10+ * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
11+ * PURPOSE. See the GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * version 3 along with this program. If not, see
15+ * <http://www.gnu.org/licenses/>
16+ */
17+
18+#include "BroadcastAdaptor.h"
19+#include "BroadcastServer.h"
20+#include <QCoreApplication>
21+
22+int main(int argc, char *argv[])
23+{
24+ QCoreApplication a(argc, argv);
25+
26+ auto broadcast = new BroadcastServer(&a);
27+ new BroadcastAdaptor(broadcast);
28+
29+ // We use the session bus for testing. The real plugin uses the system bus
30+ auto connection = QDBusConnection::sessionBus();
31+ if (!connection.registerObject("/com/canonical/Unity/Greeter/Broadcast", broadcast))
32+ return 1;
33+ if (!connection.registerService("com.canonical.Unity.Greeter.Broadcast"))
34+ return 1;
35+
36+ return a.exec();
37+}
tests/plugins/SessionBroadcast/sessionbroadcasttest.cppnew file+111
@@ -0,0 +1,111 @@
1+/*
2+ * Copyright 2016 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include <QtTest>
18+#include <QDBusInterface>
19+#include <QSignalSpy>
20+
21+#include <glib.h>
22+
23+#include "SessionBroadcast.h"
24+
25+class SessionBroadcastTest : public QObject
26+{
27+ Q_OBJECT
28+
29+private Q_SLOTS:
30+
31+ void initTestCase() {
32+ broadcastServer = new QDBusInterface("com.canonical.Unity.Greeter.Broadcast",
33+ "/com/canonical/Unity/Greeter/Broadcast",
34+ "com.canonical.Unity.Greeter.Broadcast",
35+ QDBusConnection::sessionBus(), this);
36+ serverStartUrlSpy = new QSignalSpy(broadcastServer,
37+ SIGNAL(StartUrl(const QString &, const QString &)));
38+ serverShowHomeSpy = new QSignalSpy(broadcastServer,
39+ SIGNAL(ShowHome(const QString &)));
40+ user = QString(g_get_user_name());
41+ otherUser = user + "2";
42+ }
43+
44+ void init()
45+ {
46+ serverStartUrlSpy->clear();
47+ serverShowHomeSpy->clear();
48+
49+ broadcastPlugin = new SessionBroadcast(this);
50+ pluginStartUrlSpy = new QSignalSpy(broadcastPlugin,
51+ SIGNAL(startUrl(const QString &)));
52+ pluginShowHomeSpy = new QSignalSpy(broadcastPlugin,
53+ SIGNAL(showHome()));
54+ }
55+
56+ void cleanup()
57+ {
58+ delete broadcastPlugin;
59+ }
60+
61+ void testReceiveUrlStart() {
62+ broadcastServer->call("RequestUrlStart", user, "test:");
63+ QTRY_COMPARE(serverStartUrlSpy->count(), 1);
64+ QCOMPARE(pluginStartUrlSpy->count(), 1);
65+ QCOMPARE((*pluginStartUrlSpy)[0][0], QVariant("test:"));
66+ }
67+
68+ void testReceiveUrlStartOtherUser() {
69+ broadcastServer->call("RequestUrlStart", otherUser, "test:");
70+ QTRY_COMPARE(serverStartUrlSpy->count(), 1);
71+ QCOMPARE(pluginStartUrlSpy->count(), 0);
72+ }
73+
74+ void testReceiveShowHome() {
75+ broadcastServer->call("RequestHomeShown", user);
76+ QTRY_COMPARE(serverShowHomeSpy->count(), 1);
77+ QCOMPARE(pluginShowHomeSpy->count(), 1);
78+ }
79+
80+ void testReceiveShowHomeOtherUser() {
81+ broadcastServer->call("RequestHomeShown", otherUser);
82+ QTRY_COMPARE(serverShowHomeSpy->count(), 1);
83+ QCOMPARE(pluginShowHomeSpy->count(), 0);
84+ }
85+
86+ void testSendUrlStart() {
87+ broadcastPlugin->requestUrlStart(otherUser, "test:");
88+ QTRY_COMPARE(serverStartUrlSpy->count(), 1);
89+ QCOMPARE((*serverStartUrlSpy)[0][0], QVariant(otherUser));
90+ QCOMPARE((*serverStartUrlSpy)[0][1], QVariant("test:"));
91+ }
92+
93+ void testSendShowHome() {
94+ broadcastPlugin->requestHomeShown(otherUser);
95+ QTRY_COMPARE(serverShowHomeSpy->count(), 1);
96+ QCOMPARE((*serverShowHomeSpy)[0][0], QVariant(otherUser));
97+ }
98+
99+private:
100+ QDBusInterface *broadcastServer;
101+ SessionBroadcast *broadcastPlugin;
102+ QSignalSpy *serverStartUrlSpy;
103+ QSignalSpy *serverShowHomeSpy;
104+ QSignalSpy *pluginStartUrlSpy;
105+ QSignalSpy *pluginShowHomeSpy;
106+ QString user;
107+ QString otherUser;
108+};
109+
110+QTEST_GUILESS_MAIN(SessionBroadcastTest)
111+#include "sessionbroadcasttest.moc"
tests/plugins/Utils/CMakeLists.txt+9
@@ -23,3 +23,12 @@ endforeach()
23
23
24# plain qml test
24# plain qml test
25add_unity8_qmlunittest(. UtilsStyle)
25add_unity8_qmlunittest(. UtilsStyle)
26+
27+# URLDispatcher test needs dbus-test-runner because it claims a dbus name
28+add_executable(URLDispatcherTestExec URLDispatcherTest.cpp)
29+qt5_use_modules(URLDispatcherTestExec Test Core Qml)
30+target_link_libraries(URLDispatcherTestExec Utils-qml)
31+add_unity8_unittest(URLDispatcher dbus-test-runner
32+ ARG_PREFIX "--parameter"
33+ ARGS --task $<TARGET_FILE:URLDispatcherTestExec>
34+)
tests/plugins/Utils/URLDispatcherTest.cppnew file+78
@@ -0,0 +1,78 @@
1+/*
2+ * Copyright (C) 2016 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 "URLDispatcher.h"
18+
19+#include <QDBusInterface>
20+#include <QSignalSpy>
21+#include <QTest>
22+
23+class URLDispatcherTest : public QObject
24+{
25+ Q_OBJECT
26+
27+private Q_SLOTS:
28+
29+ void initTestCase() {
30+ dispatchServer = new QDBusInterface("com.canonical.URLDispatcher",
31+ "/com/canonical/URLDispatcher",
32+ "com.canonical.URLDispatcher",
33+ QDBusConnection::sessionBus(), this);
34+ }
35+
36+ void init()
37+ {
38+ dispatcher = new URLDispatcher(this);
39+ dispatchSpy = new QSignalSpy(dispatcher,
40+ SIGNAL(urlRequested(const QString &)));
41+ }
42+
43+ void cleanup()
44+ {
45+ delete dispatcher;
46+ }
47+
48+ void testInactiveByDefault() {
49+ QVERIFY(!dispatcher->active());
50+ }
51+
52+ void testActiveRequest() {
53+ dispatcher->setActive(true);
54+ dispatchServer->call("DispatchURL", "test:", "package");
55+ QCOMPARE(dispatchSpy->count(), 1);
56+ QCOMPARE((*dispatchSpy)[0][0], QVariant("test:"));
57+ }
58+
59+ void testInactiveRequest() {
60+ dispatchServer->call("DispatchURL", "test:", "package");
61+ QCOMPARE(dispatchSpy->count(), 0);
62+ }
63+
64+ void testInactiveAfterActiveRequest() {
65+ dispatcher->setActive(true);
66+ dispatcher->setActive(false);
67+ dispatchServer->call("DispatchURL", "test:", "package");
68+ QCOMPARE(dispatchSpy->count(), 0);
69+ }
70+
71+private:
72+ QDBusInterface *dispatchServer;
73+ URLDispatcher *dispatcher;
74+ QSignalSpy *dispatchSpy;
75+};
76+
77+QTEST_GUILESS_MAIN(URLDispatcherTest)
78+#include "URLDispatcherTest.moc"
tests/qmltests/CMakeLists.txt+10−10
@@ -1,14 +1,14 @@
1
1
2add_subdirectory(Components)
2add_subdirectory(Components)
3
3
4−add_unity8_qmltest(. OrientedShell LIGHTDM)
4+add_unity8_qmltest(. OrientedShell)
5add_unity8_qmltest(. DisabledScreenNotice)
5add_unity8_qmltest(. DisabledScreenNotice)
6−add_unity8_qmltest(. Shell LIGHTDM)
6+add_unity8_qmltest(. Shell)
7−add_unity8_qmltest(. ShellWithPin LIGHTDM)
7+add_unity8_qmltest(. ShellWithPin)
8add_unity8_qmltest(. DeviceConfiguration)
8add_unity8_qmltest(. DeviceConfiguration)
9add_unity8_qmltest(Components Background)
9add_unity8_qmltest(Components Background)
10add_unity8_qmltest(Components Carousel)
10add_unity8_qmltest(Components Carousel)
11−add_unity8_qmltest(Components Dialogs LIGHTDM)
11+add_unity8_qmltest(Components Dialogs)
12add_unity8_qmltest(Components DraggingArea)
12add_unity8_qmltest(Components DraggingArea)
13add_unity8_qmltest(Components LazyImage)
13add_unity8_qmltest(Components LazyImage)
14add_unity8_qmltest(Components Lockscreen)
14add_unity8_qmltest(Components Lockscreen)
@@ -25,7 +25,7 @@ add_unity8_qmltest(Components WallpaperResolver)
25add_unity8_qmltest(Components ZoomableImage)
25add_unity8_qmltest(Components ZoomableImage)
26add_unity8_qmltest(Dash Dash)
26add_unity8_qmltest(Dash Dash)
27add_unity8_qmltest(Dash DashContent)
27add_unity8_qmltest(Dash DashContent)
28−add_unity8_qmltest(Dash DashShell LIGHTDM)
28+add_unity8_qmltest(Dash DashShell)
29add_unity8_qmltest(Dash Card)
29add_unity8_qmltest(Dash Card)
30add_unity8_qmltest(Dash CardCarousel)
30add_unity8_qmltest(Dash CardCarousel)
31add_unity8_qmlbenchmark(Dash CardBenchmark 1000)
31add_unity8_qmlbenchmark(Dash CardBenchmark 1000)
@@ -66,10 +66,10 @@ add_unity8_qmltest(Dash/ScopeSettings ScopeSettingNumber)
66add_unity8_qmltest(Dash/ScopeSettings ScopeSettingString)
66add_unity8_qmltest(Dash/ScopeSettings ScopeSettingString)
67add_unity8_qmltest(Dash/ScopeSettings ScopeSettingsWidgetFactory)
67add_unity8_qmltest(Dash/ScopeSettings ScopeSettingsWidgetFactory)
68add_unity8_qmltest(Greeter Clock)
68add_unity8_qmltest(Greeter Clock)
69−add_unity8_qmltest(Greeter Greeter LIGHTDM)
69+add_unity8_qmltest(Greeter Greeter)
70−add_unity8_qmltest(Greeter Infographics LIGHTDM)
70+add_unity8_qmltest(Greeter Infographics)
71−add_unity8_qmltest(Greeter NarrowView LIGHTDM)
71+add_unity8_qmltest(Greeter NarrowView)
72−add_unity8_qmltest(Greeter WideView LIGHTDM)
72+add_unity8_qmltest(Greeter WideView)
73add_unity8_qmltest(Launcher Launcher)
73add_unity8_qmltest(Launcher Launcher)
74add_unity8_qmltest(Notifications Notifications)
74add_unity8_qmltest(Notifications Notifications)
75add_unity8_qmltest(Notifications VisualSnapDecisionsQueue)
75add_unity8_qmltest(Notifications VisualSnapDecisionsQueue)
@@ -94,6 +94,6 @@ add_unity8_qmltest(Stages SurfaceContainer)
94add_unity8_qmltest(Stages TabletStage)
94add_unity8_qmltest(Stages TabletStage)
95add_unity8_qmltest(Stages WindowResizeArea)
95add_unity8_qmltest(Stages WindowResizeArea)
96add_unity8_qmltest(Stages Splash)
96add_unity8_qmltest(Stages Splash)
97−add_unity8_qmltest(Tutorial Tutorial LIGHTDM)
97+add_unity8_qmltest(Tutorial Tutorial)
98add_unity8_qmltest(Wizard Wizard ENVIRONMENT "OXIDE_NO_SANDBOX=1")
98add_unity8_qmltest(Wizard Wizard ENVIRONMENT "OXIDE_NO_SANDBOX=1")
99add_unity8_qmltest(utils/Unity/Test UnityTest)
99add_unity8_qmltest(utils/Unity/Test UnityTest)
tests/qmltests/Components/tst_Carousel.qml−1
@@ -268,7 +268,6 @@ Item {
268
268
269 function test_activate() {
269 function test_activate() {
270 var carouselList = findChild(carousel, "listView");
270 var carouselList = findChild(carousel, "listView");
271− tryCompareFunction(function() { return findChild(carouselList, "carouselDelegate3") ? true : false; }, true);
272 var carouselItem = findChild(carousel, "carouselDelegate3")
271 var carouselItem = findChild(carousel, "carouselDelegate3")
273 verify(carouselItem, "Could not find delegate");
272 verify(carouselItem, "Could not find delegate");
274
273
tests/qmltests/Components/tst_Lockscreen.qml+2−41
@@ -37,8 +37,7 @@ Rectangle {
37 alphaNumeric: pinPadCheckBox.checked
37 alphaNumeric: pinPadCheckBox.checked
38 minPinLength: minPinLengthTextField.text
38 minPinLength: minPinLengthTextField.text
39 maxPinLength: maxPinLengthTextField.text
39 maxPinLength: maxPinLengthTextField.text
40− delayMinutes: delayMinutesTextField.text
40+ background: "/usr/share/backgrounds/warty-final-ubuntu.png"
41− background: "../../../qml/graphics/phone_background.jpg"
42 }
41 }
43
42
44 Connections {
43 Connections {
@@ -109,16 +108,6 @@ Rectangle {
109 text: "Retries left"
108 text: "Retries left"
110 }
109 }
111 }
110 }
112− Row {
113− TextField {
114− id: delayMinutesTextField
115− width: units.gu(7)
116− text: "0"
117− }
118− Label {
119− text: "Delay Minutes"
120− }
121− }
122 Label {
111 Label {
123 id: pinLabel
112 id: pinLabel
124 anchors.verticalCenter: parent.verticalCenter
113 anchors.verticalCenter: parent.verticalCenter
@@ -172,7 +161,6 @@ Rectangle {
172
161
173 function cleanup() {
162 function cleanup() {
174 lockscreen.clear(false);
163 lockscreen.clear(false);
175− delayMinutesTextField.text = "0"
176 enteredLabel.text = "";
164 enteredLabel.text = "";
177
165
178 // Reset sizing
166 // Reset sizing
@@ -422,7 +410,6 @@ Rectangle {
422 function test_infoPopup() {
410 function test_infoPopup() {
423 verify(findChild(root, "infoPopup") === null);
411 verify(findChild(root, "infoPopup") === null);
424 lockscreen.showInfoPopup("foo", "bar");
412 lockscreen.showInfoPopup("foo", "bar");
425− tryCompareFunction(function() { return findChild(root, "infoPopup") !== null}, true);
426
413
427 var infoPopup = findChild(root, "infoPopup");
414 var infoPopup = findChild(root, "infoPopup");
428 compare(infoPopup.title, "foo");
415 compare(infoPopup.title, "foo");
@@ -434,7 +421,7 @@ Rectangle {
434 var okButton = findChild(root, "infoPopupOkButton");
421 var okButton = findChild(root, "infoPopupOkButton");
435 mouseClick(okButton);
422 mouseClick(okButton);
436
423
437− tryCompareFunction(function() { return findChild(root, "infoPopup") === null}, true);
424+ tryCompareFunction(function() { return findChild(root, "infoPopup", 0 /* timeout */) === null}, true);
438
425
439 tryCompare(signalSpy, "count", 1);
426 tryCompare(signalSpy, "count", 1);
440 }
427 }
@@ -455,32 +442,6 @@ Rectangle {
455 compare(label.text, data.text);
442 compare(label.text, data.text);
456 }
443 }
457
444
458− function test_delayMinutes() {
459− delayMinutesTextField.text = "4"
460− waitForLockscreenReady()
461− var label = findChild(lockscreen, "deviceLockedLabel")
462− compare(label.text, "Device Locked")
463− }
464−
465− function test_showText_data() {
466− return [
467− { tag: "alphanumeric", alphanumeric: true },
468− { tag: "pinPad", alphanumeric: false },
469− ]
470− }
471−
472− function test_showText(data) {
473− pinPadCheckBox.checked = data.alphanumeric;
474− waitForLockscreenReady();
475−
476− lockscreen.showText("test");
477−
478− var pinPadLoader = findChild(lockscreen, "pinPadLoader");
479− verify(pinPadLoader.waiting);
480− compare(enteredLabel.text, ""); // no entered signal should occur
481− compare(lockscreen.passphrase, "test");
482− }
483−
484 function test_resize_data() {
445 function test_resize_data() {
485 return [
446 return [
486 { tag: "small", width: units.gu(40), height: units.gu(76) },
447 { tag: "small", width: units.gu(40), height: units.gu(76) },
tests/qmltests/Components/tst_WallpaperResolver.qml+21−35
@@ -18,8 +18,6 @@ import QtQuick 2.4
18import QtTest 1.0
18import QtTest 1.0
19import Ubuntu.Components 1.3
19import Ubuntu.Components 1.3
20import Unity.Test 0.1
20import Unity.Test 0.1
21−import AccountsService 0.1
22−import GSettings 1.0
23
21
24import "../../../qml/Components"
22import "../../../qml/Components"
25
23
@@ -32,7 +30,6 @@ Image {
32
30
33 WallpaperResolver {
31 WallpaperResolver {
34 id: wallpaperResolver
32 id: wallpaperResolver
35− width: units.gu(70)
36 }
33 }
37
34
38 UnityTestCase {
35 UnityTestCase {
@@ -42,48 +39,37 @@ Image {
42
39
43 function test_background_data() {
40 function test_background_data() {
44 return [
41 return [
45− {tag: "color",
42+ {tag: "none-valid",
46− accounts: Qt.resolvedUrl("data:image/svg+xml,<svg><rect width='100%' height='100%' fill='#dd4814'/></svg>"),
43+ list: ["/first", "/middle", "/last"],
47− gsettings: "",
44+ output: "file:///last"},
48− output: Qt.resolvedUrl("data:image/svg+xml,<svg><rect width='100%' height='100%' fill='#dd4814'/></svg>")},
49
45
50− {tag: "empty", accounts: "", gsettings: "", output: "defaultBackground"},
46+ {tag: "first-valid",
51−
52− {tag: "as-specified",
53− accounts: Qt.resolvedUrl("../../data/unity/backgrounds/blue.png"),
47+ list: [Qt.resolvedUrl("../../data/unity/backgrounds/blue.png"),
54− gsettings: "",
48+ "/middle", "/last"],
55 output: Qt.resolvedUrl("../../data/unity/backgrounds/blue.png")},
49 output: Qt.resolvedUrl("../../data/unity/backgrounds/blue.png")},
56
50
57− {tag: "gs-specified",
51+ {tag: "middle-valid",
58− accounts: "",
52+ list: ["/first",
59− gsettings: Qt.resolvedUrl("../../data/unity/backgrounds/red.png"),
53+ Qt.resolvedUrl("../../data/unity/backgrounds/red.png"),
54+ "/last"],
60 output: Qt.resolvedUrl("../../data/unity/backgrounds/red.png")},
55 output: Qt.resolvedUrl("../../data/unity/backgrounds/red.png")},
61
56
62− {tag: "both-specified",
57+ {tag: "last-valid",
63− accounts: Qt.resolvedUrl("../../data/unity/backgrounds/blue.png"),
58+ list: ["/first",
64− gsettings: Qt.resolvedUrl("../../data/unity/backgrounds/red.png"),
59+ "/middle",
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"),
60+ Qt.resolvedUrl("../../data/unity/backgrounds/red.png")],
70 output: Qt.resolvedUrl("../../data/unity/backgrounds/red.png")},
61 output: Qt.resolvedUrl("../../data/unity/backgrounds/red.png")},
71
62
72− {tag: "invalid-both",
63+ {tag: "multiple-valid",
73− accounts: Qt.resolvedUrl("../../data/unity/backgrounds/nope.png"),
64+ list: [Qt.resolvedUrl("../../data/unity/backgrounds/blue.png"),
74− gsettings: Qt.resolvedUrl("../../data/unity/backgrounds/stillnope.png"),
65+ Qt.resolvedUrl("../../data/unity/backgrounds/red.png")],
75− output: "defaultBackground"},
66+ output: Qt.resolvedUrl("../../data/unity/backgrounds/blue.png")},
76 ]
67 ]
77 }
68 }
78− function test_background(data) {
79− AccountsService.backgroundFile = data.accounts;
80− GSettingsController.setPictureUri(data.gsettings);
81
69
82− if (data.output === "defaultBackground") {
70+ function test_background(data) {
83− tryCompare(wallpaperResolver, "background", wallpaperResolver.defaultBackground);
71+ wallpaperResolver.candidates = data.list;
84− } else {
85− tryCompare(wallpaperResolver, "background", data.output);
72+ tryCompare(wallpaperResolver, "background", data.output);
86− }
87 }
73 }
88 }
74 }
89}
75}
tests/qmltests/Components/tst_ZoomableImage.qml+1−1
@@ -31,7 +31,7 @@ Rectangle {
31 }
31 }
32
32
33 property var widgetData1: {
33 property var widgetData1: {
34− "source": "../../../qml/graphics/phone_background.jpg",
34+ "source": "../../../tests/graphics/avatars/amanda@12.png",
35 "zoomable": false
35 "zoomable": false
36 }
36 }
37
37
tests/qmltests/Dash/Previews/tst_Preview.qml−2
@@ -78,7 +78,6 @@ Rectangle {
78
78
79 function test_containOnFocus() {
79 function test_containOnFocus() {
80 waitForRendering(preview);
80 waitForRendering(preview);
81− tryCompareFunction(function () { return findChild(preview, "widget-10") != null }, true);
82 var widget = findChild(preview, "widget-10");
81 var widget = findChild(preview, "widget-10");
83
82
84 var bottomLeft = preview.mapFromItem(widget, 0, widget.height);
83 var bottomLeft = preview.mapFromItem(widget, 0, widget.height);
@@ -94,7 +93,6 @@ Rectangle {
94
93
95 function test_containOnGrow() {
94 function test_containOnGrow() {
96 waitForRendering(preview);
95 waitForRendering(preview);
97− tryCompareFunction(function () { return findChild(preview, "widget-13") != null }, true);
98 var widget = findChild(preview, "widget-13");
96 var widget = findChild(preview, "widget-13");
99
97
100 var bottomLeft = preview.mapFromItem(widget, 0, widget.height);
98 var bottomLeft = preview.mapFromItem(widget, 0, widget.height);
tests/qmltests/Dash/Previews/tst_PreviewActions.qml+2−1
@@ -151,15 +151,16 @@ Rectangle {
151 {
151 {
152 var button = findChild(buttonGroup, "moreLessButton");
152 var button = findChild(buttonGroup, "moreLessButton");
153 var buttonColumn = findChild(buttonGroup, "buttonColumn");
153 var buttonColumn = findChild(buttonGroup, "buttonColumn");
154+ var buttonGroupInitialHeight = buttonGroup.height;
154 verify(button != null);
155 verify(button != null);
155 mouseClick(button);
156 mouseClick(button);
156 tryCompare(buttonColumn, "height", buttonColumn.implicitHeight);
157 tryCompare(buttonColumn, "height", buttonColumn.implicitHeight);
158+ tryCompare(buttonGroup, "height", buttonGroupInitialHeight + buttonColumn.height + buttonColumn.anchors.topMargin);
157 }
159 }
158
160
159 function test_comboButton(data) {
161 function test_comboButton(data) {
160 var twoActionsY = twoActions.y
162 var twoActionsY = twoActions.y
161 pressMoreButton(buttonAndCombo);
163 pressMoreButton(buttonAndCombo);
162− tryCompareFunction(function () { return twoActions.y <= twoActionsY; }, false);
163 checkButtonPressSignal(buttonAndCombo, data.id);
164 checkButtonPressSignal(buttonAndCombo, data.id);
164 mouseClick(findChild(buttonAndCombo, "moreLessButton"));
165 mouseClick(findChild(buttonAndCombo, "moreLessButton"));
165 tryCompare(twoActions, "y", twoActionsY);
166 tryCompare(twoActions, "y", twoActionsY);
tests/qmltests/Dash/Previews/tst_PreviewCommentInput.qml+28
@@ -39,6 +39,21 @@ Rectangle {
39 widgetId: "previewCommentInput"
39 widgetId: "previewCommentInput"
40 }
40 }
41
41
42+ Loader {
43+ id: previewCommentInputLoader
44+ anchors {
45+ left: parent.left
46+ right: parent.right
47+ top: previewCommentInput.bottom
48+ topMargin: units.gu(2)
49+ }
50+
51+ sourceComponent: PreviewCommentInput {
52+ widgetData: widgetDataNewLabels
53+ widgetId: "previewCommentInputLoader"
54+ }
55+ }
56+
42 SignalSpy {
57 SignalSpy {
43 id: spy
58 id: spy
44 signalName: "triggered"
59 signalName: "triggered"
@@ -81,5 +96,18 @@ Rectangle {
81 compare(args[2]["comment"], data.inputText);
96 compare(args[2]["comment"], data.inputText);
82 }
97 }
83 }
98 }
99+
100+ function test_singleton() {
101+ var commentTextAreaLoader = findChild(previewCommentInputLoader, "commentTextArea")
102+
103+ commentTextAreaLoader.text = "singleton test text";
104+
105+ // Simulate widget being destroyed and recreated
106+ previewCommentInputLoader.active = false;
107+ previewCommentInputLoader.active = true;
108+
109+ commentTextAreaLoader = findChild(previewCommentInputLoader, "commentTextArea")
110+ tryCompare(commentTextAreaLoader, "text", "singleton test text");
111+ }
84 }
112 }
85}
113}
tests/qmltests/Dash/Previews/tst_PreviewExpandable.qml+3−3
@@ -54,8 +54,8 @@ Rectangle {
54
54
55 property var galleryData: {
55 property var galleryData: {
56 "sources": [
56 "sources": [
57− "../../graphics/phone_background.jpg",
57+ "../../../tests/graphics/avatars/amanda@12.png",
58− "../../graphics/tablet_background.jpg",
58+ "../../../tests/graphics/avatars/funky@12.png",
59 "../../../tests/graphics/clock@18.png",
59 "../../../tests/graphics/clock@18.png",
60 "../../graphics/borked"
60 "../../graphics/borked"
61 ]
61 ]
@@ -71,7 +71,7 @@ Rectangle {
71 }
71 }
72
72
73 property var imageData: {
73 property var imageData: {
74− "source": "../../graphics/phone_background.jpg",
74+ "source": "../../../tests/graphics/avatars/amanda@12.png",
75 "zoomable": false
75 "zoomable": false
76 }
76 }
77
77
tests/qmltests/Dash/Previews/tst_PreviewHeader.qml−4
@@ -116,7 +116,6 @@ Rectangle {
116
116
117 tryCompare(testCase.mascotShapeLoader, "visible", data.loaderVisible);
117 tryCompare(testCase.mascotShapeLoader, "visible", data.loaderVisible);
118 if (data.loaderVisible) {
118 if (data.loaderVisible) {
119− tryCompareFunction(function() { return findChild(previewHeader, "mascotShape") != null }, true);
120 var mascot = findChild(previewHeader, "mascotShape")
119 var mascot = findChild(previewHeader, "mascotShape")
121 tryCompare(mascot, "visible", data.visible);
120 tryCompare(mascot, "visible", data.visible);
122 }
121 }
@@ -165,20 +164,17 @@ Rectangle {
165
164
166 function test_fallback() {
165 function test_fallback() {
167 previewHeader.widgetData = brokenheaderjson;
166 previewHeader.widgetData = brokenheaderjson;
168− tryCompareFunction(function() { return findChild(previewHeader, "mascotShape") != null }, true);
169 var mascot = findChild(previewHeader, "mascotShape");
167 var mascot = findChild(previewHeader, "mascotShape");
170 compare(mascot.visible, false);
168 compare(mascot.visible, false);
171
169
172 previewHeader.widgetData = {};
170 previewHeader.widgetData = {};
173 previewHeader.widgetData = fallbackheaderjson;
171 previewHeader.widgetData = fallbackheaderjson;
174− tryCompareFunction(function() { return findChild(previewHeader, "mascotShape") != null }, true);
175 var mascot = findChild(previewHeader, "mascotShape");
172 var mascot = findChild(previewHeader, "mascotShape");
176 tryCompare(mascot, "visible", true);
173 tryCompare(mascot, "visible", true);
177 tryCompare(mascot.source, "status", Image.Ready);
174 tryCompare(mascot.source, "status", Image.Ready);
178
175
179 previewHeader.widgetData = {};
176 previewHeader.widgetData = {};
180 previewHeader.widgetData = emptyfallbackheaderjson;
177 previewHeader.widgetData = emptyfallbackheaderjson;
181− tryCompareFunction(function() { return findChild(previewHeader, "mascotShape") != null }, true);
182 var mascot = findChild(previewHeader, "mascotShape");
178 var mascot = findChild(previewHeader, "mascotShape");
183 tryCompare(mascot, "visible", true);
179 tryCompare(mascot, "visible", true);
184 tryCompare(mascot.source, "status", Image.Ready);
180 tryCompare(mascot.source, "status", Image.Ready);
tests/qmltests/Dash/Previews/tst_PreviewImageGallery.qml+6−6
@@ -31,8 +31,8 @@ Rectangle {
31
31
32 property var sourcesModel1: {
32 property var sourcesModel1: {
33 "sources": [
33 "sources": [
34− "../../graphics/phone_background.jpg",
34+ "../../../tests/graphics/avatars/amanda@12.png",
35− "../../graphics/tablet_background.jpg",
35+ "../../../tests/graphics/avatars/funky@12.png",
36 "../../../tests/graphics/clock@18.png",
36 "../../../tests/graphics/clock@18.png",
37 "../../graphics/borked"
37 "../../graphics/borked"
38 ]
38 ]
@@ -40,8 +40,8 @@ Rectangle {
40
40
41 property var sourcesModel1WithFallback: {
41 property var sourcesModel1WithFallback: {
42 "sources": [
42 "sources": [
43− "../../graphics/phone_background.jpg",
43+ "../../../tests/graphics/avatars/amanda@12.png",
44− "../../graphics/tablet_background.jpg",
44+ "../../../tests/graphics/avatars/funky@12.png",
45 "../../../tests/graphics/clock@18.png",
45 "../../../tests/graphics/clock@18.png",
46 "../../graphics/borked"
46 "../../graphics/borked"
47 ]
47 ]
@@ -50,8 +50,8 @@ Rectangle {
50
50
51 property var sourcesModelEmptyWithFallback: {
51 property var sourcesModelEmptyWithFallback: {
52 "sources": [
52 "sources": [
53− "../../graphics/phone_background.jpg",
53+ "../../../tests/graphics/avatars/amanda@12.png",
54− "../../graphics/tablet_background.jpg",
54+ "../../../tests/graphics/avatars/funky@12.png",
55 "../../../tests/graphics/clock@18.png",
55 "../../../tests/graphics/clock@18.png",
56 ""
56 ""
57 ]
57 ]
tests/qmltests/Dash/Previews/tst_PreviewInlineVideo.qml+42−1
@@ -76,8 +76,23 @@ Rectangle {
76
76
77 rootItem: inner
77 rootItem: inner
78 }
78 }
79− }
80
79
80+ Loader {
81+ id: videoPlaybackLoader
82+ anchors {
83+ left: parent.left
84+ right: parent.right
85+ top: videoPlayback.bottom
86+ topMargin: units.gu(2)
87+ }
88+
89+ sourceComponent: PreviewInlineVideo {
90+ widgetData: widgetData0
91+
92+ rootItem: inner
93+ }
94+ }
95+ }
81 }
96 }
82
97
83 Rectangle {
98 Rectangle {
@@ -98,6 +113,11 @@ Rectangle {
98 }
113 }
99 }
114 }
100
115
116+ SignalSpy {
117+ id: spy
118+ signalName: "playing"
119+ }
120+
101 UT.UnityTestCase {
121 UT.UnityTestCase {
102 name: "PreviewInlineVideoTest"
122 name: "PreviewInlineVideoTest"
103 when: windowShown
123 when: windowShown
@@ -110,5 +130,26 @@ Rectangle {
110 var screenshotSource = screenshot.source
130 var screenshotSource = screenshot.source
111 compare(screenshotSource.toString(), "file:///test-video2-screenshot");
131 compare(screenshotSource.toString(), "file:///test-video2-screenshot");
112 }
132 }
133+
134+ function test_singleton() {
135+ var services = findChild(videoPlaybackLoader, "services");
136+ spy.clear();
137+ spy.target = services.mediaPlayer;
138+ mouseClick(videoPlaybackLoader, videoPlaybackLoader.width / 2, videoPlaybackLoader.height / 2);
139+ tryCompare(spy, "count", 1);
140+ tryCompareFunction(function() { return services.position > 3000; }, true);
141+
142+ // Simulate widget being destroyed and recreated
143+ videoPlaybackLoader.active = false;
144+ videoPlaybackLoader.active = true;
145+
146+ services = findChild(videoPlaybackLoader, "services");
147+ spy.clear();
148+ spy.target = services.mediaPlayer;
149+ compare(services.position, 0);
150+ mouseClick(videoPlaybackLoader, videoPlaybackLoader.width / 2, videoPlaybackLoader.height / 2);
151+ tryCompare(spy, "count", 1);
152+ tryCompareFunction(function() { return services.position > 3000; }, true, 1000);
153+ }
113 }
154 }
114}
155}
tests/qmltests/Dash/Previews/tst_PreviewProgress.qml+14−1
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2014 Canonical Ltd.
2+ * Copyright 2014-2016 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
@@ -40,6 +40,11 @@ Rectangle {
40 "source": { "dbus-name" : "somename", "dbus-object": "error" }
40 "source": { "dbus-name" : "somename", "dbus-object": "error" }
41 }
41 }
42
42
43+ property var progressjsonProcessing: {
44+ "type": "progress",
45+ "source": { "dbus-name" : "somename", "dbus-object": "processing" }
46+ }
47+
43 SignalSpy {
48 SignalSpy {
44 id: spy
49 id: spy
45 signalName: "triggered"
50 signalName: "triggered"
@@ -82,6 +87,14 @@ Rectangle {
82 compare(args[0], "previewProgress");
87 compare(args[0], "previewProgress");
83 compare(args[1], "failed");
88 compare(args[1], "failed");
84 compare(args[2], progressjsonError);
89 compare(args[2], progressjsonError);
90+
91+ spy.clear();
92+
93+ var progressBar = findChild(previewProgress, "progressBar");
94+ compare(progressBar.indeterminate, false);
95+ previewProgress.widgetData = progressjsonProcessing;
96+ progressBar = findChild(previewProgress, "progressBar");
97+ tryCompare(progressBar, "indeterminate", true);
85 }
98 }
86 }
99 }
87}
100}
tests/qmltests/Dash/Previews/tst_PreviewRatingDisplayCreationRanges.qml+1
@@ -18,6 +18,7 @@ import QtQuick 2.4
18import QtTest 1.0
18import QtTest 1.0
19import Ubuntu.Components 1.3
19import Ubuntu.Components 1.3
20import "../../../../qml/Dash/Previews"
20import "../../../../qml/Dash/Previews"
21+import "../../../../qml/Components"
21import Unity.Test 0.1 as UT
22import Unity.Test 0.1 as UT
22
23
23Flickable {
24Flickable {
tests/qmltests/Dash/Previews/tst_PreviewRatingInput.qml+32
@@ -53,6 +53,21 @@ Rectangle {
53 widgetId: "previewRatingInput"
53 widgetId: "previewRatingInput"
54 }
54 }
55
55
56+ Loader {
57+ id: previewRatingInputLoader
58+ anchors {
59+ left: parent.left
60+ right: parent.right
61+ top: previewRatingInput.bottom
62+ topMargin: units.gu(2)
63+ }
64+
65+ sourceComponent: PreviewRatingInput {
66+ widgetData: widgetDataBoth
67+ widgetId: "previewRatingInputLoader"
68+ }
69+ }
70+
56 SignalSpy {
71 SignalSpy {
57 id: spy
72 id: spy
58 signalName: "triggered"
73 signalName: "triggered"
@@ -210,5 +225,22 @@ Rectangle {
210 compare(args[2]["review"], data.triggeredData["review"]);
225 compare(args[2]["review"], data.triggeredData["review"]);
211 verify(args[2]["author"] !== undefined); // Just verifying it exists now
226 verify(args[2]["author"] !== undefined); // Just verifying it exists now
212 }
227 }
228+
229+ function test_singleton() {
230+ var ratingLoader = findChild(previewRatingInputLoader, "rating");
231+ var reviewTextAreaLoader = findChild(previewRatingInputLoader, "reviewTextArea");
232+
233+ ratingLoader.value = 3;
234+ reviewTextAreaLoader.text = "singleton test text";
235+
236+ // Simulate widget being destroyed and recreated
237+ previewRatingInputLoader.active = false;
238+ previewRatingInputLoader.active = true;
239+
240+ ratingLoader = findChild(previewRatingInputLoader, "rating");
241+ reviewTextAreaLoader = findChild(previewRatingInputLoader, "reviewTextArea");
242+ tryCompare(ratingLoader, "value", 3);
243+ tryCompare(reviewTextAreaLoader, "text", "singleton test text");
244+ }
213 }
245 }
214}
246}
tests/qmltests/Dash/Previews/tst_PreviewZoomableImage.qml+7−7
@@ -26,7 +26,7 @@ Rectangle {
26 color: "lightgrey"
26 color: "lightgrey"
27
27
28 property var widgetData0: {
28 property var widgetData0: {
29− "source": "../../graphics/phone_background.jpg",
29+ "source": "../../../tests/graphics/avatars/amanda@12.png",
30 "zoomable": false
30 "zoomable": false
31 }
31 }
32
32
@@ -36,17 +36,17 @@ Rectangle {
36
36
37 property var widgetData2: {
37 property var widgetData2: {
38 "source": "fadsasf",
38 "source": "fadsasf",
39− "fallback": "../../graphics/phone_background.jpg"
39+ "fallback": "../../../tests/graphics/avatars/amanda@12.png"
40 }
40 }
41
41
42 property var widgetData3: {
42 property var widgetData3: {
43 "source": "",
43 "source": "",
44− "fallback": "../../graphics/phone_background.jpg"
44+ "fallback": "../../../tests/graphics/avatars/amanda@12.png"
45 }
45 }
46
46
47 property var widgetData4: {
47 property var widgetData4: {
48 "source": "fadsasf",
48 "source": "fadsasf",
49− "fallback": "../../graphics/phone_background.jpg"
49+ "fallback": "../../../tests/graphics/avatars/amanda@12.png"
50 }
50 }
51
51
52 Loader {
52 Loader {
@@ -123,11 +123,11 @@ Rectangle {
123 waitForRendering(zoomableImage);
123 waitForRendering(zoomableImage);
124 waitForRendering(lazyImage);
124 waitForRendering(lazyImage);
125 tryCompare(lazyImage, "state", "ready");
125 tryCompare(lazyImage, "state", "ready");
126− verify(lazyImage.sourceImage.source.toString().indexOf("phone_background") > -1);
126+ verify(lazyImage.sourceImage.source.toString().indexOf("amanda") > -1);
127− zoomableImage.widgetData["source"] = "../../graphics/tablet_background.jpg"
127+ zoomableImage.widgetData["source"] = "../../../tests/graphics/avatars/funky@12.png"
128 zoomableImage.widgetDataChanged();
128 zoomableImage.widgetDataChanged();
129 tryCompare(lazyImage, "state", "ready");
129 tryCompare(lazyImage, "state", "ready");
130− verify(lazyImage.sourceImage.source.toString().indexOf("tablet_background") > -1);
130+ verify(lazyImage.sourceImage.source.toString().indexOf("funky") > -1);
131 }
131 }
132 }
132 }
133}
133}
tests/qmltests/Dash/tst_Card.qml+7−2
@@ -618,12 +618,17 @@ Rectangle {
618 waitForRendering(card);
618 waitForRendering(card);
619
619
620 var touchdown = findChild(card, "touchdown");
620 var touchdown = findChild(card, "touchdown");
621+ compare(touchdown, null);
621
622
622− compare(touchdown.visible, false);
623 mousePress(card);
623 mousePress(card);
624+
625+ touchdown = findChild(card, "touchdown");
624 compare(touchdown.visible, true);
626 compare(touchdown.visible, true);
627+
625 mouseRelease(card);
628 mouseRelease(card);
629+
630+ touchdown = findChild(card, "touchdown");
626− compare(touchdown.visible, false);
631+ compare(touchdown, null);
627 }
632 }
628
633
629 function test_paddings_data() {
634 function test_paddings_data() {
tests/qmltests/Dash/tst_CardTool.qml+4−4
@@ -339,22 +339,22 @@ Rectangle {
339 function test_card_title_alignment(data) {
339 function test_card_title_alignment(data) {
340 selector.selectedIndex = data.index;
340 selector.selectedIndex = data.index;
341
341
342− tryCompareFunction(function() { return findChild(internalCard, "titleLabel").horizontalAlignment == Text.AlignLeft; }, true);
342+ tryCompareFunction(function() { return findChild(internalCard, "titleLabel", 0 /* timeout */).horizontalAlignment == Text.AlignLeft; }, true);
343
343
344 cardTool.components['title'] = { "field": "title", "align": "center" };
344 cardTool.components['title'] = { "field": "title", "align": "center" };
345 cardTool.componentsChanged();
345 cardTool.componentsChanged();
346
346
347− tryCompareFunction(function() { return findChild(internalCard, "titleLabel").horizontalAlignment == data.value; }, true);
347+ tryCompareFunction(function() { return findChild(internalCard, "titleLabel", 0 /* timeout */).horizontalAlignment == data.value; }, true);
348
348
349 cardTool.components['title'] = { "field": "title" };
349 cardTool.components['title'] = { "field": "title" };
350 cardTool.componentsChanged();
350 cardTool.componentsChanged();
351
351
352− tryCompareFunction(function() { return findChild(internalCard, "titleLabel").horizontalAlignment == data.value; }, true);
352+ tryCompareFunction(function() { return findChild(internalCard, "titleLabel", 0 /* timeout */).horizontalAlignment == data.value; }, true);
353
353
354 cardTool.components['title'] = { "field": "title", "align": "left" };
354 cardTool.components['title'] = { "field": "title", "align": "left" };
355 cardTool.componentsChanged();
355 cardTool.componentsChanged();
356
356
357− tryCompareFunction(function() { return findChild(internalCard, "titleLabel").horizontalAlignment == Text.AlignLeft; }, true);
357+ tryCompareFunction(function() { return findChild(internalCard, "titleLabel", 0 /* timeout */).horizontalAlignment == Text.AlignLeft; }, true);
358 }
358 }
359
359
360 function test_categoryLayout_data() {
360 function test_categoryLayout_data() {
tests/qmltests/Dash/tst_Dash.qml+25−15
@@ -76,7 +76,7 @@ Item {
76 var genericScopeView = dashContentList.currentItem;
76 var genericScopeView = dashContentList.currentItem;
77 var categoryListView = findChild(genericScopeView, "categoryListView");
77 var categoryListView = findChild(genericScopeView, "categoryListView");
78 tryCompareFunction(function() {
78 tryCompareFunction(function() {
79− var category = findChild(genericScopeView, categoryName);
79+ var category = findChild(genericScopeView, categoryName, 0 /* timeout */);
80 if (category && category.y > 0 && category.y < genericScopeView.height) return true;
80 if (category && category.y > 0 && category.y < genericScopeView.height) return true;
81 touchFlick(genericScopeView, genericScopeView.width/2, units.gu(20),
81 touchFlick(genericScopeView, genericScopeView.width/2, units.gu(20),
82 genericScopeView.width/2, genericScopeView.y)
82 genericScopeView.width/2, genericScopeView.y)
@@ -84,7 +84,7 @@ Item {
84 return false;
84 return false;
85 }, true);
85 }, true);
86
86
87− tryCompareFunction(function() { return findChild(genericScopeView, "delegate0") !== null; }, true);
87+ tryCompareFunction(function() { return findChild(genericScopeView, "delegate0", 0 /* timeout */) !== null; }, true);
88 return findChild(genericScopeView, categoryName);
88 return findChild(genericScopeView, categoryName);
89 }
89 }
90
90
@@ -94,9 +94,9 @@ Item {
94 if (category === undefined) category = 0;
94 if (category === undefined) category = 0;
95 if (delegate === undefined) delegate = 0;
95 if (delegate === undefined) delegate = 0;
96 tryCompareFunction(function() {
96 tryCompareFunction(function() {
97− var cardGrid = findChild(genericScopeView, "dashCategory"+category);
97+ var cardGrid = findChild(genericScopeView, "dashCategory"+category, 0 /* timeout */);
98 if (cardGrid != null) {
98 if (cardGrid != null) {
99− var tile = findChild(cardGrid, "delegate"+delegate);
99+ var tile = findChild(cardGrid, "delegate"+delegate, 0 /* timeout */);
100 return tile != null;
100 return tile != null;
101 }
101 }
102 return false;
102 return false;
@@ -215,6 +215,8 @@ Item {
215
215
216 // Click in first item
216 // Click in first item
217 var favScopesListCategoryList = findChild(findChild(dash, "scopesListCategoryfavorites"), "scopesListCategoryInnerList");
217 var favScopesListCategoryList = findChild(findChild(dash, "scopesListCategoryfavorites"), "scopesListCategoryInnerList");
218+ tryCompareFunction(function() { return favScopesListCategoryList.currentItem !== null }, true);
219+ waitForRendering(favScopesListCategoryList.currentItem);
218 mouseClick(favScopesListCategoryList.currentItem);
220 mouseClick(favScopesListCategoryList.currentItem);
219
221
220 // Make sure animation went back
222 // Make sure animation went back
@@ -409,7 +411,7 @@ Item {
409 compare(dashContentList.currentItem.scopeId, "clickscope");
411 compare(dashContentList.currentItem.scopeId, "clickscope");
410
412
411 // Move to second scope
413 // Move to second scope
412− touchFlick(dash, dash.width / 2, units.gu(2), dash.width / 5, units.gu(2));
414+ mouseFlick(dash, dash.width / 2, units.gu(2), dash.width / 5, units.gu(2));
413 tryCompare(dashContentList, "currentIndex", 1);
415 tryCompare(dashContentList, "currentIndex", 1);
414 compare(dashContentList.currentItem.scopeId, "MockScope1");
416 compare(dashContentList.currentItem.scopeId, "MockScope1");
415 }
417 }
@@ -473,9 +475,9 @@ Item {
473 compare(dashContentList.currentItem.scopeId, "MockScope1");
475 compare(dashContentList.currentItem.scopeId, "MockScope1");
474
476
475 tryCompareFunction(function() {
477 tryCompareFunction(function() {
476− var cardGrid = findChild(dashContentList, "dashCategory0");
478+ var cardGrid = findChild(dashContentList, "dashCategory0", 0 /* timeout */);
477 if (cardGrid != null) {
479 if (cardGrid != null) {
478− var tile = findChild(cardGrid, "delegate0");
480+ var tile = findChild(cardGrid, "delegate0", 0 /* timeout */);
479 return tile != null;
481 return tile != null;
480 }
482 }
481 return false;
483 return false;
@@ -520,9 +522,9 @@ Item {
520 var categoryListView = findChild(dashTempScopeItem, "categoryListView");
522 var categoryListView = findChild(dashTempScopeItem, "categoryListView");
521 categoryListView.positionAtBeginning();
523 categoryListView.positionAtBeginning();
522 tryCompareFunction(function() {
524 tryCompareFunction(function() {
523− var cardGrid = findChild(dashTempScopeItem, "dashCategory0");
525+ var cardGrid = findChild(dashTempScopeItem, "dashCategory0", 0 /* timeout */);
524 if (cardGrid != null) {
526 if (cardGrid != null) {
525− var tile = findChild(cardGrid, "delegate0");
527+ var tile = findChild(cardGrid, "delegate0", 0 /* timeout */);
526 return tile != null;
528 return tile != null;
527 }
529 }
528 return false;
530 return false;
@@ -631,18 +633,26 @@ Item {
631 tryCompare(dashContent.currentScope, "id", "clickscope");
633 tryCompare(dashContent.currentScope, "id", "clickscope");
632
634
633 scrollToCategory("dashCategorypredefined");
635 scrollToCategory("dashCategorypredefined");
636+ tryCompareFunction(function() {
634− var tile = getCategoryDelegate("predefined", 2);
637+ var tile = getCategoryDelegate("predefined", 2);
635− var proportionalShape = findChildsByType(tile, "UCProportionalShape");
638+ var proportionalShape = findChildsByType(tile, "UCProportionalShape");
636− compare(proportionalShape.length, 1);
639+ return proportionalShape.length === 1;
640+ },
641+ true
642+ );
637
643
638 dash.setCurrentScope("libertine-scope.ubuntu_libertine-scope");
644 dash.setCurrentScope("libertine-scope.ubuntu_libertine-scope");
639 var dashContent = findChild(dash, "dashContent");
645 var dashContent = findChild(dash, "dashContent");
640 tryCompare(dashContent.currentScope, "id", "libertine-scope.ubuntu_libertine-scope");
646 tryCompare(dashContent.currentScope, "id", "libertine-scope.ubuntu_libertine-scope");
641
647
642 scrollToCategory("dashCategory2");
648 scrollToCategory("dashCategory2");
649+ tryCompareFunction(function() {
643− tile = getCategoryDelegate("2", 2);
650+ var tile = getCategoryDelegate("2", 2);
644− proportionalShape = findChildsByType(tile, "UCProportionalShape");
651+ var proportionalShape = findChildsByType(tile, "UCProportionalShape");
645− compare(proportionalShape.length, 1);
652+ return proportionalShape.length === 1;
653+ },
654+ true
655+ );
646 }
656 }
647
657
648 function test_tempScopeItemXOnResize()
658 function test_tempScopeItemXOnResize()
tests/qmltests/Dash/tst_DashContent.qml+5−7
@@ -261,11 +261,11 @@ Item {
261
261
262 function test_carouselAspectRatio() {
262 function test_carouselAspectRatio() {
263 tryCompareFunction(function() {
263 tryCompareFunction(function() {
264− var scope = findChild(dashContent, "scopeLoader0");
264+ var scope = findChild(dashContent, "scopeLoader0", 0 /* timeout */);
265 if (scope != null) {
265 if (scope != null) {
266− var dashCategory1 = findChild(scope, "dashCategory1");
266+ var dashCategory1 = findChild(scope, "dashCategory1", 0 /* timeout */);
267 if (dashCategory1 != null) {
267 if (dashCategory1 != null) {
268− var tile = findChild(dashCategory1, "carouselDelegate1");
268+ var tile = findChild(dashCategory1, "carouselDelegate1", 0 /* timeout */);
269 return tile != null;
269 return tile != null;
270 }
270 }
271 }
271 }
@@ -282,7 +282,6 @@ Item {
282
282
283 function test_mainNavigation() {
283 function test_mainNavigation() {
284 var dashContentList = findChild(dashContent, "dashContentList");
284 var dashContentList = findChild(dashContent, "dashContentList");
285− tryCompareFunction(function() { return findChild(dashContentList.currentItem, "dashNavigation") != null; }, true);
286 var dashNavigation = findChild(dashContentList.currentItem, "dashNavigation");
285 var dashNavigation = findChild(dashContentList.currentItem, "dashNavigation");
287 var peExtraPanel = findChild(dashContentList.currentItem, "peExtraPanel");
286 var peExtraPanel = findChild(dashContentList.currentItem, "peExtraPanel");
288 var searchButton = findChild(dashContentList.currentItem, "search_button");
287 var searchButton = findChild(dashContentList.currentItem, "search_button");
@@ -405,7 +404,6 @@ Item {
405
404
406 function goToSecondLevel() {
405 function goToSecondLevel() {
407 var dashContentList = findChild(dashContent, "dashContentList");
406 var dashContentList = findChild(dashContent, "dashContentList");
408− tryCompareFunction(function() { return findChild(dashContentList.currentItem, "dashNavigation") != null; }, true);
409 var dashNavigation = findChild(dashContentList.currentItem, "dashNavigation");
407 var dashNavigation = findChild(dashContentList.currentItem, "dashNavigation");
410 var peExtraPanel = findChild(dashContentList.currentItem, "peExtraPanel");
408 var peExtraPanel = findChild(dashContentList.currentItem, "peExtraPanel");
411 var searchButton = findChild(dashContentList.currentItem, "search_button");
409 var searchButton = findChild(dashContentList.currentItem, "search_button");
@@ -580,7 +578,7 @@ Item {
580
578
581 function test_primaryFilter() {
579 function test_primaryFilter() {
582 var dashContentList = findChild(dashContent, "dashContentList");
580 var dashContentList = findChild(dashContent, "dashContentList");
583− tryCompareFunction(function() { return findChild(dashContentList.currentItem, "dashNavigation") != null; }, true);
581+ tryCompareFunction(function() { return findChild(dashContentList.currentItem, "dashNavigation", 0 /* timeout */) != null; }, true);
584 dashContentList.currentItem.item.scope.setHasNavigation(false);
582 dashContentList.currentItem.item.scope.setHasNavigation(false);
585 var peExtraPanel = findChild(dashContentList.currentItem, "peExtraPanel");
583 var peExtraPanel = findChild(dashContentList.currentItem, "peExtraPanel");
586 var searchButton = findChild(dashContentList.currentItem, "search_button");
584 var searchButton = findChild(dashContentList.currentItem, "search_button");
@@ -589,7 +587,7 @@ Item {
589 mouseClick(searchButton);
587 mouseClick(searchButton);
590 tryCompare(peExtraPanel, "visible", true);
588 tryCompare(peExtraPanel, "visible", true);
591
589
592− tryCompareFunction(function() { return findChild(peExtraPanel, "OSF3") != null; }, true);
590+ tryCompareFunction(function() { return findChild(peExtraPanel, "OSF3", 0 /* timeout */) != null; }, true);
593 }
591 }
594
592
595 function test_searchHint() {
593 function test_searchHint() {
tests/qmltests/Dash/tst_GenericScopeView.qml+58−61
@@ -22,6 +22,7 @@ import "../../../qml/Dash"
22import "../../../qml/Components"
22import "../../../qml/Components"
23import Ubuntu.Components 1.3
23import Ubuntu.Components 1.3
24import Unity.Test 0.1 as UT
24import Unity.Test 0.1 as UT
25+import Qt.test.qtestroot 1.0
25
26
26Item {
27Item {
27 id: shell
28 id: shell
@@ -40,7 +41,12 @@ Item {
40 Scopes {
41 Scopes {
41 id: scopes
42 id: scopes
42 // for tryGenericScopeView
43 // for tryGenericScopeView
44+ onLoadedChanged: {
45+ if (loaded && UT.Util.isInstanceOf(QTestRootObject, "DummyTestRootObject")) {
43− onLoadedChanged: if (loaded) genericScopeView.scope = scopes.getScope(2);
46+ genericScopeView.scope = scopes.getScope(2);
47+ genericScopeView.isCurrent = true;
48+ }
49+ }
44 }
50 }
45
51
46 MockScope {
52 MockScope {
@@ -92,7 +98,7 @@ Item {
92 function scrollToCategory(categoryName) {
98 function scrollToCategory(categoryName) {
93 var categoryListView = findChild(genericScopeView, "categoryListView");
99 var categoryListView = findChild(genericScopeView, "categoryListView");
94 tryCompareFunction(function() {
100 tryCompareFunction(function() {
95− var category = findChild(genericScopeView, categoryName);
101+ var category = findChild(genericScopeView, categoryName, 0 /* timeout */);
96 if (category && category.y > 0 && category.y < genericScopeView.height) return true;
102 if (category && category.y > 0 && category.y < genericScopeView.height) return true;
97 touchFlick(genericScopeView, genericScopeView.width/2, units.gu(20),
103 touchFlick(genericScopeView, genericScopeView.width/2, units.gu(20),
98 genericScopeView.width/2, genericScopeView.y)
104 genericScopeView.width/2, genericScopeView.y)
@@ -100,7 +106,7 @@ Item {
100 return false;
106 return false;
101 }, true);
107 }, true);
102
108
103− tryCompareFunction(function() { return findChild(genericScopeView, "delegate0") !== null; }, true);
109+ tryCompareFunction(function() { return findChild(genericScopeView, "delegate0", 0 /* timeout */) !== null; }, true);
104 return findChild(genericScopeView, categoryName);
110 return findChild(genericScopeView, categoryName);
105 }
111 }
106
112
@@ -163,7 +169,7 @@ Item {
163 }
169 }
164
170
165 function test_expand_collapse() {
171 function test_expand_collapse() {
166− tryCompareFunction(function() { return findChild(genericScopeView, "dashSectionHeader0") != null; }, true);
172+ tryCompareFunction(function() { return findChild(genericScopeView, "dashSectionHeader0", 0 /* timeout */) != null; }, true);
167
173
168 var category = findChild(genericScopeView, "dashCategory0")
174 var category = findChild(genericScopeView, "dashCategory0")
169 var seeAll = findChild(category, "seeAll")
175 var seeAll = findChild(category, "seeAll")
@@ -184,7 +190,7 @@ Item {
184
190
185 function test_expand_expand_collapse() {
191 function test_expand_expand_collapse() {
186 // wait for the item to be there
192 // wait for the item to be there
187− tryCompareFunction(function() { return findChild(genericScopeView, "dashSectionHeader2") != null; }, true);
193+ tryCompareFunction(function() { return findChild(genericScopeView, "dashSectionHeader2", 0 /* timeout */) != null; }, true);
188
194
189 var categoryListView = findChild(genericScopeView, "categoryListView");
195 var categoryListView = findChild(genericScopeView, "categoryListView");
190 categoryListView.contentY = categoryListView.height;
196 categoryListView.contentY = categoryListView.height;
@@ -212,7 +218,6 @@ Item {
212 }
218 }
213
219
214 function test_headerLink() {
220 function test_headerLink() {
215− tryCompareFunction(function() { return findChild(genericScopeView, "dashSectionHeader1") != null; }, true);
216 var header = findChild(genericScopeView, "dashSectionHeader1");
221 var header = findChild(genericScopeView, "dashSectionHeader1");
217
222
218 spy.target = genericScopeView.scope;
223 spy.target = genericScopeView.scope;
@@ -231,7 +236,7 @@ Item {
231 categoryListView.contentY = categoryListView.height * 2;
236 categoryListView.contentY = categoryListView.height * 2;
232
237
233 // wait for the item to be there
238 // wait for the item to be there
234− tryCompareFunction(function() { return findChild(genericScopeView, "dashSectionHeader4") != null; }, true);
239+ tryCompareFunction(function() { return findChild(genericScopeView, "dashSectionHeader4", 0 /* timeout */) != null; }, true);
235
240
236 var categoryView = findChild(genericScopeView, "dashCategory4");
241 var categoryView = findChild(genericScopeView, "dashCategory4");
237 verify(categoryView, "Can't find the category view.");
242 verify(categoryView, "Can't find the category view.");
@@ -243,7 +248,6 @@ Item {
243 }
248 }
244
249
245 function test_narrow_delegate_ranges_expand() {
250 function test_narrow_delegate_ranges_expand() {
246− tryCompareFunction(function() { return findChild(genericScopeView, "dashCategory0") !== null; }, true);
247 var category = findChild(genericScopeView, "dashCategory0")
251 var category = findChild(genericScopeView, "dashCategory0")
248 tryCompare(category, "expanded", false);
252 tryCompare(category, "expanded", false);
249
253
@@ -275,7 +279,6 @@ Item {
275 function test_single_category_expansion() {
279 function test_single_category_expansion() {
276 genericScopeView.scope = scopes.getScope(3);
280 genericScopeView.scope = scopes.getScope(3);
277
281
278− tryCompareFunction(function() { return findChild(genericScopeView, "dashCategory0") != undefined; }, true);
279 var category = findChild(genericScopeView, "dashCategory0")
282 var category = findChild(genericScopeView, "dashCategory0")
280 compare(category.expandable, false, "Only category should not be expandable.");
283 compare(category.expandable, false, "Only category should not be expandable.");
281
284
@@ -289,9 +292,9 @@ Item {
289 if (category === undefined) category = 0;
292 if (category === undefined) category = 0;
290 if (delegate === undefined) delegate = 0;
293 if (delegate === undefined) delegate = 0;
291 tryCompareFunction(function() {
294 tryCompareFunction(function() {
292− var cardGrid = findChild(genericScopeView, "dashCategory"+category);
295+ var cardGrid = findChild(genericScopeView, "dashCategory"+category, 0 /* timeout */);
293 if (cardGrid != null) {
296 if (cardGrid != null) {
294− var tile = findChild(cardGrid, "delegate"+delegate);
297+ var tile = findChild(cardGrid, "delegate"+delegate, 0 /* timeout */);
295 return tile != null;
298 return tile != null;
296 }
299 }
297 return false;
300 return false;
@@ -332,9 +335,9 @@ Item {
332 genericScopeView.scope = scopes.getScope("NullPreviewScope");
335 genericScopeView.scope = scopes.getScope("NullPreviewScope");
333
336
334 tryCompareFunction(function() {
337 tryCompareFunction(function() {
335− var cardGrid = findChild(genericScopeView, 0);
338+ var cardGrid = findChild(genericScopeView, 0, 0 /* timeout */);
336 if (cardGrid != null) {
339 if (cardGrid != null) {
337− var tile = findChild(cardGrid, 0);
340+ var tile = findChild(cardGrid, 0, 0 /* timeout */);
338 return tile != null;
341 return tile != null;
339 }
342 }
340 return false;
343 return false;
@@ -385,7 +388,6 @@ Item {
385
388
386 tryCompare(testCase.subPageLoader, "open", false);
389 tryCompare(testCase.subPageLoader, "open", false);
387
390
388− tryCompareFunction(function() { return findChild(category, "delegate1") != null; }, true);
389 var tile = findChild(category, "delegate1");
391 var tile = findChild(category, "delegate1");
390
392
391 mouseClick(tile);
393 mouseClick(tile);
@@ -487,7 +489,6 @@ Item {
487
489
488 scrollToEnd();
490 scrollToEnd();
489
491
490− tryCompareFunction(function() { return findChild(genericScopeView, "dashCategory1") !== null; }, true);
491 var category1 = findChild(genericScopeView, "dashCategory1")
492 var category1 = findChild(genericScopeView, "dashCategory1")
492 var seeAll1 = findChild(category1, "seeAll")
493 var seeAll1 = findChild(category1, "seeAll")
493 verify(category1.expandable);
494 verify(category1.expandable);
@@ -570,53 +571,40 @@ Item {
570 genericScopeView.scope = !genericScopeView.scope;
571 genericScopeView.scope = !genericScopeView.scope;
571 }
572 }
572
573
573− function test_pullToRefresh() {
574+ function test_pullToRefresh_data() {
574− waitForRendering(genericScopeView)
575+ return [
575−
576+ { tag: "Tall window long flick", shellHeight: units.gu(100), flickLength: units.gu(70), refresh: true },
576− mouseFlick(genericScopeView,
577+ { tag: "Tall window short flick", shellHeight: units.gu(100), flickLength: units.gu(25), refresh: false },
577− genericScopeView.width/2, units.gu(10),
578+ { tag: "Short window", shellHeight: units.gu(30), flickLength: units.gu(25), refresh: true }
578− genericScopeView.width/2, units.gu(80),
579+ ];
579− true, false)
580+ }
580−
581− var pullToRefresh = findChild(genericScopeView, "pullToRefresh")
582− tryCompare(pullToRefresh, "releaseToRefresh", true)
583−
584− spy.target = genericScopeView.scope
585− spy.signalName = "refreshed"
586−
587− mouseRelease(genericScopeView)
588− tryCompare(pullToRefresh, "releaseToRefresh", false)
589
581
590− spy.wait()
582+ function test_pullToRefresh(data) {
591− compare(spy.count, 1)
583+ var initialHeight = shell.height;
584+ shell.height = data.shellHeight;
585+ waitForRendering(shell);
592
586
593− // test short swipe doesn't refresh on tall window
594 mouseFlick(genericScopeView,
587 mouseFlick(genericScopeView,
595− genericScopeView.width/2, units.gu(10),
588+ genericScopeView.width/2, units.gu(6),
596− genericScopeView.width/2, units.gu(20),
589+ genericScopeView.width/2, units.gu(6) + data.flickLength,
597− true, false)
590+ true, false);
598− mouseRelease(genericScopeView)
591+
599− compare(spy.count, 1)
600−
601− // resize window, repeat the test
602− var initialHeight = shell.height
603− shell.height = units.gu(30)
604− waitForRendering(shell)
605− mouseFlick(genericScopeView,
592+ var pullToRefresh = findChild(genericScopeView, "pullToRefresh");
606− genericScopeView.width/2, units.gu(10),
593+ tryCompare(pullToRefresh, "releaseToRefresh", data.refresh);
607− genericScopeView.width/2, units.gu(20),
608− true, false)
609
594
610− tryCompare(pullToRefresh, "releaseToRefresh", true)
595+ spy.target = genericScopeView.scope;
596+ spy.signalName = "refreshed";
611
597
612− mouseRelease(genericScopeView)
598+ mouseRelease(genericScopeView);
613− tryCompare(pullToRefresh, "releaseToRefresh", false)
599+ tryCompare(pullToRefresh, "releaseToRefresh", false);
614
600
601+ if (data.refresh) {
615− spy.wait()
602+ spy.wait();
616− compare(spy.count, 2)
603+ compare(spy.count, 1);
604+ }
617
605
618− shell.height = initialHeight
606+ shell.height = initialHeight;
619− waitForRendering(shell)
607+ waitForRendering(shell);
620 }
608 }
621
609
622 function test_item_noninteractive() {
610 function test_item_noninteractive() {
@@ -642,11 +630,15 @@ Item {
642 var item0 = findChild(cardGrid, "delegate0");
630 var item0 = findChild(cardGrid, "delegate0");
643 waitForRendering(item0);
631 waitForRendering(item0);
644 compare(item0.enabled, false);
632 compare(item0.enabled, false);
633+
645 var touchdown = findChild(item0, "touchdown");
634 var touchdown = findChild(item0, "touchdown");
635+ compare(touchdown, null);
646
636
647− compare(touchdown.visible, false);
648 mousePress(item0);
637 mousePress(item0);
638+
639+ touchdown = findChild(item0, "touchdown");
649− compare(touchdown.visible, false);
640+ compare(touchdown, null);
641+
650 mouseRelease(item0);
642 mouseRelease(item0);
651
643
652 cardTool.template["non-interactive"] = false;
644 cardTool.template["non-interactive"] = false;
@@ -656,20 +648,25 @@ Item {
656 waitForRendering(category0);
648 waitForRendering(category0);
657 item0 = findChild(cardGrid, "delegate0");
649 item0 = findChild(cardGrid, "delegate0");
658 compare(item0.enabled, true);
650 compare(item0.enabled, true);
659− var touchdown = findChild(item0, "touchdown");
660
651
652+ touchdown = findChild(item0, "touchdown");
661− compare(touchdown.visible, false);
653+ compare(touchdown, null);
654+
662 mousePress(item0);
655 mousePress(item0);
656+
657+ touchdown = findChild(item0, "touchdown");
663 compare(touchdown.visible, true);
658 compare(touchdown.visible, true);
659+
664 mouseRelease(item0);
660 mouseRelease(item0);
661+
662+ touchdown = findChild(item0, "touchdown");
665− compare(touchdown.visible, false);
663+ compare(touchdown, null);
666 closePreview();
664 closePreview();
667 }
665 }
668
666
669 function test_carousel_borderSource() {
667 function test_carousel_borderSource() {
670 var category = scrollToCategory("dashCategory1");
668 var category = scrollToCategory("dashCategory1");
671 var tile = findChild(category, "carouselDelegate0");
669 var tile = findChild(category, "carouselDelegate0");
672− tryCompareFunction(function() { return findChild(tile, "artShapeLoader") !== null; }, true);
673 var artShapeLoader = findChild(tile, "artShapeLoader");
670 var artShapeLoader = findChild(tile, "artShapeLoader");
674 var shape = findChildsByType(artShapeLoader, "UCUbuntuShape");
671 var shape = findChildsByType(artShapeLoader, "UCUbuntuShape");
675 compare(shape.borderSource, undefined);
672 compare(shape.borderSource, undefined);
tests/qmltests/Greeter/TestView.qml+4−3
@@ -27,6 +27,7 @@ Item {
27 property int delayMinutes
27 property int delayMinutes
28 property real backgroundTopMargin
28 property real backgroundTopMargin
29 property url background
29 property url background
30+ property bool hasCustomBackground
30 property bool locked
31 property bool locked
31 property bool alphanumeric
32 property bool alphanumeric
32 property var userModel
33 property var userModel
@@ -51,7 +52,7 @@ Item {
51 signal _notifyAuthenticationSucceededCalled(bool showFakePassword)
52 signal _notifyAuthenticationSucceededCalled(bool showFakePassword)
52 signal _notifyAuthenticationFailedCalled()
53 signal _notifyAuthenticationFailedCalled()
53 signal _showErrorMessageCalled(string msg)
54 signal _showErrorMessageCalled(string msg)
54− signal _resetCalled()
55+ signal _resetCalled(bool forceShow)
55 signal _tryToUnlockCalled(bool toTheRight)
56 signal _tryToUnlockCalled(bool toTheRight)
56
57
57 function showMessage(html) {
58 function showMessage(html) {
@@ -84,8 +85,8 @@ Item {
84 _showErrorMessageCalled(msg);
85 _showErrorMessageCalled(msg);
85 }
86 }
86
87
87− function reset() {
88+ function reset(forceShow) {
88− _resetCalled();
89+ _resetCalled(forceShow);
89 }
90 }
90
91
91 function tryToUnlock(toTheRight) {
92 function tryToUnlock(toTheRight) {
tests/qmltests/Greeter/tst_Greeter.qml+8−1
@@ -29,7 +29,7 @@ Item {
29 width: units.gu(120)
29 width: units.gu(120)
30 height: units.gu(80)
30 height: units.gu(80)
31
31
32− property url defaultBackground: Qt.resolvedUrl("../../../qml/graphics/tablet_background.jpg")
32+ property url defaultBackground: "/usr/share/backgrounds/warty-final-ubuntu.png"
33
33
34 Component.onCompleted: {
34 Component.onCompleted: {
35 // set the mock mode before loading
35 // set the mock mode before loading
@@ -368,6 +368,12 @@ Item {
368 compare(view.background, Qt.resolvedUrl("testing"));
368 compare(view.background, Qt.resolvedUrl("testing"));
369 }
369 }
370
370
371+ function test_hasCustomBackground() {
372+ verify(!view.hasCustomBackground);
373+ greeter.hasCustomBackground = true;
374+ verify(view.hasCustomBackground);
375+ }
376+
371 function test_notifyAboutToFocusApp() {
377 function test_notifyAboutToFocusApp() {
372 greeter.notifyUserRequestedApp("fake-app");
378 greeter.notifyUserRequestedApp("fake-app");
373 compare(viewTryToUnlockSpy.count, 1);
379 compare(viewTryToUnlockSpy.count, 1);
@@ -559,6 +565,7 @@ Item {
559
565
560 LightDM.Greeter.showGreeter();
566 LightDM.Greeter.showGreeter();
561 compare(viewResetSpy.count, 1);
567 compare(viewResetSpy.count, 1);
568+ compare(viewResetSpy.signalArguments[0][0], true);
562 tryCompare(viewShowPromptSpy, "count", 1);
569 tryCompare(viewShowPromptSpy, "count", 1);
563 }
570 }
564
571
tests/qmltests/Greeter/tst_NarrowView.qml+47−12
@@ -20,6 +20,7 @@ import ".."
20import "../../../qml/Greeter"
20import "../../../qml/Greeter"
21import LightDM.IntegratedLightDM 0.1 as LightDM
21import LightDM.IntegratedLightDM 0.1 as LightDM
22import Ubuntu.Components 1.3
22import Ubuntu.Components 1.3
23+import Ubuntu.Telephony 0.1 as Telephony
23import Unity.Test 0.1 as UT
24import Unity.Test 0.1 as UT
24
25
25Item {
26Item {
@@ -39,7 +40,7 @@ Item {
39 property bool itemDestroyed: false
40 property bool itemDestroyed: false
40 sourceComponent: Component {
41 sourceComponent: Component {
41 NarrowView {
42 NarrowView {
42− background: Qt.resolvedUrl("../../../qml/graphics/phone_background.jpg")
43+ background: "/usr/share/backgrounds/warty-final-ubuntu.png"
43 userModel: LightDM.Users
44 userModel: LightDM.Users
44 infographicModel: LightDM.Infographic
45 infographicModel: LightDM.Infographic
45
46
@@ -293,6 +294,8 @@ Item {
293 function init() {
294 function init() {
294 view.currentIndex = 0; // break binding with text field
295 view.currentIndex = 0; // break binding with text field
295
296
297+ telepathyHelper.ready = true;
298+ telepathyHelper.emergencyCallsAvailable = true;
296 selectedSpy.clear();
299 selectedSpy.clear();
297 respondedSpy.clear();
300 respondedSpy.clear();
298 teaseSpy.clear();
301 teaseSpy.clear();
@@ -328,14 +331,6 @@ Item {
328 waitForRendering(view);
331 waitForRendering(view);
329 }
332 }
330
333
331− function enterPin(pin) {
332− for (var i = 0; i < pin.length; ++i) {
333− var character = pin.charAt(i);
334− var button = findChild(view, "pinPadButton" + character);
335− tap(button);
336− }
337− }
338−
339 function test_tease_data() {
334 function test_tease_data() {
340 return [
335 return [
341 {tag: "left", x: 0, offset: 0, count: 1},
336 {tag: "left", x: 0, offset: 0, count: 1},
@@ -350,10 +345,23 @@ Item {
350 compare(teaseSpy.count, data.count);
345 compare(teaseSpy.count, data.count);
351 }
346 }
352
347
348+ function test_customBackground() {
349+ var lockscreenShade = findChild(view, "lockscreenShade");
350+ var backgroundShade = findChild(view, "backgroundShade");
351+
352+ compare(lockscreenShade.opacity, 0);
353+ verify(!backgroundShade.visible);
354+
355+ view.hasCustomBackground = true;
356+ compare(lockscreenShade.opacity, 0.4);
357+ verify(backgroundShade.visible);
358+ }
359+
353 function test_respondedWithPin() {
360 function test_respondedWithPin() {
354 view.locked = true;
361 view.locked = true;
362+ view.showPrompt("", true, true);
355 swipeAwayCover();
363 swipeAwayCover();
356− enterPin("1234");
364+ typeString("1234");
357 compare(respondedSpy.count, 1);
365 compare(respondedSpy.count, 1);
358 compare(respondedSpy.signalArguments[0][0], "1234");
366 compare(respondedSpy.signalArguments[0][0], "1234");
359 }
367 }
@@ -361,6 +369,7 @@ Item {
361 function test_respondedWithPassphrase() {
369 function test_respondedWithPassphrase() {
362 view.locked = true;
370 view.locked = true;
363 view.alphanumeric = true;
371 view.alphanumeric = true;
372+ view.showPrompt("", true, true);
364 swipeAwayCover();
373 swipeAwayCover();
365 typeString("test");
374 typeString("test");
366 keyClick(Qt.Key_Enter);
375 keyClick(Qt.Key_Enter);
@@ -382,12 +391,38 @@ Item {
382 compare(respondedSpy.signalArguments[0][0], "");
391 compare(respondedSpy.signalArguments[0][0], "");
383 }
392 }
384
393
394+ function test_emergencyCall_data() {
395+ return [
396+ {tag: "phone", available: true},
397+ {tag: "desktop", available: false},
398+ ];
399+ }
385− function test_emergencyCall() {
400+ function test_emergencyCall(data) {
401+ telepathyHelper.emergencyCallsAvailable = data.available;
386 view.locked = true;
402 view.locked = true;
387 swipeAwayCover();
403 swipeAwayCover();
388 var emergencyCallLabel = findChild(view, "emergencyCallLabel");
404 var emergencyCallLabel = findChild(view, "emergencyCallLabel");
389 tap(emergencyCallLabel);
405 tap(emergencyCallLabel);
406+ if (!data.available) {
407+ expectFail("", "Bug 1616538 prevents us supporting conditional emergency button support");
408+ }
409+ compare(emergencyCallLabel.visible, data.available ? true : false);
390− compare(emergencySpy.count, 1);
410+ compare(emergencySpy.count, data.available ? 1 : 0);
411+ }
412+
413+ function test_emergencyCallAvailability() {
414+ view.locked = true;
415+ var emergencyCallLabel = findChild(view, "emergencyCallLabel");
416+ verify(emergencyCallLabel.visible);
417+
418+ telepathyHelper.emergencyCallsAvailable = false;
419+ telepathyHelper.ready = true;
420+ expectFail("", "Bug 1616538 prevents us supporting conditional emergency button support");
421+ verify(!emergencyCallLabel.visible);
422+
423+ telepathyHelper.emergencyCallsAvailable = true;
424+ telepathyHelper.ready = false;
425+ verify(!emergencyCallLabel.visible);
391 }
426 }
392
427
393 function test_fullyShown() {
428 function test_fullyShown() {
tests/qmltests/Greeter/tst_WideView.qml+154−7
@@ -24,6 +24,7 @@ import Unity.Test 0.1 as UT
24
24
25StyledItem {
25StyledItem {
26 id: root
26 id: root
27+
27 width: units.gu(120)
28 width: units.gu(120)
28 height: units.gu(80)
29 height: units.gu(80)
29 focus: true
30 focus: true
@@ -49,7 +50,7 @@ StyledItem {
49 WideView {
50 WideView {
50 id: view
51 id: view
51
52
52− background: Qt.resolvedUrl("../../../qml/graphics/tablet_background.jpg")
53+ background: "/usr/share/backgrounds/warty-final-ubuntu.png"
53 userModel: LightDM.Users
54 userModel: LightDM.Users
54 infographicModel: LightDM.Infographic
55 infographicModel: LightDM.Infographic
55
56
@@ -111,11 +112,9 @@ StyledItem {
111 color: theme.palette.normal.background
112 color: theme.palette.normal.background
112 width: units.gu(40)
113 width: units.gu(40)
113 height: parent.height
114 height: parent.height
114−
115 Column {
115 Column {
116 anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
116 anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
117 spacing: units.gu(1)
117 spacing: units.gu(1)
118−
119 Row {
118 Row {
120 Button {
119 Button {
121 text: "Show Last Chance"
120 text: "Show Last Chance"
@@ -266,6 +265,51 @@ StyledItem {
266 text: "emergency: " + emergencySpy.count
265 text: "emergency: " + emergencySpy.count
267 }
266 }
268 }
267 }
268+ Row {
269+ id: multipleSessions
270+ CheckBox {
271+ id: multipleSessionsCheckbox
272+ onClicked: {
273+ if (checked) {
274+ LightDM.Sessions.testScenario = "multipleSessions"
275+ } else {
276+ LightDM.Sessions.testScenario = "singleSession"
277+ }
278+ }
279+ Connections {
280+ target: LightDM.Sessions
281+ onTestScenarioChanged: {
282+ if (LightDM.Sessions.testScenario === "multipleSessions") {
283+ multipleSessionsCheckbox.checked = true;
284+ } else {
285+ multipleSessionsCheckbox.checked = false;
286+ }
287+ }
288+ }
289+ }
290+ Label {
291+ text: "Multiple Sessions"
292+ }
293+ }
294+ Row {
295+ Slider {
296+ id: numSessionsSlider
297+
298+ width: units.gu(10)
299+ minimumValue: 0
300+ maximumValue: LightDM.Sessions.numAvailableSessions
301+ value: LightDM.Sessions.numSessions
302+ visible: LightDM.Sessions.testScenario === "multipleSessions"
303+ Binding {
304+ target: LightDM.Sessions
305+ property: "numSessions"
306+ value: numSessionsSlider.value
307+ }
308+ }
309+ Label {
310+ text: "Available Sessions"
311+ }
312+ }
269 Row {
313 Row {
270 Button {
314 Button {
271 text: "Reload View"
315 text: "Reload View"
@@ -316,6 +360,7 @@ StyledItem {
316 when: windowShown
360 when: windowShown
317
361
318 property Item view: loader.status === Loader.Ready ? loader.item : null
362 property Item view: loader.status === Loader.Ready ? loader.item : null
363+ property url testIconDirectory: Qt.resolvedUrl("../../../qml/Greeter/graphics/session_icons")
319
364
320 function init() {
365 function init() {
321 selectIndex(0); // break binding with text field
366 selectIndex(0); // break binding with text field
@@ -323,6 +368,7 @@ StyledItem {
323 respondedSpy.clear();
368 respondedSpy.clear();
324 teaseSpy.clear();
369 teaseSpy.clear();
325 emergencySpy.clear();
370 emergencySpy.clear();
371+ LightDM.Sessions.testScenario = "multipleSessions"
326 }
372 }
327
373
328 function cleanup() {
374 function cleanup() {
@@ -378,6 +424,92 @@ StyledItem {
378 {tag: "right", x: view.width, offset: 0, count: 1, locked: false},
424 {tag: "right", x: view.width, offset: 0, count: 1, locked: false},
379 ]
425 ]
380 }
426 }
427+
428+ function test_sessionIconsAreValid() {
429+ LightDM.Sessions.testScenario = "multipleSessions"
430+ var originalDirectories = LightDM.Sessions.iconSearchDirectories
431+ LightDM.Sessions.iconSearchDirectories = [testIconDirectory]
432+
433+ selectUser("has-password");
434+
435+ // Test the login list icon is valid
436+ var sessionChooserButton = findChild(view, "sessionChooserButton");
437+ compare(sessionChooserButton.visible, true);
438+
439+ var session = String(view.sessionToStart).toLowerCase();
440+ var icon = String(sessionChooserButton.icon);
441+ compare(icon.indexOf(session) > -1, true);
442+
443+ // Test the session list icons are valid
444+ var coverPage = findChild(view, "coverPage");
445+ coverPage.state = "SessionsList"
446+ var sessionsList = findChild(view, "sessionsList");
447+ tryCompare(sessionsList, "visible", true);
448+ }
449+
450+ function test_choosingNewSessionChangesLoginListIcon() {
451+ // Ensure the default session is selected (Ubuntu)
452+ loader.active = false;
453+ loader.active = true;
454+
455+ selectUser("has-password");
456+
457+ LightDM.Sessions.testScenario = "multipleSessions";
458+ var sessionChooserButton = findChild(view, "sessionChooserButton");
459+ var icon = String(sessionChooserButton.icon);
460+ compare(icon.indexOf("ubuntu") > -1, true);
461+
462+ tap(sessionChooserButton)
463+ for(var i = 0; i < LightDM.Sessions.count; i++) {
464+ var delegateName = "sessionDelegate" + String(i);
465+ var currentDelegate = findChild(view, delegateName);
466+ var sessionKey = LightDM.Sessions.data(i,LightDM.SessionRoles.KeyRole);
467+ if (sessionKey === "gnome-classic") {
468+ tap(currentDelegate);
469+ var sessionChooserButton = findChild(view, "sessionChooserButton");
470+ waitForRendering(sessionChooserButton);
471+ var icon = String(sessionChooserButton.icon);
472+ break;
473+ }
474+ }
475+
476+ compare(icon.indexOf("gnome") > -1, true,
477+ "Expected icon to contain gnome but it was " + icon);
478+ }
479+
480+ function test_noSessionsDoesntBreakView() {
481+ LightDM.Sessions.testScenario = "noSessions"
482+ compare(LightDM.Sessions.count, 0)
483+ }
484+
485+ function test_sessionIconNotShownWithOneSession() {
486+ LightDM.Sessions.testScenario = "singleSession"
487+ compare(LightDM.Sessions.count, 1);
488+
489+ var sessionChooserButton = findChild(view, "sessionChooserButton");
490+ tryCompare(sessionChooserButton, "visible", false);
491+ }
492+
493+ function test_sessionIconNotShownWithActiveUser() {
494+ LightDM.Sessions.testScenario = "multipleSessions";
495+ compare(LightDM.Sessions.count > 1, true);
496+
497+ selectUser("active");
498+
499+ var sessionChooserButton = findChild(view, "sessionChooserButton");
500+ tryCompare(sessionChooserButton, "visible", false);
501+ }
502+
503+ function test_sessionIconShownWithMultipleSessions() {
504+ LightDM.Sessions.testScenario = "multipleSessions"
505+ compare(LightDM.Sessions.count > 1, true);
506+
507+ selectUser("has-password");
508+
509+ var sessionChooserButton = findChild(view, "sessionChooserButton");
510+ tryCompare(sessionChooserButton, "visible", true);
511+ }
512+
381 function test_tease(data) {
513 function test_tease(data) {
382 view.locked = data.locked;
514 view.locked = data.locked;
383 view.dragHandleLeftMargin = data.offset;
515 view.dragHandleLeftMargin = data.offset;
@@ -393,6 +525,17 @@ StyledItem {
393 compare(view.currentIndex, 0); // confirm we didn't change
525 compare(view.currentIndex, 0); // confirm we didn't change
394 }
526 }
395
527
528+ function test_customBackground() {
529+ var coverPage = findChild(view, "coverPage");
530+ var backgroundShade = findChild(coverPage, "backgroundShade");
531+
532+ verify(!view.hasCustomBackground);
533+ verify(!backgroundShade.visible);
534+
535+ view.hasCustomBackground = true;
536+ verify(backgroundShade.visible);
537+ }
538+
396 function test_respondedWithPassword() {
539 function test_respondedWithPassword() {
397 view.locked = true;
540 view.locked = true;
398 view.showPrompt("Prompt", true, false);
541 view.showPrompt("Prompt", true, false);
@@ -509,17 +652,21 @@ StyledItem {
509
652
510 keyboardVisibleCheckBox.checked = true;
653 keyboardVisibleCheckBox.checked = true;
511
654
655+ var halfway = (view.height - loginList.highlightedHeight) / 2;
512− tryCompare(loginList, "height", view.height - view.inputMethod.keyboardRectangle.height);
656+ var halfwayWithOsk = halfway - view.inputMethod.keyboardRectangle.height / 2;
657+ tryCompare(loginList, "boxVerticalOffset", halfwayWithOsk);
658+
659+ var highlightItem = findChild(loginList, "highlightItem");
513 tryCompareFunction( function() {
660 tryCompareFunction( function() {
514− var loginListRect = loginList.mapToItem(view, 0, 0, loginList.width, loginList.height);
661+ var highlightRect = highlightItem.mapToItem(view, 0, 0, highlightItem.width, highlightItem.height);
515− return loginListRect.y + loginListRect.height <= view.inputMethod.keyboardRectangle.y;
662+ return highlightRect.y + highlightRect.height <= view.inputMethod.keyboardRectangle.y;
516 }, true);
663 }, true);
517
664
518 // once the vkb goes away, loginList goes back to its full height
665 // once the vkb goes away, loginList goes back to its full height
519
666
520 keyboardVisibleCheckBox.checked = false;
667 keyboardVisibleCheckBox.checked = false;
521
668
522− tryCompare(loginList, "height", view.height);
669+ tryCompare(loginList, "boxVerticalOffset", halfway);
523 }
670 }
524
671
525 function test_passphrase() {
672 function test_passphrase() {
tests/qmltests/Panel/Indicators/tst_MenuItemFactory.qml+1−1
@@ -409,7 +409,7 @@ Item {
409 menuData.sensitive = data.enabled;
409 menuData.sensitive = data.enabled;
410 menuData.icon = data.icon;
410 menuData.icon = data.icon;
411 menuData.ext = {
411 menuData.ext = {
412− 'xCanonicalColor': data.colour,
412+ 'xCanonicalColor': data.color,
413 'xCanonicalTime': data.time.getTime() / 1000 // expected in seconds
413 'xCanonicalTime': data.time.getTime() / 1000 // expected in seconds
414 };
414 };
415
415
tests/qmltests/Panel/tst_IndicatorItem.qml+4−3
@@ -190,9 +190,10 @@ Rectangle {
190
190
191 indicatorItem.expanded = true;
191 indicatorItem.expanded = true;
192 tryCompare(findChild(indicatorItem, "icon0"), "color", theme.palette.disabled.backgroundText);
192 tryCompare(findChild(indicatorItem, "icon0"), "color", theme.palette.disabled.backgroundText);
193+ // WORKAROUND for a Qt bug in comparing colors: https://bugs.launchpad.net/ubuntu/+source/qtdeclarative-opensource-src/+bug/1619281
193− tryCompare(findChild(indicatorItem, "leftLabel"), "color", theme.palette.disabled.backgroundText);
194+ fuzzyCompare(findChild(indicatorItem, "leftLabel").color, theme.palette.disabled.backgroundText, 0.001);
194− tryCompare(findChild(indicatorItem, "rightLabel"), "color", theme.palette.disabled.backgroundText);
195+ fuzzyCompare(findChild(indicatorItem, "rightLabel").color, theme.palette.disabled.backgroundText, 0.001);
195− tryCompare(findChild(indicatorItem, "indicatorName"), "color", theme.palette.disabled.backgroundText);
196+ fuzzyCompare(findChild(indicatorItem, "indicatorName").color, theme.palette.disabled.backgroundText, 0.001);
196
197
197 indicatorItem.selected = true;
198 indicatorItem.selected = true;
198 tryCompare(findChild(indicatorItem, "icon0"), "color", theme.palette.normal.backgroundText);
199 tryCompare(findChild(indicatorItem, "icon0"), "color", theme.palette.normal.backgroundText);
tests/qmltests/Panel/tst_IndicatorPage.qml−2
@@ -177,8 +177,6 @@ Item {
177 initializeMenuData(fullMenuData);
177 initializeMenuData(fullMenuData);
178
178
179 var menuId = "menu"+data.remove
179 var menuId = "menu"+data.remove
180−
181− tryCompareFunction(function() { return findChild(page, menuId) !== null;}, true);
182 var menu = findChild(page, menuId);
180 var menu = findChild(page, menuId);
183
181
184 menu.menuSelected();
182 menu.menuSelected();
tests/qmltests/Panel/tst_Panel.qml+19−2
@@ -182,6 +182,12 @@ IndicatorTest {
182 signalName: "pressedChanged"
182 signalName: "pressedChanged"
183 }
183 }
184
184
185+ SignalSpy {
186+ id: windowControlButtonsSpy
187+ target: PanelState
188+ signalName: "closeClicked"
189+ }
190+
185 function init() {
191 function init() {
186 panel.fullscreenMode = false;
192 panel.fullscreenMode = false;
187 callManager.foregroundCall = null;
193 callManager.foregroundCall = null;
@@ -197,6 +203,8 @@ IndicatorTest {
197
203
198 backgroundPressedSpy.clear();
204 backgroundPressedSpy.clear();
199 compare(backgroundPressedSpy.valid, true);
205 compare(backgroundPressedSpy.valid, true);
206+ windowControlButtonsSpy.clear();
207+ compare(windowControlButtonsSpy.valid, true);
200 }
208 }
201
209
202 function get_indicator_item(index) {
210 function get_indicator_item(index) {
@@ -380,8 +388,6 @@ IndicatorTest {
380 tap(panel, stepLength * i, tapY);
388 tap(panel, stepLength * i, tapY);
381 tryCompare(panel.indicators, "fullyClosed", true);
389 tryCompare(panel.indicators, "fullyClosed", true);
382 }
390 }
383−
384− compare(backgroundPressedSpy.count, 0);
385 }
391 }
386
392
387 function test_darkenedAreaEatsAllEvents() {
393 function test_darkenedAreaEatsAllEvents() {
@@ -494,5 +500,16 @@ IndicatorTest {
494 compare(panel.indicators.shown, false);
500 compare(panel.indicators.shown, false);
495 tryCompare(panel.indicators, "fullyClosed", true);
501 tryCompare(panel.indicators, "fullyClosed", true);
496 }
502 }
503+
504+ // https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1611959
505+ function test_windowControlButtonsFittsLaw() {
506+ var buttonsVisible = PanelState.buttonsVisible;
507+ PanelState.buttonsVisible = true;
508+ // click in very topleft corner and verify the close button got clicked too
509+ mouseMove(panel, 0, 0);
510+ mouseClick(panel, 0, 0, undefined /*button*/, undefined /*modifiers*/, 100 /*short delay*/);
511+ compare(windowControlButtonsSpy.count, 1);
512+ PanelState.buttonsVisible = buttonsVisible;
513+ }
497 }
514 }
498}
515}
tests/qmltests/Stages/tst_DesktopStage.qml+3−3
@@ -75,7 +75,7 @@ Item {
75 sourceComponent: Component {
75 sourceComponent: Component {
76 DesktopStage {
76 DesktopStage {
77 anchors.fill: parent
77 anchors.fill: parent
78− background: "../../../qml/graphics/tablet_background.jpg"
78+ background: "/usr/share/backgrounds/warty-final-ubuntu.png"
79 focus: true
79 focus: true
80
80
81 Component.onCompleted: {
81 Component.onCompleted: {
@@ -487,7 +487,7 @@ Item {
487
487
488 // click again to restore
488 // click again to restore
489 mouseClick(dialerMaximizeButton);
489 mouseClick(dialerMaximizeButton);
490− tryCompare(dialerDelegate, "windowState", WindowStateStorage.WindowStateNormal);
490+ tryCompare(dialerDelegate, "windowState", WindowStateStorage.WindowStateRestored);
491 }
491 }
492
492
493 function test_windowMaximizeVertically() {
493 function test_windowMaximizeVertically() {
@@ -502,7 +502,7 @@ Item {
502
502
503 // click again to restore
503 // click again to restore
504 mouseClick(dialerMaximizeButton);
504 mouseClick(dialerMaximizeButton);
505− tryCompare(dialerDelegate, "windowState", WindowStateStorage.WindowStateNormal);
505+ tryCompare(dialerDelegate, "windowState", WindowStateStorage.WindowStateRestored);
506 }
506 }
507
507
508 function test_smashCursorKeys() {
508 function test_smashCursorKeys() {
tests/qmltests/Stages/tst_Splash.qml+15−1
@@ -55,7 +55,7 @@ Rectangle {
55 bottom: parent.bottom
55 bottom: parent.bottom
56 left: parent.left
56 left: parent.left
57 }
57 }
58− width: units.gu(40)
58+ width: landscapeCheckbox.checked ? units.gu(70) : units.gu(40)
59 sourceComponent: splashComponent
59 sourceComponent: splashComponent
60 }
60 }
61
61
@@ -97,6 +97,20 @@ Rectangle {
97 CheckBox {id: footerColorCheckbox; checked: false; }
97 CheckBox {id: footerColorCheckbox; checked: false; }
98 Label { text: "custom footerColor"; anchors.verticalCenter: parent.verticalCenter }
98 Label { text: "custom footerColor"; anchors.verticalCenter: parent.verticalCenter }
99 }
99 }
100+ Row {
101+ anchors { left: parent.left; right: parent.right }
102+ CheckBox {
103+ id: landscapeCheckbox
104+ checked: false
105+ onCheckedChanged: {
106+ if (checked) {
107+ window.width = Math.max(window.width, units.gu(110));
108+ }
109+ window.height = checked ? units.gu(40) : units.gu(70);
110+ }
111+ }
112+ Label { text: "Landscape"; anchors.verticalCenter: parent.verticalCenter }
113+ }
100 }
114 }
101 }
115 }
102
116
tests/qmltests/Stages/tst_WindowResizeArea.qml+9−1
@@ -60,9 +60,12 @@ Item {
60 property real heightIncrement: 0
60 property real heightIncrement: 0
61
61
62 property int windowState: WindowStateStorage.WindowStateNormal
62 property int windowState: WindowStateStorage.WindowStateNormal
63+ property real restoredX
64+ property real restoredY
63
65
64 states: [
66 states: [
65 State { name: "normal"; when: windowState == WindowStateStorage.WindowStateNormal },
67 State { name: "normal"; when: windowState == WindowStateStorage.WindowStateNormal },
68+ State { name: "restored"; when: windowState == WindowStateStorage.WindowStateRestored },
66 State { name: "maximized"; when: windowState == WindowStateStorage.WindowStateMaximized }
69 State { name: "maximized"; when: windowState == WindowStateStorage.WindowStateMaximized }
67 ]
70 ]
68
71
@@ -71,7 +74,12 @@ Item {
71 }
74 }
72
75
73 function restoreFromMaximized() {
76 function restoreFromMaximized() {
74− windowState = WindowStateStorage.WindowStateNormal
77+ windowState = WindowStateStorage.WindowStateRestored
78+ }
79+
80+ function restore(animated,state) {
81+ windowState = state || WindowStateStorage.WindowStateRestored;
82+ windowState &= ~WindowStateStorage.WindowStateMinimized; // clear the minimized bit
75 }
83 }
76
84
77 WindowResizeArea {
85 WindowResizeArea {
tests/qmltests/tst_OrientedShell.qml+1
@@ -28,6 +28,7 @@ import Unity.InputInfo 0.1
28import Utils 0.1
28import Utils 0.1
29
29
30import "../../qml"
30import "../../qml"
31+import "../../qml/Components"
31import "Stages"
32import "Stages"
32
33
33Rectangle {
34Rectangle {
tests/qmltests/tst_Shell.qml+160−7
@@ -19,6 +19,7 @@ import QtTest 1.0
19import AccountsService 0.1
19import AccountsService 0.1
20import GSettings 1.0
20import GSettings 1.0
21import LightDM.IntegratedLightDM 0.1 as LightDM
21import LightDM.IntegratedLightDM 0.1 as LightDM
22+import SessionBroadcast 0.1
22import Ubuntu.Components 1.3
23import Ubuntu.Components 1.3
23import Ubuntu.Components.ListItems 1.3 as ListItem
24import Ubuntu.Components.ListItems 1.3 as ListItem
24import Ubuntu.Telephony 0.1 as Telephony
25import Ubuntu.Telephony 0.1 as Telephony
@@ -405,6 +406,18 @@ Rectangle {
405 signalName: "setCurrentScopeCalled"
406 signalName: "setCurrentScopeCalled"
406 }
407 }
407
408
409+ SignalSpy {
410+ id: broadcastUrlSpy
411+ target: SessionBroadcast
412+ signalName: "startUrl"
413+ }
414+
415+ SignalSpy {
416+ id: broadcastHomeSpy
417+ target: SessionBroadcast
418+ signalName: "showHome"
419+ }
420+
408 SignalSpy {
421 SignalSpy {
409 id: unlockAllModemsSpy
422 id: unlockAllModemsSpy
410 target: Connectivity
423 target: Connectivity
@@ -506,7 +519,7 @@ Rectangle {
506 var greeter = findChild(shell, "greeter");
519 var greeter = findChild(shell, "greeter");
507 verify(greeter);
520 verify(greeter);
508
521
509− var loginList = findChild(greeter, "loginList");
522+ var loginList = findChild(greeter, "loginList", 0 /* timeout */);
510 // Only present in WideView
523 // Only present in WideView
511 if (loginList) {
524 if (loginList) {
512 var userList = findChild(loginList, "userList");
525 var userList = findChild(loginList, "userList");
@@ -536,6 +549,7 @@ Rectangle {
536
549
537 AccountsService.demoEdges = false;
550 AccountsService.demoEdges = false;
538 AccountsService.demoEdgesCompleted = [];
551 AccountsService.demoEdgesCompleted = [];
552+ AccountsService.backgroundFile = "";
539 Wizard.System.wizardEnabled = false;
553 Wizard.System.wizardEnabled = false;
540 shellLoader.mode = "full-greeter";
554 shellLoader.mode = "full-greeter";
541
555
@@ -546,8 +560,11 @@ Rectangle {
546 LightDM.Greeter.authenticate(""); // reset greeter
560 LightDM.Greeter.authenticate(""); // reset greeter
547
561
548 sessionSpy.clear();
562 sessionSpy.clear();
563+ broadcastUrlSpy.clear();
564+ broadcastHomeSpy.clear();
549
565
550 GSettingsController.setLifecycleExemptAppids([]);
566 GSettingsController.setLifecycleExemptAppids([]);
567+ GSettingsController.setPictureUri("");
551
568
552 // there should be only unity8-dash window over there
569 // there should be only unity8-dash window over there
553 tryCompare(ApplicationManager, "count", 1);
570 tryCompare(ApplicationManager, "count", 1);
@@ -672,7 +689,7 @@ Rectangle {
672 waitUntilDashIsFocused();
689 waitUntilDashIsFocused();
673 tryCompare(greeter, "shown", false);
690 tryCompare(greeter, "shown", false);
674 } else {
691 } else {
675− compare(greeter.fullyShown, data.greeterShown);
692+ tryCompare(greeter, "fullyShown", data.greeterShown);
676 }
693 }
677
694
678 var launcher = findChild(shell, "launcherPanel");
695 var launcher = findChild(shell, "launcherPanel");
@@ -919,7 +936,7 @@ Rectangle {
919
936
920 function findAppWindowForSurfaceId(surfaceId) {
937 function findAppWindowForSurfaceId(surfaceId) {
921 // for PhoneStage and TabletStage
938 // for PhoneStage and TabletStage
922− var delegate = findChild(shell, "spreadDelegate_" + surfaceId);
939+ var delegate = findChild(shell, "spreadDelegate_" + surfaceId, 0 /* timeout */);
923 if (!delegate) {
940 if (!delegate) {
924 // for DesktopStage
941 // for DesktopStage
925 delegate = findChild(shell, "appDelegate_" + surfaceId);
942 delegate = findChild(shell, "appDelegate_" + surfaceId);
@@ -1319,6 +1336,32 @@ Rectangle {
1319 tryCompare(tutorial, "paused", false);
1336 tryCompare(tutorial, "paused", false);
1320 }
1337 }
1321
1338
1339+ function test_customBackground() {
1340+ loadShell("desktop");
1341+ shell.usageScenario = "desktop";
1342+ waitForRendering(shell);
1343+
1344+ var wallpaperResolver = findInvisibleChild(shell, "wallpaperResolver");
1345+ var greeter = findChild(shell, "greeter");
1346+ verify(!greeter.hasCustomBackground);
1347+ compare(wallpaperResolver.background, wallpaperResolver.defaultBackground);
1348+
1349+ AccountsService.backgroundFile = Qt.resolvedUrl("../graphics/applicationIcons/dash.png");
1350+ tryCompare(greeter, "hasCustomBackground", true);
1351+ compare(wallpaperResolver.background, AccountsService.backgroundFile);
1352+ }
1353+
1354+ function test_cachedBackground() {
1355+ loadShell("desktop");
1356+ shell.usageScenario = "desktop";
1357+ waitForRendering(shell);
1358+
1359+ var greeter = findChild(shell, "greeter");
1360+ verify(!greeter.hasCustomBackground);
1361+ compare(greeter.background.toString().indexOf("image://unity8imagecache/file:///"), 0);
1362+ verify(greeter.background.toString().indexOf("?name=wallpaper") > 0);
1363+ }
1364+
1322 function test_tapOnRightEdgeReachesApplicationSurface() {
1365 function test_tapOnRightEdgeReachesApplicationSurface() {
1323 loadShell("phone");
1366 loadShell("phone");
1324 swipeAwayGreeter();
1367 swipeAwayGreeter();
@@ -2420,6 +2463,119 @@ Rectangle {
2420 compare(topmostSurfaceItem.touchReleaseCount, 2);
2463 compare(topmostSurfaceItem.touchReleaseCount, 2);
2421 }
2464 }
2422
2465
2466+ function test_background_data() {
2467+ return [
2468+ {tag: "color",
2469+ accounts: Qt.resolvedUrl("data:image/svg+xml,<svg><rect width='100%' height='100%' fill='#dd4814'/></svg>"),
2470+ gsettings: "",
2471+ output: Qt.resolvedUrl("data:image/svg+xml,<svg><rect width='100%' height='100%' fill='#dd4814'/></svg>")},
2472+
2473+ {tag: "empty", accounts: "", gsettings: "", output: "defaultBackground"},
2474+
2475+ {tag: "as-specified",
2476+ accounts: Qt.resolvedUrl("../data/unity/backgrounds/blue.png"),
2477+ gsettings: "",
2478+ output: Qt.resolvedUrl("../data/unity/backgrounds/blue.png")},
2479+
2480+ {tag: "gs-specified",
2481+ accounts: "",
2482+ gsettings: Qt.resolvedUrl("../data/unity/backgrounds/red.png"),
2483+ output: Qt.resolvedUrl("../data/unity/backgrounds/red.png")},
2484+
2485+ {tag: "both-specified",
2486+ accounts: Qt.resolvedUrl("../data/unity/backgrounds/blue.png"),
2487+ gsettings: Qt.resolvedUrl("../data/unity/backgrounds/red.png"),
2488+ output: Qt.resolvedUrl("../data/unity/backgrounds/blue.png")},
2489+
2490+ {tag: "invalid-as",
2491+ accounts: Qt.resolvedUrl("../data/unity/backgrounds/nope.png"),
2492+ gsettings: Qt.resolvedUrl("../data/unity/backgrounds/red.png"),
2493+ output: Qt.resolvedUrl("../data/unity/backgrounds/red.png")},
2494+
2495+ {tag: "invalid-both",
2496+ accounts: Qt.resolvedUrl("../data/unity/backgrounds/nope.png"),
2497+ gsettings: Qt.resolvedUrl("../data/unity/backgrounds/stillnope.png"),
2498+ output: "defaultBackground"},
2499+ ]
2500+ }
2501+ function test_background(data) {
2502+ loadShell("phone");
2503+ shell.usageScenario = "phone";
2504+ waitForRendering(shell);
2505+
2506+ AccountsService.backgroundFile = data.accounts;
2507+ GSettingsController.setPictureUri(data.gsettings);
2508+
2509+ var wallpaperResolver = findChild(shell, "wallpaperResolver");
2510+ if (data.output === "defaultBackground") {
2511+ tryCompare(wallpaperResolver, "background", wallpaperResolver.defaultBackground);
2512+ verify(!wallpaperResolver.hasCustomBackground);
2513+ } else {
2514+ tryCompare(wallpaperResolver, "background", data.output);
2515+ verify(wallpaperResolver.hasCustomBackground);
2516+ }
2517+ }
2518+
2519+ function test_greeterModeBroadcastsApp() {
2520+ setLightDMMockMode("single-pin");
2521+ shellLoader.mode = "greeter";
2522+ loadShell("phone");
2523+ shell.usageScenario = "phone";
2524+ waitForRendering(shell);
2525+
2526+ dragLauncherIntoView();
2527+ var appIcon = findChild(shell, "launcherDelegate0")
2528+ tap(appIcon);
2529+
2530+ tryCompare(broadcastUrlSpy, "count", 1);
2531+ compare(broadcastUrlSpy.signalArguments[0][0], "application:///" + appIcon.appId + ".desktop");
2532+ compare(ApplicationManager.count, 1); // confirm only dash is open, we didn't start new app
2533+
2534+ var coverPage = findChild(shell, "coverPage");
2535+ tryCompare(coverPage, "showProgress", 0);
2536+ }
2537+
2538+ function test_greeterModeBroadcastsHome() {
2539+ setLightDMMockMode("single-pin");
2540+ shellLoader.mode = "greeter";
2541+ loadShell("phone");
2542+ shell.usageScenario = "phone";
2543+ waitForRendering(shell);
2544+
2545+ var gallerySurfaceId = topLevelSurfaceList.nextId;
2546+ var galleryApp = ApplicationManager.startApplication("gallery-app");
2547+ waitUntilAppWindowIsFullyLoaded(gallerySurfaceId);
2548+ compare(ApplicationManager.focusedApplicationId, "gallery-app");
2549+
2550+ dragLauncherIntoView();
2551+ tap(findChild(shell, "buttonShowDashHome"));
2552+
2553+ tryCompare(broadcastHomeSpy, "count", 1);
2554+ compare(ApplicationManager.focusedApplicationId, "gallery-app"); // confirm we didn't focus dash
2555+
2556+ var coverPage = findChild(shell, "coverPage");
2557+ tryCompare(coverPage, "showProgress", 0);
2558+ }
2559+
2560+ function test_greeterModeDispatchesURL() {
2561+ setLightDMMockMode("single-pin");
2562+ shellLoader.mode = "greeter";
2563+ loadShell("phone");
2564+ shell.usageScenario = "phone";
2565+ waitForRendering(shell);
2566+
2567+ var urlDispatcher = findInvisibleChild(shell, "urlDispatcher");
2568+ verify(urlDispatcher.active);
2569+ urlDispatcher.urlRequested("test:"); // force signal emission
2570+
2571+ tryCompare(broadcastUrlSpy, "count", 1);
2572+ compare(broadcastUrlSpy.signalArguments[0][0], "test:");
2573+ compare(ApplicationManager.count, 1); // confirm only dash is open, we didn't start new app
2574+
2575+ var coverPage = findChild(shell, "coverPage");
2576+ tryCompare(coverPage, "showProgress", 0);
2577+ }
2578+
2423 function test_switchKeymap() {
2579 function test_switchKeymap() {
2424 // start with phone shell
2580 // start with phone shell
2425 loadShell("phone");
2581 loadShell("phone");
@@ -2508,10 +2664,7 @@ Rectangle {
2508 appDelegate.maximize();
2664 appDelegate.maximize();
2509 tryCompare(appDelegate, "state", "maximized");
2665 tryCompare(appDelegate, "state", "maximized");
2510
2666
2511− mousePress(panel);
2667+ mouseDrag(panel, panel.width/3, panel.height/2, 0, shell.height/3, Qt.LeftButton, Qt.NoModifier, 500);
2512− mouseMove(shell, shell.width/2, shell.height/2);
2513− mouseRelease(shell);
2514−
2515 tryCompare(appDelegate, "state", "normal");
2668 tryCompare(appDelegate, "state", "normal");
2516 }
2669 }
2517
2670
tests/qmltests/tst_ShellWithPin.qml+44−40
@@ -154,7 +154,6 @@ Item {
154 var greeter = findChild(shell, "greeter");
154 var greeter = findChild(shell, "greeter");
155 sessionSpy.target = greeter;
155 sessionSpy.target = greeter;
156 swipeAwayGreeter(true);
156 swipeAwayGreeter(true);
157− waitForLockscreen()
158 greeter.failedLoginsDelayAttempts = -1;
157 greeter.failedLoginsDelayAttempts = -1;
159 greeter.maxFailedLogins = -1;
158 greeter.maxFailedLogins = -1;
160
159
@@ -214,20 +213,16 @@ Item {
214 }
213 }
215 }
214 }
216
215
217− function waitForLockscreen() {
216+ function enterPin(pin) {
218− var lockscreen = findChild(shell, "lockscreen");
217+ typeString(pin);
219− var pinPadLoader = findChild(lockscreen, "pinPadLoader");
220− tryCompare(pinPadLoader, "status", Loader.Ready)
221− waitForRendering(lockscreen)
222 }
218 }
223
219
224− function enterPin(pin) {
220+ function showGreeter() {
225− for (var i = 0; i < pin.length; ++i) {
221+ LightDM.Greeter.showGreeter();
226− var character = pin.charAt(i)
222+ tryCompare(shell, "waitingOnGreeter", false);
227− var button = findChild(shell, "pinPadButton" + character)
223+ var coverPage = findChild(shell, "coverPage");
228− tryCompare(button, "enabled", true);
224+ tryCompare(coverPage, "showProgress", 1);
229− tap(button)
225+ removeTimeConstraintsFromSwipeAreas(shell);
230− }
231 }
226 }
232
227
233 function confirmLockedApp(app) {
228 function confirmLockedApp(app) {
@@ -248,7 +243,7 @@ Item {
248 LightDM.Greeter.hideGreeter();
243 LightDM.Greeter.hideGreeter();
249 tryCompare(panel.indicators.indicatorsModel, "profile", shell.indicatorProfile);
244 tryCompare(panel.indicators.indicatorsModel, "profile", shell.indicatorProfile);
250
245
251− LightDM.Greeter.showGreeter();
246+ showGreeter();
252 tryCompare(panel.indicators.indicatorsModel, "profile", shell.indicatorProfile + "_greeter");
247 tryCompare(panel.indicators.indicatorsModel, "profile", shell.indicatorProfile + "_greeter");
253
248
254 LightDM.Greeter.hideGreeter();
249 LightDM.Greeter.hideGreeter();
@@ -293,7 +288,7 @@ Item {
293
288
294 // Cancel emergency mode, and go back to normal
289 // Cancel emergency mode, and go back to normal
295 waitForRendering(greeter)
290 waitForRendering(greeter)
296− LightDM.Greeter.showGreeter()
291+ showGreeter()
297
292
298 tryCompare(greeter, "shown", true)
293 tryCompare(greeter, "shown", true)
299 tryCompare(greeter, "lockedApp", "")
294 tryCompare(greeter, "lockedApp", "")
@@ -328,6 +323,22 @@ Item {
328 tryCompare(greeter, "shown", true);
323 tryCompare(greeter, "shown", true);
329 }
324 }
330
325
326+ function test_emergencyCallPausesTutorial() {
327+ var greeter = findChild(shell, "greeter");
328+ var tutorial = findChild(shell, "tutorial");
329+
330+ AccountsService.demoEdges = true;
331+ enterPin("1234");
332+ tryCompare(tutorial, "paused", false);
333+
334+ showGreeter();
335+ verify(tutorial.paused);
336+
337+ swipeAwayGreeter(true);
338+ tap(findChild(greeter, "emergencyCallLabel"));
339+ verify(tutorial.paused);
340+ }
341+
331 function test_failedLoginsCount() {
342 function test_failedLoginsCount() {
332 AccountsService.failedLogins = 0
343 AccountsService.failedLogins = 0
333
344
@@ -342,33 +353,33 @@ Item {
342 var greeter = findChild(shell, "greeter");
353 var greeter = findChild(shell, "greeter");
343 greeter.failedLoginsDelayAttempts = 3;
354 greeter.failedLoginsDelayAttempts = 3;
344
355
345− var placeHolder = findChild(shell, "wrongNoticeLabel")
356+ var wrongPasswordAnimation = findInvisibleChild(shell, "wrongPasswordAnimation");
346− tryCompare(placeHolder, "text", "")
347
357
348 enterPin("1111")
358 enterPin("1111")
349− tryCompare(placeHolder, "text", "Sorry, incorrect passcode")
359+ verify(wrongPasswordAnimation.running);
350
360
351 enterPin("1111")
361 enterPin("1111")
352− tryCompare(placeHolder, "text", "Sorry, incorrect passcode")
362+ verify(wrongPasswordAnimation.running);
353
363
354− var lockscreen = findChild(shell, "lockscreen")
364+ var delayedLockscreen = findChild(shell, "delayedLockscreen");
355− tryCompare(lockscreen, "delayMinutes", 0)
365+ tryCompare(delayedLockscreen, "delayMinutes", 0);
356 enterPin("1111")
366 enterPin("1111")
357− tryCompare(lockscreen, "delayMinutes", greeter.failedLoginsDelayMinutes);
367+ tryCompare(delayedLockscreen, "delayMinutes", greeter.failedLoginsDelayMinutes);
358 }
368 }
359
369
360 function test_factoryReset() {
370 function test_factoryReset() {
371+ skip("Factory reset support is not finished")
372+
361 maxRetriesTextField.text = "3"
373 maxRetriesTextField.text = "3"
362 resetSpy.clear()
374 resetSpy.clear()
363
375
364 enterPin("1111")
376 enterPin("1111")
365 enterPin("1111")
377 enterPin("1111")
366− tryCompareFunction(function() {return findChild(root, "infoPopup") !== null}, true)
367
378
368 var dialog = findChild(root, "infoPopup")
379 var dialog = findChild(root, "infoPopup")
369 var button = findChild(dialog, "infoPopupOkButton")
380 var button = findChild(dialog, "infoPopupOkButton")
370 tap(button)
381 tap(button)
371− tryCompareFunction(function() {return findChild(root, "infoPopup")}, null)
382+ tryCompareFunction(function() {return findChild(root, "infoPopup", 0 /* timeout */)}, null)
372
383
373 tryCompare(resetSpy, "count", 0)
384 tryCompare(resetSpy, "count", 0)
374 enterPin("1111")
385 enterPin("1111")
@@ -388,8 +399,7 @@ Item {
388 compare(shell.usageScenario, "phone");
399 compare(shell.usageScenario, "phone");
389 compare(applicationsDisplayLoader.usageScenario, "phone");
400 compare(applicationsDisplayLoader.usageScenario, "phone");
390
401
391− var lockscreen = findChild(shell, "lockscreen")
402+ tap(findChild(shell, "emergencyCallLabel"));
392− lockscreen.emergencyCall()
393 confirmLockedApp("dialer-app")
403 confirmLockedApp("dialer-app")
394
404
395 // OK, we're in. Now try (but fail) to switch to tablet mode
405 // OK, we're in. Now try (but fail) to switch to tablet mode
@@ -416,16 +426,13 @@ Item {
416 // - Should be back in emergency mode dialer
426 // - Should be back in emergency mode dialer
417
427
418 var greeter = findChild(shell, "greeter");
428 var greeter = findChild(shell, "greeter");
419− var lockscreen = findChild(shell, "lockscreen");
420− verify(lockscreen);
421
429
422− lockscreen.emergencyCall();
430+ tap(findChild(shell, "emergencyCallLabel"));
423 confirmLockedApp("dialer-app");
431 confirmLockedApp("dialer-app");
424 callManager.foregroundCall = phoneCall;
432 callManager.foregroundCall = phoneCall;
425
433
426− LightDM.Greeter.showGreeter();
434+ showGreeter();
427− lockscreen = findChild(shell, "lockscreen");
435+ var lockscreen = findChild(shell, "lockscreen");
428− verify(lockscreen);
429 tryCompare(lockscreen, "shown", true);
436 tryCompare(lockscreen, "shown", true);
430 tryCompare(greeter, "hasLockedApp", false);
437 tryCompare(greeter, "hasLockedApp", false);
431
438
@@ -468,7 +475,7 @@ Item {
468 }
475 }
469
476
470 function test_focusRequestedHidesCoverPage() {
477 function test_focusRequestedHidesCoverPage() {
471− LightDM.Greeter.showGreeter();
478+ showGreeter();
472
479
473 var app = ApplicationManager.startApplication("gallery-app");
480 var app = ApplicationManager.startApplication("gallery-app");
474 // wait until the app is fully loaded (ie, real surface replaces splash screen)
481 // wait until the app is fully loaded (ie, real surface replaces splash screen)
@@ -480,8 +487,7 @@ Item {
480 tryCompare(coverPage, "showProgress", 0);
487 tryCompare(coverPage, "showProgress", 0);
481 tryCompare(greeter, "fullyShown", true);
488 tryCompare(greeter, "fullyShown", true);
482
489
483− LightDM.Greeter.showGreeter();
490+ showGreeter();
484− tryCompare(coverPage, "showProgress", 1);
485
491
486 // Make sure focusing same app triggers same behavior
492 // Make sure focusing same app triggers same behavior
487 ApplicationManager.requestFocusApplication("gallery-app");
493 ApplicationManager.requestFocusApplication("gallery-app");
@@ -555,9 +561,7 @@ Item {
555 only after its idle timeout is triggered.
561 only after its idle timeout is triggered.
556 */
562 */
557 function test_shortLeftEdgeSwipeMakesLauncherStayVisible() {
563 function test_shortLeftEdgeSwipeMakesLauncherStayVisible() {
558− LightDM.Greeter.showGreeter();
564+ showGreeter();
559− var coverPage = testCase.findChild(shell, "coverPage");
560− tryCompare(coverPage, "showProgress", 1);
561
565
562 var launcher = testCase.findChild(shell, "launcher")
566 var launcher = testCase.findChild(shell, "launcher")
563 var launcherPanel = testCase.findChild(launcher, "launcherPanel");
567 var launcherPanel = testCase.findChild(launcher, "launcherPanel");
@@ -573,6 +577,7 @@ Item {
573 tryCompare(launcherPanel, "x", 0);
577 tryCompare(launcherPanel, "x", 0);
574
578
575 // Greeter should be displaced
579 // Greeter should be displaced
580+ var coverPage = findChild(shell, "coverPage");
576 tryCompareFunction(function() { return coverPage.mapToItem(shell, 0, 0).x > shell.width*0.2; }, true);
581 tryCompareFunction(function() { return coverPage.mapToItem(shell, 0, 0).x > shell.width*0.2; }, true);
577
582
578 touchRelease(shell, toX, shell.height * 0.5);
583 touchRelease(shell, toX, shell.height * 0.5);
@@ -595,8 +600,7 @@ Item {
595 tryCompare(shell, "mainApp", galleryApp);
600 tryCompare(shell, "mainApp", galleryApp);
596
601
597 // Show greeter
602 // Show greeter
598− LightDM.Greeter.showGreeter();
603+ showGreeter();
599− tryCompare(coverPage, "showProgress", 1);
600
604
601 // Swipe cover page away
605 // Swipe cover page away
602 touchFlick(shell, 2, shell.height / 2, units.gu(30), shell.height / 2);
606 touchFlick(shell, 2, shell.height / 2, units.gu(30), shell.height / 2);