Merge lp:~ci-train-bot/unity8/unity8-ubuntu-zesty-2272 into lp:unity8

Proposed by Michał Sawicz
Status: Merged
Merged at revision: 2797
Proposed branch: lp:~ci-train-bot/unity8/unity8-ubuntu-zesty-2272
Merge into: lp:unity8
Diff against target: 10072 lines (+3585/-2466)
134 files modified
CMakeLists.txt (+1/-1)
cmake/modules/QmlTest.cmake (+8/-3)
data/com.canonical.Unity8.gschema.xml (+5/-0)
debian/changelog (+51/-0)
debian/control (+1/-1)
debian/rules (+1/-1)
plugins/LightDM/CMakeLists.txt (+1/-1)
plugins/LightDM/DBusGreeterList.cpp (+6/-6)
plugins/LightDM/DBusGreeterList.h (+2/-2)
plugins/LightDM/Greeter.cpp (+142/-28)
plugins/LightDM/Greeter.h (+17/-13)
plugins/LightDM/GreeterPrivate.h (+14/-9)
plugins/LightDM/IntegratedLightDM/liblightdm/Greeter.cpp (+7/-4)
plugins/LightDM/IntegratedLightDM/liblightdm/GreeterPrivate.cpp (+27/-22)
plugins/LightDM/IntegratedLightDM/liblightdm/GreeterPrivate.h (+1/-0)
plugins/LightDM/PromptsModel.cpp (+97/-0)
plugins/LightDM/PromptsModel.h (+70/-0)
plugins/LightDM/SessionsModel.cpp (+1/-2)
plugins/LightDM/SessionsModel.h (+4/-5)
plugins/LightDM/UsersModel.cpp (+171/-9)
plugins/LightDM/UsersModel.h (+6/-8)
plugins/LightDM/plugin.cpp (+12/-4)
plugins/Unity/Launcher/launchermodel.cpp (+0/-1)
plugins/Utils/CMakeLists.txt (+1/-0)
plugins/Utils/appdrawerproxymodel.cpp (+1/-1)
plugins/Utils/plugin.cpp (+3/-1)
plugins/Utils/tabfocusfence.cpp (+72/-0)
plugins/Utils/tabfocusfence.h (+38/-0)
po/unity8.pot (+71/-43)
qml/ApplicationMenus/MenuBar.qml (+14/-0)
qml/ApplicationMenus/MenuPopup.qml (+178/-139)
qml/Components/Dialogs.qml (+42/-3)
qml/Components/KeyboardShortcutsOverlay.qml (+1/-1)
qml/Components/KeymapSwitcher.qml (+15/-7)
qml/Components/Lockscreen.qml (+3/-1)
qml/Components/ModeSwitchWarningDialog.qml (+8/-2)
qml/Components/ShellDialog.qml (+21/-3)
qml/Greeter/Circle.qml (+1/-1)
qml/Greeter/FullLightDMImpl.qml (+1/-0)
qml/Greeter/Greeter.qml (+31/-61)
qml/Greeter/GreeterPrompt.qml (+32/-31)
qml/Greeter/IntegratedLightDMImpl.qml (+1/-0)
qml/Greeter/LightDMService.qml (+4/-4)
qml/Greeter/LoginList.qml (+46/-115)
qml/Greeter/NarrowView.qml (+11/-21)
qml/Greeter/PromptList.qml (+148/-0)
qml/Greeter/WideView.qml (+13/-22)
qml/Launcher/Drawer.qml (+86/-4)
qml/Launcher/DrawerGridView.qml (+3/-1)
qml/Launcher/DrawerListView.qml (+6/-0)
qml/Launcher/Launcher.qml (+11/-8)
qml/Launcher/LauncherDelegate.qml (+8/-9)
qml/Launcher/LauncherPanel.qml (+11/-6)
qml/Launcher/MoreAppsHeader.qml (+12/-5)
qml/Launcher/graphics/launcher-app-focus-ring.svg (+0/-12)
qml/OrientedShell.qml (+2/-1)
qml/Panel/PanelBar.qml (+1/-0)
qml/Panel/PanelMenu.qml (+14/-0)
qml/Shell.qml (+49/-5)
qml/Stage/DecoratedWindow.qml (+39/-46)
qml/Stage/WindowDecoration.qml (+6/-2)
src/MouseTouchAdaptor.cpp (+28/-1)
src/MouseTouchAdaptor.h (+1/-0)
tests/CMakeLists.txt (+5/-3)
tests/autopilot/unity8/fixture_setup.py (+1/-1)
tests/autopilot/unity8/shell/tests/__init__.py (+1/-1)
tests/mocks/AccountsService/AccountsService.cpp (+2/-3)
tests/mocks/AccountsService/AccountsService.h (+3/-6)
tests/mocks/AccountsService/CMakeLists.txt (+3/-2)
tests/mocks/CMakeLists.txt (+2/-1)
tests/mocks/GSettings.1.0/fake_gsettings.cpp (+14/-0)
tests/mocks/GSettings.1.0/fake_gsettings.h (+7/-0)
tests/mocks/LightDM/CMakeLists.txt (+0/-1)
tests/mocks/LightDM/IntegratedLightDM/CMakeLists.txt (+0/-50)
tests/mocks/LightDM/IntegratedLightDM/MockGreeter.cpp (+0/-51)
tests/mocks/LightDM/IntegratedLightDM/MockGreeter.h (+0/-42)
tests/mocks/LightDM/IntegratedLightDM/MockSessionsModel.cpp (+0/-66)
tests/mocks/LightDM/IntegratedLightDM/MockSessionsModel.h (+0/-44)
tests/mocks/LightDM/IntegratedLightDM/MockUsersModel.cpp (+0/-44)
tests/mocks/LightDM/IntegratedLightDM/MockUsersModel.h (+0/-39)
tests/mocks/LightDM/IntegratedLightDM/QLightDM/Greeter (+0/-17)
tests/mocks/LightDM/IntegratedLightDM/QLightDM/SessionsModel (+0/-17)
tests/mocks/LightDM/IntegratedLightDM/QLightDM/UsersModel (+0/-17)
tests/mocks/LightDM/IntegratedLightDM/liblightdm/GreeterPrivate.cpp (+0/-132)
tests/mocks/LightDM/IntegratedLightDM/liblightdm/GreeterPrivate.h (+0/-54)
tests/mocks/LightDM/IntegratedLightDM/liblightdm/SessionsModelPrivate.cpp (+0/-83)
tests/mocks/LightDM/IntegratedLightDM/liblightdm/SessionsModelPrivate.h (+0/-63)
tests/mocks/LightDM/IntegratedLightDM/liblightdm/UsersModelPrivate.cpp (+0/-124)
tests/mocks/LightDM/IntegratedLightDM/liblightdm/UsersModelPrivate.h (+0/-64)
tests/mocks/LightDM/IntegratedLightDM/plugin.cpp (+0/-79)
tests/mocks/LightDM/IntegratedLightDM/plugin.h (+0/-32)
tests/mocks/LightDM/IntegratedLightDM/qmldir (+0/-2)
tests/mocks/LightDMController/CMakeLists.txt (+15/-0)
tests/mocks/LightDMController/plugin.cpp (+35/-0)
tests/mocks/LightDMController/plugin.h (+29/-0)
tests/mocks/LightDMController/qmldir (+2/-0)
tests/mocks/Unity/Application/MirMock.cpp (+13/-0)
tests/mocks/Unity/Application/MirMock.h (+4/-0)
tests/mocks/Unity/Launcher/MockAppDrawerModel.cpp (+0/-1)
tests/mocks/Utils/CMakeLists.txt (+1/-0)
tests/mocks/Utils/plugin.cpp (+3/-1)
tests/mocks/liblightdm/CMakeLists.txt (+8/-11)
tests/mocks/liblightdm/MockController.cpp (+191/-0)
tests/mocks/liblightdm/MockController.h (+106/-0)
tests/mocks/liblightdm/MockGreeter.cpp (+157/-42)
tests/mocks/liblightdm/MockGreeter.h (+9/-22)
tests/mocks/liblightdm/MockSessionsModel.cpp (+47/-49)
tests/mocks/liblightdm/MockSessionsModel.h (+9/-17)
tests/mocks/liblightdm/MockUsersModel.cpp (+118/-20)
tests/mocks/liblightdm/MockUsersModel.h (+8/-16)
tests/plugins/LightDM/IntegratedLightDM/CMakeLists.txt (+37/-9)
tests/plugins/LightDM/IntegratedLightDM/dbus.cpp (+8/-12)
tests/plugins/LightDM/IntegratedLightDM/greeter.qml (+2/-7)
tests/plugins/LightDM/IntegratedLightDM/promptsmodel.cpp (+175/-0)
tests/plugins/LightDM/IntegratedLightDM/sessionsmodel.cpp (+15/-13)
tests/plugins/LightDM/IntegratedLightDM/usersmodel.cpp (+87/-16)
tests/plugins/Utils/CMakeLists.txt (+4/-1)
tests/plugins/Utils/WindowStateStorageTest.cpp (+64/-0)
tests/qmltests/ApplicationMenuDataLoader.qml (+13/-5)
tests/qmltests/ApplicationMenus/tst_MenuBar.qml (+18/-5)
tests/qmltests/ApplicationMenus/tst_MenuPopup.qml (+156/-45)
tests/qmltests/Dash/tst_DashShell.qml (+8/-40)
tests/qmltests/Greeter/TestView.qml (+7/-16)
tests/qmltests/Greeter/tst_Greeter.qml (+63/-111)
tests/qmltests/Greeter/tst_Infographics.qml (+1/-11)
tests/qmltests/Greeter/tst_NarrowView.qml (+20/-43)
tests/qmltests/Greeter/tst_WideView.qml (+125/-121)
tests/qmltests/Launcher/tst_Drawer.qml (+58/-7)
tests/qmltests/Launcher/tst_Launcher.qml (+15/-6)
tests/qmltests/Panel/tst_Panel.qml (+36/-0)
tests/qmltests/Tutorial/tst_Tutorial.qml (+3/-3)
tests/qmltests/tst_OrientedShell.qml (+90/-8)
tests/qmltests/tst_Shell.qml (+87/-81)
tests/qmltests/tst_ShellWithPin.qml (+22/-13)
To merge this branch: bzr merge lp:~ci-train-bot/unity8/unity8-ubuntu-zesty-2272
Reviewer Review Type Date Requested Status
Unity Team Pending
Review via email: mp+315584@code.launchpad.net
To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'CMakeLists.txt'
--- CMakeLists.txt 2017-01-10 14:46:09 +0000
+++ CMakeLists.txt 2017-01-25 16:04:08 +0000
@@ -70,7 +70,7 @@
70find_package(Qt5Concurrent 5.6 REQUIRED)70find_package(Qt5Concurrent 5.6 REQUIRED)
71find_package(Qt5Sql 5.6 REQUIRED)71find_package(Qt5Sql 5.6 REQUIRED)
7272
73pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=23)73pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=25)
74pkg_check_modules(GEONAMES REQUIRED geonames>=0.2)74pkg_check_modules(GEONAMES REQUIRED geonames>=0.2)
75pkg_check_modules(GIO REQUIRED gio-2.0>=2.32)75pkg_check_modules(GIO REQUIRED gio-2.0>=2.32)
76pkg_check_modules(GLIB REQUIRED glib-2.0>=2.32)76pkg_check_modules(GLIB REQUIRED glib-2.0>=2.32)
7777
=== modified file 'cmake/modules/QmlTest.cmake'
--- cmake/modules/QmlTest.cmake 2016-06-07 13:25:58 +0000
+++ cmake/modules/QmlTest.cmake 2017-01-25 16:04:08 +0000
@@ -264,12 +264,16 @@
264# installed system.264# installed system.
265265
266function(add_meta_test TARGET_NAME)266function(add_meta_test TARGET_NAME)
267 cmake_parse_arguments(TEST "" "" "DEPENDS" ${ARGN})267 cmake_parse_arguments(TEST "SERIAL" "" "DEPENDS" ${ARGN})
268268
269 add_custom_target(${TARGET_NAME})269 add_custom_target(${TARGET_NAME})
270270
271 set(filename "${CMAKE_BINARY_DIR}/tests/scripts/${TARGET_NAME}.sh")271 set(filename "${CMAKE_BINARY_DIR}/tests/scripts/${TARGET_NAME}.sh")
272 file(WRITE "${filename}" "#!/usr/bin/parallel --shebang --no-notice\n\n")272 if(TEST_SERIAL)
273 file(WRITE "${filename}" "#!/bin/sh\n\n")
274 else()
275 file(WRITE "${filename}" "#!/usr/bin/parallel --shebang --no-notice\n\n")
276 endif()
273277
274 add_meta_dependencies(${TARGET_NAME} DEPENDS ${TEST_DEPENDS})278 add_meta_dependencies(${TARGET_NAME} DEPENDS ${TEST_DEPENDS})
275 # else we will write the rest of the script as we add cmake targets279 # else we will write the rest of the script as we add cmake targets
@@ -307,6 +311,7 @@
307 foreach(ONE_CMD ${TEST_COMMAND})311 foreach(ONE_CMD ${TEST_COMMAND})
308 set(script "${script}'${ONE_CMD}' ")312 set(script "${script}'${ONE_CMD}' ")
309 endforeach()313 endforeach()
314 set(script "${script}\"\$@\"") # Allow passing arguments if desired
310315
311 set(filename "${CMAKE_BINARY_DIR}/tests/scripts/${TARGET_NAME}.sh")316 set(filename "${CMAKE_BINARY_DIR}/tests/scripts/${TARGET_NAME}.sh")
312317
@@ -370,7 +375,7 @@
370 # add depend to the meta test script that we will install on system375 # add depend to the meta test script that we will install on system
371 set(filename "${CMAKE_BINARY_DIR}/tests/scripts/${UPSTREAM_TARGET}.sh")376 set(filename "${CMAKE_BINARY_DIR}/tests/scripts/${UPSTREAM_TARGET}.sh")
372 if (EXISTS "${filename}")377 if (EXISTS "${filename}")
373 file(APPEND "${filename}" "${CMAKE_INSTALL_PREFIX}/${SHELL_PRIVATE_LIBDIR}/tests/scripts/${depend}.sh\n")378 file(APPEND "${filename}" "${CMAKE_INSTALL_PREFIX}/${SHELL_PRIVATE_LIBDIR}/tests/scripts/${depend}.sh \"\$@\" 2>&1\n")
374 endif()379 endif()
375 endforeach()380 endforeach()
376endfunction()381endfunction()
377382
=== modified file 'data/com.canonical.Unity8.gschema.xml'
--- data/com.canonical.Unity8.gschema.xml 2016-11-30 10:32:23 +0000
+++ data/com.canonical.Unity8.gschema.xml 2017-01-25 16:04:08 +0000
@@ -53,6 +53,11 @@
53 <summary>Enable or disable the indicator pull down menus</summary>53 <summary>Enable or disable the indicator pull down menus</summary>
54 <description>Toggle the availability of the indicator pull down menus</description>54 <description>Toggle the availability of the indicator pull down menus</description>
55 </key>55 </key>
56 <key type="s" name="appstore-uri">
57 <default>'scope://com.canonical.scopes.clickstore'</default>
58 <summary>The uri to the app store</summary>
59 <description>This will be used whenever the user triggers an action to open the app store.</description>
60 </key>
56 </schema>61 </schema>
5762
58 <schema path="/com/canonical/unity8/greeter/" id="com.canonical.Unity8.Greeter" gettext-domain="unity8">63 <schema path="/com/canonical/unity8/greeter/" id="com.canonical.Unity8.Greeter" gettext-domain="unity8">
5964
=== modified file 'debian/changelog'
--- debian/changelog 2017-01-10 14:48:43 +0000
+++ debian/changelog 2017-01-25 16:04:08 +0000
@@ -1,3 +1,54 @@
1unity8 (8.15+17.04.20170124-0ubuntu1) zesty; urgency=medium
2
3 [ Albert Astals Cid ]
4 * Limit tab-focus travelling on dialogs with a fence
5 * Restore focus to where it was when our ShellDialogs get unloaded
6 * Update current session after changing the user
7 * Add keyboard navigation for Indicators
8 * a window -> the current window
9 * There's no spreadDelegate_ anymore
10
11 [ Daniel d'Andrada ]
12 * Simplify DecoratedWindow
13 * Remove unnecessary warning message
14
15 [ Josh Arenson ]
16 * Add a test for the session chooser icon in the greeter's sessions
17 list
18
19 [ Lukáš Tinkl ]
20 * Fix keymap not being applied on the shell itself (LP: #1626435)
21 * Shell dialog improvements (kbd focus, mouse eater)
22 * Start searching directly as you type, w/o having to first
23 focus/click the search field.
24 * Add a test for the real implementation of WindowStateStorage
25 * Use a four finger gesture to open the drawer, much like in u7
26
27 [ Michael Terry ]
28 * Simplify the lightdm mock to make future greeter improvements easier
29 to test.
30 * Add support for guest sessions in unity8-greeter.
31 * Use a model for PAM prompts, supporting more possible interactions.
32 * Fix grouping of autopkg output and allow optionally passing
33 arguments to installed test scripts.
34 * Add support for LightDM hints for manual logins and hiding normal
35 users.
36
37 [ Michael Zanetti ]
38 * hint the launcher to indicate a successful size change to the user
39 (LP: #1646457)
40 * Improvements for the appdrawer (LP: #1648173)
41 * Adjust home key to still focus the dash instead of messing with the
42 drawer
43 * allow 4 finger simulation with mousetouchadaptor
44
45 [ Nick Dedekind ]
46 * Added Alt+F10 shortcut to open app menus. (LP: #1656896)
47 * Fixed menu layout width calculations. (LP: #1657050)
48 * Skip Panel::test_drag_indicator_item_down_shows_menu
49
50 -- Michał Sawicz <michal.sawicz@canonical.com> Tue, 24 Jan 2017 07:46:58 +0000
51
1unity8 (8.15+17.04.20170110.4-0ubuntu1) zesty; urgency=medium52unity8 (8.15+17.04.20170110.4-0ubuntu1) zesty; urgency=medium
253
3 [ Albert Astals Cid ]54 [ Albert Astals Cid ]
455
=== modified file 'debian/control'
--- debian/control 2017-01-10 14:46:09 +0000
+++ debian/control 2017-01-25 16:04:08 +0000
@@ -38,7 +38,7 @@
38 libubuntugestures5-private-dev (>= 1.3.2030),38 libubuntugestures5-private-dev (>= 1.3.2030),
39 libudev-dev,39 libudev-dev,
40 libudm-common-dev,40 libudm-common-dev,
41 libunity-api-dev (>= 8.0),41 libunity-api-dev (>= 8.1),
42 libusermetricsoutput1-dev,42 libusermetricsoutput1-dev,
43# Need those X11 libs touch emulation from mouse events in manual QML tests on a X11 desktop43# Need those X11 libs touch emulation from mouse events in manual QML tests on a X11 desktop
44 libx11-dev[!arm64 !armhf],44 libx11-dev[!arm64 !armhf],
4545
=== modified file 'debian/rules'
--- debian/rules 2017-01-03 14:02:23 +0000
+++ debian/rules 2017-01-25 16:04:08 +0000
@@ -40,4 +40,4 @@
40override_dh_shlibdeps:40override_dh_shlibdeps:
41 # Some mock libraries link against liblightdm-qt5-3.so which we want to41 # 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 # avoid, since we only really link against our mock one, not the system one.
43 dh_shlibdeps -XlibMockLightDM-qml.so -XlibMockAccountsService-qml.so -Lunity8-private43 dh_shlibdeps -XlibMockAccountsService-qml.so -Lunity8-private
4444
=== modified file 'plugins/LightDM/CMakeLists.txt'
--- plugins/LightDM/CMakeLists.txt 2016-03-02 02:54:30 +0000
+++ plugins/LightDM/CMakeLists.txt 2017-01-25 16:04:08 +0000
@@ -7,7 +7,6 @@
7 ${CMAKE_CURRENT_SOURCE_DIR}7 ${CMAKE_CURRENT_SOURCE_DIR}
8 ${CMAKE_CURRENT_BINARY_DIR}8 ${CMAKE_CURRENT_BINARY_DIR}
9 ${CMAKE_SOURCE_DIR}/plugins/Utils9 ${CMAKE_SOURCE_DIR}/plugins/Utils
10 ${CMAKE_BINARY_DIR}/tests/mocks/LightDM/IntegratedLightDM
11 ${libunity8-private_SOURCE_DIR}10 ${libunity8-private_SOURCE_DIR}
12 ${LIBUSERMETRICSOUTPUT_INCLUDE_DIRS}11 ${LIBUSERMETRICSOUTPUT_INCLUDE_DIRS}
13)12)
@@ -18,6 +17,7 @@
18 DBusGreeterList.cpp17 DBusGreeterList.cpp
19 Greeter.cpp18 Greeter.cpp
20 plugin.cpp19 plugin.cpp
20 PromptsModel.cpp
21 SessionsModel.cpp21 SessionsModel.cpp
22 UsersModel.cpp22 UsersModel.cpp
23 )23 )
2424
=== modified file 'plugins/LightDM/DBusGreeterList.cpp'
--- plugins/LightDM/DBusGreeterList.cpp 2015-10-01 23:54:24 +0000
+++ plugins/LightDM/DBusGreeterList.cpp 2017-01-25 16:04:08 +0000
@@ -25,7 +25,7 @@
25 m_greeter(greeter)25 m_greeter(greeter)
26{26{
27 connect(m_greeter, &Greeter::authenticationUserChanged, this, &DBusGreeterList::authenticationUserChangedHandler);27 connect(m_greeter, &Greeter::authenticationUserChanged, this, &DBusGreeterList::authenticationUserChangedHandler);
28 connect(m_greeter, &Greeter::promptlessChanged, this, &DBusGreeterList::promptlessChangedHandler);28 connect(m_greeter, &Greeter::isAuthenticatedChanged, this, &DBusGreeterList::authenticatedChangedHandler);
29}29}
3030
31QString DBusGreeterList::GetActiveEntry() const31QString DBusGreeterList::GetActiveEntry() const
@@ -40,16 +40,16 @@
4040
41bool DBusGreeterList::entryIsLocked() const41bool DBusGreeterList::entryIsLocked() const
42{42{
43 return !m_greeter->promptless();43 return !m_greeter->isAuthenticated();
44}44}
4545
46void DBusGreeterList::authenticationUserChangedHandler(const QString &user)46void DBusGreeterList::authenticationUserChangedHandler()
47{47{
48 notifyPropertyChanged(QStringLiteral("ActiveEntry"), user);48 notifyPropertyChanged(QStringLiteral("ActiveEntry"), m_greeter->authenticationUser());
49 Q_EMIT EntrySelected(user);49 Q_EMIT EntrySelected(m_greeter->authenticationUser());
50}50}
5151
52void DBusGreeterList::promptlessChangedHandler()52void DBusGreeterList::authenticatedChangedHandler()
53{53{
54 notifyPropertyChanged(QStringLiteral("EntryIsLocked"), entryIsLocked());54 notifyPropertyChanged(QStringLiteral("EntryIsLocked"), entryIsLocked());
55 Q_EMIT entryIsLockedChanged();55 Q_EMIT entryIsLockedChanged();
5656
=== modified file 'plugins/LightDM/DBusGreeterList.h'
--- plugins/LightDM/DBusGreeterList.h 2015-09-11 13:38:45 +0000
+++ plugins/LightDM/DBusGreeterList.h 2017-01-25 16:04:08 +0000
@@ -47,8 +47,8 @@
47 void entryIsLockedChanged();47 void entryIsLockedChanged();
4848
49private Q_SLOTS:49private Q_SLOTS:
50 void authenticationUserChangedHandler(const QString &user);50 void authenticationUserChangedHandler();
51 void promptlessChangedHandler();51 void authenticatedChangedHandler();
5252
53private:53private:
54 Greeter *m_greeter;54 Greeter *m_greeter;
5555
=== modified file 'plugins/LightDM/Greeter.cpp'
--- plugins/LightDM/Greeter.cpp 2016-07-28 15:34:29 +0000
+++ plugins/LightDM/Greeter.cpp 2017-01-25 16:04:08 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2013, 2015 Canonical, Ltd.2 * Copyright (C) 2013-2017 Canonical, Ltd.
3 *3 *
4 * This program is free software; you can redistribute it and/or modify4 * 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 by5 * it under the terms of the GNU General Public License as published by
@@ -17,13 +17,16 @@
1717
18#include "Greeter.h"18#include "Greeter.h"
19#include "GreeterPrivate.h"19#include "GreeterPrivate.h"
20#include <QCoreApplication>
20#include <libintl.h>21#include <libintl.h>
2122
23static Greeter *singleton = nullptr;
24
22GreeterPrivate::GreeterPrivate(Greeter* parent)25GreeterPrivate::GreeterPrivate(Greeter* parent)
23 : m_greeter(new QLightDM::Greeter(parent)),26 : m_greeter(new QLightDM::Greeter(parent)),
24 m_active(false),27 m_active(false),
25 wasPrompted(false),28 responded(false),
26 promptless(false),29 everResponded(false),
27 q_ptr(parent)30 q_ptr(parent)
28{31{
29}32}
@@ -41,9 +44,32 @@
41 connect(d->m_greeter, &QLightDM::Greeter::authenticationComplete,44 connect(d->m_greeter, &QLightDM::Greeter::authenticationComplete,
42 this, &Greeter::authenticationCompleteFilter);45 this, &Greeter::authenticationCompleteFilter);
4346
47 // Don't get stuck waiting for PAM as we shut down.
48 connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit,
49 d->m_greeter, &QLightDM::Greeter::cancelAuthentication);
50
44 d->m_greeter->connectSync();51 d->m_greeter->connectSync();
45}52}
4653
54Greeter::~Greeter()
55{
56 singleton = nullptr;
57}
58
59Greeter *Greeter::instance()
60{
61 if (!singleton) {
62 singleton = new Greeter;
63 }
64 return singleton;
65}
66
67PromptsModel *Greeter::promptsModel()
68{
69 Q_D(Greeter);
70 return &d->prompts;
71}
72
47bool Greeter::isActive() const73bool Greeter::isActive() const
48{74{
49 Q_D(const Greeter);75 Q_D(const Greeter);
@@ -68,7 +94,16 @@
68QString Greeter::authenticationUser() const94QString Greeter::authenticationUser() const
69{95{
70 Q_D(const Greeter);96 Q_D(const Greeter);
71 return d->m_greeter->authenticationUser();97 return d->cachedAuthUser;
98}
99
100void Greeter::checkAuthenticationUser()
101{
102 Q_D(Greeter);
103 if (d->cachedAuthUser != d->m_greeter->authenticationUser()) {
104 d->cachedAuthUser = d->m_greeter->authenticationUser();
105 Q_EMIT authenticationUserChanged();
106 }
72}107}
73108
74QString Greeter::defaultSessionHint() const109QString Greeter::defaultSessionHint() const
@@ -77,35 +112,64 @@
77 return d->m_greeter->defaultSessionHint();112 return d->m_greeter->defaultSessionHint();
78}113}
79114
80bool Greeter::promptless() const
81{
82 Q_D(const Greeter);
83 return d->promptless;
84}
85
86QString Greeter::selectUser() const115QString Greeter::selectUser() const
87{116{
88 Q_D(const Greeter);117 Q_D(const Greeter);
89 return d->m_greeter->selectUserHint();118 if (hasGuestAccount() && d->m_greeter->selectGuestHint()) {
119 return QStringLiteral("*guest");
120 } else {
121 return d->m_greeter->selectUserHint();
122 }
123}
124
125bool Greeter::hasGuestAccount() const
126{
127 Q_D(const Greeter);
128 return d->m_greeter->hasGuestAccountHint();
129}
130
131bool Greeter::showManualLoginHint() const
132{
133 Q_D(const Greeter);
134 return d->m_greeter->showManualLoginHint();
135}
136
137bool Greeter::hideUsersHint() const
138{
139 Q_D(const Greeter);
140 return d->m_greeter->hideUsersHint();
90}141}
91142
92void Greeter::authenticate(const QString &username)143void Greeter::authenticate(const QString &username)
93{144{
94 Q_D(Greeter);145 Q_D(Greeter);
95 d->wasPrompted = false;146 d->prompts.clear();
96 if (d->promptless) {147 d->responded = false;
97 d->promptless = false;148 d->everResponded = false;
98 Q_EMIT promptlessChanged();149
99 }150 if (authenticationUser() == username) {
100151 d->prompts = d->leftovers;
101 d->m_greeter->authenticate(username);152 }
153 d->leftovers.clear();
154
155 if (username == QStringLiteral("*guest")) {
156 d->m_greeter->authenticateAsGuest();
157 } else if (username == QStringLiteral("*other")) {
158 d->m_greeter->authenticate(nullptr);
159 } else {
160 d->m_greeter->authenticate(username);
161 }
162
163 Q_EMIT authenticationStarted();
102 Q_EMIT isAuthenticatedChanged();164 Q_EMIT isAuthenticatedChanged();
103 Q_EMIT authenticationUserChanged(username);165 checkAuthenticationUser();
104}166}
105167
106void Greeter::respond(const QString &response)168void Greeter::respond(const QString &response)
107{169{
108 Q_D(Greeter);170 Q_D(Greeter);
171 d->responded = true;
172 d->everResponded = true;
109 d->m_greeter->respond(response);173 d->m_greeter->respond(response);
110}174}
111175
@@ -118,32 +182,82 @@
118void Greeter::showPromptFilter(const QString &text, QLightDM::Greeter::PromptType type)182void Greeter::showPromptFilter(const QString &text, QLightDM::Greeter::PromptType type)
119{183{
120 Q_D(Greeter);184 Q_D(Greeter);
121 d->wasPrompted = true;185
186 checkAuthenticationUser(); // may have changed in liblightdm
122187
123 bool isDefaultPrompt = (text == dgettext("Linux-PAM", "Password: "));188 bool isDefaultPrompt = (text == dgettext("Linux-PAM", "Password: "));
189 bool isSecret = type == QLightDM::Greeter::PromptTypeSecret;
190
191 QString trimmedText;
192 if (!isDefaultPrompt)
193 trimmedText = text.trimmed();
124194
125 // Strip prompt of any colons at the end195 // Strip prompt of any colons at the end
126 QString trimmedText = text.trimmed();
127 if (trimmedText.endsWith(':') || trimmedText.endsWith(QStringLiteral(":"))) {196 if (trimmedText.endsWith(':') || trimmedText.endsWith(QStringLiteral(":"))) {
128 trimmedText.chop(1);197 trimmedText.chop(1);
129 }198 }
130199
131 Q_EMIT showPrompt(trimmedText, type == QLightDM::Greeter::PromptTypeSecret, isDefaultPrompt);200 if (trimmedText == "login") {
201 // 'login' is provided untranslated by LightDM when asking for a manual
202 // login username.
203 trimmedText = gettext("Username");
204 }
205
206 if (d->responded) {
207 d->prompts.clear();
208 d->responded = false;
209 }
210
211 d->prompts.append(trimmedText, isSecret ? PromptsModel::Secret : PromptsModel::Question);
132}212}
133213
134void Greeter::showMessageFilter(const QString &text, QLightDM::Greeter::MessageType type)214void Greeter::showMessageFilter(const QString &text, QLightDM::Greeter::MessageType type)
135{215{
136 Q_EMIT showMessage(text, type == QLightDM::Greeter::MessageTypeError);216 Q_D(Greeter);
217
218 checkAuthenticationUser(); // may have changed in liblightdm
219
220 bool isError = type == QLightDM::Greeter::MessageTypeError;
221
222 if (d->responded) {
223 d->prompts.clear();
224 d->responded = false;
225 }
226 d->prompts.append(text, isError? PromptsModel::Error : PromptsModel::Message);
137}227}
138228
139void Greeter::authenticationCompleteFilter()229void Greeter::authenticationCompleteFilter()
140{230{
141 Q_D(Greeter);231 Q_D(Greeter);
142 if (!d->wasPrompted) {
143 d->promptless = true;
144 Q_EMIT promptlessChanged();
145 }
146232
147 Q_EMIT isAuthenticatedChanged();233 Q_EMIT isAuthenticatedChanged();
148 Q_EMIT authenticationComplete();234
235 bool automatic = !d->everResponded;
236 bool pamHasLeftoverMessages = !d->prompts.hasPrompt() && d->prompts.rowCount() > 0;
237
238 if (!isAuthenticated()) {
239 if (pamHasLeftoverMessages) {
240 d->leftovers = d->prompts; // Prefer PAM's messages
241 } else if (automatic) {
242 d->leftovers.append(gettext("Failed to authenticate"), PromptsModel::Error);
243 } else {
244 d->leftovers.append(gettext("Invalid password, please try again"), PromptsModel::Error);
245 }
246 } else if (pamHasLeftoverMessages) {
247 automatic = true; // treat this successful login as automatic, so user sees message
248 d->leftovers = d->prompts;
249 }
250
251 if (automatic) {
252 d->prompts = d->leftovers; // OK, we'll just use these now
253 d->leftovers.clear();
254 d->prompts.append(isAuthenticated() ? gettext("Log In") : gettext("Retry"),
255 PromptsModel::Button);
256 }
257
258 if (isAuthenticated()) {
259 Q_EMIT loginSuccess(automatic);
260 } else {
261 Q_EMIT loginError(automatic);
262 }
149}263}
150264
=== modified file 'plugins/LightDM/Greeter.h'
--- plugins/LightDM/Greeter.h 2016-07-28 15:34:29 +0000
+++ plugins/LightDM/Greeter.h 2017-01-25 16:04:08 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2012,2013,2015 Canonical, Ltd.2 * Copyright (C) 2012-2016 Canonical, Ltd.
3 *3 *
4 * This program is free software; you can redistribute it and/or modify4 * 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 by5 * it under the terms of the GNU General Public License as published by
@@ -21,13 +21,13 @@
21 such edits in the future, and by inserting ourselves here, we have more21 such edits in the future, and by inserting ourselves here, we have more
22 control. */22 control. */
2323
24#ifndef UNITY_GREETER_H24#pragma once
25#define UNITY_GREETER_H
2625
27#include <QLightDM/Greeter>26#include <QLightDM/Greeter>
28#include <QtCore/QObject>27#include <QtCore/QObject>
2928
30class GreeterPrivate;29class GreeterPrivate;
30class PromptsModel;
3131
32class Greeter : public QObject32class Greeter : public QObject
33{33{
@@ -37,18 +37,22 @@
37 Q_PROPERTY(bool authenticated READ isAuthenticated NOTIFY isAuthenticatedChanged)37 Q_PROPERTY(bool authenticated READ isAuthenticated NOTIFY isAuthenticatedChanged)
38 Q_PROPERTY(QString authenticationUser READ authenticationUser NOTIFY authenticationUserChanged)38 Q_PROPERTY(QString authenticationUser READ authenticationUser NOTIFY authenticationUserChanged)
39 Q_PROPERTY(QString defaultSession READ defaultSessionHint CONSTANT)39 Q_PROPERTY(QString defaultSession READ defaultSessionHint CONSTANT)
40 Q_PROPERTY(bool promptless READ promptless NOTIFY promptlessChanged)
41 Q_PROPERTY(QString selectUser READ selectUser CONSTANT)40 Q_PROPERTY(QString selectUser READ selectUser CONSTANT)
4241
43public:42public:
44 explicit Greeter(QObject* parent=0);43 static Greeter *instance();
44 virtual ~Greeter();
4545
46 bool isActive() const;46 bool isActive() const;
47 bool isAuthenticated() const;47 bool isAuthenticated() const;
48 QString authenticationUser() const;48 QString authenticationUser() const;
49 QString defaultSessionHint() const;49 QString defaultSessionHint() const;
50 bool promptless() const;
51 QString selectUser() const;50 QString selectUser() const;
51 bool hasGuestAccount() const;
52 bool showManualLoginHint() const;
53 bool hideUsersHint() const;
54
55 PromptsModel *promptsModel();
5256
53public Q_SLOTS:57public Q_SLOTS:
54 void authenticate(const QString &username=QString());58 void authenticate(const QString &username=QString());
@@ -57,21 +61,22 @@
57 void setIsActive(bool isActive);61 void setIsActive(bool isActive);
5862
59Q_SIGNALS:63Q_SIGNALS:
60 void showMessage(const QString &text, bool isError);64 void authenticationUserChanged();
61 void showPrompt(const QString &text, bool isSecret, bool isDefaultPrompt);
62 void authenticationComplete();
63 void authenticationUserChanged(const QString &user);
64 void isActiveChanged();65 void isActiveChanged();
65 void isAuthenticatedChanged();66 void isAuthenticatedChanged();
66 void promptlessChanged();
67 void showGreeter();67 void showGreeter();
68 void hideGreeter();68 void hideGreeter();
69 void loginError(bool automatic);
70 void loginSuccess(bool automatic);
71 void authenticationStarted(); // useful for testing
6972
70 // This signal is emitted by external agents like indicators, and the UI73 // This signal is emitted by external agents like indicators, and the UI
71 // should switch to this user if possible.74 // should switch to this user if possible.
72 void requestAuthenticationUser(const QString &user);75 void requestAuthenticationUser(const QString &user);
7376
74protected:77protected:
78 explicit Greeter(QObject* parent=0);
79
75 GreeterPrivate * const d_ptr;80 GreeterPrivate * const d_ptr;
7681
77 Q_DECLARE_PRIVATE(Greeter)82 Q_DECLARE_PRIVATE(Greeter)
@@ -80,6 +85,5 @@
80 void showMessageFilter(const QString &text, QLightDM::Greeter::MessageType type);85 void showMessageFilter(const QString &text, QLightDM::Greeter::MessageType type);
81 void showPromptFilter(const QString &text, QLightDM::Greeter::PromptType type);86 void showPromptFilter(const QString &text, QLightDM::Greeter::PromptType type);
82 void authenticationCompleteFilter();87 void authenticationCompleteFilter();
88 void checkAuthenticationUser();
83};89};
84
85#endif
8690
=== modified file 'plugins/LightDM/GreeterPrivate.h'
--- plugins/LightDM/GreeterPrivate.h 2015-09-11 13:38:45 +0000
+++ plugins/LightDM/GreeterPrivate.h 2017-01-25 16:04:08 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2014 Canonical, Ltd.2 * Copyright (C) 2015-2017 Canonical, Ltd.
3 *3 *
4 * This program is free software; you can redistribute it and/or modify4 * 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 by5 * it under the terms of the GNU General Public License as published by
@@ -14,10 +14,14 @@
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 */
1616
17#ifndef UNITY_GREETER_PRIVATE_H17#pragma once
18#define UNITY_GREETER_PRIVATE_H18
1919#include "PromptsModel.h"
20#include <QLightDM/Greeter>20#include <QObject>
21
22namespace QLightDM {
23 class Greeter;
24}
2125
22class GreeterPrivate26class GreeterPrivate
23{27{
@@ -26,8 +30,11 @@
2630
27 QLightDM::Greeter *m_greeter;31 QLightDM::Greeter *m_greeter;
28 bool m_active;32 bool m_active;
29 bool wasPrompted;33 PromptsModel prompts;
30 bool promptless;34 PromptsModel leftovers; // prompts to show during next auth for same user
35 bool responded;
36 bool everResponded;
37 QString cachedAuthUser;
3138
32protected:39protected:
33 Greeter * const q_ptr;40 Greeter * const q_ptr;
@@ -35,5 +42,3 @@
35private:42private:
36 Q_DECLARE_PUBLIC(Greeter)43 Q_DECLARE_PUBLIC(Greeter)
37};44};
38
39#endif // UNITY_GREETER_PRIVATE_H
4045
=== modified file 'plugins/LightDM/IntegratedLightDM/liblightdm/Greeter.cpp'
--- plugins/LightDM/IntegratedLightDM/liblightdm/Greeter.cpp 2016-07-28 15:34:29 +0000
+++ plugins/LightDM/IntegratedLightDM/liblightdm/Greeter.cpp 2017-01-25 16:04:08 +0000
@@ -49,7 +49,7 @@
4949
50bool Greeter::hasGuestAccountHint() const50bool Greeter::hasGuestAccountHint() const
51{51{
52 return true;52 return false;
53}53}
5454
55QString Greeter::getHint(const QString &name) const55QString Greeter::getHint(const QString &name) const
@@ -70,12 +70,12 @@
7070
71bool Greeter::showManualLoginHint() const71bool Greeter::showManualLoginHint() const
72{72{
73 return true;73 return false;
74}74}
7575
76bool Greeter::showRemoteLoginHint() const76bool Greeter::showRemoteLoginHint() const
77{77{
78 return true;78 return false;
79}79}
8080
81QString Greeter::selectUserHint() const81QString Greeter::selectUserHint() const
@@ -146,7 +146,10 @@
146}146}
147147
148void Greeter::cancelAuthentication()148void Greeter::cancelAuthentication()
149{}149{
150 Q_D(Greeter);
151 d->cancelAuthentication();
152}
150153
151void Greeter::setLanguage (const QString &language)154void Greeter::setLanguage (const QString &language)
152{155{
153156
=== modified file 'plugins/LightDM/IntegratedLightDM/liblightdm/GreeterPrivate.cpp'
--- plugins/LightDM/IntegratedLightDM/liblightdm/GreeterPrivate.cpp 2015-11-02 17:22:30 +0000
+++ plugins/LightDM/IntegratedLightDM/liblightdm/GreeterPrivate.cpp 2017-01-25 16:04:08 +0000
@@ -206,6 +206,28 @@
206 }206 }
207 }207 }
208208
209 void cancelPam()
210 {
211 if (pamHandle != nullptr) {
212 QFuture<int> pamFuture = futureWatcher.future();
213 pam_handle *handle = pamHandle;
214 pamHandle = nullptr; // to disable normal finishPam() handling
215 pamFuture.cancel();
216
217 // Note the empty loop, we just want to clear the futures queue.
218 // Any further prompts from the pam thread will be immediately
219 // responded to/dismissed in handlePrompt().
220 while (respond(QString()));
221
222 // Now let signal/slot handling happen so the thread can finish
223 while (!pamFuture.isFinished()) {
224 QCoreApplication::processEvents();
225 }
226
227 pam_end(handle, PAM_CONV_ERR);
228 }
229 }
230
209Q_SIGNALS:231Q_SIGNALS:
210 void showMessage(pam_handle *handle, QString text, QLightDM::Greeter::MessageType type);232 void showMessage(pam_handle *handle, QString text, QLightDM::Greeter::MessageType type);
211 void showPrompt(pam_handle *handle, QString text, QLightDM::Greeter::PromptType type, QLightDM::GreeterImpl::ResponseFuture response);233 void showPrompt(pam_handle *handle, QString text, QLightDM::Greeter::PromptType type, QLightDM::GreeterImpl::ResponseFuture response);
@@ -247,28 +269,6 @@
247 }269 }
248270
249private:271private:
250 void cancelPam()
251 {
252 if (pamHandle != nullptr) {
253 QFuture<int> pamFuture = futureWatcher.future();
254 pam_handle *handle = pamHandle;
255 pamHandle = nullptr; // to disable normal finishPam() handling
256 pamFuture.cancel();
257
258 // Note the empty loop, we just want to clear the futures queue.
259 // Any further prompts from the pam thread will be immediately
260 // responded to/dismissed in handlePrompt().
261 while (respond(QString()));
262
263 // Now let signal/slot handling happen so the thread can finish
264 while (!pamFuture.isFinished()) {
265 QCoreApplication::processEvents();
266 }
267
268 pam_end(handle, PAM_CONV_ERR);
269 }
270 }
271
272 Greeter *greeter;272 Greeter *greeter;
273 GreeterPrivate *greeterPrivate;273 GreeterPrivate *greeterPrivate;
274 pam_handle* pamHandle;274 pam_handle* pamHandle;
@@ -299,6 +299,11 @@
299 m_impl->respond(response);299 m_impl->respond(response);
300}300}
301301
302void GreeterPrivate::cancelAuthentication()
303{
304 m_impl->cancelPam();
305}
306
302}307}
303308
304#include "GreeterPrivate.moc"309#include "GreeterPrivate.moc"
305310
=== modified file 'plugins/LightDM/IntegratedLightDM/liblightdm/GreeterPrivate.h'
--- plugins/LightDM/IntegratedLightDM/liblightdm/GreeterPrivate.h 2015-09-11 13:38:45 +0000
+++ plugins/LightDM/IntegratedLightDM/liblightdm/GreeterPrivate.h 2017-01-25 16:04:08 +0000
@@ -38,6 +38,7 @@
3838
39 void handleAuthenticate();39 void handleAuthenticate();
40 void handleRespond(const QString &response);40 void handleRespond(const QString &response);
41 void cancelAuthentication();
4142
42protected:43protected:
43 GreeterImpl *m_impl; // if the backend needs more private data44 GreeterImpl *m_impl; // if the backend needs more private data
4445
=== added file 'plugins/LightDM/PromptsModel.cpp'
--- plugins/LightDM/PromptsModel.cpp 1970-01-01 00:00:00 +0000
+++ plugins/LightDM/PromptsModel.cpp 2017-01-25 16:04:08 +0000
@@ -0,0 +1,97 @@
1/*
2 * Copyright (C) 2017 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 */
17
18#include "PromptsModel.h"
19
20PromptsModel::PromptsModel(QObject* parent)
21 : QAbstractListModel(parent)
22{
23 m_roleNames[TypeRole] = "type";
24 m_roleNames[TextRole] = "text";
25}
26
27PromptsModel& PromptsModel::operator=(const PromptsModel &other)
28{
29 beginResetModel();
30 m_prompts = other.m_prompts;
31 endResetModel();
32 Q_EMIT countChanged();
33 return *this;
34}
35
36int PromptsModel::rowCount(const QModelIndex &parent) const
37{
38 if (parent.isValid())
39 return 0;
40
41 return m_prompts.size();
42}
43
44QVariant PromptsModel::data(const QModelIndex &index, int role) const
45{
46 if (!index.isValid() || index.column() > 0 || index.row() >= m_prompts.size())
47 return QVariant();
48
49 switch (role) {
50 case Qt::DisplayRole: // fallthrough
51 case TextRole: return m_prompts[index.row()].prompt;
52 case TypeRole: return m_prompts[index.row()].type;
53 default: return QVariant();
54 }
55}
56
57QHash<int, QByteArray> PromptsModel::roleNames() const
58{
59 return m_roleNames;
60}
61
62void PromptsModel::prepend(const QString &text, PromptType type)
63{
64 beginInsertRows(QModelIndex(), 0, 0);
65 m_prompts.prepend(PromptInfo{text, type});
66 endInsertRows();
67
68 Q_EMIT countChanged();
69}
70
71void PromptsModel::append(const QString &text, PromptType type)
72{
73 beginInsertRows(QModelIndex(), m_prompts.size(), m_prompts.size());
74 m_prompts.append(PromptInfo{text, type});
75 endInsertRows();
76
77 Q_EMIT countChanged();
78}
79
80void PromptsModel::clear()
81{
82 beginResetModel();
83 m_prompts.clear();
84 endResetModel();
85
86 Q_EMIT countChanged();
87}
88
89bool PromptsModel::hasPrompt() const
90{
91 Q_FOREACH(const PromptInfo &info, m_prompts) {
92 if (info.type == PromptType::Secret || info.type == PromptType::Question) {
93 return true;
94 }
95 }
96 return false;
97}
098
=== added file 'plugins/LightDM/PromptsModel.h'
--- plugins/LightDM/PromptsModel.h 1970-01-01 00:00:00 +0000
+++ plugins/LightDM/PromptsModel.h 2017-01-25 16:04:08 +0000
@@ -0,0 +1,70 @@
1/*
2 * Copyright (C) 2017 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 */
17
18#pragma once
19
20#include <QAbstractListModel>
21
22class PromptsModel : public QAbstractListModel
23{
24 Q_OBJECT
25
26 Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
27
28public:
29 enum PromptsModelRoles {
30 TypeRole = Qt::UserRole,
31 TextRole,
32 };
33 Q_ENUM(PromptsModelRoles)
34
35 enum PromptType {
36 Message,
37 Error,
38 Secret,
39 Question,
40 Button,
41 };
42 Q_ENUM(PromptType)
43
44 explicit PromptsModel(QObject* parent=0);
45
46 PromptsModel& operator=(const PromptsModel &other);
47
48 int rowCount(const QModelIndex &parent = QModelIndex()) const override;
49 QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
50 QHash<int, QByteArray> roleNames() const override;
51
52 Q_INVOKABLE void prepend(const QString &text, PromptType type);
53 Q_INVOKABLE void append(const QString &text, PromptType type);
54
55 void clear();
56
57 bool hasPrompt() const;
58
59Q_SIGNALS:
60 void countChanged();
61
62private:
63 struct PromptInfo {
64 QString prompt;
65 PromptType type;
66 };
67
68 QList<PromptInfo> m_prompts;
69 QHash<int, QByteArray> m_roleNames;
70};
071
=== modified file 'plugins/LightDM/SessionsModel.cpp'
--- plugins/LightDM/SessionsModel.cpp 2016-10-07 17:24:55 +0000
+++ plugins/LightDM/SessionsModel.cpp 2017-01-25 16:04:08 +0000
@@ -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 modify4 * 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 by5 * it under the terms of the GNU General Public License as published by
@@ -128,7 +128,6 @@
128 }128 }
129129
130 setModel(m_model);130 setModel(m_model);
131 setSourceModel(m_model);
132 setSortCaseSensitivity(Qt::CaseInsensitive);131 setSortCaseSensitivity(Qt::CaseInsensitive);
133 setSortLocaleAware(true);132 setSortLocaleAware(true);
134 setSortRole(Qt::DisplayRole);133 setSortRole(Qt::DisplayRole);
135134
=== modified file 'plugins/LightDM/SessionsModel.h'
--- plugins/LightDM/SessionsModel.h 2016-12-23 11:04:53 +0000
+++ plugins/LightDM/SessionsModel.h 2017-01-25 16:04:08 +0000
@@ -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 modify4 * 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 by5 * it under the terms of the GNU General Public License as published by
@@ -15,8 +15,7 @@
15 *15 *
16 */16 */
1717
18#ifndef UNITY_SESSIONSMODEL_H18#pragma once
19#define UNITY_SESSIONSMODEL_H
2019
21#include <unitysortfilterproxymodelqml.h>20#include <unitysortfilterproxymodelqml.h>
22#include <QLightDM/SessionsModel>21#include <QLightDM/SessionsModel>
@@ -30,6 +29,7 @@
3029
31 Q_PROPERTY(QList<QUrl> iconSearchDirectories READ iconSearchDirectories30 Q_PROPERTY(QList<QUrl> iconSearchDirectories READ iconSearchDirectories
32 WRITE setIconSearchDirectories NOTIFY iconSearchDirectoriesChanged)31 WRITE setIconSearchDirectories NOTIFY iconSearchDirectoriesChanged)
32
33Q_SIGNALS:33Q_SIGNALS:
34 void iconSearchDirectoriesChanged();34 void iconSearchDirectoriesChanged();
3535
@@ -54,6 +54,7 @@
54 Q_INVOKABLE QUrl iconUrl(const QString sessionName) const;54 Q_INVOKABLE QUrl iconUrl(const QString sessionName) const;
5555
56 void setIconSearchDirectories(const QList<QUrl> searchDirectories);56 void setIconSearchDirectories(const QList<QUrl> searchDirectories);
57
57private:58private:
58 QLightDM::SessionsModel* m_model;59 QLightDM::SessionsModel* m_model;
59 QHash<int, QByteArray> m_roleNames;60 QHash<int, QByteArray> m_roleNames;
@@ -63,5 +64,3 @@
63 QUrl("/usr/share/unity-greeter/")};64 QUrl("/usr/share/unity-greeter/")};
6465
65};66};
66
67#endif // UNITY_SESSIONSMODEL_H
6867
=== modified file 'plugins/LightDM/UsersModel.cpp'
--- plugins/LightDM/UsersModel.cpp 2015-10-26 20:15:08 +0000
+++ plugins/LightDM/UsersModel.cpp 2017-01-25 16:04:08 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2013 Canonical, Ltd.2 * Copyright (C) 2013,2015-2016 Canonical, Ltd.
3 *3 *
4 * This program is free software; you can redistribute it and/or modify4 * 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 by5 * it under the terms of the GNU General Public License as published by
@@ -12,20 +12,22 @@
12 *12 *
13 * You should have received a copy of the GNU General Public License13 * 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 */
1816
17#include "Greeter.h"
19#include "UsersModel.h"18#include "UsersModel.h"
19#include <QIdentityProxyModel>
20#include <QLightDM/UsersModel>20#include <QLightDM/UsersModel>
21#include <QtCore/QSortFilterProxyModel>21
22#include <libintl.h>
2223
23// First, we define an internal class that wraps LightDM's UsersModel. This24// First, we define an internal class that wraps LightDM's UsersModel. This
24// class will modify some of the data coming from LightDM. For example, we25// class will modify some of the data coming from LightDM. For example, we
25// modify any empty Real Names into just normal Names.26// modify any empty Real Names into just normal Names. We also add optional
27// rows, depending on configuration.
26// (We can't modify the data directly in UsersModel below because it won't sort28// (We can't modify the data directly in UsersModel below because it won't sort
27// using the modified data.)29// using the modified data.)
28class MangleModel : public QSortFilterProxyModel30class MangleModel : public QIdentityProxyModel
29{31{
30 Q_OBJECT32 Q_OBJECT
3133
@@ -33,21 +35,75 @@
33 explicit MangleModel(QObject* parent=0);35 explicit MangleModel(QObject* parent=0);
3436
35 QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;37 QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
38 int rowCount(const QModelIndex &parent = QModelIndex()) const override;
39 QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
40
41private:
42 struct CustomRow {
43 QString name;
44 QString realName;
45 };
46
47 int sourceRowCount() const;
48
49 void updateGuestRow();
50 void updateManualRow();
51 void updateCustomRows();
52
53 void addCustomRow(const CustomRow &newRow);
54 void removeCustomRow(const QString &rowName);
55
56 QList<CustomRow> m_customRows;
57 bool m_updatingCustomRows;
36};58};
3759
38MangleModel::MangleModel(QObject* parent)60MangleModel::MangleModel(QObject* parent)
39 : QSortFilterProxyModel(parent)61 : QIdentityProxyModel(parent)
62 , m_updatingCustomRows(false)
40{63{
41 setSourceModel(new QLightDM::UsersModel(this));64 setSourceModel(new QLightDM::UsersModel(this));
65
66 updateCustomRows();
67
68 // Would be nice if there were a rowCountChanged signal in the base class.
69 // We redo all custom rows on any row count change, because (A) some of
70 // custom rows (manual login) use row count information and (B) when
71 // testing, we use a modelReset signal as a way to indicate that a custom
72 // row has been toggled off or on.
73 connect(this, &QIdentityProxyModel::modelReset,
74 this, &MangleModel::updateCustomRows);
75 connect(this, &QIdentityProxyModel::rowsInserted,
76 this, &MangleModel::updateCustomRows);
77 connect(this, &QIdentityProxyModel::rowsRemoved,
78 this, &MangleModel::updateCustomRows);
42}79}
4380
44QVariant MangleModel::data(const QModelIndex &index, int role) const81QVariant MangleModel::data(const QModelIndex &index, int role) const
45{82{
46 QVariant variantData = QSortFilterProxyModel::data(index, role);83 QVariant variantData;
84
85 if (index.row() >= rowCount())
86 return QVariant();
87
88 bool isCustomRow = index.row() >= sourceRowCount();
89 if (isCustomRow && index.column() == 0) {
90 int customIndex = index.row() - sourceRowCount();
91 if (role == QLightDM::UsersModel::NameRole) {
92 variantData = m_customRows[customIndex].name;
93 } else if (role == QLightDM::UsersModel::RealNameRole) {
94 variantData = m_customRows[customIndex].realName;
95 } else if (role == QLightDM::UsersModel::LoggedInRole) {
96 variantData = false;
97 } else if (role == QLightDM::UsersModel::SessionRole) {
98 variantData = Greeter::instance()->defaultSessionHint();
99 }
100 } else {
101 variantData = QIdentityProxyModel::data(index, role);
102 }
47103
48 // If user's real name is empty, switch to unix name104 // If user's real name is empty, switch to unix name
49 if (role == QLightDM::UsersModel::RealNameRole && variantData.toString().isEmpty()) {105 if (role == QLightDM::UsersModel::RealNameRole && variantData.toString().isEmpty()) {
50 variantData = QSortFilterProxyModel::data(index, QLightDM::UsersModel::NameRole);106 variantData = data(index, QLightDM::UsersModel::NameRole);
51 } else if (role == QLightDM::UsersModel::BackgroundPathRole && variantData.toString().startsWith('#')) {107 } else if (role == QLightDM::UsersModel::BackgroundPathRole && variantData.toString().startsWith('#')) {
52 const QString stringData = "data:image/svg+xml,<svg><rect width='100%' height='100%' fill='" + variantData.toString() + "'/></svg>";108 const QString stringData = "data:image/svg+xml,<svg><rect width='100%' height='100%' fill='" + variantData.toString() + "'/></svg>";
53 variantData = stringData;109 variantData = stringData;
@@ -56,6 +112,95 @@
56 return variantData;112 return variantData;
57}113}
58114
115void MangleModel::addCustomRow(const CustomRow &newRow)
116{
117 for (int i = 0; i < m_customRows.size(); i++) {
118 if (m_customRows[i].name == newRow.name) {
119 return; // we don't have custom rows that change content yet
120 }
121 }
122
123 beginInsertRows(QModelIndex(), rowCount(), rowCount());
124 m_customRows << newRow;
125 endInsertRows();
126}
127
128void MangleModel::removeCustomRow(const QString &rowName)
129{
130 for (int i = 0; i < m_customRows.size(); i++) {
131 if (m_customRows[i].name == rowName) {
132 int rowNum = sourceRowCount() + i;
133 beginRemoveRows(QModelIndex(), rowNum, rowNum);
134 m_customRows.removeAt(i);
135 endRemoveRows();
136 break;
137 }
138 }
139}
140
141void MangleModel::updateManualRow()
142{
143 bool hasAnotherEntry = sourceRowCount() > 0;
144 for (int i = 0; !hasAnotherEntry && i < m_customRows.size(); i++) {
145 if (m_customRows[i].name != QStringLiteral("*other")) {
146 hasAnotherEntry = true;
147 }
148 }
149
150 // Show manual login if we are asked to OR if no other entry exists
151 if (Greeter::instance()->showManualLoginHint() || !hasAnotherEntry)
152 addCustomRow({QStringLiteral("*other"), gettext("Login")});
153 else
154 removeCustomRow(QStringLiteral("*other"));
155}
156
157void MangleModel::updateGuestRow()
158{
159 if (Greeter::instance()->hasGuestAccount())
160 addCustomRow({QStringLiteral("*guest"), gettext("Guest Session")});
161 else
162 removeCustomRow(QStringLiteral("*guest"));
163}
164
165void MangleModel::updateCustomRows()
166{
167 // We update when rowCount changes, but we also insert/remove rows here.
168 // So guard this function to avoid recursion.
169 if (m_updatingCustomRows)
170 return;
171
172 m_updatingCustomRows = true;
173 updateGuestRow();
174 updateManualRow();
175 m_updatingCustomRows = false;
176}
177
178int MangleModel::rowCount(const QModelIndex &parent) const
179{
180 if (parent.isValid())
181 return 0;
182 else
183 return sourceRowCount() + m_customRows.size();
184}
185
186int MangleModel::sourceRowCount() const
187{
188 return Greeter::instance()->hideUsersHint() ? 0 : sourceModel()->rowCount();
189}
190
191QModelIndex MangleModel::index(int row, int column, const QModelIndex &parent) const
192{
193 if (row >= rowCount())
194 return QModelIndex();
195
196 bool isCustomRow = row >= sourceRowCount();
197 if (isCustomRow && !parent.isValid()) {
198 return createIndex(row, column);
199 } else {
200 return QIdentityProxyModel::index(row, column, parent);
201 }
202}
203
59// **** Now we continue with actual UsersModel class ****204// **** Now we continue with actual UsersModel class ****
60205
61UsersModel::UsersModel(QObject* parent)206UsersModel::UsersModel(QObject* parent)
@@ -68,4 +213,21 @@
68 sort(0);213 sort(0);
69}214}
70215
216bool UsersModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
217{
218 auto leftName = source_left.data(QLightDM::UsersModel::NameRole);
219 auto rightName = source_right.data(QLightDM::UsersModel::NameRole);
220
221 if (leftName == QStringLiteral("*guest"))
222 return false;
223 if (rightName == QStringLiteral("*guest"))
224 return true;
225 if (leftName == QStringLiteral("*other"))
226 return false;
227 if (rightName == QStringLiteral("*other"))
228 return true;
229
230 return UnitySortFilterProxyModelQML::lessThan(source_left, source_right);
231}
232
71#include "UsersModel.moc"233#include "UsersModel.moc"
72234
=== modified file 'plugins/LightDM/UsersModel.h'
--- plugins/LightDM/UsersModel.h 2015-09-11 13:38:45 +0000
+++ plugins/LightDM/UsersModel.h 2017-01-25 16:04:08 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2012,2013 Canonical, Ltd.2 * Copyright (C) 2012-2013,2015-2016 Canonical, Ltd.
3 *3 *
4 * This program is free software; you can redistribute it and/or modify4 * 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 by5 * it under the terms of the GNU General Public License as published by
@@ -12,19 +12,16 @@
12 *12 *
13 * You should have received a copy of the GNU General Public License13 * 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 */
1816
19/* This class is a really tiny filter around QLightDM::UsersModel. There are17/* This class is a really tiny filter around QLightDM::UsersModel. There are
20 some operations that we want to edit a bit for the benefit of Qml.18 some operations that we want to edit a bit for the benefit of Qml.
21 Specifically, we want to sort users according to realName. */19 Specifically, we want to sort users according to realName. */
2220
23#ifndef UNITY_USERSMODEL_H21#pragma once
24#define UNITY_USERSMODEL_H
2522
26#include <unitysortfilterproxymodelqml.h>23#include <unitysortfilterproxymodelqml.h>
27#include <QtCore/QObject>24#include <QObject>
2825
29class UsersModel : public UnitySortFilterProxyModelQML26class UsersModel : public UnitySortFilterProxyModelQML
30{27{
@@ -32,6 +29,7 @@
3229
33public:30public:
34 explicit UsersModel(QObject* parent=0);31 explicit UsersModel(QObject* parent=0);
32
33protected:
34 bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override;
35};35};
36
37#endif
3836
=== modified file 'plugins/LightDM/plugin.cpp'
--- plugins/LightDM/plugin.cpp 2016-06-16 17:23:24 +0000
+++ plugins/LightDM/plugin.cpp 2017-01-25 16:04:08 +0000
@@ -19,6 +19,7 @@
19#include "DBusGreeter.h"19#include "DBusGreeter.h"
20#include "DBusGreeterList.h"20#include "DBusGreeterList.h"
21#include "Greeter.h"21#include "Greeter.h"
22#include "PromptsModel.h"
22#include "SessionsModel.h"23#include "SessionsModel.h"
23#include "UsersModel.h"24#include "UsersModel.h"
24#include <libusermetricsoutput/ColorTheme.h>25#include <libusermetricsoutput/ColorTheme.h>
@@ -32,16 +33,24 @@
3233
33static QObject *greeter_provider(QQmlEngine *engine, QJSEngine *scriptEngine)34static QObject *greeter_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
34{35{
35 Q_UNUSED(engine)
36 Q_UNUSED(scriptEngine)36 Q_UNUSED(scriptEngine)
3737
38 Greeter *greeter = new Greeter();38 Greeter *greeter = Greeter::instance();
39 new DBusGreeter(greeter, QStringLiteral("/"));39 new DBusGreeter(greeter, QStringLiteral("/"));
40 new DBusGreeterList(greeter, QStringLiteral("/list"));40 new DBusGreeterList(greeter, QStringLiteral("/list"));
4141
42 engine->setObjectOwnership(greeter, QQmlEngine::CppOwnership);
43
42 return greeter;44 return greeter;
43}45}
4446
47static QObject *prompts_provider(QQmlEngine *engine, QJSEngine *)
48{
49 auto model = Greeter::instance()->promptsModel();
50 engine->setObjectOwnership(model, QQmlEngine::CppOwnership);
51 return model;
52}
53
45static QObject *sessions_provider(QQmlEngine *engine, QJSEngine *scriptEngine)54static QObject *sessions_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
46{55{
47 Q_UNUSED(engine)56 Q_UNUSED(engine)
@@ -78,8 +87,7 @@
78 #error No library defined in LightDM plugin87 #error No library defined in LightDM plugin
79#endif88#endif
8089
81 qRegisterMetaType<QLightDM::Greeter::MessageType>("QLightDM::Greeter::MessageType");90 qmlRegisterSingletonType<PromptsModel>(uri, 0, 1, "Prompts", prompts_provider);
82 qRegisterMetaType<QLightDM::Greeter::PromptType>("QLightDM::Greeter::PromptType");
8391
84 qmlRegisterSingletonType<SessionsModel>(uri, 0, 1, "Sessions", sessions_provider);92 qmlRegisterSingletonType<SessionsModel>(uri, 0, 1, "Sessions", sessions_provider);
85 qmlRegisterUncreatableType<QLightDM::SessionsModel>(uri, 0, 1, "SessionRoles", QStringLiteral("Type is not instantiable"));93 qmlRegisterUncreatableType<QLightDM::SessionsModel>(uri, 0, 1, "SessionRoles", QStringLiteral("Type is not instantiable"));
8694
=== modified file 'plugins/Unity/Launcher/launchermodel.cpp'
--- plugins/Unity/Launcher/launchermodel.cpp 2016-11-01 18:18:56 +0000
+++ plugins/Unity/Launcher/launchermodel.cpp 2017-01-25 16:04:08 +0000
@@ -214,7 +214,6 @@
214void LauncherModel::setUser(const QString &username)214void LauncherModel::setUser(const QString &username)
215{215{
216 Q_UNUSED(username)216 Q_UNUSED(username)
217 qWarning() << "This backend doesn't support multiple users";
218}217}
219218
220QString LauncherModel::getUrlForAppId(const QString &appId) const219QString LauncherModel::getUrlForAppId(const QString &appId) const
221220
=== modified file 'plugins/Utils/CMakeLists.txt'
--- plugins/Utils/CMakeLists.txt 2016-12-07 13:43:25 +0000
+++ plugins/Utils/CMakeLists.txt 2017-01-25 16:04:08 +0000
@@ -34,6 +34,7 @@
34 deviceconfigparser.cpp34 deviceconfigparser.cpp
35 globalfunctions.cpp35 globalfunctions.cpp
36 URLDispatcher.cpp36 URLDispatcher.cpp
37 tabfocusfence.cpp
37 plugin.cpp38 plugin.cpp
38 )39 )
3940
4041
=== modified file 'plugins/Utils/appdrawerproxymodel.cpp'
--- plugins/Utils/appdrawerproxymodel.cpp 2016-12-23 11:04:53 +0000
+++ plugins/Utils/appdrawerproxymodel.cpp 2017-01-25 16:04:08 +0000
@@ -185,5 +185,5 @@
185 return adpm->appId(sourceIndex.row());185 return adpm->appId(sourceIndex.row());
186 }186 }
187 }187 }
188 return nullptr;188 return QString();
189}189}
190190
=== modified file 'plugins/Utils/plugin.cpp'
--- plugins/Utils/plugin.cpp 2016-11-01 18:18:56 +0000
+++ plugins/Utils/plugin.cpp 2017-01-25 16:04:08 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2012-2015 Canonical, Ltd.2 * Copyright (C) 2012-2017 Canonical, Ltd.
3 *3 *
4 * This program is free software; you can redistribute it and/or modify4 * 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 by5 * it under the terms of the GNU General Public License as published by
@@ -40,6 +40,7 @@
40#include "globalfunctions.h"40#include "globalfunctions.h"
41#include "URLDispatcher.h"41#include "URLDispatcher.h"
42#include "appdrawerproxymodel.h"42#include "appdrawerproxymodel.h"
43#include "tabfocusfence.h"
4344
44static QObject *createWindowStateStorage(QQmlEngine *engine, QJSEngine *scriptEngine)45static QObject *createWindowStateStorage(QQmlEngine *engine, QJSEngine *scriptEngine)
45{46{
@@ -84,4 +85,5 @@
84 qmlRegisterSingletonType<GlobalFunctions>(uri, 0, 1, "Functions", createGlobalFunctions);85 qmlRegisterSingletonType<GlobalFunctions>(uri, 0, 1, "Functions", createGlobalFunctions);
85 qmlRegisterType<URLDispatcher>(uri, 0, 1, "URLDispatcher");86 qmlRegisterType<URLDispatcher>(uri, 0, 1, "URLDispatcher");
86 qmlRegisterType<AppDrawerProxyModel>(uri, 0, 1, "AppDrawerProxyModel");87 qmlRegisterType<AppDrawerProxyModel>(uri, 0, 1, "AppDrawerProxyModel");
88 qmlRegisterType<TabFocusFenceItem>(uri, 0, 1, "TabFocusFence");
87}89}
8890
=== added file 'plugins/Utils/tabfocusfence.cpp'
--- plugins/Utils/tabfocusfence.cpp 1970-01-01 00:00:00 +0000
+++ plugins/Utils/tabfocusfence.cpp 2017-01-25 16:04:08 +0000
@@ -0,0 +1,72 @@
1/*
2 * Copyright 2017 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 "tabfocusfence.h"
18
19#include <private/qquickitem_p.h>
20
21TabFocusFenceItem::TabFocusFenceItem(QQuickItem *parent) : QQuickItem(parent)
22{
23 QQuickItemPrivate *d = QQuickItemPrivate::get(this);
24 d->isTabFence = true;
25 setFlag(ItemIsFocusScope);
26}
27
28bool TabFocusFenceItem::focusNext()
29{
30 QQuickItem * current = scopedFocusItem();
31 if (current) {
32 QQuickItem * next = current->nextItemInFocusChain(true);
33 if (next) {
34 next->setFocus(true, Qt::TabFocusReason);
35 return true;
36 }
37 }
38 return false;
39}
40
41bool TabFocusFenceItem::focusPrev()
42{
43 QQuickItem * current = scopedFocusItem();
44 if (current) {
45 QQuickItem * prev = current->nextItemInFocusChain(false);
46 if (prev) {
47 prev->setFocus(true, Qt::BacktabFocusReason);
48 return true;
49 }
50 }
51 return false;
52}
53
54void TabFocusFenceItem::keyPressEvent(QKeyEvent *event)
55{
56 // Needed so we eat Tab keys when there's only one item inside the fence
57 if (event->key() == Qt::Key_Tab) {
58 event->accept();
59 } else {
60 QQuickItem::keyPressEvent(event);
61 }
62}
63
64void TabFocusFenceItem::keyReleaseEvent(QKeyEvent *event)
65{
66 // Needed so we eat Tab keys when there's only one item inside the fence
67 if (event->key() == Qt::Key_Tab) {
68 event->accept();
69 } else {
70 QQuickItem::keyReleaseEvent(event);
71 }
72}
073
=== added file 'plugins/Utils/tabfocusfence.h'
--- plugins/Utils/tabfocusfence.h 1970-01-01 00:00:00 +0000
+++ plugins/Utils/tabfocusfence.h 2017-01-25 16:04:08 +0000
@@ -0,0 +1,38 @@
1/*
2 * Copyright 2017 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15*/
16
17#ifndef TABFOCUSFENCE_H
18#define TABFOCUSFENCE_H
19
20#include <QQuickItem>
21
22// An item that restricts focus Tab travelling
23// to its children
24class TabFocusFenceItem : public QQuickItem
25{
26Q_OBJECT
27
28public:
29 TabFocusFenceItem(QQuickItem *parent = nullptr);
30
31 Q_INVOKABLE bool focusNext();
32 Q_INVOKABLE bool focusPrev();
33
34 void keyPressEvent(QKeyEvent *event) override;
35 void keyReleaseEvent(QKeyEvent *event) override;
36};
37
38#endif
039
=== modified file 'po/unity8.pot'
--- po/unity8.pot 2017-01-10 14:48:43 +0000
+++ po/unity8.pot 2017-01-25 16:04:08 +0000
@@ -8,7 +8,7 @@
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: 2017-01-10 14:48+0000\n"11"POT-Creation-Date: 2017-01-24 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"
@@ -18,10 +18,38 @@
18"Content-Transfer-Encoding: 8bit\n"18"Content-Transfer-Encoding: 8bit\n"
19"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"19"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
2020
21#: plugins/LightDM/Greeter.cpp:12321#: plugins/LightDM/Greeter.cpp:188
22msgid "Password: "22msgid "Password: "
23msgstr ""23msgstr ""
2424
25#: plugins/LightDM/Greeter.cpp:203
26msgid "Username"
27msgstr ""
28
29#: plugins/LightDM/Greeter.cpp:242
30msgid "Failed to authenticate"
31msgstr ""
32
33#: plugins/LightDM/Greeter.cpp:244
34msgid "Invalid password, please try again"
35msgstr ""
36
37#: plugins/LightDM/Greeter.cpp:254
38msgid "Log In"
39msgstr ""
40
41#: plugins/LightDM/Greeter.cpp:254
42msgid "Retry"
43msgstr ""
44
45#: plugins/LightDM/UsersModel.cpp:152
46msgid "Login"
47msgstr ""
48
49#: plugins/LightDM/UsersModel.cpp:160
50msgid "Guest Session"
51msgstr ""
52
25#: plugins/Unity/Launcher/launcheritem.cpp:5053#: plugins/Unity/Launcher/launcheritem.cpp:50
26#: plugins/Unity/Launcher/launcheritem.cpp:12254#: plugins/Unity/Launcher/launcheritem.cpp:122
27msgid "Pin shortcut"55msgid "Pin shortcut"
@@ -35,66 +63,66 @@
35msgid "Unpin shortcut"63msgid "Unpin shortcut"
36msgstr ""64msgstr ""
3765
38#: qml/Components/Dialogs.qml:14966#: qml/Components/Dialogs.qml:177
39msgctxt "Title: Lock/Log out dialog"67msgctxt "Title: Lock/Log out dialog"
40msgid "Log out"68msgid "Log out"
41msgstr ""69msgstr ""
4270
43#: qml/Components/Dialogs.qml:15071#: qml/Components/Dialogs.qml:178
44msgid "Are you sure you want to log out?"72msgid "Are you sure you want to log out?"
45msgstr ""73msgstr ""
4674
47#: qml/Components/Dialogs.qml:15275#: qml/Components/Dialogs.qml:181
48msgctxt "Button: Lock the system"76msgctxt "Button: Lock the system"
49msgid "Lock"77msgid "Lock"
50msgstr ""78msgstr ""
5179
52#: qml/Components/Dialogs.qml:16080#: qml/Components/Dialogs.qml:191
53msgctxt "Button: Log out from the system"81msgctxt "Button: Log out from the system"
54msgid "Log Out"82msgid "Log Out"
55msgstr ""83msgstr ""
5684
57#: qml/Components/Dialogs.qml:167 qml/Components/Dialogs.qml:22585#: qml/Components/Dialogs.qml:199 qml/Components/Dialogs.qml:264
58#: qml/Dash/DashPageHeader.qml:324 qml/Greeter/NarrowView.qml:23286#: qml/Dash/DashPageHeader.qml:324 qml/Greeter/NarrowView.qml:222
59#: qml/Wizard/Pages/passcode-confirm.qml:3287#: qml/Wizard/Pages/passcode-confirm.qml:32
60#: qml/Wizard/Pages/passcode-set.qml:3288#: qml/Wizard/Pages/passcode-set.qml:32
61msgid "Cancel"89msgid "Cancel"
62msgstr ""90msgstr ""
6391
64#: qml/Components/Dialogs.qml:17992#: qml/Components/Dialogs.qml:211
65msgctxt "Title: Reboot dialog"93msgctxt "Title: Reboot dialog"
66msgid "Reboot"94msgid "Reboot"
67msgstr ""95msgstr ""
6896
69#: qml/Components/Dialogs.qml:18097#: qml/Components/Dialogs.qml:212
70msgid "Are you sure you want to reboot?"98msgid "Are you sure you want to reboot?"
71msgstr ""99msgstr ""
72100
73#: qml/Components/Dialogs.qml:182101#: qml/Components/Dialogs.qml:215
74msgid "No"102msgid "No"
75msgstr ""103msgstr ""
76104
77#: qml/Components/Dialogs.qml:189105#: qml/Components/Dialogs.qml:223
78msgid "Yes"106msgid "Yes"
79msgstr ""107msgstr ""
80108
81#: qml/Components/Dialogs.qml:204109#: qml/Components/Dialogs.qml:239
82msgctxt "Title: Power off/Restart dialog"110msgctxt "Title: Power off/Restart dialog"
83msgid "Power"111msgid "Power"
84msgstr ""112msgstr ""
85113
86#: qml/Components/Dialogs.qml:205114#: qml/Components/Dialogs.qml:240
87msgid ""115msgid ""
88"Are you sure you would like\n"116"Are you sure you would like\n"
89"to power off?"117"to power off?"
90msgstr ""118msgstr ""
91119
92#: qml/Components/Dialogs.qml:208120#: qml/Components/Dialogs.qml:244
93msgctxt "Button: Power off the system"121msgctxt "Button: Power off the system"
94msgid "Power off"122msgid "Power off"
95msgstr ""123msgstr ""
96124
97#: qml/Components/Dialogs.qml:217125#: qml/Components/Dialogs.qml:255
98msgctxt "Button: Restart the system"126msgctxt "Button: Restart the system"
99msgid "Restart"127msgid "Restart"
100msgstr ""128msgstr ""
@@ -120,7 +148,7 @@
120msgstr ""148msgstr ""
121149
122#: qml/Components/KeyboardShortcutsOverlay.qml:80150#: qml/Components/KeyboardShortcutsOverlay.qml:80
123msgid "Takes a screenshot of a window."151msgid "Takes a screenshot of the current window."
124msgstr ""152msgstr ""
125153
126#: qml/Components/KeyboardShortcutsOverlay.qml:88154#: qml/Components/KeyboardShortcutsOverlay.qml:88
@@ -267,7 +295,7 @@
267msgid "Closes the current window."295msgid "Closes the current window."
268msgstr ""296msgstr ""
269297
270#: qml/Components/Lockscreen.qml:212 qml/Greeter/NarrowView.qml:252298#: qml/Components/Lockscreen.qml:212 qml/Greeter/NarrowView.qml:242
271msgid "Return to Call"299msgid "Return to Call"
272msgstr ""300msgstr ""
273301
@@ -275,7 +303,7 @@
275msgid "Emergency Call"303msgid "Emergency Call"
276msgstr ""304msgstr ""
277305
278#: qml/Components/Lockscreen.qml:244306#: qml/Components/Lockscreen.qml:245
279msgid "OK"307msgid "OK"
280msgstr ""308msgstr ""
281309
@@ -289,29 +317,29 @@
289msgid "%1:%2"317msgid "%1:%2"
290msgstr ""318msgstr ""
291319
292#: qml/Components/ModeSwitchWarningDialog.qml:32320#: qml/Components/ModeSwitchWarningDialog.qml:33
293msgid "Apps may have unsaved data:"321msgid "Apps may have unsaved data:"
294msgstr ""322msgstr ""
295323
296#: qml/Components/ModeSwitchWarningDialog.qml:57324#: qml/Components/ModeSwitchWarningDialog.qml:60
297msgctxt ""325msgctxt ""
298"Re-dock means connect the device again to an external screen/mouse/keyboard"326"Re-dock means connect the device again to an external screen/mouse/keyboard"
299msgid "Re-dock, save your work and close these apps to continue."327msgid "Re-dock, save your work and close these apps to continue."
300msgstr ""328msgstr ""
301329
302#: qml/Components/ModeSwitchWarningDialog.qml:63330#: qml/Components/ModeSwitchWarningDialog.qml:67
303msgid "Or force close now (unsaved data will be lost)."331msgid "Or force close now (unsaved data will be lost)."
304msgstr ""332msgstr ""
305333
306#: qml/Components/ModeSwitchWarningDialog.qml:75334#: qml/Components/ModeSwitchWarningDialog.qml:80
307msgid "OK, I will reconnect"335msgid "OK, I will reconnect"
308msgstr ""336msgstr ""
309337
310#: qml/Components/ModeSwitchWarningDialog.qml:76338#: qml/Components/ModeSwitchWarningDialog.qml:81
311msgid "Reconnect now!"339msgid "Reconnect now!"
312msgstr ""340msgstr ""
313341
314#: qml/Components/ModeSwitchWarningDialog.qml:88342#: qml/Components/ModeSwitchWarningDialog.qml:94
315msgid "Close all"343msgid "Close all"
316msgstr ""344msgstr ""
317345
@@ -461,44 +489,44 @@
461msgstr[0] ""489msgstr[0] ""
462msgstr[1] ""490msgstr[1] ""
463491
464#: qml/Greeter/Greeter.qml:592492#: qml/Greeter/Greeter.qml:561
465msgid "Try again"493msgid "Try again"
466msgstr ""494msgstr ""
467495
468#: qml/Greeter/LoginList.qml:70496#: qml/Greeter/Greeter.qml:562
497msgid "Enter passphrase to unlock"
498msgstr ""
499
500#: qml/Greeter/Greeter.qml:563
501msgid "Enter passcode to unlock"
502msgstr ""
503
504#: qml/Greeter/NarrowView.qml:242
505msgid "Emergency"
506msgstr ""
507
508#: qml/Greeter/PromptList.qml:126
469msgid "Passphrase"509msgid "Passphrase"
470msgstr ""510msgstr ""
471511
472#: qml/Greeter/LoginList.qml:71512#: qml/Greeter/PromptList.qml:126
473msgid "Passcode"513msgid "Passcode"
474msgstr ""514msgstr ""
475515
476#: qml/Greeter/LoginList.qml:98
477msgid "Retry"
478msgstr ""
479
480#: qml/Greeter/LoginList.qml:99
481msgid "Log In"
482msgstr ""
483
484#: qml/Greeter/NarrowView.qml:252
485msgid "Emergency"
486msgstr ""
487
488#: qml/Greeter/SessionsList.qml:122516#: qml/Greeter/SessionsList.qml:122
489msgid "Select desktop environment"517msgid "Select desktop environment"
490msgstr ""518msgstr ""
491519
492#: qml/Launcher/Drawer.qml:80520#: qml/Launcher/Drawer.qml:92
493msgid "Search…"521msgid "Search…"
494msgstr ""522msgstr ""
495523
496#: qml/Launcher/Drawer.qml:101524#: qml/Launcher/Drawer.qml:123
497msgctxt "Apps sorted alphabetically"525msgctxt "Apps sorted alphabetically"
498msgid "A-Z"526msgid "A-Z"
499msgstr ""527msgstr ""
500528
501#: qml/Launcher/MoreAppsHeader.qml:32529#: qml/Launcher/MoreAppsHeader.qml:39
502msgid "More apps in the store"530msgid "More apps in the store"
503msgstr ""531msgstr ""
504532
505533
=== modified file 'qml/ApplicationMenus/MenuBar.qml'
--- qml/ApplicationMenus/MenuBar.qml 2017-01-09 15:26:05 +0000
+++ qml/ApplicationMenus/MenuBar.qml 2017-01-25 16:04:08 +0000
@@ -57,6 +57,20 @@
57 onReleased: d.stopSHortcutTimer()57 onReleased: d.stopSHortcutTimer()
58 }58 }
5959
60 GlobalShortcut {
61 shortcut: Qt.AltModifier | Qt.Key_F10
62 active: enableKeyFilter && d.currentItem == null
63 onTriggered: {
64 for (var i = 0; i < rowRepeater.count; i++) {
65 var item = rowRepeater.itemAt(i);
66 if (item.enabled) {
67 item.show();
68 break;
69 }
70 }
71 }
72 }
73
60 InverseMouseArea {74 InverseMouseArea {
61 acceptedButtons: Qt.LeftButton | Qt.MiddleButton | Qt.RightButton75 acceptedButtons: Qt.LeftButton | Qt.MiddleButton | Qt.RightButton
62 anchors.fill: parent76 anchors.fill: parent
6377
=== modified file 'qml/ApplicationMenus/MenuPopup.qml'
--- qml/ApplicationMenus/MenuPopup.qml 2017-01-09 15:26:05 +0000
+++ qml/ApplicationMenus/MenuPopup.qml 2017-01-25 16:04:08 +0000
@@ -26,19 +26,7 @@
26 objectName: "menu"26 objectName: "menu"
27 backgroundColor: theme.palette.normal.overlay27 backgroundColor: theme.palette.normal.overlay
2828
29 property alias unityMenuModel: listView.model29 property alias unityMenuModel: repeater.model
30
31 readonly property real __ajustedMinimumHeight: {
32 if (listView.contentHeight > __minimumHeight) {
33 return units.gu(30);
34 }
35 return Math.max(listView.contentHeight, units.gu(2));
36 }
37
38 readonly property real __minimumWidth: units.gu(20)
39 readonly property real __minimumHeight: units.gu(30)
40 readonly property real __maximumWidth: Screen.width * 0.7
41 readonly property real __maximumHeight: Screen.height * 0.7
4230
43 function show() {31 function show() {
44 visible = true;32 visible = true;
@@ -63,18 +51,23 @@
63 d.dismissAll();51 d.dismissAll();
64 }52 }
6553
66 implicitWidth: container.width54 implicitWidth: focusScope.width
67 implicitHeight: MathUtils.clamp(listView.contentHeight, __ajustedMinimumHeight, __maximumHeight)55 implicitHeight: focusScope.height
6856
69 MenuNavigator {57 MenuNavigator {
70 id: d58 id: d
71 objectName: "d"59 objectName: "d"
72 itemView: listView60 itemView: repeater
7361
74 property Item currentItem: null62 property Item currentItem: null
75 property Item hoveredItem: null63 property Item hoveredItem: null
76 readonly property int currentIndex: currentItem ? currentItem.__ownIndex : -164 readonly property int currentIndex: currentItem ? currentItem.__ownIndex : -1
7765
66 property real __minimumWidth: units.gu(20)
67 property real __maximumWidth: Screen.width * 0.7
68 property real __minimumHeight: units.gu(2)
69 property real __maximumHeight: Screen.height * 0.7
70
78 signal dismissAll()71 signal dismissAll()
7972
80 onCurrentItemChanged: {73 onCurrentItemChanged: {
@@ -86,13 +79,26 @@
86 }79 }
8780
88 onSelect: {81 onSelect: {
89 currentItem = listView.itemAt(index);82 currentItem = repeater.itemAt(index);
83 if (currentItem) {
84 if (currentItem.y < listView.contentY) {
85 listView.contentY = currentItem.y;
86 } else if (currentItem.y + currentItem.height > listView.contentY + listView.height) {
87 listView.contentY = currentItem.y + currentItem.height - listView.height;
88 }
89 }
90 }90 }
91 }91 }
9292
93 MouseArea {
94 // Eat events.
95 anchors.fill: parent
96 }
97
93 Item {98 Item {
94 id: focusScope99 id: focusScope
95 anchors.fill: parent100 width: container.width
101 height: container.height
96 focus: visible102 focus: visible
97103
98 Keys.onUpPressed: d.selectPrevious(d.currentIndex)104 Keys.onUpPressed: d.selectPrevious(d.currentIndex)
@@ -108,17 +114,17 @@
108 id: container114 id: container
109 objectName: "container"115 objectName: "container"
110116
111 width: listView.contentWidth117 height: MathUtils.clamp(listView.contentHeight, d.__minimumHeight, d.__maximumHeight)
112 height: parent.height118 width: menuColumn.width
113 spacing: 0119 spacing: 0
114120
115 // FIXME use ListView.header - tried but was flaky with positionViewAtIndex.121 // Header - scroll up
116 Item {122 Item {
117 Layout.fillWidth: true;123 Layout.fillWidth: true
118 Layout.maximumHeight: units.gu(3)124 height: units.gu(3)
119 Layout.minimumHeight: units.gu(3)
120 visible: listView.contentHeight > root.height125 visible: listView.contentHeight > root.height
121 enabled: !listView.atYBeginning126 enabled: !listView.atYBeginning
127 z: 1
122128
123 Rectangle {129 Rectangle {
124 color: enabled ? theme.palette.normal.overlayText :130 color: enabled ? theme.palette.normal.overlayText :
@@ -143,47 +149,35 @@
143 MouseArea {149 MouseArea {
144 anchors.fill: parent150 anchors.fill: parent
145 onPressed: {151 onPressed: {
146 var index = listView.indexAt(0, listView.contentY);152 var item = menuColumn.childAt(0, listView.contentY);
147 listView.positionViewAtIndex(index-1, ListView.Beginning);153 if (item) {
154 var previousItem = item;
155 do {
156 previousItem = repeater.itemAt(previousItem.__ownIndex-1);
157 if (!previousItem) {
158 listView.contentY = 0;
159 return;
160 }
161 } while (previousItem.__isSeparator);
162
163 listView.contentY = previousItem.y
164 }
148 }165 }
149 }166 }
150 }167 }
151168
152 ListView {169 // Menu Items
170 Flickable {
153 id: listView171 id: listView
154 objectName: "listView"172 clip: interactive
173
155 Layout.fillHeight: true174 Layout.fillHeight: true
156 Layout.fillWidth: true175 Layout.fillWidth: true
157 contentWidth: MathUtils.clamp(contentItem.childrenRect.width,176 contentHeight: menuColumn.height
158 __minimumWidth,177 interactive: height < contentHeight
159 __maximumWidth)
160
161 orientation: Qt.Vertical
162 interactive: contentHeight > height
163 clip: interactive
164 highlightFollowsCurrentItem: false
165
166 highlight: Rectangle {
167 color: "transparent"
168 border.width: units.dp(1)
169 border.color: UbuntuColors.orange
170 z: 1
171
172 width: listView.width
173 height: d.currentItem ? d.currentItem.height : 0
174 y: d.currentItem ? d.currentItem.y : 0
175 visible: d.currentItem
176 }
177
178 function itemAt(index) {
179 if (index > count || index < 0) return null;
180 currentIndex = index;
181 return currentItem;
182 }
183178
184 MouseArea {179 MouseArea {
185 id: menuMouseArea180 anchors.fill: parent
186 anchors.fill: listView
187 hoverEnabled: true181 hoverEnabled: true
188 propagateComposedEvents: true // propogate events so we send clicks to children.182 propagateComposedEvents: true // propogate events so we send clicks to children.
189 z: 1 // on top so we override any other hovers183 z: 1 // on top so we override any other hovers
@@ -195,7 +189,7 @@
195189
196 if (!d.hoveredItem || !d.currentItem ||190 if (!d.hoveredItem || !d.currentItem ||
197 !d.hoveredItem.contains(Qt.point(pos.x - d.currentItem.x, pos.y - d.currentItem.y))) {191 !d.hoveredItem.contains(Qt.point(pos.x - d.currentItem.x, pos.y - d.currentItem.y))) {
198 d.hoveredItem = listView.itemAt(listView.indexAt(pos.x, pos.y));192 d.hoveredItem = menuColumn.childAt(pos.x, pos.y)
199 if (!d.hoveredItem || !d.hoveredItem.enabled)193 if (!d.hoveredItem || !d.hoveredItem.enabled)
200 return false;194 return false;
201 d.currentItem = d.hoveredItem;195 d.currentItem = d.hoveredItem;
@@ -216,89 +210,123 @@
216 }210 }
217 }211 }
218212
219 delegate: Loader {213 ColumnLayout {
220 id: loader214 id: menuColumn
221 objectName: root.objectName + "-item" + __ownIndex215 spacing: 0
222216
223 property int __ownIndex: index217 width: MathUtils.clamp(implicitWidth, d.__minimumWidth, d.__maximumWidth)
224218
225 width: root.width219 Repeater {
226 enabled: model.isSeparator ? false : model.sensitive220 id: repeater
227221
228 sourceComponent: {222 Loader {
229 if (model.isSeparator) {223 id: loader
230 return separatorComponent;224 objectName: root.objectName + "-item" + __ownIndex
231 }225
232 return menuItemComponent;226 property int __ownIndex: index
233 }227 property bool __isSeparator: model.isSeparator
234228
235 property Item popup: null229 enabled: __isSeparator ? false : model.sensitive
236230
237 Component {231 sourceComponent: {
238 id: menuItemComponent232 if (model.isSeparator) {
239 MenuItem {233 return separatorComponent;
240 id: menuItem234 }
241 menuData: model235 return menuItemComponent;
242 objectName: loader.objectName + "-actionItem"236 }
243237
244 action.onTriggered: {238 property Item popup: null
245 d.currentItem = loader;239
246240 Layout.fillWidth: true
247 if (hasSubmenu) {241
248 if (!popup) {242 Component {
249 var model = root.unityMenuModel.submenu(__ownIndex);243 id: menuItemComponent
250 popup = submenuComponent.createObject(focusScope, {244 MenuItem {
251 objectName: loader.objectName + "-",245 id: menuItem
252 unityMenuModel: model,246 menuData: model
253 x: Qt.binding(function() { return root.width }),247 objectName: loader.objectName + "-actionItem"
254 y: Qt.binding(function() { return loader.y })248
255 });249 width: MathUtils.clamp(implicitWidth, d.__minimumWidth, d.__maximumWidth)
256 } else if (popup) {250
257 popup.visible = true;251 action.onTriggered: {
258 }252 d.currentItem = loader;
259 popup.retreat.connect(function() {253
260 popup.destroy();254 if (hasSubmenu) {
261 popup = null;255 if (!popup) {
262 menuItem.forceActiveFocus();256 var model = root.unityMenuModel.submenu(__ownIndex);
263 })257 popup = submenuComponent.createObject(focusScope, {
264 } else {258 objectName: loader.objectName + "-",
265 root.unityMenuModel.activate(__ownIndex);259 unityMenuModel: model,
266 }260 x: Qt.binding(function() { return root.width }),
267 }261 y: Qt.binding(function() {
268262 var dummy = listView.contentY; // force a recalc on contentY change.
269 Connections {263 return mapToItem(container, 0, y).y;
270 target: d264 })
271 onCurrentIndexChanged: {265 });
272 if (popup && d.currentIndex != __ownIndex) {266 } else if (popup) {
273 popup.visible = false;267 popup.visible = true;
274 }268 }
275 }269 popup.retreat.connect(function() {
276 onDismissAll: {270 popup.destroy();
277 if (popup) {271 popup = null;
278 popup.destroy();272 menuItem.forceActiveFocus();
279 popup = null;273 })
280 }274 } else {
281 }275 root.unityMenuModel.activate(__ownIndex);
282 }276 }
283 }277 }
284 }278
285279 Connections {
286 Component {280 target: d
287 id: separatorComponent281 onCurrentIndexChanged: {
288 ListItems.ThinDivider {282 if (popup && d.currentIndex != __ownIndex) {
289 objectName: loader.objectName + "-separator"283 popup.visible = false;
290 }284 }
291 }285 }
292 }286 onDismissAll: {
293 } // ListView287 if (popup) {
294288 popup.destroy();
295 // FIXME use ListView.footer - tried but was flaky with positionViewAtIndex.289 popup = null;
290 }
291 }
292 }
293 }
294 }
295
296 Component {
297 id: separatorComponent
298 ListItems.ThinDivider {
299 objectName: loader.objectName + "-separator"
300 implicitHeight: units.dp(2)
301 }
302 }
303 }
304
305 }
306 }
307
308 // Highlight
309 Rectangle {
310 color: "transparent"
311 border.width: units.dp(1)
312 border.color: UbuntuColors.orange
313 z: 1
314
315 width: listView.width
316 height: d.currentItem ? d.currentItem.height : 0
317 y: d.currentItem ? d.currentItem.y : 0
318 visible: d.currentItem
319 }
320
321 } // Flickable
322
323 // Header - scroll down
296 Item {324 Item {
297 Layout.fillWidth: true;325 Layout.fillWidth: true
298 Layout.maximumHeight: units.gu(3)326 height: units.gu(3)
299 Layout.minimumHeight: units.gu(3)
300 visible: listView.contentHeight > root.height327 visible: listView.contentHeight > root.height
301 enabled: !listView.atYEnd328 enabled: !listView.atYEnd
329 z: 1
302330
303 Rectangle {331 Rectangle {
304 color: enabled ? theme.palette.normal.overlayText :332 color: enabled ? theme.palette.normal.overlayText :
@@ -323,8 +351,19 @@
323 MouseArea {351 MouseArea {
324 anchors.fill: parent352 anchors.fill: parent
325 onPressed: {353 onPressed: {
326 var index = listView.indexAt(0, listView.contentY);354 var item = menuColumn.childAt(0, listView.contentY + listView.height);
327 listView.positionViewAtIndex(index+1, ListView.Beginning);355 if (item) {
356 var nextItem = item;
357 do {
358 nextItem = repeater.itemAt(nextItem.__ownIndex+1);
359 if (!nextItem) {
360 listView.contentY = listView.contentHeight - listView.height;
361 return;
362 }
363 } while (nextItem.__isSeparator);
364
365 listView.contentY = nextItem.y - listView.height
366 }
328 }367 }
329 }368 }
330 }369 }
331370
=== modified file 'qml/Components/Dialogs.qml'
--- qml/Components/Dialogs.qml 2016-12-20 08:55:23 +0000
+++ qml/Components/Dialogs.qml 2017-01-25 16:04:08 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2014-2016 Canonical, Ltd.2 * Copyright (C) 2014-2017 Canonical, Ltd.
3 *3 *
4 * This program is free software; you can redistribute it and/or modify4 * 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 by5 * it under the terms of the GNU General Public License as published by
@@ -24,10 +24,13 @@
24import Utils 0.124import Utils 0.1
25import "../Greeter"25import "../Greeter"
2626
27Item {27MouseArea {
28 id: root28 id: root
29 acceptedButtons: Qt.AllButtons
30 hoverEnabled: true
31 onWheel: wheel.accepted = true
2932
30 readonly property alias hasActiveDialog: dialogLoader.active33 readonly property bool hasActiveDialog: dialogLoader.active || d.modeSwitchWarningPopup
3134
32 // to be set from outside, useful mostly for testing purposes35 // to be set from outside, useful mostly for testing purposes
33 property var unitySessionService: DBusUnitySessionService36 property var unitySessionService: DBusUnitySessionService
@@ -42,6 +45,7 @@
42 }45 }
43 property string usageScenario46 property string usageScenario
44 property size screenSize: Qt.size(Screen.width, Screen.height)47 property size screenSize: Qt.size(Screen.width, Screen.height)
48 property bool hasKeyboard: false
4549
46 signal powerOffClicked();50 signal powerOffClicked();
4751
@@ -120,6 +124,11 @@
120 onTriggered: LightDMService.greeter.showGreeter()124 onTriggered: LightDMService.greeter.showGreeter()
121 }125 }
122126
127 GlobalShortcut { // lock screen
128 shortcut: Qt.MetaModifier|Qt.Key_L
129 onTriggered: LightDMService.greeter.showGreeter()
130 }
131
123 QtObject {132 QtObject {
124 id: d // private stuff133 id: d // private stuff
125 objectName: "dialogsPrivate"134 objectName: "dialogsPrivate"
@@ -140,6 +149,25 @@
140 objectName: "dialogLoader"149 objectName: "dialogLoader"
141 anchors.fill: parent150 anchors.fill: parent
142 active: false151 active: false
152 onActiveChanged: {
153 if (!active) {
154 if (previousFocusedItem) {
155 previousFocusedItem.forceActiveFocus(Qt.OtherFocusReason);
156 previousFocusedItem = undefined;
157 }
158 previousSourceComponent = undefined;
159 sourceComponent = undefined;
160 }
161 }
162 onSourceComponentChanged: {
163 if (previousSourceComponent !== sourceComponent) {
164 previousSourceComponent = sourceComponent;
165 previousFocusedItem = window.activeFocusItem;
166 }
167 }
168
169 property var previousSourceComponent: undefined
170 property var previousFocusedItem: undefined
143 }171 }
144172
145 Component {173 Component {
@@ -149,13 +177,16 @@
149 title: i18n.ctr("Title: Lock/Log out dialog", "Log out")177 title: i18n.ctr("Title: Lock/Log out dialog", "Log out")
150 text: i18n.tr("Are you sure you want to log out?")178 text: i18n.tr("Are you sure you want to log out?")
151 Button {179 Button {
180 width: parent.width
152 text: i18n.ctr("Button: Lock the system", "Lock")181 text: i18n.ctr("Button: Lock the system", "Lock")
153 onClicked: {182 onClicked: {
154 LightDMService.greeter.showGreeter()183 LightDMService.greeter.showGreeter()
155 logoutDialog.hide();184 logoutDialog.hide();
156 }185 }
186 Component.onCompleted: if (root.hasKeyboard) forceActiveFocus(Qt.TabFocusReason)
157 }187 }
158 Button {188 Button {
189 width: parent.width
159 focus: true190 focus: true
160 text: i18n.ctr("Button: Log out from the system", "Log Out")191 text: i18n.ctr("Button: Log out from the system", "Log Out")
161 onClicked: {192 onClicked: {
@@ -164,6 +195,7 @@
164 }195 }
165 }196 }
166 Button {197 Button {
198 width: parent.width
167 text: i18n.tr("Cancel")199 text: i18n.tr("Cancel")
168 onClicked: {200 onClicked: {
169 logoutDialog.hide();201 logoutDialog.hide();
@@ -179,12 +211,14 @@
179 title: i18n.ctr("Title: Reboot dialog", "Reboot")211 title: i18n.ctr("Title: Reboot dialog", "Reboot")
180 text: i18n.tr("Are you sure you want to reboot?")212 text: i18n.tr("Are you sure you want to reboot?")
181 Button {213 Button {
214 width: parent.width
182 text: i18n.tr("No")215 text: i18n.tr("No")
183 onClicked: {216 onClicked: {
184 rebootDialog.hide();217 rebootDialog.hide();
185 }218 }
186 }219 }
187 Button {220 Button {
221 width: parent.width
188 focus: true222 focus: true
189 text: i18n.tr("Yes")223 text: i18n.tr("Yes")
190 onClicked: {224 onClicked: {
@@ -193,6 +227,7 @@
193 rebootDialog.hide();227 rebootDialog.hide();
194 }228 }
195 color: theme.palette.normal.negative229 color: theme.palette.normal.negative
230 Component.onCompleted: if (root.hasKeyboard) forceActiveFocus(Qt.TabFocusReason)
196 }231 }
197 }232 }
198 }233 }
@@ -204,6 +239,7 @@
204 title: i18n.ctr("Title: Power off/Restart dialog", "Power")239 title: i18n.ctr("Title: Power off/Restart dialog", "Power")
205 text: i18n.tr("Are you sure you would like\nto power off?")240 text: i18n.tr("Are you sure you would like\nto power off?")
206 Button {241 Button {
242 width: parent.width
207 focus: true243 focus: true
208 text: i18n.ctr("Button: Power off the system", "Power off")244 text: i18n.ctr("Button: Power off the system", "Power off")
209 onClicked: {245 onClicked: {
@@ -212,8 +248,10 @@
212 root.powerOffClicked();248 root.powerOffClicked();
213 }249 }
214 color: theme.palette.normal.negative250 color: theme.palette.normal.negative
251 Component.onCompleted: if (root.hasKeyboard) forceActiveFocus(Qt.TabFocusReason)
215 }252 }
216 Button {253 Button {
254 width: parent.width
217 text: i18n.ctr("Button: Restart the system", "Restart")255 text: i18n.ctr("Button: Restart the system", "Restart")
218 onClicked: {256 onClicked: {
219 root.closeAllApps();257 root.closeAllApps();
@@ -222,6 +260,7 @@
222 }260 }
223 }261 }
224 Button {262 Button {
263 width: parent.width
225 text: i18n.tr("Cancel")264 text: i18n.tr("Cancel")
226 onClicked: {265 onClicked: {
227 powerDialog.hide();266 powerDialog.hide();
228267
=== modified file 'qml/Components/KeyboardShortcutsOverlay.qml'
--- qml/Components/KeyboardShortcutsOverlay.qml 2016-11-29 10:35:21 +0000
+++ qml/Components/KeyboardShortcutsOverlay.qml 2017-01-25 16:04:08 +0000
@@ -77,7 +77,7 @@
77 font.weight: Font.Medium77 font.weight: Font.Medium
78 }78 }
79 Label {79 Label {
80 text: i18n.tr("Takes a screenshot of a window.")80 text: i18n.tr("Takes a screenshot of the current window.")
81 fontSize: "small"81 fontSize: "small"
82 font.weight: Font.Light82 font.weight: Font.Light
83 wrapMode: Text.Wrap83 wrapMode: Text.Wrap
8484
=== modified file 'qml/Components/KeymapSwitcher.qml'
--- qml/Components/KeymapSwitcher.qml 2016-11-30 19:24:02 +0000
+++ qml/Components/KeymapSwitcher.qml 2017-01-25 16:04:08 +0000
@@ -51,6 +51,9 @@
51 nextIndex = currentKeymapIndex + 1;51 nextIndex = currentKeymapIndex + 1;
52 }52 }
53 currentKeymapIndex = nextIndex;53 currentKeymapIndex = nextIndex;
54 if (actionGroup.currentAction.valid) {
55 actionGroup.currentAction.updateState(currentKeymapIndex);
56 }
54 }57 }
5558
56 function previousKeymap() {59 function previousKeymap() {
@@ -60,30 +63,35 @@
60 prevIndex = currentKeymapIndex - 1;63 prevIndex = currentKeymapIndex - 1;
61 }64 }
62 currentKeymapIndex = prevIndex;65 currentKeymapIndex = prevIndex;
66 if (actionGroup.currentAction.valid) {
67 actionGroup.currentAction.updateState(currentKeymapIndex);
68 }
63 }69 }
6470
65 property Binding surfaceKeymapBinding: Binding {71 property Binding surfaceKeymapBinding: Binding { // NB: needed mainly for xmir & libertine apps
66 target: root.focusedSurface72 target: root.focusedSurface
67 property: "keymap"73 property: "keymap"
68 value: root.currentKeymap74 value: root.currentKeymap
69 }75 }
7076
77 property Binding unityKeymapBinding: Binding {
78 target: Mir
79 property: "currentKeymap"
80 value: root.currentKeymap
81 }
82
71 // indicator83 // indicator
72 property QDBusActionGroup actionGroup: QDBusActionGroup {84 property QDBusActionGroup actionGroup: QDBusActionGroup {
73 busType: DBus.SessionBus85 busType: DBus.SessionBus
74 busName: "com.canonical.indicator.keyboard"86 busName: "com.canonical.indicator.keyboard"
75 objectPath: "/com/canonical/indicator/keyboard"87 objectPath: "/com/canonical/indicator/keyboard"
7688
77 property variant currentAction: action("current")89 property variant currentAction: action("current") // the one that's checked by the indicator
78 property variant activeAction: action("active")90 property variant activeAction: action("active") // the one that we clicked
7991
80 Component.onCompleted: actionGroup.start();92 Component.onCompleted: actionGroup.start();
81 }93 }
8294
83 onCurrentKeymapIndexChanged: {
84 actionGroup.currentAction.updateState(currentKeymapIndex);
85 }
86
87 readonly property int activeActionState: actionGroup.activeAction.valid ? actionGroup.activeAction.state : -195 readonly property int activeActionState: actionGroup.activeAction.valid ? actionGroup.activeAction.state : -1
8896
89 onActiveActionStateChanged: {97 onActiveActionStateChanged: {
9098
=== modified file 'qml/Components/Lockscreen.qml'
--- qml/Components/Lockscreen.qml 2016-08-30 14:06:47 +0000
+++ qml/Components/Lockscreen.qml 2017-01-25 16:04:08 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2013 Canonical, Ltd.2 * Copyright (C) 2013-2017 Canonical, Ltd.
3 *3 *
4 * This program is free software; you can redistribute it and/or modify4 * 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 by5 * it under the terms of the GNU General Public License as published by
@@ -240,8 +240,10 @@
240 property var dialogLoader // dummy to satisfy ShellDialog's context dependent prop240 property var dialogLoader // dummy to satisfy ShellDialog's context dependent prop
241241
242 Button {242 Button {
243 width: parent.width
243 objectName: "infoPopupOkButton"244 objectName: "infoPopupOkButton"
244 text: i18n.tr("OK")245 text: i18n.tr("OK")
246 focus: true
245 onClicked: {247 onClicked: {
246 PopupUtils.close(dialog)248 PopupUtils.close(dialog)
247 root.infoPopupConfirmed();249 root.infoPopupConfirmed();
248250
=== modified file 'qml/Components/ModeSwitchWarningDialog.qml'
--- qml/Components/ModeSwitchWarningDialog.qml 2016-05-17 20:46:51 +0000
+++ qml/Components/ModeSwitchWarningDialog.qml 2017-01-25 16:04:08 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2015 Canonical, Ltd.2 * Copyright (C) 2015-2017 Canonical, Ltd.
3 *3 *
4 * This program is free software; you can redistribute it and/or modify4 * 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 by5 * it under the terms of the GNU General Public License as published by
@@ -29,6 +29,7 @@
29 signal forceClose();29 signal forceClose();
3030
31 Label {31 Label {
32 width: parent.width
32 text: i18n.tr("Apps may have unsaved data:")33 text: i18n.tr("Apps may have unsaved data:")
33 fontSize: "large"34 fontSize: "large"
34 color: "#5D5D5D"35 color: "#5D5D5D"
@@ -37,6 +38,7 @@
37 Repeater {38 Repeater {
38 id: appRepeater39 id: appRepeater
39 RowLayout {40 RowLayout {
41 width: parent.width
40 spacing: units.gu(2)42 spacing: units.gu(2)
41 Image {43 Image {
42 Layout.preferredHeight: units.gu(2)44 Layout.preferredHeight: units.gu(2)
@@ -54,20 +56,23 @@
54 }56 }
5557
56 Label {58 Label {
59 width: parent.width
57 text: i18n.ctr("Re-dock means connect the device again to an external screen/mouse/keyboard", "Re-dock, save your work and close these apps to continue.")60 text: i18n.ctr("Re-dock means connect the device again to an external screen/mouse/keyboard", "Re-dock, save your work and close these apps to continue.")
58 wrapMode: Text.WordWrap61 wrapMode: Text.WordWrap
59 color: "#888888"62 color: "#888888"
60 }63 }
6164
62 Label {65 Label {
66 width: parent.width
63 text: i18n.tr("Or force close now (unsaved data will be lost).")67 text: i18n.tr("Or force close now (unsaved data will be lost).")
64 wrapMode: Text.WordWrap68 wrapMode: Text.WordWrap
65 color: "#888888"69 color: "#888888"
66 }70 }
6771
68 ThinDivider {}72 ThinDivider { width: parent.width }
6973
70 RowLayout {74 RowLayout {
75 width: parent.width
71 Label {76 Label {
72 objectName: "reconnectLabel"77 objectName: "reconnectLabel"
73 Layout.fillWidth: true78 Layout.fillWidth: true
@@ -84,6 +89,7 @@
84 }89 }
8590
86 Button {91 Button {
92 focus: true
87 objectName: "forceCloseButton"93 objectName: "forceCloseButton"
88 text: i18n.tr("Close all")94 text: i18n.tr("Close all")
89 color: theme.palette.normal.negative95 color: theme.palette.normal.negative
9096
=== modified file 'qml/Components/ShellDialog.qml'
--- qml/Components/ShellDialog.qml 2016-03-29 03:47:39 +0000
+++ qml/Components/ShellDialog.qml 2017-01-25 16:04:08 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2014 Canonical, Ltd.2 * Copyright (C) 2014-2017 Canonical, Ltd.
3 *3 *
4 * This program is free software; you can redistribute it and/or modify4 * 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 by5 * it under the terms of the GNU General Public License as published by
@@ -19,6 +19,7 @@
19import Ubuntu.Components 1.319import Ubuntu.Components 1.3
20import Ubuntu.Components.Themes 1.320import Ubuntu.Components.Themes 1.3
21import Ubuntu.Components.Popups 1.321import Ubuntu.Components.Popups 1.3
22import Utils 0.1
2223
23/*24/*
24 A Dialog configured for use as a proper in-scene Dialog25 A Dialog configured for use as a proper in-scene Dialog
@@ -32,10 +33,10 @@
32 // NB: PopupBase, Dialog's superclass, will check for the existence of this property33 // NB: PopupBase, Dialog's superclass, will check for the existence of this property
33 property bool reparentToRootItem: false34 property bool reparentToRootItem: false
3435
36 default property alias columnContents: column.data
37
35 onVisibleChanged: { if (!visible && dialogLoader) { dialogLoader.active = false; } }38 onVisibleChanged: { if (!visible && dialogLoader) { dialogLoader.active = false; } }
3639
37 Keys.onEscapePressed: hide()
38
39 focus: true40 focus: true
4041
41 // FIXME: this is a hack because Dialog subtheming seems broken atm42 // FIXME: this is a hack because Dialog subtheming seems broken atm
@@ -50,4 +51,21 @@
50 __foreground.theme = themeHack51 __foreground.theme = themeHack
51 show();52 show();
52 }53 }
54
55 TabFocusFence {
56 width: parent.width
57 height: column.height
58 focus: true
59 Column {
60 id: column
61 width: parent.width
62 spacing: units.gu(2)
63 }
64 Keys.onDownPressed: {
65 event.accepted = focusNext();
66 }
67 Keys.onUpPressed: {
68 event.accepted = focusPrev();
69 }
70 }
53}71}
5472
=== modified file 'qml/Greeter/Circle.qml'
--- qml/Greeter/Circle.qml 2016-08-30 14:10:12 +0000
+++ qml/Greeter/Circle.qml 2017-01-25 16:04:08 +0000
@@ -27,7 +27,7 @@
27 onCenterCircleChanged: requestPaint()27 onCenterCircleChanged: requestPaint()
2828
29 onPaint: {29 onPaint: {
30 if (circleScale <= 0) {30 if (circleScale <= 0 || width <= 0 || height <= 0) {
31 return;31 return;
32 }32 }
3333
3434
=== modified file 'qml/Greeter/FullLightDMImpl.qml'
--- qml/Greeter/FullLightDMImpl.qml 2015-11-19 21:47:32 +0000
+++ qml/Greeter/FullLightDMImpl.qml 2017-01-25 16:04:08 +0000
@@ -22,6 +22,7 @@
2222
23 property var greeter: LightDM.Greeter23 property var greeter: LightDM.Greeter
24 property var infographic: LightDM.Infographic24 property var infographic: LightDM.Infographic
25 property var prompts: LightDM.Prompts
25 property var sessions: LightDM.Sessions26 property var sessions: LightDM.Sessions
26 property var sessionRoles: LightDM.SessionRoles27 property var sessionRoles: LightDM.SessionRoles
27 property var users: LightDM.Users28 property var users: LightDM.Users
2829
=== modified file 'qml/Greeter/Greeter.qml'
--- qml/Greeter/Greeter.qml 2016-11-29 00:13:45 +0000
+++ qml/Greeter/Greeter.qml 2017-01-25 16:04:08 +0000
@@ -74,11 +74,11 @@
74 forcedUnlock = false;74 forcedUnlock = false;
75 if (required) {75 if (required) {
76 if (loader.item) {76 if (loader.item) {
77 loader.item.reset(true /* forceShow */);77 loader.item.forceShow();
78 }78 }
79 // Normally loader.onLoaded will select a user, but if we're79 // Normally loader.onLoaded will select a user, but if we're
80 // already shown, do it manually.80 // already shown, do it manually.
81 d.selectUser(d.currentIndex, false);81 d.selectUser(d.currentIndex);
82 }82 }
8383
84 // Even though we may already be shown, we want to call show() for its84 // Even though we may already be shown, we want to call show() for its
@@ -154,7 +154,7 @@
154 readonly property bool multiUser: LightDMService.users.count > 1154 readonly property bool multiUser: LightDMService.users.count > 1
155 readonly property int selectUserIndex: d.getUserIndex(LightDMService.greeter.selectUser)155 readonly property int selectUserIndex: d.getUserIndex(LightDMService.greeter.selectUser)
156 property int currentIndex: Math.max(selectUserIndex, 0)156 property int currentIndex: Math.max(selectUserIndex, 0)
157 property bool waiting157 readonly property bool waiting: LightDMService.prompts.count == 0 && !root.forcedUnlock
158 property bool isLockscreen // true when we are locking an active session, rather than first user login158 property bool isLockscreen // true when we are locking an active session, rather than first user login
159 readonly property bool secureFingerprint: isLockscreen &&159 readonly property bool secureFingerprint: isLockscreen &&
160 AccountsService.failedFingerprintLogins <160 AccountsService.failedFingerprintLogins <
@@ -189,13 +189,9 @@
189 return -1;189 return -1;
190 }190 }
191191
192 function selectUser(index, reset) {192 function selectUser(index) {
193 if (index < 0 || index >= LightDMService.users.count)193 if (index < 0 || index >= LightDMService.users.count)
194 return;194 return;
195 d.waiting = true;
196 if (reset) {
197 loader.item.reset(false /* forceShow */);
198 }
199 currentIndex = index;195 currentIndex = index;
200 var user = LightDMService.users.data(index, LightDMService.userRoles.NameRole);196 var user = LightDMService.users.data(index, LightDMService.userRoles.NameRole);
201 AccountsService.user = user;197 AccountsService.user = user;
@@ -206,20 +202,17 @@
206 function hideView() {202 function hideView() {
207 if (loader.item) {203 if (loader.item) {
208 loader.item.enabled = false; // drop OSK and prevent interaction204 loader.item.enabled = false; // drop OSK and prevent interaction
209 loader.item.notifyAuthenticationSucceeded(false /* showFakePassword */);
210 loader.item.hide();205 loader.item.hide();
211 }206 }
212 }207 }
213208
214 function login() {209 function login() {
215 d.waiting = true;
216 if (LightDMService.greeter.startSessionSync(root.sessionToStart())) {210 if (LightDMService.greeter.startSessionSync(root.sessionToStart())) {
217 sessionStarted();211 sessionStarted();
218 hideView();212 hideView();
219 } else if (loader.item) {213 } else if (loader.item) {
220 loader.item.notifyAuthenticationFailed();214 loader.item.notifyAuthenticationFailed();
221 }215 }
222 d.waiting = false;
223 }216 }
224217
225 function startUnlock(toTheRight) {218 function startUnlock(toTheRight) {
@@ -239,27 +232,13 @@
239 }232 }
240 }233 }
241234
242 function showPromptMessage(text, isError) {
243 // inefficient, but we only rarely deal with messages
244 var html = text.replace(/&/g, "&amp;")
245 .replace(/</g, "&lt;")
246 .replace(/>/g, "&gt;")
247 .replace(/\n/g, "<br>");
248 if (isError) {
249 html = "<font color=\"#df382c\">" + html + "</font>";
250 }
251
252 if (loader.item) {
253 loader.item.showMessage(html);
254 }
255 }
256
257 function showFingerprintMessage(msg) {235 function showFingerprintMessage(msg) {
236 d.selectUser(d.currentIndex);
237 LightDMService.prompts.prepend(msg, LightDMService.prompts.Error);
258 if (loader.item) {238 if (loader.item) {
259 loader.item.reset(false /* forceShow */);
260 loader.item.showErrorMessage(msg);239 loader.item.showErrorMessage(msg);
240 loader.item.notifyAuthenticationFailed();
261 }241 }
262 showPromptMessage(msg, true);
263 }242 }
264 }243 }
265244
@@ -285,7 +264,6 @@
285264
286 onRequiredChanged: {265 onRequiredChanged: {
287 if (required) {266 if (required) {
288 d.waiting = true;
289 lockedApp = "";267 lockedApp = "";
290 }268 }
291 }269 }
@@ -377,14 +355,14 @@
377 onLoaded: {355 onLoaded: {
378 root.lockedApp = "";356 root.lockedApp = "";
379 item.forceActiveFocus();357 item.forceActiveFocus();
380 d.selectUser(d.currentIndex, true);358 d.selectUser(d.currentIndex);
381 LightDMService.infographic.readyForDataChange();359 LightDMService.infographic.readyForDataChange();
382 }360 }
383361
384 Connections {362 Connections {
385 target: loader.item363 target: loader.item
386 onSelected: {364 onSelected: {
387 d.selectUser(index, true);365 d.selectUser(index);
388 }366 }
389 onResponded: {367 onResponded: {
390 if (root.locked) {368 if (root.locked) {
@@ -481,27 +459,15 @@
481 onShowGreeter: root.forceShow()459 onShowGreeter: root.forceShow()
482 onHideGreeter: root.forcedUnlock = true460 onHideGreeter: root.forcedUnlock = true
483461
484 onShowMessage: d.showPromptMessage(text, isError)462 onLoginError: {
485463 if (!loader.item) {
486 onShowPrompt: {464 return;
487 if (loader.item) {
488 loader.item.showPrompt(text, isSecret, isDefaultPrompt);
489 }465 }
490466
491 d.waiting = false;467 loader.item.notifyAuthenticationFailed();
492 }468
493469 if (!automatic) {
494 onAuthenticationComplete: {470 AccountsService.failedLogins++;
495 d.waiting = false;
496
497 if (LightDMService.greeter.authenticated) {
498 if (!LightDMService.greeter.promptless) {
499 d.login();
500 }
501 } else {
502 if (!LightDMService.greeter.promptless) {
503 AccountsService.failedLogins++;
504 }
505471
506 // Check if we should initiate a factory reset472 // Check if we should initiate a factory reset
507 if (maxFailedLogins >= 2) { // require at least a warning473 if (maxFailedLogins >= 2) { // require at least a warning
@@ -519,14 +485,17 @@
519 forcedDelayTimer.forceDelay();485 forcedDelayTimer.forceDelay();
520 }486 }
521487
522 loader.item.notifyAuthenticationFailed();488 d.selectUser(d.currentIndex);
523 if (!LightDMService.greeter.promptless) {489 }
524 d.selectUser(d.currentIndex, false);490 }
525 }491
526 }492 onLoginSuccess: {
527 }493 if (!automatic) {
528494 d.login();
529 onRequestAuthenticationUser: d.selectUser(d.getUserIndex(user), true)495 }
496 }
497
498 onRequestAuthenticationUser: d.selectUser(d.getUserIndex(user))
530 }499 }
531500
532 Connections {501 Connections {
@@ -589,7 +558,9 @@
589 if (!d.secureFingerprint) {558 if (!d.secureFingerprint) {
590 d.startUnlock(false /* toTheRight */); // use normal login instead559 d.startUnlock(false /* toTheRight */); // use normal login instead
591 }560 }
592 var msg = d.secureFingerprint ? i18n.tr("Try again") : "";561 var msg = d.secureFingerprint ? i18n.tr("Try again") :
562 d.alphanumeric ? i18n.tr("Enter passphrase to unlock") :
563 i18n.tr("Enter passcode to unlock");
593 d.showFingerprintMessage(msg);564 d.showFingerprintMessage(msg);
594 }565 }
595566
@@ -609,8 +580,7 @@
609 }580 }
610 console.log("Identified user by fingerprint:", result);581 console.log("Identified user by fingerprint:", result);
611 if (loader.item) {582 if (loader.item) {
612 loader.item.enabled = false;583 loader.item.showFakePassword();
613 loader.item.notifyAuthenticationSucceeded(true /* showFakePassword */);
614 }584 }
615 if (root.active)585 if (root.active)
616 root.forcedUnlock = true;586 root.forcedUnlock = true;
617587
=== modified file 'qml/Greeter/GreeterPrompt.qml'
--- qml/Greeter/GreeterPrompt.qml 2016-09-22 10:33:39 +0000
+++ qml/Greeter/GreeterPrompt.qml 2017-01-25 16:04:08 +0000
@@ -27,31 +27,25 @@
27 property bool isAlphanumeric27 property bool isAlphanumeric
28 property string text28 property string text
29 property bool isSecret29 property bool isSecret
30 property bool interactive: true
31 readonly property alias enteredText: passwordInput.text
3032
31 signal clicked()33 signal clicked()
32 signal canceled()34 signal canceled()
33 signal responded(string text)35 signal accepted()
34
35 function reset() {
36 passwordInput.text = "";
37 fakeLabel.text = "";
38 d.enabled = true;
39 }
4036
41 function showFakePassword() {37 function showFakePassword() {
42 // Just a silly hack for looking like 4 pin numbers got entered, if38 // 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 was39 // a fingerprint was used and we happen to be using a pin. This was
44 // a request from Design.40 // a request from Design.
45 if (isSecret && isPrompt && !isAlphanumeric) {41 if (isSecret && isPrompt && !isAlphanumeric) {
46 d.enabled = false;42 passwordInput.text = "...."; // actual text doesn't matter
47 text = "...."; // actual text doesn't matter
48 }43 }
49 }44 }
5045
51 StyledItem {46 StyledItem {
52 id: d47 id: d
5348
54 property bool enabled: true
55 readonly property color textColor: passwordInput.enabled ? theme.palette.normal.raisedText49 readonly property color textColor: passwordInput.enabled ? theme.palette.normal.raisedText
56 : theme.palette.disabled.raisedText50 : theme.palette.disabled.raisedText
57 readonly property color selectedColor: passwordInput.enabled ? theme.palette.normal.raised51 readonly property color selectedColor: passwordInput.enabled ? theme.palette.normal.raised
@@ -60,12 +54,6 @@
60 : theme.palette.disabled.raisedSecondaryText54 : theme.palette.disabled.raisedSecondaryText
61 readonly property color errorColor: passwordInput.enabled ? theme.palette.normal.negative55 readonly property color errorColor: passwordInput.enabled ? theme.palette.normal.negative
62 : theme.palette.disabled.negative56 : theme.palette.disabled.negative
63
64 onEnabledChanged: {
65 if (!enabled) {
66 fakeLabel.text = passwordInput.displayText;
67 }
68 }
69 }57 }
7058
71 Rectangle {59 Rectangle {
@@ -86,15 +74,17 @@
86 }74 }
87 }75 }
8876
89 Rectangle {77 StyledItem {
90 id: promptButton78 id: promptButton
91 objectName: "promptButton"79 objectName: "promptButton"
92 anchors.fill: parent80 anchors.fill: parent
93 visible: !root.isPrompt81 visible: !root.isPrompt
82 activeFocusOnTab: true
83
84 styleName: "FocusShape"
9485
95 function triggered() {86 function triggered() {
96 if (d.enabled) {87 if (root.interactive) {
97 d.enabled = false;
98 root.clicked();88 root.clicked();
99 }89 }
100 }90 }
@@ -109,6 +99,7 @@
109 }99 }
110 }100 }
111101
102 Keys.onSpacePressed: triggered();
112 Keys.onReturnPressed: triggered();103 Keys.onReturnPressed: triggered();
113 Keys.onEnterPressed: triggered();104 Keys.onEnterPressed: triggered();
114 MouseArea {105 MouseArea {
@@ -129,6 +120,7 @@
129 anchors.fill: parent120 anchors.fill: parent
130 visible: root.isPrompt121 visible: root.isPrompt
131 opacity: fakeLabel.visible ? 0 : 1122 opacity: fakeLabel.visible ? 0 : 1
123 activeFocusOnTab: true
132124
133 validator: RegExpValidator {125 validator: RegExpValidator {
134 regExp: root.isAlphanumeric ? /^.*$/ : /^\d{4}$/126 regExp: root.isAlphanumeric ? /^.*$/ : /^\d{4}$/
@@ -142,15 +134,23 @@
142134
143 readonly property real frameSpacing: units.gu(0.5)135 readonly property real frameSpacing: units.gu(0.5)
144136
145 style: Item {137 style: StyledItem {
146 property color color: d.textColor
147 property color selectedTextColor: d.selectedColor
148 property color selectionColor: d.textColor
149 property color borderColor: "transparent"
150 property color backgroundColor: "transparent"
151 property color errorColor: d.errorColor
152 property real frameSpacing: passwordInput.frameSpacing
153 anchors.fill: parent138 anchors.fill: parent
139 styleName: "FocusShape"
140
141 // Properties needed by TextField
142 readonly property color color: d.textColor
143 readonly property color selectedTextColor: d.selectedColor
144 readonly property color selectionColor: d.textColor
145 readonly property color borderColor: "transparent"
146 readonly property color backgroundColor: "transparent"
147 readonly property color errorColor: d.errorColor
148 readonly property real frameSpacing: styledItem.frameSpacing
149
150 // Properties needed by FocusShape
151 readonly property bool enabled: styledItem.enabled
152 readonly property bool keyNavigationFocus: styledItem.keyNavigationFocus
153 property bool activeFocusOnTab
154 }154 }
155155
156 secondaryItem: [156 secondaryItem: [
@@ -178,9 +178,8 @@
178 onAccepted: respond()178 onAccepted: respond()
179179
180 function respond() {180 function respond() {
181 if (d.enabled) {181 if (root.interactive) {
182 d.enabled = false;182 root.accepted();
183 root.responded(text);
184 }183 }
185 }184 }
186185
@@ -194,6 +193,7 @@
194 // palette color, whereas we want raisedSecondaryText.193 // palette color, whereas we want raisedSecondaryText.
195 Label {194 Label {
196 id: hint195 id: hint
196 objectName: "promptHint"
197 anchors {197 anchors {
198 left: parent.left198 left: parent.left
199 right: parent.right199 right: parent.right
@@ -223,6 +223,7 @@
223 anchors.leftMargin: passwordInput.frameSpacing * 2223 anchors.leftMargin: passwordInput.frameSpacing * 2
224 anchors.rightMargin: passwordInput.frameSpacing * 2 + capsIcon.visibleWidth224 anchors.rightMargin: passwordInput.frameSpacing * 2 + capsIcon.visibleWidth
225 color: d.drawColor225 color: d.drawColor
226 visible: root.isPrompt && !d.enabled226 text: passwordInput.displayText
227 visible: root.isPrompt && !root.interactive
227 }228 }
228}229}
229230
=== modified file 'qml/Greeter/IntegratedLightDMImpl.qml'
--- qml/Greeter/IntegratedLightDMImpl.qml 2015-11-19 21:47:32 +0000
+++ qml/Greeter/IntegratedLightDMImpl.qml 2017-01-25 16:04:08 +0000
@@ -22,6 +22,7 @@
2222
23 property var greeter: LightDM.Greeter23 property var greeter: LightDM.Greeter
24 property var infographic: LightDM.Infographic24 property var infographic: LightDM.Infographic
25 property var prompts: LightDM.Prompts
25 property var sessions: LightDM.Sessions26 property var sessions: LightDM.Sessions
26 property var sessionRoles: LightDM.SessionRoles27 property var sessionRoles: LightDM.SessionRoles
27 property var users: LightDM.Users28 property var users: LightDM.Users
2829
=== modified file 'qml/Greeter/LightDMService.qml'
--- qml/Greeter/LightDMService.qml 2015-11-19 21:47:32 +0000
+++ qml/Greeter/LightDMService.qml 2017-01-25 16:04:08 +0000
@@ -27,6 +27,7 @@
2727
28 property var greeter: d.valid ? loader.item.greeter : null28 property var greeter: d.valid ? loader.item.greeter : null
29 property var infographic: d.valid ? loader.item.infographic : null29 property var infographic: d.valid ? loader.item.infographic : null
30 property var prompts: d.valid ? loader.item.prompts : null
30 property var sessions: d.valid ? loader.item.sessions : null31 property var sessions: d.valid ? loader.item.sessions : null
31 property var sessionRoles: d.valid ? loader.item.sessionRoles : null32 property var sessionRoles: d.valid ? loader.item.sessionRoles : null
32 property var users: d.valid ? loader.item.users : null33 property var users: d.valid ? loader.item.users : null
@@ -35,10 +36,9 @@
35 // This trickery handles cases where applicationArguments aren't provided36 // This trickery handles cases where applicationArguments aren't provided
36 // such as during testing37 // such as during testing
37 property var fullLightDM: {38 property var fullLightDM: {
38 if (typeof applicationArguments !== "undefined") {39 if (typeof applicationArguments === "undefined" ||
39 if (applicationArguments.mode === "greeter") {40 applicationArguments.mode === "greeter") {
40 return true;41 return true;
41 }
42 }42 }
43 return false;43 return false;
44 }44 }
4545
=== modified file 'qml/Greeter/LoginList.qml'
--- qml/Greeter/LoginList.qml 2016-11-29 00:13:45 +0000
+++ qml/Greeter/LoginList.qml 2017-01-25 16:04:08 +0000
@@ -25,95 +25,50 @@
25 focus: true25 focus: true
2626
27 property alias model: userList.model27 property alias model: userList.model
28 property bool alphanumeric: true28 property alias alphanumeric: promptList.alphanumeric
29 property int currentIndex29 property int currentIndex
30 property bool locked30 property bool locked
31 property bool waiting31 property bool waiting
32 property alias boxVerticalOffset: highlightItem.y32 property alias boxVerticalOffset: highlightItem.y
3333
34 readonly property alias passwordInput: passwordInput
35 readonly property int numAboveBelow: 434 readonly property int numAboveBelow: 4
36 readonly property int cellHeight: units.gu(5)35 readonly property int cellHeight: units.gu(5)
37 readonly property int highlightedHeight: units.gu(15)36 readonly property int highlightedHeight: highlightItem.height
38 readonly property int moveDuration: UbuntuAnimation.FastDuration37 readonly property int moveDuration: UbuntuAnimation.FastDuration
39 property string selectedSession
40 property string currentSession38 property string currentSession
41 readonly property string currentUser: userList.currentItem.username39 readonly property string currentUser: userList.currentItem.username
42 property bool wasPrompted: false
4340
44 signal loginListSessionChanged(string session)
45 signal responded(string response)41 signal responded(string response)
46 signal selected(int index)42 signal selected(int index)
47 signal sessionChooserButtonClicked()43 signal sessionChooserButtonClicked()
4844
49 function tryToUnlock() {45 function tryToUnlock() {
50 if (wasPrompted) {46 promptList.forceActiveFocus();
51 passwordInput.forceActiveFocus();
52 } else {
53 if (root.locked) {
54 root.selected(currentIndex);
55 } else {
56 root.responded("");
57 }
58 }
59 }
60
61 function showMessage(html) {
62 if (infoLabel.text === "") {
63 infoLabel.text = html;
64 } else {
65 infoLabel.text += "<br>" + html;
66 }
67 }
68
69 function showPrompt(text, isSecret, isDefaultPrompt) {
70 passwordInput.text = isDefaultPrompt ? alphanumeric ? i18n.tr("Passphrase")
71 : i18n.tr("Passcode")
72 : text;
73 passwordInput.isPrompt = true;
74 passwordInput.isSecret = isSecret;
75 passwordInput.reset();
76 wasPrompted = true;
77 }47 }
7848
79 function showError() {49 function showError() {
80 wrongPasswordAnimation.start();50 wrongPasswordAnimation.start();
81 root.resetAuthentication();
82 }
83
84 function reset() {
85 root.resetAuthentication();
86 }51 }
8752
88 function showFakePassword() {53 function showFakePassword() {
89 passwordInput.showFakePassword();54 promptList.interactive = false;
90 }55 promptList.showFakePassword();
9156 }
92 QtObject {
93 id: d
94
95 function checkIfPromptless() {
96 if (!waiting && !wasPrompted) {
97 passwordInput.isPrompt = false;
98 passwordInput.text = root.locked ? i18n.tr("Retry")
99 : i18n.tr("Log In")
100 }
101 }
102 }
103
104 onWaitingChanged: d.checkIfPromptless()
105 onLockedChanged: d.checkIfPromptless()
10657
107 theme: ThemeSettings {58 theme: ThemeSettings {
108 name: "Ubuntu.Components.Themes.Ambiance"59 name: "Ubuntu.Components.Themes.Ambiance"
109 }60 }
11061
111 Keys.onUpPressed: {62 Keys.onUpPressed: {
112 selected(currentIndex - 1);63 if (currentIndex > 0) {
64 selected(currentIndex - 1);
65 }
113 event.accepted = true;66 event.accepted = true;
114 }67 }
115 Keys.onDownPressed: {68 Keys.onDownPressed: {
116 selected(currentIndex + 1);69 if (currentIndex + 1 < model.count) {
70 selected(currentIndex + 1);
71 }
117 event.accepted = true;72 event.accepted = true;
118 }73 }
119 Keys.onEscapePressed: {74 Keys.onEscapePressed: {
@@ -135,7 +90,8 @@
135 rightMargin: units.gu(2)90 rightMargin: units.gu(2)
136 }91 }
13792
138 height: root.highlightedHeight93 height: Math.max(units.gu(15), promptList.height + units.gu(8))
94 Behavior on height { NumberAnimation { duration: root.moveDuration; easing.type: Easing.InOutQuad; } }
139 }95 }
14096
141 ListView {97 ListView {
@@ -153,14 +109,8 @@
153 interactive: count > 1109 interactive: count > 1
154110
155 readonly property bool movingInternally: moveTimer.running || userList.moving111 readonly property bool movingInternally: moveTimer.running || userList.moving
156 onMovingInternallyChanged: {
157 if (!movingInternally) {
158 root.selected(currentIndex);
159 }
160 }
161112
162 onCurrentIndexChanged: {113 onCurrentIndexChanged: {
163 root.resetAuthentication();
164 moveTimer.start();114 moveTimer.start();
165 }115 }
166116
@@ -203,7 +153,10 @@
203 // Add an offset to bottomMargin for any items below the highlight153 // Add an offset to bottomMargin for any items below the highlight
204 bottomMargin: -(units.gu(4) + (parent.belowHighlight ? parent.belowOffset : 0))154 bottomMargin: -(units.gu(4) + (parent.belowHighlight ? parent.belowOffset : 0))
205 }155 }
206 text: realName156 text: userList.currentIndex === index
157 && name === "*other"
158 && LightDMService.greeter.authenticationUser !== ""
159 ? LightDMService.greeter.authenticationUser : realName
207 color: userList.currentIndex !== index ? theme.palette.normal.raised160 color: userList.currentIndex !== index ? theme.palette.normal.raised
208 : theme.palette.normal.raisedText161 : theme.palette.normal.raisedText
209162
@@ -309,64 +262,42 @@
309 }262 }
310 }263 }
311264
312 FadingLabel {265 PromptList {
313 id: infoLabel266 id: promptList
314 objectName: "infoLabel"267 objectName: "promptList"
315 anchors {
316 bottom: passwordInput.top
317 left: highlightItem.left
318 topMargin: units.gu(1)
319 bottomMargin: units.gu(1)
320 leftMargin: units.gu(2)
321 rightMargin: units.gu(1)
322 }
323
324 color: theme.palette.normal.raisedText
325 width: root.width - anchors.leftMargin - anchors.rightMargin
326 fontSize: "small"
327 textFormat: Text.StyledText
328
329 opacity: (userList.movingInternally || text == "") ? 0 : 1
330 Behavior on opacity {
331 NumberAnimation { duration: 100 }
332 }
333 }
334
335 GreeterPrompt {
336 id: passwordInput
337 objectName: "passwordInput"
338 anchors {268 anchors {
339 bottom: highlightItem.bottom269 bottom: highlightItem.bottom
340 horizontalCenter: highlightItem.horizontalCenter270 horizontalCenter: highlightItem.horizontalCenter
341 margins: units.gu(2)271 margins: units.gu(2)
342 }272 }
343 width: highlightItem.width - anchors.margins * 2273 width: highlightItem.width - anchors.margins * 2
344 opacity: userList.movingInternally ? 0 : 1274
345275 onClicked: {
346 activeFocusOnTab: true276 interactive = false;
347 isAlphanumeric: root.alphanumeric277 if (root.locked) {
348278 root.selected(currentIndex);
349 onClicked: root.tryToUnlock()279 } else {
350 onResponded: root.responded(text)280 root.responded("");
351 onCanceled: root.selected(currentIndex)281 }
352282 }
353 Behavior on opacity {283 onResponded: {
354 NumberAnimation { duration: 100 }284 interactive = false;
355 }285 root.responded(text);
356286 }
357 WrongPasswordAnimation {287 onCanceled: {
358 id: wrongPasswordAnimation288 interactive = false;
359 objectName: "wrongPasswordAnimation"289 root.selected(currentIndex);
360 target: passwordInput290 }
291
292 Connections {
293 target: LightDMService.prompts
294 onModelReset: promptList.interactive = true
361 }295 }
362 }296 }
363297
364 function resetAuthentication() {298 WrongPasswordAnimation {
365 if (!userList.currentItem) {299 id: wrongPasswordAnimation
366 return;300 objectName: "wrongPasswordAnimation"
367 }301 target: promptList
368 infoLabel.text = "";
369 passwordInput.reset();
370 root.wasPrompted = false;
371 }302 }
372}303}
373304
=== modified file 'qml/Greeter/NarrowView.qml'
--- qml/Greeter/NarrowView.qml 2016-08-30 20:23:15 +0000
+++ qml/Greeter/NarrowView.qml 2017-01-25 16:04:08 +0000
@@ -48,14 +48,6 @@
48 signal tease()48 signal tease()
49 signal emergencyCall()49 signal emergencyCall()
5050
51 function showMessage(html) {
52 loginList.showMessage(html);
53 }
54
55 function showPrompt(text, isSecret, isDefaultPrompt) {
56 loginList.showPrompt(text, isSecret, isDefaultPrompt);
57 }
58
59 function showLastChance() {51 function showLastChance() {
60 /* TODO: when we finish support for resetting device after too many52 /* TODO: when we finish support for resetting device after too many
61 failed logins, we should re-add this popup.53 failed logins, we should re-add this popup.
@@ -75,10 +67,8 @@
75 coverPage.hide();67 coverPage.hide();
76 }68 }
7769
78 function notifyAuthenticationSucceeded(showFakePassword) {70 function showFakePassword() {
79 if (showFakePassword) {71 loginList.showFakePassword();
80 loginList.showFakePassword();
81 }
82 }72 }
8373
84 function notifyAuthenticationFailed() {74 function notifyAuthenticationFailed() {
@@ -89,11 +79,8 @@
89 coverPage.showErrorMessage(msg);79 coverPage.showErrorMessage(msg);
90 }80 }
9181
92 function reset(forceShow) {82 function forceShow() {
93 loginList.reset();83 coverPage.show();
94 if (forceShow) {
95 coverPage.show();
96 }
97 }84 }
9885
99 function tryToUnlock(toTheRight) {86 function tryToUnlock(toTheRight) {
@@ -120,6 +107,7 @@
120 objectName: "lockscreen"107 objectName: "lockscreen"
121 anchors.fill: parent108 anchors.fill: parent
122 shown: false109 shown: false
110 opacity: 0
123111
124 showAnimation: StandardAnimation { property: "opacity"; to: 1 }112 showAnimation: StandardAnimation { property: "opacity"; to: 1 }
125 hideAnimation: StandardAnimation { property: "opacity"; to: 0 }113 hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
@@ -195,10 +183,12 @@
195 onClicked: hide()183 onClicked: hide()
196184
197 onShowProgressChanged: {185 onShowProgressChanged: {
198 if (showProgress === 1) {186 if (showProgress === 0) {
199 loginList.reset();187 if (lockscreen.shown) {
200 } else if (showProgress === 0) {188 loginList.tryToUnlock();
201 loginList.tryToUnlock();189 } else {
190 root.responded("");
191 }
202 }192 }
203 }193 }
204194
205195
=== added file 'qml/Greeter/PromptList.qml'
--- qml/Greeter/PromptList.qml 1970-01-01 00:00:00 +0000
+++ qml/Greeter/PromptList.qml 2017-01-25 16:04:08 +0000
@@ -0,0 +1,148 @@
1/*
2 * Copyright (C) 2017 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
17import QtQuick 2.4
18import Ubuntu.Components 1.3
19import "../Components"
20import "." 0.1
21
22FocusScope {
23 id: root
24 height: childrenRect.height
25
26 property bool alphanumeric: true
27 property bool interactive: true
28
29 signal responded(string text)
30 signal clicked()
31 signal canceled()
32
33 function showFakePassword() {
34 for (var i = 0; i < repeater.count; i++) {
35 var item = repeater.itemAt(i).item;
36 if (item.isPrompt) {
37 item.showFakePassword();
38 }
39 }
40 }
41
42 QtObject {
43 id: d
44
45 function sendResponse() {
46 for (var i = 0; i < repeater.count; i++) {
47 var item = repeater.itemAt(i).item;
48 if (item.isPrompt) {
49 root.responded(item.enteredText);
50 }
51 }
52 }
53 }
54
55 Column {
56 width: parent.width
57 spacing: units.gu(0.5)
58
59 Repeater {
60 id: repeater
61 model: LightDMService.prompts
62
63 delegate: Loader {
64 id: loader
65
66 readonly property bool isLabel: model.type == LightDMService.prompts.Message ||
67 model.type == LightDMService.prompts.Error
68 readonly property var modelData: model
69
70 sourceComponent: isLabel ? infoLabel : greeterPrompt
71
72 onLoaded: {
73 for (var i = 0; i < repeater.count; i++) {
74 var item = repeater.itemAt(i);
75 if (item && !item.isLabel) {
76 item.focus = true;
77 break;
78 }
79 }
80 loader.item.opacity = 1;
81 }
82
83 Binding {
84 target: loader.item
85 property: "model"
86 value: loader.modelData
87 }
88 }
89 }
90 }
91
92 Component {
93 id: infoLabel
94
95 FadingLabel {
96 objectName: "infoLabel" + model.index
97 width: root.width
98
99 property var model
100 readonly property bool isPrompt: false
101
102 color: model.type === LightDMService.prompts.Message ? theme.palette.normal.raisedText
103 : theme.palette.normal.negative
104 fontSize: "small"
105 textFormat: Text.PlainText
106 text: model.text
107
108 Behavior on opacity { UbuntuNumberAnimation {} }
109 opacity: 0
110 }
111 }
112
113 Component {
114 id: greeterPrompt
115
116 GreeterPrompt {
117 objectName: "greeterPrompt" + model.index
118 width: root.width
119
120 property var model
121
122 interactive: root.interactive
123 isAlphanumeric: model.text !== "" || root.alphanumeric
124 isPrompt: model.type !== LightDMService.prompts.Button
125 isSecret: model.type === LightDMService.prompts.Secret
126 text: model.text ? model.text : (isAlphanumeric ? i18n.tr("Passphrase") : i18n.tr("Passcode"))
127
128 onClicked: root.clicked()
129 onAccepted: {
130 // If there is another GreeterPrompt, focus it.
131 for (var i = model.index + 1; i < repeater.count; i++) {
132 var item = repeater.itemAt(i).item;
133 if (item.isPrompt) {
134 item.forceActiveFocus();
135 return;
136 }
137 }
138
139 // Nope we're the last one; just send our response.
140 d.sendResponse();
141 }
142 onCanceled: root.canceled()
143
144 Behavior on opacity { UbuntuNumberAnimation {} }
145 opacity: 0
146 }
147 }
148}
0149
=== modified file 'qml/Greeter/WideView.qml'
--- qml/Greeter/WideView.qml 2016-11-29 00:13:45 +0000
+++ qml/Greeter/WideView.qml 2017-01-25 16:04:08 +0000
@@ -52,16 +52,8 @@
52 loginList.showError();52 loginList.showError();
53 }53 }
5454
55 function reset(forceShow) {55 function forceShow() {
56 loginList.reset();56 // Nothing to do, we are always fully shown
57 }
58
59 function showMessage(html) {
60 loginList.showMessage(html);
61 }
62
63 function showPrompt(text, isSecret, isDefaultPrompt) {
64 loginList.showPrompt(text, isSecret, isDefaultPrompt);
65 }57 }
6658
67 function tryToUnlock(toTheRight) {59 function tryToUnlock(toTheRight) {
@@ -84,10 +76,8 @@
84 coverPage.hide();76 coverPage.hide();
85 }77 }
8678
87 function notifyAuthenticationSucceeded(showFakePassword) {79 function showFakePassword() {
88 if (showFakePassword) {80 loginList.showFakePassword();
89 loginList.showFakePassword();
90 }
91 }81 }
9282
93 function showLastChance() {83 function showLastChance() {
@@ -125,8 +115,6 @@
125 id: loginList115 id: loginList
126 objectName: "loginList"116 objectName: "loginList"
127117
128 property int selectedUserIndex: 0
129
130 width: units.gu(40)118 width: units.gu(40)
131 anchors {119 anchors {
132 left: parent.left120 left: parent.left
@@ -141,15 +129,18 @@
141 Behavior on boxVerticalOffset { UbuntuNumberAnimation {} }129 Behavior on boxVerticalOffset { UbuntuNumberAnimation {} }
142130
143 model: root.userModel131 model: root.userModel
144 currentSession: LightDMService.users.data(selectedUserIndex, LightDMService.userRoles.SessionRole);
145 onResponded: root.responded(response)132 onResponded: root.responded(response)
146 onSelected: {133 onSelected: root.selected(index)
147 root.selected(index)
148 loginList.selectedUserIndex = index;
149 }
150 onSessionChooserButtonClicked: parent.state = "SessionsList"134 onSessionChooserButtonClicked: parent.state = "SessionsList"
135 onCurrentIndexChanged: setCurrentSession()
151136
152 Keys.forwardTo: [sessionChooserLoader.item]137 Keys.forwardTo: [sessionChooserLoader.item]
138
139 Component.onCompleted: setCurrentSession()
140
141 function setCurrentSession() {
142 currentSession = LightDMService.users.data(currentIndex, LightDMService.userRoles.SessionRole);
143 }
153 }144 }
154145
155 Loader {146 Loader {
@@ -177,7 +168,7 @@
177 onSessionSelected: loginList.currentSession = sessionKey168 onSessionSelected: loginList.currentSession = sessionKey
178 onShowLoginList: {169 onShowLoginList: {
179 coverPage.state = "LoginList"170 coverPage.state = "LoginList"
180 loginList.passwordInput.forceActiveFocus();171 loginList.tryToUnlock();
181 }172 }
182 ignoreUnknownSignals: true173 ignoreUnknownSignals: true
183 }174 }
184175
=== modified file 'qml/Launcher/Drawer.qml'
--- qml/Launcher/Drawer.qml 2016-11-28 10:17:22 +0000
+++ qml/Launcher/Drawer.qml 2017-01-25 16:04:08 +0000
@@ -20,6 +20,7 @@
20import Utils 0.120import Utils 0.1
21import "../Components"21import "../Components"
22import Qt.labs.settings 1.022import Qt.labs.settings 1.0
23import GSettings 1.0
2324
24FocusScope {25FocusScope {
25 id: root26 id: root
@@ -43,6 +44,16 @@
43 searchField.focus = true;44 searchField.focus = true;
44 }45 }
4546
47 Keys.onPressed: {
48 if (event.text.trim() !== "") {
49 focusInput();
50 searchField.text = event.text;
51 }
52 // Catch all presses here in case the navigation lets something through
53 // We never want to end up in the launcher with focus
54 event.accepted = true;
55 }
56
46 Settings {57 Settings {
47 property alias selectedTab: sections.selectedIndex58 property alias selectedTab: sections.selectedIndex
48 }59 }
@@ -76,9 +87,13 @@
7687
77 TextField {88 TextField {
78 id: searchField89 id: searchField
90 objectName: "searchField"
79 anchors { left: parent.left; top: parent.top; right: parent.right; margins: units.gu(1) }91 anchors { left: parent.left; top: parent.top; right: parent.right; margins: units.gu(1) }
80 placeholderText: i18n.tr("Search…")92 placeholderText: i18n.tr("Search…")
81 focus: true93 focus: true
94
95 KeyNavigation.down: sections
96
82 onAccepted: {97 onAccepted: {
83 if (searchField.displayText != "" && listLoader.item && listLoader.item.currentItem) {98 if (searchField.displayText != "" && listLoader.item && listLoader.item.currentItem) {
84 root.applicationSelected(listLoader.item.getFirstAppId());99 root.applicationSelected(listLoader.item.getFirstAppId());
@@ -95,7 +110,14 @@
95110
96 Sections {111 Sections {
97 id: sections112 id: sections
113 objectName: "drawerSections"
98 width: parent.width114 width: parent.width
115
116 KeyNavigation.up: searchField
117 KeyNavigation.down: headerFocusScope
118 KeyNavigation.backtab: searchField
119 KeyNavigation.tab: headerFocusScope
120
99 actions: [121 actions: [
100 Action {122 Action {
101 text: i18n.ctr("Apps sorted alphabetically", "A-Z")123 text: i18n.ctr("Apps sorted alphabetically", "A-Z")
@@ -115,9 +137,45 @@
115 }137 }
116 }138 }
117139
140 FocusScope {
141 id: headerFocusScope
142 objectName: "headerFocusScope"
143 KeyNavigation.up: sections
144 KeyNavigation.down: listLoader.item
145 KeyNavigation.backtab: sections
146 KeyNavigation.tab: listLoader.item
147 activeFocusOnTab: true
148
149 GSettings {
150 id: settings
151 schema.id: "com.canonical.Unity8"
152 }
153
154 Keys.onPressed: {
155 switch (event.key) {
156 case Qt.Key_Return:
157 case Qt.Key_Enter:
158 case Qt.Key_Space:
159 trigger();
160 event.accepted = true;
161 }
162 }
163
164 function trigger() {
165 Qt.openUrlExternally(settings.appstoreUri)
166 }
167 }
168
118 Loader {169 Loader {
119 id: listLoader170 id: listLoader
120 anchors { left: parent.left; top: sectionsContainer.bottom; right: parent.right; bottom: parent.bottom; leftMargin: units.gu(1); rightMargin: units.gu(1) }171 objectName: "drawerListLoader"
172 anchors { left: parent.left; top: sectionsContainer.bottom; right: parent.right; bottom: parent.bottom }
173
174 KeyNavigation.up: headerFocusScope
175 KeyNavigation.down: searchField
176 KeyNavigation.backtab: headerFocusScope
177 KeyNavigation.tab: searchField
178
121 sourceComponent: {179 sourceComponent: {
122 switch (sections.selectedIndex) {180 switch (sections.selectedIndex) {
123 case 0: return aToZComponent;181 case 0: return aToZComponent;
@@ -167,10 +225,13 @@
167 Component {225 Component {
168 id: mostUsedComponent226 id: mostUsedComponent
169 DrawerListView {227 DrawerListView {
228 id: mostUsedListView
170229
171 header: MoreAppsHeader {230 header: MoreAppsHeader {
172 width: parent.width231 width: parent.width
173 height: units.gu(6)232 height: units.gu(6)
233 highlighted: headerFocusScope.activeFocus
234 onClicked: headerFocusScope.trigger();
174 }235 }
175236
176 model: AppDrawerProxyModel {237 model: AppDrawerProxyModel {
@@ -180,7 +241,8 @@
180 }241 }
181242
182 delegate: UbuntuShape {243 delegate: UbuntuShape {
183 width: parent.width244 width: parent.width - units.gu(2)
245 anchors.horizontalCenter: parent.horizontalCenter
184 color: "#20ffffff"246 color: "#20ffffff"
185 aspect: UbuntuShape.Flat247 aspect: UbuntuShape.Flat
186 // NOTE: Cannot use gridView.rows here as it would evaluate to 0 at first and only update later,248 // NOTE: Cannot use gridView.rows here as it would evaluate to 0 at first and only update later,
@@ -196,6 +258,9 @@
196 bottomMargin: units.gu(1)258 bottomMargin: units.gu(1)
197 clip: true259 clip: true
198260
261 interactive: true
262 focus: index == mostUsedListView.currentIndex
263
199 model: sortProxyModel264 model: sortProxyModel
200265
201 delegateWidth: units.gu(8)266 delegateWidth: units.gu(8)
@@ -209,10 +274,13 @@
209 Component {274 Component {
210 id: aToZComponent275 id: aToZComponent
211 DrawerListView {276 DrawerListView {
277 id: aToZListView
212278
213 header: MoreAppsHeader {279 header: MoreAppsHeader {
214 width: parent.width280 width: parent.width
215 height: units.gu(6)281 height: units.gu(6)
282 highlighted: headerFocusScope.activeFocus
283 onClicked: headerFocusScope.trigger();
216 }284 }
217285
218 model: AppDrawerProxyModel {286 model: AppDrawerProxyModel {
@@ -222,7 +290,8 @@
222 }290 }
223291
224 delegate: UbuntuShape {292 delegate: UbuntuShape {
225 width: parent.width293 width: parent.width - units.gu(2)
294 anchors.horizontalCenter: parent.horizontalCenter
226 color: "#20ffffff"295 color: "#20ffffff"
227 aspect: UbuntuShape.Flat296 aspect: UbuntuShape.Flat
228297
@@ -244,7 +313,8 @@
244 anchors { left: parent.left; top: categoryNameLabel.bottom; right: parent.right; topMargin: units.gu(1) }313 anchors { left: parent.left; top: categoryNameLabel.bottom; right: parent.right; topMargin: units.gu(1) }
245 height: rows * delegateHeight314 height: rows * delegateHeight
246315
247 interactive: false316 interactive: true
317 focus: index == aToZListView.currentIndex
248318
249 model: AppDrawerProxyModel {319 model: AppDrawerProxyModel {
250 id: categoryModel320 id: categoryModel
@@ -263,10 +333,13 @@
263 Component {333 Component {
264 id: drawerDelegateComponent334 id: drawerDelegateComponent
265 AbstractButton {335 AbstractButton {
336 id: drawerDelegate
266 width: GridView.view.cellWidth337 width: GridView.view.cellWidth
267 height: units.gu(10)338 height: units.gu(10)
268 objectName: "drawerItem_" + model.appId339 objectName: "drawerItem_" + model.appId
269340
341 readonly property bool focused: index === GridView.view.currentIndex && GridView.view.activeFocus
342
270 onClicked: root.applicationSelected(model.appId)343 onClicked: root.applicationSelected(model.appId)
271344
272 Column {345 Column {
@@ -290,6 +363,15 @@
290 source: model.icon363 source: model.icon
291 }364 }
292 sourceFillMode: UbuntuShape.PreserveAspectCrop365 sourceFillMode: UbuntuShape.PreserveAspectCrop
366
367 StyledItem {
368 styleName: "FocusShape"
369 anchors.fill: parent
370 StyleHints {
371 visible: drawerDelegate.focused
372 radius: units.gu(2.55)
373 }
374 }
293 }375 }
294376
295 Label {377 Label {
296378
=== modified file 'qml/Launcher/DrawerGridView.qml'
--- qml/Launcher/DrawerGridView.qml 2016-11-10 14:39:18 +0000
+++ qml/Launcher/DrawerGridView.qml 2017-01-25 16:04:08 +0000
@@ -17,7 +17,7 @@
17import QtQuick 2.417import QtQuick 2.4
18import "../Components"18import "../Components"
1919
20Item {20FocusScope {
21 id: root21 id: root
2222
23 property int delegateWidth: units.gu(10)23 property int delegateWidth: units.gu(10)
@@ -25,6 +25,7 @@
25 property alias delegate: gridView.delegate25 property alias delegate: gridView.delegate
26 property alias model: gridView.model26 property alias model: gridView.model
27 property alias interactive: gridView.interactive27 property alias interactive: gridView.interactive
28 property alias currentIndex: gridView.currentIndex
2829
29 property alias header: gridView.header30 property alias header: gridView.header
30 property alias topMargin: gridView.topMargin31 property alias topMargin: gridView.topMargin
@@ -37,6 +38,7 @@
37 id: gridView38 id: gridView
38 anchors.fill: parent39 anchors.fill: parent
39 leftMargin: spacing40 leftMargin: spacing
41 focus: true
4042
41 readonly property int overflow: width - (root.columns * root.delegateWidth)43 readonly property int overflow: width - (root.columns * root.delegateWidth)
42 readonly property real spacing: overflow / (root.columns)44 readonly property real spacing: overflow / (root.columns)
4345
=== modified file 'qml/Launcher/DrawerListView.qml'
--- qml/Launcher/DrawerListView.qml 2016-11-28 14:56:02 +0000
+++ qml/Launcher/DrawerListView.qml 2017-01-25 16:04:08 +0000
@@ -25,6 +25,12 @@
25 bottomMargin: units.gu(1)25 bottomMargin: units.gu(1)
26 spacing: units.gu(1)26 spacing: units.gu(1)
27 clip: true27 clip: true
28 focus: true
29
30 onActiveFocusChanged: {
31 currentIndex = -1;
32 currentIndex = 0;
33 }
2834
29 function getFirstAppId() {35 function getFirstAppId() {
30 return model.appId(0);36 return model.appId(0);
3137
=== modified file 'qml/Launcher/Launcher.qml'
--- qml/Launcher/Launcher.qml 2016-12-07 13:47:15 +0000
+++ qml/Launcher/Launcher.qml 2017-01-25 16:04:08 +0000
@@ -74,7 +74,7 @@
74 } else {74 } else {
75 superPressTimer.stop();75 superPressTimer.stop();
76 superLongPressTimer.stop();76 superLongPressTimer.stop();
77 launcher.switchToNextState("");77 switchToNextState("");
78 panel.shortcutHintsShown = false;78 panel.shortcutHintsShown = false;
79 }79 }
80 }80 }
@@ -87,14 +87,14 @@
87 superPressTimer.stop();87 superPressTimer.stop();
88 superLongPressTimer.stop();88 superLongPressTimer.stop();
89 } else {89 } else {
90 switchToNextState("");
91 root.focus = false;
90 if (panel.highlightIndex == -1) {92 if (panel.highlightIndex == -1) {
91 showDashHome();93 root.showDashHome();
92 } else if (panel.highlightIndex >= 0){94 } else if (panel.highlightIndex >= 0){
93 launcherApplicationSelected(LauncherModel.get(panel.highlightIndex).appId);95 launcherApplicationSelected(LauncherModel.get(panel.highlightIndex).appId);
94 }96 }
95 panel.highlightIndex = -2;97 panel.highlightIndex = -2;
96 switchToNextState("");
97 root.focus = false;
98 }98 }
99 }99 }
100100
@@ -108,6 +108,10 @@
108 }108 }
109 }109 }
110110
111 onPanelWidthChanged: {
112 hint();
113 }
114
111 function hide(flags) {115 function hide(flags) {
112 if ((flags & ignoreHideIfMouseOverLauncher) && Utils.Functions.itemUnderMouse(panel)) {116 if ((flags & ignoreHideIfMouseOverLauncher) && Utils.Functions.itemUnderMouse(panel)) {
113 return;117 return;
@@ -206,7 +210,7 @@
206 case Qt.Key_Return:210 case Qt.Key_Return:
207 case Qt.Key_Space:211 case Qt.Key_Space:
208 if (panel.highlightIndex == -1) {212 if (panel.highlightIndex == -1) {
209 showDashHome();213 root.showDashHome();
210 } else if (panel.highlightIndex >= 0) {214 } else if (panel.highlightIndex >= 0) {
211 launcherApplicationSelected(LauncherModel.get(panel.highlightIndex).appId);215 launcherApplicationSelected(LauncherModel.get(panel.highlightIndex).appId);
212 }216 }
@@ -469,7 +473,7 @@
469 }473 }
470 onPassed: {474 onPassed: {
471 if (root.drawerEnabled) {475 if (root.drawerEnabled) {
472 root.switchToNextState("drawer");476 root.openDrawer()
473 }477 }
474 }478 }
475479
@@ -545,8 +549,7 @@
545 if (!dragging) {549 if (!dragging) {
546 if (distance > panel.width / 2) {550 if (distance > panel.width / 2) {
547 if (root.drawerEnabled && distance > panel.width * 3 && dragDirection() !== "left") {551 if (root.drawerEnabled && distance > panel.width * 3 && dragDirection() !== "left") {
548 root.switchToNextState("drawer");552 root.openDrawer(false)
549 root.focus = true;
550 } else {553 } else {
551 root.switchToNextState("visible");554 root.switchToNextState("visible");
552 }555 }
553556
=== modified file 'qml/Launcher/LauncherDelegate.qml'
--- qml/Launcher/LauncherDelegate.qml 2016-09-26 11:58:56 +0000
+++ qml/Launcher/LauncherDelegate.qml 2017-01-25 16:04:08 +0000
@@ -124,15 +124,14 @@
124 height: parent.itemHeight + units.gu(1)124 height: parent.itemHeight + units.gu(1)
125 anchors.centerIn: parent125 anchors.centerIn: parent
126126
127 Image {127 StyledItem {
128 objectName: "focusRing"128 styleName: "FocusShape"
129 anchors.centerIn: iconShape129 anchors.fill: iconShape
130 height: width * 15 / 16130 activeFocusOnTab: true
131 width: iconShape.width + units.gu(1)131 StyleHints {
132 source: "graphics/launcher-app-focus-ring.svg"132 visible: root.highlighted
133 sourceSize.width: width133 radius: units.gu(2.55)
134 sourceSize.height: height134 }
135 visible: root.highlighted
136 }135 }
137136
138 ProportionalShape {137 ProportionalShape {
139138
=== modified file 'qml/Launcher/LauncherPanel.qml'
--- qml/Launcher/LauncherPanel.qml 2017-01-10 14:44:00 +0000
+++ qml/Launcher/LauncherPanel.qml 2017-01-25 16:04:08 +0000
@@ -80,6 +80,7 @@
80 }80 }
8181
82 Rectangle {82 Rectangle {
83 id: bfb
83 objectName: "buttonShowDashHome"84 objectName: "buttonShowDashHome"
84 width: parent.width85 width: parent.width
85 height: width * .986 height: width * .9
@@ -100,13 +101,15 @@
100 activeFocusOnPress: false101 activeFocusOnPress: false
101 onClicked: root.showDashHome()102 onClicked: root.showDashHome()
102 }103 }
103 Rectangle {104
104 objectName: "bfbFocusHighlight"105 StyledItem {
106 styleName: "FocusShape"
105 anchors.fill: parent107 anchors.fill: parent
106 border.color: "white"108 anchors.margins: units.gu(.5)
107 border.width: units.dp(1)109 StyleHints {
108 color: "transparent"110 visible: bfb.highlighted
109 visible: parent.highlighted111 radius: 0
112 }
110 }113 }
111 }114 }
112115
@@ -788,6 +791,8 @@
788 quickList.model = launcherListView.model.get(index).quickList;791 quickList.model = launcherListView.model.get(index).quickList;
789 quickList.appId = launcherListView.model.get(index).appId;792 quickList.appId = launcherListView.model.get(index).appId;
790 quickList.state = "open";793 quickList.state = "open";
794 root.highlightIndex = index;
795 quickList.forceActiveFocus();
791 }796 }
792797
793 Item {798 Item {
794799
=== modified file 'qml/Launcher/MoreAppsHeader.qml'
--- qml/Launcher/MoreAppsHeader.qml 2016-11-04 10:10:26 +0000
+++ qml/Launcher/MoreAppsHeader.qml 2017-01-25 16:04:08 +0000
@@ -4,17 +4,24 @@
4AbstractButton {4AbstractButton {
5 id: root5 id: root
66
7 onClicked: {7 property bool highlighted: false
8 // TODO: Make this point to the snappy store as soon as we stop landing to vivid
9 Qt.openUrlExternally("scope://com.canonical.scopes.clickstore")
10 }
118
12 UbuntuShape {9 UbuntuShape {
13 width: parent.width10 width: parent.width - units.gu(2)
11 anchors.horizontalCenter: parent.horizontalCenter
14 height: parent.height - units.gu(1)12 height: parent.height - units.gu(1)
15 color: "#20ffffff"13 color: "#20ffffff"
16 aspect: UbuntuShape.Flat14 aspect: UbuntuShape.Flat
1715
16 StyledItem {
17 styleName: "FocusShape"
18 anchors.fill: parent
19 activeFocusOnTab: true
20 StyleHints {
21 visible: root.highlighted
22 }
23 }
24
18 Row {25 Row {
19 anchors.fill: parent26 anchors.fill: parent
20 anchors.margins: units.gu(1)27 anchors.margins: units.gu(1)
2128
=== removed file 'qml/Launcher/graphics/launcher-app-focus-ring.svg'
--- qml/Launcher/graphics/launcher-app-focus-ring.svg 2015-12-15 12:53:53 +0000
+++ qml/Launcher/graphics/launcher-app-focus-ring.svg 1970-01-01 00:00:00 +0000
@@ -1,12 +0,0 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<svg width="172px" height="163px" viewBox="0 0 172 163" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
3 <!-- Generator: Sketch 3.4.4 (17249) - http://www.bohemiancoding.com/sketch -->
4 <title>Shape</title>
5 <desc>Created with Sketch.</desc>
6 <defs></defs>
7 <g id="•-Launcher" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
8 <g id="Artboard-9" sketch:type="MSArtboardGroup" transform="translate(-163.000000, -1436.000000)" fill="#E95420">
9 <path d="M221.983432,1440 L221.983432,1440 C195.6127,1440 184.708233,1442.4723 177.107949,1450.10734 C169.476819,1457.77336 167,1468.79245 167,1495.3481 L167,1538.9019 C167,1565.45755 169.476819,1576.47664 177.107949,1584.14266 C184.708233,1591.7777 195.6127,1594.25 221.983432,1594.25 L276.016868,1594.25 C302.387595,1594.25 313.291998,1591.77771 320.892221,1584.14264 C328.523252,1576.47663 331,1565.45769 331,1538.9019 L331,1495.3481 C331,1468.79231 328.523252,1457.77337 320.892221,1450.10736 C313.291998,1442.47229 302.387595,1440 276.016868,1440 L221.983432,1440 Z M221.983432,1436 L276.016868,1436 C302.345315,1436 314.848953,1438.36655 323.727108,1447.2854 C332.633306,1456.23243 335,1468.85167 335,1495.3481 L335,1538.9019 C335,1565.39833 332.633306,1578.01757 323.727108,1586.9646 C314.848953,1595.88345 302.345315,1598.25 276.016868,1598.25 L221.983432,1598.25 C195.654985,1598.25 183.151291,1595.88345 174.273077,1586.96463 C165.366772,1578.0176 163,1565.39822 163,1538.9019 L163,1495.3481 C163,1468.85178 165.366772,1456.2324 174.273077,1447.28537 C183.151291,1438.36655 195.654985,1436 221.983432,1436 L221.983432,1436 Z" id="Shape" sketch:type="MSShapeGroup"></path>
10 </g>
11 </g>
12</svg>
13\ No newline at end of file0\ No newline at end of file
141
=== modified file 'qml/OrientedShell.qml'
--- qml/OrientedShell.qml 2016-12-13 09:56:20 +0000
+++ qml/OrientedShell.qml 2017-01-25 16:04:08 +0000
@@ -266,7 +266,8 @@
266 nativeWidth: root.width266 nativeWidth: root.width
267 nativeHeight: root.height267 nativeHeight: root.height
268 mode: applicationArguments.mode268 mode: applicationArguments.mode
269 hasMouse: miceModel.count + touchPadModel.count > 0269 hasMouse: pointerInputDevices > 0
270 hasKeyboard: keyboardsModel.count > 0
270 // TODO: Factor in if the current screen is a touch screen and if the user wants to271 // TODO: Factor in if the current screen is a touch screen and if the user wants to
271 // have multiple keyboards around. For now we only enable one keyboard at a time272 // have multiple keyboards around. For now we only enable one keyboard at a time
272 // thus hiding it here if there is a physical one around or if we have a second273 // thus hiding it here if there is a physical one around or if we have a second
273274
=== modified file 'qml/Panel/PanelBar.qml'
--- qml/Panel/PanelBar.qml 2016-09-30 14:27:30 +0000
+++ qml/Panel/PanelBar.qml 2017-01-25 16:04:08 +0000
@@ -49,6 +49,7 @@
49 row.resetCurrentItem();49 row.resetCurrentItem();
50 }50 }
51 row.setCurrentItemIndex(index);51 row.setCurrentItemIndex(index);
52 d.alignIndicators();
52 }53 }
5354
54 function addScrollOffset(scrollAmmout) {55 function addScrollOffset(scrollAmmout) {
5556
=== modified file 'qml/Panel/PanelMenu.qml'
--- qml/Panel/PanelMenu.qml 2016-12-22 14:55:39 +0000
+++ qml/Panel/PanelMenu.qml 2017-01-25 16:04:08 +0000
@@ -140,6 +140,19 @@
140 visible: !root.fullyClosed140 visible: !root.fullyClosed
141 }141 }
142142
143 Keys.onPressed: {
144 if (event.key === Qt.Key_Left) {
145 bar.setCurrentItemIndex(bar.currentItemIndex - 1);
146 event.accepted = true;
147 } else if (event.key === Qt.Key_Right) {
148 bar.setCurrentItemIndex(bar.currentItemIndex + 1);
149 event.accepted = true;
150 } else if (event.key === Qt.Key_Escape) {
151 root.hide();
152 event.accepted = true;
153 }
154 }
155
143 PanelBar {156 PanelBar {
144 id: bar157 id: bar
145 objectName: "indicatorsBar"158 objectName: "indicatorsBar"
@@ -368,6 +381,7 @@
368 State {381 State {
369 name: "commit"382 name: "commit"
370 extend: "locked"383 extend: "locked"
384 PropertyChanges { target: root; focus: true }
371 PropertyChanges { target: bar; interactive: true }385 PropertyChanges { target: bar; interactive: true }
372 PropertyChanges {386 PropertyChanges {
373 target: d;387 target: d;
374388
=== modified file 'qml/Shell.qml'
--- qml/Shell.qml 2017-01-03 12:04:08 +0000
+++ qml/Shell.qml 2017-01-25 16:04:08 +0000
@@ -68,6 +68,7 @@
68 stage.updateFocusedAppOrientationAnimated();68 stage.updateFocusedAppOrientationAnimated();
69 }69 }
70 property bool hasMouse: false70 property bool hasMouse: false
71 property bool hasKeyboard: false
7172
72 // to be read from outside73 // to be read from outside
73 readonly property int mainAppWindowOrientationAngle: stage.mainAppWindowOrientationAngle74 readonly property int mainAppWindowOrientationAngle: stage.mainAppWindowOrientationAngle
@@ -240,7 +241,7 @@
240 // Ignore when greeter is active, to avoid pocket presses241 // Ignore when greeter is active, to avoid pocket presses
241 if (!greeter.active) {242 if (!greeter.active) {
242 launcher.fadeOut();243 launcher.fadeOut();
243 shell.showHome();244 ApplicationManager.requestFocusApplication("unity8-dash");
244 }245 }
245 }246 }
246 onTouchBegun: { cursor.opacity = 0; }247 onTouchBegun: { cursor.opacity = 0; }
@@ -319,6 +320,35 @@
319 panel.applicationMenus.hide();320 panel.applicationMenus.hide();
320 }321 }
321 }322 }
323
324 TouchGestureArea {
325 anchors.fill: stage
326
327 minimumTouchPoints: 4
328 maximumTouchPoints: minimumTouchPoints
329
330 readonly property bool recognisedPress: status == TouchGestureArea.Recognized &&
331 touchPoints.length >= minimumTouchPoints &&
332 touchPoints.length <= maximumTouchPoints
333 property bool wasPressed: false
334
335 onRecognisedPressChanged: {
336 if (recognisedPress) {
337 wasPressed = true;
338 }
339 }
340
341 onStatusChanged: {
342 if (status !== TouchGestureArea.Recognized) {
343 if (status === TouchGestureArea.WaitingForTouch) {
344 if (wasPressed && !dragging) {
345 launcher.openDrawer(true);
346 }
347 }
348 wasPressed = false;
349 }
350 }
351 }
322 }352 }
323353
324 InputMethod {354 InputMethod {
@@ -343,6 +373,16 @@
343 onLoaded: {373 onLoaded: {
344 item.objectName = "greeter"374 item.objectName = "greeter"
345 }375 }
376 property bool openDrawerAfterUnlock: false
377 Connections {
378 target: greeter
379 onActiveChanged: {
380 if (!greeter.active && greeterLoader.openDrawerAfterUnlock) {
381 launcher.openDrawer(false);
382 greeterLoader.openDrawerAfterUnlock = false;
383 }
384 }
385 }
346 }386 }
347387
348 Component {388 Component {
@@ -434,9 +474,11 @@
434 if (shell.mode === "greeter") {474 if (shell.mode === "greeter") {
435 SessionBroadcast.requestHomeShown(AccountsService.user);475 SessionBroadcast.requestHomeShown(AccountsService.user);
436 } else {476 } else {
437 var animate = !LightDMService.greeter.active && !stages.shown;477 if (!greeter.active) {
438 dash.setCurrentScope(0, animate, false);478 launcher.openDrawer(false);
439 ApplicationManager.requestFocusApplication("unity8-dash");479 } else {
480 greeterLoader.openDrawerAfterUnlock = true;
481 }
440 }482 }
441 }483 }
442484
@@ -512,7 +554,7 @@
512 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode554 lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode
513 blurSource: greeter.shown ? greeter : stages555 blurSource: greeter.shown ? greeter : stages
514 topPanelHeight: panel.panelHeight556 topPanelHeight: panel.panelHeight
515 drawerEnabled: !greeter.shown557 drawerEnabled: !greeter.active
516558
517 onShowDashHome: showHome()559 onShowDashHome: showHome()
518 onLauncherApplicationSelected: {560 onLauncherApplicationSelected: {
@@ -657,8 +699,10 @@
657 id: dialogs699 id: dialogs
658 objectName: "dialogs"700 objectName: "dialogs"
659 anchors.fill: parent701 anchors.fill: parent
702 visible: hasActiveDialog
660 z: overlay.z + 10703 z: overlay.z + 10
661 usageScenario: shell.usageScenario704 usageScenario: shell.usageScenario
705 hasKeyboard: shell.hasKeyboard
662 onPowerOffClicked: {706 onPowerOffClicked: {
663 shutdownFadeOutRectangle.enabled = true;707 shutdownFadeOutRectangle.enabled = true;
664 shutdownFadeOutRectangle.visible = true;708 shutdownFadeOutRectangle.visible = true;
665709
=== modified file 'qml/Stage/DecoratedWindow.qml'
--- qml/Stage/DecoratedWindow.qml 2016-12-12 11:16:47 +0000
+++ qml/Stage/DecoratedWindow.qml 2017-01-25 16:04:08 +0000
@@ -196,18 +196,19 @@
196 ]196 ]
197 }197 }
198198
199 MouseArea {199 WindowDecoration {
200 id: decoration
201 closeButtonVisible: root.application.appId !== "unity8-dash"
202 objectName: "appWindowDecoration"
203
200 anchors { left: parent.left; top: parent.top; right: parent.right }204 anchors { left: parent.left; top: parent.top; right: parent.right }
201 height: units.gu(3)205 height: units.gu(3)
202206
207 title: applicationWindow.title
208
203 opacity: root.hasDecoration ? Math.min(1, root.showDecoration) : 0209 opacity: root.hasDecoration ? Math.min(1, root.showDecoration) : 0
204
205 Behavior on opacity { UbuntuNumberAnimation { } }210 Behavior on opacity { UbuntuNumberAnimation { } }
206211
207 drag.target: Item {}
208 drag.filterChildren: true
209 drag.threshold: 0
210
211 onPressed: root.decorationPressed();212 onPressed: root.decorationPressed();
212 onPressedChanged: moveHandler.handlePressedChanged(pressed, pressedButtons, mouseX, mouseY)213 onPressedChanged: moveHandler.handlePressedChanged(pressed, pressedButtons, mouseX, mouseY)
213 onPositionChanged: moveHandler.handlePositionChanged(mouse)214 onPositionChanged: moveHandler.handlePositionChanged(mouse)
@@ -216,47 +217,39 @@
216 moveHandler.handleReleased();217 moveHandler.handleReleased();
217 }218 }
218219
219 WindowDecoration {220 onCloseClicked: root.closeClicked();
220 id: decoration221 onMaximizeClicked: { root.decorationPressed(); root.maximizeClicked(); }
221 closeButtonVisible: root.application.appId !== "unity8-dash"222 onMaximizeHorizontallyClicked: { root.decorationPressed(); root.maximizeHorizontallyClicked(); }
222 objectName: "appWindowDecoration"223 onMaximizeVerticallyClicked: { root.decorationPressed(); root.maximizeVerticallyClicked(); }
223 anchors.fill: parent224 onMinimizeClicked: root.minimizeClicked();
224 title: applicationWindow.title225
225226 enableMenus: {
226 onCloseClicked: root.closeClicked();227 return active &&
227 onMaximizeClicked: { root.decorationPressed(); root.maximizeClicked(); }228 surface &&
228 onMaximizeHorizontallyClicked: { root.decorationPressed(); root.maximizeHorizontallyClicked(); }229 (PanelState.focusedPersistentSurfaceId === surface.persistentId && !PanelState.decorationsVisible)
229 onMaximizeVerticallyClicked: { root.decorationPressed(); root.maximizeVerticallyClicked(); }230 }
230 onMinimizeClicked: root.minimizeClicked();231 menu: sharedAppModel.model
231232
232 enableMenus: {233 Indicators.SharedUnityMenuModel {
233 return active &&234 id: sharedAppModel
234 surface &&235 property var menus: surface ? ApplicationMenuRegistry.getMenusForSurface(surface.persistentId) : []
235 (PanelState.focusedPersistentSurfaceId === surface.persistentId && !PanelState.decorationsVisible)236 property var menuService: menus.length > 0 ? menus[0] : undefined
236 }237
237 menu: sharedAppModel.model238 busName: menuService ? menuService.service : ""
238239 menuObjectPath: menuService && menuService.menuPath ? menuService.menuPath : ""
239 Indicators.SharedUnityMenuModel {240 actions: menuService && menuService.actionPath ? { "unity": menuService.actionPath } : {}
240 id: sharedAppModel241 }
241 property var menus: surface ? ApplicationMenuRegistry.getMenusForSurface(surface.persistentId) : []242
242 property var menuService: menus.length > 0 ? menus[0] : undefined243 Connections {
243244 target: ApplicationMenuRegistry
244 busName: menuService ? menuService.service : ""245 onSurfaceMenuRegistered: {
245 menuObjectPath: menuService && menuService.menuPath ? menuService.menuPath : ""246 if (surface && surfaceId === surface.persistentId) {
246 actions: menuService && menuService.actionPath ? { "unity": menuService.actionPath } : {}247 sharedAppModel.menus = Qt.binding(function() { return surface ? ApplicationMenuRegistry.getMenusForSurface(surface.persistentId) : [] });
247 }
248
249 Connections {
250 target: ApplicationMenuRegistry
251 onSurfaceMenuRegistered: {
252 if (surface && surfaceId === surface.persistentId) {
253 sharedAppModel.menus = Qt.binding(function() { return surface ? ApplicationMenuRegistry.getMenusForSurface(surface.persistentId) : [] });
254 }
255 }248 }
256 onSurfaceMenuUnregistered: {249 }
257 if (surface && surfaceId === surface.persistentId) {250 onSurfaceMenuUnregistered: {
258 sharedAppModel.menus = Qt.binding(function() { return surface ? ApplicationMenuRegistry.getMenusForSurface(surface.persistentId) : [] });251 if (surface && surfaceId === surface.persistentId) {
259 }252 sharedAppModel.menus = Qt.binding(function() { return surface ? ApplicationMenuRegistry.getMenusForSurface(surface.persistentId) : [] });
260 }253 }
261 }254 }
262 }255 }
263256
=== modified file 'qml/Stage/WindowDecoration.qml'
--- qml/Stage/WindowDecoration.qml 2016-12-12 11:16:47 +0000
+++ qml/Stage/WindowDecoration.qml 2017-01-25 16:04:08 +0000
@@ -38,6 +38,10 @@
38 acceptedButtons: Qt.AllButtons // prevent leaking unhandled mouse events38 acceptedButtons: Qt.AllButtons // prevent leaking unhandled mouse events
39 hoverEnabled: true39 hoverEnabled: true
4040
41 drag.target: Item {}
42 drag.filterChildren: true
43 drag.threshold: 0
44
41 signal closeClicked()45 signal closeClicked()
42 signal minimizeClicked()46 signal minimizeClicked()
43 signal maximizeClicked()47 signal maximizeClicked()
@@ -64,8 +68,8 @@
64 (menuBar.showRequested || root.containsMouse)68 (menuBar.showRequested || root.containsMouse)
65 }69 }
6670
67 // We dont want touch events to fall through to parent,71 // We dont want touch events to fall through to parent as it expect some child MouseArea to have them
68 // otherwise the containsMouse will not work.72 // If not some MouseArea in the menu bar, it will be this one.
69 MouseArea {73 MouseArea {
70 anchors.fill: parent74 anchors.fill: parent
71 propagateComposedEvents: true75 propagateComposedEvents: true
7276
=== modified file 'src/MouseTouchAdaptor.cpp'
--- src/MouseTouchAdaptor.cpp 2016-03-29 03:47:39 +0000
+++ src/MouseTouchAdaptor.cpp 2017-01-25 16:04:08 +0000
@@ -72,6 +72,7 @@
72MouseTouchAdaptor *g_instance = nullptr;72MouseTouchAdaptor *g_instance = nullptr;
7373
74const Qt::KeyboardModifiers TRI_PRESS_MODIFIER = Qt::ShiftModifier|Qt::ControlModifier|Qt::AltModifier;74const Qt::KeyboardModifiers TRI_PRESS_MODIFIER = Qt::ShiftModifier|Qt::ControlModifier|Qt::AltModifier;
75const Qt::KeyboardModifiers QUAD_PRESS_MODIFIER = TRI_PRESS_MODIFIER|Qt::MetaModifier;
7576
76Qt::MouseButton translateMouseButton(xcb_button_t detail)77Qt::MouseButton translateMouseButton(xcb_button_t detail)
77{78{
@@ -91,7 +92,7 @@
91 if (mod & 0x01) qtMod |= Qt::ShiftModifier;92 if (mod & 0x01) qtMod |= Qt::ShiftModifier;
92 if (mod & 0x04) qtMod |= Qt::ControlModifier;93 if (mod & 0x04) qtMod |= Qt::ControlModifier;
93 if (mod & 0x08) qtMod |= Qt::AltModifier;94 if (mod & 0x08) qtMod |= Qt::AltModifier;
94 if (mod & 0x80) qtMod |= Qt::MetaModifier;95 if (mod & 0x40) qtMod |= Qt::MetaModifier;
9596
96 return qtMod;97 return qtMod;
97}98}
@@ -101,6 +102,7 @@
101 : QObject(nullptr)102 : QObject(nullptr)
102 , m_leftButtonIsPressed(false)103 , m_leftButtonIsPressed(false)
103 , m_triPressModifier(false)104 , m_triPressModifier(false)
105 , m_quadPressModifier(false)
104 , m_enabled(true)106 , m_enabled(true)
105{107{
106 QCoreApplication::instance()->installNativeEventFilter(this);108 QCoreApplication::instance()->installNativeEventFilter(this);
@@ -297,6 +299,13 @@
297 touchEvent.press(2, windowPos);299 touchEvent.press(2, windowPos);
298 m_triPressModifier = true;300 m_triPressModifier = true;
299 }301 }
302 if (qtMod == QUAD_PRESS_MODIFIER) {
303 touchEvent.press(1, windowPos);
304 touchEvent.press(2, windowPos);
305 touchEvent.press(3, windowPos);
306 m_quadPressModifier = true;
307 }
308
300 touchEvent.commit(false /* processEvents */);309 touchEvent.commit(false /* processEvents */);
301310
302 m_leftButtonIsPressed = true;311 m_leftButtonIsPressed = true;
@@ -322,10 +331,16 @@
322 touchEvent.release(1, windowPos);331 touchEvent.release(1, windowPos);
323 touchEvent.release(2, windowPos);332 touchEvent.release(2, windowPos);
324 }333 }
334 if (m_quadPressModifier) {
335 touchEvent.release(1, windowPos);
336 touchEvent.release(2, windowPos);
337 touchEvent.release(3, windowPos);
338 }
325 touchEvent.commit(false /* processEvents */);339 touchEvent.commit(false /* processEvents */);
326340
327 m_leftButtonIsPressed = false;341 m_leftButtonIsPressed = false;
328 m_triPressModifier = false;342 m_triPressModifier = false;
343 m_quadPressModifier = false;
329 return true;344 return true;
330}345}
331346
@@ -354,6 +369,18 @@
354 m_triPressModifier = false;369 m_triPressModifier = false;
355 }370 }
356 }371 }
372 if (m_quadPressModifier) {
373 if (qtMod == QUAD_PRESS_MODIFIER) {
374 touchEvent.move(1, windowPos);
375 touchEvent.move(2, windowPos);
376 touchEvent.move(3, windowPos);
377 } else {
378 touchEvent.release(1, windowPos);
379 touchEvent.release(2, windowPos);
380 touchEvent.release(3, windowPos);
381 m_quadPressModifier = false;
382 }
383 }
357 touchEvent.commit(false /* processEvents */);384 touchEvent.commit(false /* processEvents */);
358385
359 return true;386 return true;
360387
=== modified file 'src/MouseTouchAdaptor.h'
--- src/MouseTouchAdaptor.h 2016-03-29 03:47:39 +0000
+++ src/MouseTouchAdaptor.h 2017-01-25 16:04:08 +0000
@@ -59,6 +59,7 @@
59 QTouchDevice *m_touchDevice;59 QTouchDevice *m_touchDevice;
60 bool m_leftButtonIsPressed;60 bool m_leftButtonIsPressed;
61 bool m_triPressModifier;61 bool m_triPressModifier;
62 bool m_quadPressModifier;
6263
6364
64 bool m_enabled;65 bool m_enabled;
6566
=== modified file 'tests/CMakeLists.txt'
--- tests/CMakeLists.txt 2016-12-06 20:16:56 +0000
+++ tests/CMakeLists.txt 2017-01-25 16:04:08 +0000
@@ -7,8 +7,10 @@
7add_meta_test(uitests)7add_meta_test(uitests)
8add_meta_test(xvfbuitests)8add_meta_test(xvfbuitests)
99
10add_meta_test(alltests DEPENDS unittests uitests)10# Run our meta-meta tests serially because we don't need to nest
11add_meta_test(xvfballtests DEPENDS unittests xvfbuitests)11# parallelized tests.
12add_meta_test(alltests SERIAL DEPENDS unittests uitests)
13add_meta_test(xvfballtests SERIAL DEPENDS unittests xvfbuitests)
1214
13# Support libraries and plugins15# Support libraries and plugins
14add_subdirectory(mocks)16add_subdirectory(mocks)
@@ -42,8 +44,8 @@
4244
43 set(ld_paths)45 set(ld_paths)
44 list(APPEND ld_paths46 list(APPEND ld_paths
47 ${UNITY_MOCKPATH}/liblightdm
45 ${UNITY_MOCKPATH}/libusermetrics48 ${UNITY_MOCKPATH}/libusermetrics
46 ${UNITY_MOCKPATH}/LightDM/IntegratedLightDM/liblightdm
47 )49 )
4850
49 string(REPLACE ";" ":" ld_library_path "${ld_paths}")51 string(REPLACE ";" ":" ld_library_path "${ld_paths}")
5052
=== modified file 'tests/autopilot/unity8/fixture_setup.py'
--- tests/autopilot/unity8/fixture_setup.py 2015-10-26 20:15:08 +0000
+++ tests/autopilot/unity8/fixture_setup.py 2017-01-25 16:04:08 +0000
@@ -74,7 +74,7 @@
74 def _get_lightdm_mock_path(self):74 def _get_lightdm_mock_path(self):
75 lib_path = get_mocks_library_path()75 lib_path = get_mocks_library_path()
76 lightdm_mock_path = os.path.abspath(76 lightdm_mock_path = os.path.abspath(
77 os.path.join(lib_path, "LightDM" ,"IntegratedLightDM", "liblightdm")77 os.path.join(lib_path, "liblightdm")
78 )78 )
7979
80 if not os.path.exists(lightdm_mock_path):80 if not os.path.exists(lightdm_mock_path):
8181
=== modified file 'tests/autopilot/unity8/shell/tests/__init__.py'
--- tests/autopilot/unity8/shell/tests/__init__.py 2016-10-25 13:11:19 +0000
+++ tests/autopilot/unity8/shell/tests/__init__.py 2017-01-25 16:04:08 +0000
@@ -264,7 +264,7 @@
264 def _get_lightdm_mock_path(self):264 def _get_lightdm_mock_path(self):
265 lib_path = get_mocks_library_path()265 lib_path = get_mocks_library_path()
266 lightdm_mock_path = os.path.abspath(266 lightdm_mock_path = os.path.abspath(
267 os.path.join(lib_path, "LightDM" ,"IntegratedLightDM", "liblightdm")267 os.path.join(lib_path, "liblightdm")
268 )268 )
269269
270 if not os.path.exists(lightdm_mock_path):270 if not os.path.exists(lightdm_mock_path):
271271
=== modified file 'tests/mocks/AccountsService/AccountsService.cpp'
--- tests/mocks/AccountsService/AccountsService.cpp 2016-08-04 14:05:54 +0000
+++ tests/mocks/AccountsService/AccountsService.cpp 2017-01-25 16:04:08 +0000
@@ -15,7 +15,7 @@
15 */15 */
1616
17#include "AccountsService.h"17#include "AccountsService.h"
18#include "MockUsersModel.h"18#include "UsersModel.h"
1919
20#include <QLightDM/UsersModel>20#include <QLightDM/UsersModel>
21#include <paths.h>21#include <paths.h>
@@ -33,9 +33,8 @@
33 m_demoEdgesCompleted(),33 m_demoEdgesCompleted(),
34 m_hereEnabled(false),34 m_hereEnabled(false),
35 m_hereLicensePath(""),35 m_hereLicensePath(""),
36 m_usersModel(new MockUsersModel(this))36 m_usersModel(new UsersModel(this))
37{37{
38 m_usersModel->setMockMode("full");
39}38}
4039
41QString AccountsService::user() const40QString AccountsService::user() const
4241
=== modified file 'tests/mocks/AccountsService/AccountsService.h'
--- tests/mocks/AccountsService/AccountsService.h 2016-12-23 11:04:53 +0000
+++ tests/mocks/AccountsService/AccountsService.h 2017-01-25 16:04:08 +0000
@@ -14,15 +14,14 @@
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 */
1616
17#ifndef UNITY_MOCK_ACCOUNTSSERVICE_H17#pragma once
18#define UNITY_MOCK_ACCOUNTSSERVICE_H
1918
20#include <QObject>19#include <QObject>
21#include <QString>20#include <QString>
22#include <QStringList>21#include <QStringList>
23#include <QVariant>22#include <QVariant>
2423
25class MockUsersModel;24class UsersModel;
2625
27class AccountsService: public QObject26class AccountsService: public QObject
28{27{
@@ -165,7 +164,5 @@
165 QString m_realName;164 QString m_realName;
166 QStringList m_kbdMap;165 QStringList m_kbdMap;
167 QString m_email;166 QString m_email;
168 MockUsersModel *m_usersModel;167 UsersModel *m_usersModel;
169};168};
170
171#endif
172169
=== modified file 'tests/mocks/AccountsService/CMakeLists.txt'
--- tests/mocks/AccountsService/CMakeLists.txt 2016-12-06 20:16:56 +0000
+++ tests/mocks/AccountsService/CMakeLists.txt 2017-01-25 16:04:08 +0000
@@ -1,14 +1,15 @@
1include_directories(1include_directories(
2 ${CMAKE_CURRENT_BINARY_DIR}2 ${CMAKE_CURRENT_BINARY_DIR}
3 ${CMAKE_SOURCE_DIR}/plugins/LightDM3 ${CMAKE_SOURCE_DIR}/plugins/LightDM
4 ${CMAKE_SOURCE_DIR}/plugins/LightDM/IntegratedLightDM
4 ${CMAKE_SOURCE_DIR}/plugins/Utils5 ${CMAKE_SOURCE_DIR}/plugins/Utils
5 ${CMAKE_SOURCE_DIR}/tests/mocks/LightDM/IntegratedLightDM
6 )6 )
77
8add_library(MockAccountsService-qml MODULE8add_library(MockAccountsService-qml MODULE
9 ${CMAKE_SOURCE_DIR}/plugins/LightDM/Greeter.cpp
10 ${CMAKE_SOURCE_DIR}/plugins/LightDM/PromptsModel.cpp
9 ${CMAKE_SOURCE_DIR}/plugins/LightDM/UsersModel.cpp11 ${CMAKE_SOURCE_DIR}/plugins/LightDM/UsersModel.cpp
10 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitysortfilterproxymodelqml.cpp12 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitysortfilterproxymodelqml.cpp
11 ${CMAKE_SOURCE_DIR}/tests/mocks/LightDM/IntegratedLightDM/MockUsersModel.cpp
12 AccountsService.cpp13 AccountsService.cpp
13 plugin.cpp14 plugin.cpp
14 )15 )
1516
=== modified file 'tests/mocks/CMakeLists.txt'
--- tests/mocks/CMakeLists.txt 2016-12-06 20:16:56 +0000
+++ tests/mocks/CMakeLists.txt 2017-01-25 16:04:08 +0000
@@ -32,8 +32,9 @@
32add_subdirectory(Cursor)32add_subdirectory(Cursor)
33add_subdirectory(GSettings.1.0)33add_subdirectory(GSettings.1.0)
34add_subdirectory(indicator-service)34add_subdirectory(indicator-service)
35add_subdirectory(liblightdm)
35add_subdirectory(libusermetrics)36add_subdirectory(libusermetrics)
36add_subdirectory(LightDM)37add_subdirectory(LightDMController)
37add_subdirectory(Lights)38add_subdirectory(Lights)
38add_subdirectory(MeeGo)39add_subdirectory(MeeGo)
39add_subdirectory(Powerd)40add_subdirectory(Powerd)
4041
=== modified file 'tests/mocks/GSettings.1.0/fake_gsettings.cpp'
--- tests/mocks/GSettings.1.0/fake_gsettings.cpp 2016-11-16 05:54:50 +0000
+++ tests/mocks/GSettings.1.0/fake_gsettings.cpp 2017-01-25 16:04:08 +0000
@@ -29,6 +29,7 @@
29 , m_edgeDragWidth(2)29 , m_edgeDragWidth(2)
30 , m_enableLauncher(true)30 , m_enableLauncher(true)
31 , m_enableIndicatorMenu(true)31 , m_enableIndicatorMenu(true)
32 , m_appstoreUri("http://uappexplorer.com")
32{33{
33}34}
3435
@@ -173,6 +174,11 @@
173 }174 }
174}175}
175176
177QString GSettingsControllerQml::appstoreUri() const
178{
179 return m_appstoreUri;
180}
181
176GSettingsSchemaQml::GSettingsSchemaQml(QObject *parent): QObject(parent) {182GSettingsSchemaQml::GSettingsSchemaQml(QObject *parent): QObject(parent) {
177}183}
178184
@@ -376,6 +382,14 @@
376 }382 }
377}383}
378384
385QVariant GSettingsQml::appstoreUri() const
386{
387 if (m_valid && m_schema->id() == "com.canonical.Unity8") {
388 return GSettingsControllerQml::instance()->appstoreUri();
389 }
390 return QVariant();
391}
392
379void GSettingsQml::setLifecycleExemptAppids(const QVariant &appIds)393void GSettingsQml::setLifecycleExemptAppids(const QVariant &appIds)
380{394{
381 if (m_valid && m_schema->id() == "com.canonical.qtmir") {395 if (m_valid && m_schema->id() == "com.canonical.qtmir") {
382396
=== modified file 'tests/mocks/GSettings.1.0/fake_gsettings.h'
--- tests/mocks/GSettings.1.0/fake_gsettings.h 2016-11-16 05:54:50 +0000
+++ tests/mocks/GSettings.1.0/fake_gsettings.h 2017-01-25 16:04:08 +0000
@@ -59,6 +59,7 @@
59 Q_PROPERTY(QVariant edgeDragWidth READ edgeDragWidth WRITE setEdgeDragWidth NOTIFY edgeDragWidthChanged)59 Q_PROPERTY(QVariant edgeDragWidth READ edgeDragWidth WRITE setEdgeDragWidth NOTIFY edgeDragWidthChanged)
60 Q_PROPERTY(QVariant enableLauncher READ enableLauncher WRITE setEnableLauncher NOTIFY enableLauncherChanged)60 Q_PROPERTY(QVariant enableLauncher READ enableLauncher WRITE setEnableLauncher NOTIFY enableLauncherChanged)
61 Q_PROPERTY(QVariant enableIndicatorMenu READ enableIndicatorMenu WRITE setEnableIndicatorMenu NOTIFY enableIndicatorMenuChanged)61 Q_PROPERTY(QVariant enableIndicatorMenu READ enableIndicatorMenu WRITE setEnableIndicatorMenu NOTIFY enableIndicatorMenuChanged)
62 Q_PROPERTY(QVariant appstoreUri READ appstoreUri NOTIFY appstoreUriChanged)
6263
63public:64public:
64 GSettingsQml(QObject *parent = nullptr);65 GSettingsQml(QObject *parent = nullptr);
@@ -77,6 +78,7 @@
77 QVariant edgeDragWidth() const;78 QVariant edgeDragWidth() const;
78 QVariant enableLauncher() const;79 QVariant enableLauncher() const;
79 QVariant enableIndicatorMenu() const;80 QVariant enableIndicatorMenu() const;
81 QVariant appstoreUri() const;
8082
81 void setDisableHeight(const QVariant &val);83 void setDisableHeight(const QVariant &val);
82 void setPictureUri(const QVariant &str);84 void setPictureUri(const QVariant &str);
@@ -101,6 +103,7 @@
101 void edgeDragWidthChanged();103 void edgeDragWidthChanged();
102 void enableLauncherChanged();104 void enableLauncherChanged();
103 void enableIndicatorMenuChanged();105 void enableIndicatorMenuChanged();
106 void appstoreUriChanged();
104107
105private:108private:
106 GSettingsSchemaQml* m_schema;109 GSettingsSchemaQml* m_schema;
@@ -147,6 +150,8 @@
147 bool enableIndicatorMenu() const;150 bool enableIndicatorMenu() const;
148 Q_INVOKABLE void setEnableIndicatorMenu(bool enableIndicatorMenu);151 Q_INVOKABLE void setEnableIndicatorMenu(bool enableIndicatorMenu);
149152
153 QString appstoreUri() const;
154
150Q_SIGNALS:155Q_SIGNALS:
151 void disableHeightChanged();156 void disableHeightChanged();
152 void pictureUriChanged(const QString&);157 void pictureUriChanged(const QString&);
@@ -158,6 +163,7 @@
158 void edgeDragWidthChanged(uint edgeDragWidth);163 void edgeDragWidthChanged(uint edgeDragWidth);
159 void enableLauncherChanged(bool enableLauncher);164 void enableLauncherChanged(bool enableLauncher);
160 void enableIndicatorMenuChanged(bool enableIndicatorMenu);165 void enableIndicatorMenuChanged(bool enableIndicatorMenu);
166 void appstoreUriChanged(const QString &appstoreUri);
161167
162private:168private:
163 GSettingsControllerQml();169 GSettingsControllerQml();
@@ -172,6 +178,7 @@
172 uint m_edgeDragWidth;178 uint m_edgeDragWidth;
173 bool m_enableLauncher;179 bool m_enableLauncher;
174 bool m_enableIndicatorMenu;180 bool m_enableIndicatorMenu;
181 QString m_appstoreUri;
175182
176 static GSettingsControllerQml* s_controllerInstance;183 static GSettingsControllerQml* s_controllerInstance;
177 QList<GSettingsQml *> m_registeredGSettings;184 QList<GSettingsQml *> m_registeredGSettings;
178185
=== removed directory 'tests/mocks/LightDM'
=== removed file 'tests/mocks/LightDM/CMakeLists.txt'
--- tests/mocks/LightDM/CMakeLists.txt 2015-09-25 13:01:00 +0000
+++ tests/mocks/LightDM/CMakeLists.txt 1970-01-01 00:00:00 +0000
@@ -1,1 +0,0 @@
1add_subdirectory(IntegratedLightDM)
20
=== removed directory 'tests/mocks/LightDM/IntegratedLightDM'
=== removed file 'tests/mocks/LightDM/IntegratedLightDM/CMakeLists.txt'
--- tests/mocks/LightDM/IntegratedLightDM/CMakeLists.txt 2016-07-14 13:04:10 +0000
+++ tests/mocks/LightDM/IntegratedLightDM/CMakeLists.txt 1970-01-01 00:00:00 +0000
@@ -1,50 +0,0 @@
1# This is a copy of the normal LightDM plugin, but instead of statically
2# linking in the lightdm bits, this one uses shared libraries so we can swap
3# out different sets of users for different tests. When we finally switch to
4# actually using the system liblightdm in the normal plugin, this version can
5# be deleted.
6
7add_subdirectory(liblightdm)
8
9include_directories(
10 ${CMAKE_CURRENT_SOURCE_DIR}
11 ${CMAKE_CURRENT_BINARY_DIR}
12 ${CMAKE_SOURCE_DIR}/plugins/Utils
13 ${CMAKE_SOURCE_DIR}/plugins/LightDM
14 ${CMAKE_SOURCE_DIR}/tests/mocks/libusermetrics
15 ${libunity8-private_SOURCE_DIR}
16)
17
18set(QMLPLUGIN_SRC
19 ${CMAKE_SOURCE_DIR}/plugins/LightDM/DBusGreeter.cpp
20 ${CMAKE_SOURCE_DIR}/plugins/LightDM/DBusGreeterList.cpp
21 ${CMAKE_SOURCE_DIR}/plugins/LightDM/Greeter.cpp
22 ${CMAKE_SOURCE_DIR}/plugins/LightDM/SessionsModel.cpp
23 ${CMAKE_SOURCE_DIR}/plugins/LightDM/UsersModel.cpp
24 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitysortfilterproxymodelqml.cpp
25 MockGreeter.cpp
26 MockSessionsModel.cpp
27 MockUsersModel.cpp
28 plugin.cpp
29 )
30
31add_library(MockLightDM-qml MODULE
32 ${QMLPLUGIN_SRC}
33 )
34
35# We want to link to liblightdm-qt5-3, but we don't want to depend on it being
36# installed on the system. So we make sure we link to our full fake version
37# At run time, we can point to whichever version we happen to be using via
38# LD_LIBRARY_PATH.
39target_link_libraries(MockLightDM-qml
40 MockLightDM
41 MockUserMetrics
42 unity8-private
43 )
44
45qt5_use_modules(MockLightDM-qml DBus Gui Qml)
46
47add_unity8_mock(LightDM.IntegratedLightDM 0.1 LightDM/IntegratedLightDM
48 TARGETS MockLightDM-qml
49 ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_CURRENT_BINARY_DIR}/liblightdm"
50)
510
=== removed file 'tests/mocks/LightDM/IntegratedLightDM/MockGreeter.cpp'
--- tests/mocks/LightDM/IntegratedLightDM/MockGreeter.cpp 2016-06-09 21:45:09 +0000
+++ tests/mocks/LightDM/IntegratedLightDM/MockGreeter.cpp 1970-01-01 00:00:00 +0000
@@ -1,51 +0,0 @@
1/*
2 * Copyright (C) 2014 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 */
17
18#include "MockGreeter.h"
19#include <GreeterPrivate.h>
20
21QString MockGreeter::mockMode() const
22{
23 Q_D(const Greeter);
24 return d->m_greeter->mockMode();
25}
26
27void MockGreeter::setMockMode(QString mockMode)
28{
29 Q_D(Greeter);
30
31 if (d->m_greeter->mockMode() != mockMode) {
32 d->m_greeter->setMockMode(mockMode);
33 Q_EMIT mockModeChanged(mockMode);
34 }
35}
36
37QString MockGreeter::selectUserHint() const
38{
39 Q_D(const Greeter);
40 return d->m_greeter->selectUserHint();
41}
42
43void MockGreeter::setSelectUserHint(const QString &selectUserHint)
44{
45 Q_D(Greeter);
46
47 if (d->m_greeter->selectUserHint() != selectUserHint) {
48 d->m_greeter->setSelectUserHint(selectUserHint);
49 Q_EMIT selectUserHintChanged();
50 }
51}
520
=== removed file 'tests/mocks/LightDM/IntegratedLightDM/MockGreeter.h'
--- tests/mocks/LightDM/IntegratedLightDM/MockGreeter.h 2016-06-09 21:45:09 +0000
+++ tests/mocks/LightDM/IntegratedLightDM/MockGreeter.h 1970-01-01 00:00:00 +0000
@@ -1,42 +0,0 @@
1/*
2 * Copyright (C) 2014 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 */
17
18// The real, production, Greeter
19#include <Greeter.h>
20
21#ifndef MOCK_UNITY_GREETER_H
22#define MOCK_UNITY_GREETER_H
23
24class MockGreeter : public Greeter {
25 Q_OBJECT
26
27 Q_PROPERTY(QString mockMode READ mockMode WRITE setMockMode NOTIFY mockModeChanged)
28 Q_PROPERTY(QString selectUser READ selectUserHint WRITE setSelectUserHint NOTIFY selectUserHintChanged)
29
30public:
31 QString mockMode() const;
32 void setMockMode(QString mockMode);
33
34 QString selectUserHint() const;
35 void setSelectUserHint(const QString &selectUserHint);
36
37Q_SIGNALS:
38 void mockModeChanged(QString mode);
39 void selectUserHintChanged();
40};
41
42#endif // MOCK_UNITY_GREETER_H
430
=== removed file 'tests/mocks/LightDM/IntegratedLightDM/MockSessionsModel.cpp'
--- tests/mocks/LightDM/IntegratedLightDM/MockSessionsModel.cpp 2016-07-13 20:24:24 +0000
+++ tests/mocks/LightDM/IntegratedLightDM/MockSessionsModel.cpp 1970-01-01 00:00:00 +0000
@@ -1,66 +0,0 @@
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
18#include "MockSessionsModel.h"
19#include <QLightDM/SessionsModel>
20
21
22int MockSessionsModel::numSessions() const
23{
24 QLightDM::SessionsModel* qSessionsModel =
25 static_cast<QLightDM::SessionsModel*>(sourceModel());
26
27 return qSessionsModel->numSessions();
28}
29
30int MockSessionsModel::numAvailableSessions() const
31{
32 QLightDM::SessionsModel* qSessionsModel =
33 static_cast<QLightDM::SessionsModel*>(sourceModel());
34
35 return qSessionsModel->numAvailableSessions();
36}
37
38QString MockSessionsModel::testScenario() const
39{
40 QLightDM::SessionsModel* qSessionsModel =
41 static_cast<QLightDM::SessionsModel*>(sourceModel());
42
43 return qSessionsModel->testScenario();
44}
45
46void 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
57void MockSessionsModel::setTestScenario(const QString testScenario)
58{
59 QLightDM::SessionsModel* qSessionsModel =
60 static_cast<QLightDM::SessionsModel*>(sourceModel());
61
62 if (qSessionsModel->testScenario() != testScenario) {
63 qSessionsModel->setTestScenario(testScenario);
64 Q_EMIT testScenarioChanged();
65 }
66}
670
=== removed file 'tests/mocks/LightDM/IntegratedLightDM/MockSessionsModel.h'
--- tests/mocks/LightDM/IntegratedLightDM/MockSessionsModel.h 2016-07-13 20:24:24 +0000
+++ tests/mocks/LightDM/IntegratedLightDM/MockSessionsModel.h 1970-01-01 00:00:00 +0000
@@ -1,44 +0,0 @@
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
18#ifndef MOCK_UNITY_SESSIONSMODEL_H
19#define MOCK_UNITY_SESSIONSMODEL_H
20
21#include <SessionsModel.h>
22
23class MockSessionsModel : public SessionsModel
24{
25 Q_OBJECT
26
27 Q_PROPERTY(int numAvailableSessions READ numAvailableSessions CONSTANT)
28 Q_PROPERTY(int numSessions READ numSessions WRITE setNumSessions NOTIFY numSessionsChanged)
29 Q_PROPERTY(QString testScenario READ testScenario WRITE setTestScenario NOTIFY testScenarioChanged)
30
31public:
32 int numAvailableSessions() const;
33 int numSessions() const;
34 QString testScenario() const;
35 void setNumSessions(const int numSessions);
36 void setTestScenario(const QString testScenario);
37
38Q_SIGNALS:
39 void numSessionsChanged();
40 void testScenarioChanged();
41
42};
43
44#endif // MOCK_UNITY_SESSIONSMODEL_H
450
=== removed file 'tests/mocks/LightDM/IntegratedLightDM/MockUsersModel.cpp'
--- tests/mocks/LightDM/IntegratedLightDM/MockUsersModel.cpp 2016-08-04 14:05:54 +0000
+++ tests/mocks/LightDM/IntegratedLightDM/MockUsersModel.cpp 1970-01-01 00:00:00 +0000
@@ -1,44 +0,0 @@
1/*
2 * Copyright (C) 2014 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 */
17
18#include "MockUsersModel.h"
19#include <QLightDM/UsersModel>
20#include <QSortFilterProxyModel>
21
22MockUsersModel::MockUsersModel(QObject* parent)
23 : UsersModel(parent)
24{
25}
26
27QString MockUsersModel::mockMode() const
28{
29 QLightDM::UsersModel* qUsersModel =
30 static_cast<QLightDM::UsersModel*>(static_cast<QSortFilterProxyModel*>(sourceModel())->sourceModel());
31
32 return qUsersModel->mockMode();
33}
34
35void MockUsersModel::setMockMode(QString mockMode)
36{
37 QLightDM::UsersModel* qUsersModel =
38 static_cast<QLightDM::UsersModel*>(static_cast<QSortFilterProxyModel*>(sourceModel())->sourceModel());
39
40 if (qUsersModel->mockMode() != mockMode) {
41 qUsersModel->setMockMode(mockMode);
42 Q_EMIT mockModeChanged(mockMode);
43 }
44}
450
=== removed file 'tests/mocks/LightDM/IntegratedLightDM/MockUsersModel.h'
--- tests/mocks/LightDM/IntegratedLightDM/MockUsersModel.h 2016-08-04 14:05:54 +0000
+++ tests/mocks/LightDM/IntegratedLightDM/MockUsersModel.h 1970-01-01 00:00:00 +0000
@@ -1,39 +0,0 @@
1/*
2 * Copyright (C) 2014 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 */
17
18#ifndef MOCK_UNITY_USERSMODEL_H
19#define MOCK_UNITY_USERSMODEL_H
20
21#include <UsersModel.h>
22
23class MockUsersModel : public UsersModel
24{
25 Q_OBJECT
26
27 Q_PROPERTY(QString mockMode READ mockMode WRITE setMockMode NOTIFY mockModeChanged)
28
29public:
30 explicit MockUsersModel(QObject* parent=0);
31
32 QString mockMode() const;
33 void setMockMode(QString mockMode);
34
35Q_SIGNALS:
36 void mockModeChanged(QString mode);
37};
38
39#endif // MOCK_UNITY_USERSMODEL_H
400
=== removed directory 'tests/mocks/LightDM/IntegratedLightDM/QLightDM'
=== removed file 'tests/mocks/LightDM/IntegratedLightDM/QLightDM/Greeter'
--- tests/mocks/LightDM/IntegratedLightDM/QLightDM/Greeter 2015-01-20 11:50:19 +0000
+++ tests/mocks/LightDM/IntegratedLightDM/QLightDM/Greeter 1970-01-01 00:00:00 +0000
@@ -1,17 +0,0 @@
1/*
2 * Copyright (C) 2014 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "../liblightdm/Greeter.h"
180
=== removed file 'tests/mocks/LightDM/IntegratedLightDM/QLightDM/SessionsModel'
--- tests/mocks/LightDM/IntegratedLightDM/QLightDM/SessionsModel 2015-11-18 03:52:01 +0000
+++ tests/mocks/LightDM/IntegratedLightDM/QLightDM/SessionsModel 1970-01-01 00:00:00 +0000
@@ -1,17 +0,0 @@
1/*
2 * Copyright (C) 2015 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "../liblightdm/SessionsModel.h"
180
=== removed file 'tests/mocks/LightDM/IntegratedLightDM/QLightDM/UsersModel'
--- tests/mocks/LightDM/IntegratedLightDM/QLightDM/UsersModel 2015-01-20 11:50:19 +0000
+++ tests/mocks/LightDM/IntegratedLightDM/QLightDM/UsersModel 1970-01-01 00:00:00 +0000
@@ -1,17 +0,0 @@
1/*
2 * Copyright (C) 2014 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches