Merge lp:~ci-train-bot/unity8/unity8-ubuntu-zesty-2272 into lp:unity8
- unity8-ubuntu-zesty-2272
- Merge into trunk
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Unity Team | Pending | ||
Review via email: mp+315584@code.launchpad.net |
Commit message
Description of the change
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
1 | === modified file 'CMakeLists.txt' |
2 | --- CMakeLists.txt 2017-01-10 14:46:09 +0000 |
3 | +++ CMakeLists.txt 2017-01-25 16:04:08 +0000 |
4 | @@ -70,7 +70,7 @@ |
5 | find_package(Qt5Concurrent 5.6 REQUIRED) |
6 | find_package(Qt5Sql 5.6 REQUIRED) |
7 | |
8 | -pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=23) |
9 | +pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=25) |
10 | pkg_check_modules(GEONAMES REQUIRED geonames>=0.2) |
11 | pkg_check_modules(GIO REQUIRED gio-2.0>=2.32) |
12 | pkg_check_modules(GLIB REQUIRED glib-2.0>=2.32) |
13 | |
14 | === modified file 'cmake/modules/QmlTest.cmake' |
15 | --- cmake/modules/QmlTest.cmake 2016-06-07 13:25:58 +0000 |
16 | +++ cmake/modules/QmlTest.cmake 2017-01-25 16:04:08 +0000 |
17 | @@ -264,12 +264,16 @@ |
18 | # installed system. |
19 | |
20 | function(add_meta_test TARGET_NAME) |
21 | - cmake_parse_arguments(TEST "" "" "DEPENDS" ${ARGN}) |
22 | + cmake_parse_arguments(TEST "SERIAL" "" "DEPENDS" ${ARGN}) |
23 | |
24 | add_custom_target(${TARGET_NAME}) |
25 | |
26 | set(filename "${CMAKE_BINARY_DIR}/tests/scripts/${TARGET_NAME}.sh") |
27 | - file(WRITE "${filename}" "#!/usr/bin/parallel --shebang --no-notice\n\n") |
28 | + if(TEST_SERIAL) |
29 | + file(WRITE "${filename}" "#!/bin/sh\n\n") |
30 | + else() |
31 | + file(WRITE "${filename}" "#!/usr/bin/parallel --shebang --no-notice\n\n") |
32 | + endif() |
33 | |
34 | add_meta_dependencies(${TARGET_NAME} DEPENDS ${TEST_DEPENDS}) |
35 | # else we will write the rest of the script as we add cmake targets |
36 | @@ -307,6 +311,7 @@ |
37 | foreach(ONE_CMD ${TEST_COMMAND}) |
38 | set(script "${script}'${ONE_CMD}' ") |
39 | endforeach() |
40 | + set(script "${script}\"\$@\"") # Allow passing arguments if desired |
41 | |
42 | set(filename "${CMAKE_BINARY_DIR}/tests/scripts/${TARGET_NAME}.sh") |
43 | |
44 | @@ -370,7 +375,7 @@ |
45 | # add depend to the meta test script that we will install on system |
46 | set(filename "${CMAKE_BINARY_DIR}/tests/scripts/${UPSTREAM_TARGET}.sh") |
47 | if (EXISTS "${filename}") |
48 | - file(APPEND "${filename}" "${CMAKE_INSTALL_PREFIX}/${SHELL_PRIVATE_LIBDIR}/tests/scripts/${depend}.sh\n") |
49 | + file(APPEND "${filename}" "${CMAKE_INSTALL_PREFIX}/${SHELL_PRIVATE_LIBDIR}/tests/scripts/${depend}.sh \"\$@\" 2>&1\n") |
50 | endif() |
51 | endforeach() |
52 | endfunction() |
53 | |
54 | === modified file 'data/com.canonical.Unity8.gschema.xml' |
55 | --- data/com.canonical.Unity8.gschema.xml 2016-11-30 10:32:23 +0000 |
56 | +++ data/com.canonical.Unity8.gschema.xml 2017-01-25 16:04:08 +0000 |
57 | @@ -53,6 +53,11 @@ |
58 | <summary>Enable or disable the indicator pull down menus</summary> |
59 | <description>Toggle the availability of the indicator pull down menus</description> |
60 | </key> |
61 | + <key type="s" name="appstore-uri"> |
62 | + <default>'scope://com.canonical.scopes.clickstore'</default> |
63 | + <summary>The uri to the app store</summary> |
64 | + <description>This will be used whenever the user triggers an action to open the app store.</description> |
65 | + </key> |
66 | </schema> |
67 | |
68 | <schema path="/com/canonical/unity8/greeter/" id="com.canonical.Unity8.Greeter" gettext-domain="unity8"> |
69 | |
70 | === modified file 'debian/changelog' |
71 | --- debian/changelog 2017-01-10 14:48:43 +0000 |
72 | +++ debian/changelog 2017-01-25 16:04:08 +0000 |
73 | @@ -1,3 +1,54 @@ |
74 | +unity8 (8.15+17.04.20170124-0ubuntu1) zesty; urgency=medium |
75 | + |
76 | + [ Albert Astals Cid ] |
77 | + * Limit tab-focus travelling on dialogs with a fence |
78 | + * Restore focus to where it was when our ShellDialogs get unloaded |
79 | + * Update current session after changing the user |
80 | + * Add keyboard navigation for Indicators |
81 | + * a window -> the current window |
82 | + * There's no spreadDelegate_ anymore |
83 | + |
84 | + [ Daniel d'Andrada ] |
85 | + * Simplify DecoratedWindow |
86 | + * Remove unnecessary warning message |
87 | + |
88 | + [ Josh Arenson ] |
89 | + * Add a test for the session chooser icon in the greeter's sessions |
90 | + list |
91 | + |
92 | + [ Lukáš Tinkl ] |
93 | + * Fix keymap not being applied on the shell itself (LP: #1626435) |
94 | + * Shell dialog improvements (kbd focus, mouse eater) |
95 | + * Start searching directly as you type, w/o having to first |
96 | + focus/click the search field. |
97 | + * Add a test for the real implementation of WindowStateStorage |
98 | + * Use a four finger gesture to open the drawer, much like in u7 |
99 | + |
100 | + [ Michael Terry ] |
101 | + * Simplify the lightdm mock to make future greeter improvements easier |
102 | + to test. |
103 | + * Add support for guest sessions in unity8-greeter. |
104 | + * Use a model for PAM prompts, supporting more possible interactions. |
105 | + * Fix grouping of autopkg output and allow optionally passing |
106 | + arguments to installed test scripts. |
107 | + * Add support for LightDM hints for manual logins and hiding normal |
108 | + users. |
109 | + |
110 | + [ Michael Zanetti ] |
111 | + * hint the launcher to indicate a successful size change to the user |
112 | + (LP: #1646457) |
113 | + * Improvements for the appdrawer (LP: #1648173) |
114 | + * Adjust home key to still focus the dash instead of messing with the |
115 | + drawer |
116 | + * allow 4 finger simulation with mousetouchadaptor |
117 | + |
118 | + [ Nick Dedekind ] |
119 | + * Added Alt+F10 shortcut to open app menus. (LP: #1656896) |
120 | + * Fixed menu layout width calculations. (LP: #1657050) |
121 | + * Skip Panel::test_drag_indicator_item_down_shows_menu |
122 | + |
123 | + -- Michał Sawicz <michal.sawicz@canonical.com> Tue, 24 Jan 2017 07:46:58 +0000 |
124 | + |
125 | unity8 (8.15+17.04.20170110.4-0ubuntu1) zesty; urgency=medium |
126 | |
127 | [ Albert Astals Cid ] |
128 | |
129 | === modified file 'debian/control' |
130 | --- debian/control 2017-01-10 14:46:09 +0000 |
131 | +++ debian/control 2017-01-25 16:04:08 +0000 |
132 | @@ -38,7 +38,7 @@ |
133 | libubuntugestures5-private-dev (>= 1.3.2030), |
134 | libudev-dev, |
135 | libudm-common-dev, |
136 | - libunity-api-dev (>= 8.0), |
137 | + libunity-api-dev (>= 8.1), |
138 | libusermetricsoutput1-dev, |
139 | # Need those X11 libs touch emulation from mouse events in manual QML tests on a X11 desktop |
140 | libx11-dev[!arm64 !armhf], |
141 | |
142 | === modified file 'debian/rules' |
143 | --- debian/rules 2017-01-03 14:02:23 +0000 |
144 | +++ debian/rules 2017-01-25 16:04:08 +0000 |
145 | @@ -40,4 +40,4 @@ |
146 | override_dh_shlibdeps: |
147 | # Some mock libraries link against liblightdm-qt5-3.so which we want to |
148 | # avoid, since we only really link against our mock one, not the system one. |
149 | - dh_shlibdeps -XlibMockLightDM-qml.so -XlibMockAccountsService-qml.so -Lunity8-private |
150 | + dh_shlibdeps -XlibMockAccountsService-qml.so -Lunity8-private |
151 | |
152 | === modified file 'plugins/LightDM/CMakeLists.txt' |
153 | --- plugins/LightDM/CMakeLists.txt 2016-03-02 02:54:30 +0000 |
154 | +++ plugins/LightDM/CMakeLists.txt 2017-01-25 16:04:08 +0000 |
155 | @@ -7,7 +7,6 @@ |
156 | ${CMAKE_CURRENT_SOURCE_DIR} |
157 | ${CMAKE_CURRENT_BINARY_DIR} |
158 | ${CMAKE_SOURCE_DIR}/plugins/Utils |
159 | - ${CMAKE_BINARY_DIR}/tests/mocks/LightDM/IntegratedLightDM |
160 | ${libunity8-private_SOURCE_DIR} |
161 | ${LIBUSERMETRICSOUTPUT_INCLUDE_DIRS} |
162 | ) |
163 | @@ -18,6 +17,7 @@ |
164 | DBusGreeterList.cpp |
165 | Greeter.cpp |
166 | plugin.cpp |
167 | + PromptsModel.cpp |
168 | SessionsModel.cpp |
169 | UsersModel.cpp |
170 | ) |
171 | |
172 | === modified file 'plugins/LightDM/DBusGreeterList.cpp' |
173 | --- plugins/LightDM/DBusGreeterList.cpp 2015-10-01 23:54:24 +0000 |
174 | +++ plugins/LightDM/DBusGreeterList.cpp 2017-01-25 16:04:08 +0000 |
175 | @@ -25,7 +25,7 @@ |
176 | m_greeter(greeter) |
177 | { |
178 | connect(m_greeter, &Greeter::authenticationUserChanged, this, &DBusGreeterList::authenticationUserChangedHandler); |
179 | - connect(m_greeter, &Greeter::promptlessChanged, this, &DBusGreeterList::promptlessChangedHandler); |
180 | + connect(m_greeter, &Greeter::isAuthenticatedChanged, this, &DBusGreeterList::authenticatedChangedHandler); |
181 | } |
182 | |
183 | QString DBusGreeterList::GetActiveEntry() const |
184 | @@ -40,16 +40,16 @@ |
185 | |
186 | bool DBusGreeterList::entryIsLocked() const |
187 | { |
188 | - return !m_greeter->promptless(); |
189 | + return !m_greeter->isAuthenticated(); |
190 | } |
191 | |
192 | -void DBusGreeterList::authenticationUserChangedHandler(const QString &user) |
193 | +void DBusGreeterList::authenticationUserChangedHandler() |
194 | { |
195 | - notifyPropertyChanged(QStringLiteral("ActiveEntry"), user); |
196 | - Q_EMIT EntrySelected(user); |
197 | + notifyPropertyChanged(QStringLiteral("ActiveEntry"), m_greeter->authenticationUser()); |
198 | + Q_EMIT EntrySelected(m_greeter->authenticationUser()); |
199 | } |
200 | |
201 | -void DBusGreeterList::promptlessChangedHandler() |
202 | +void DBusGreeterList::authenticatedChangedHandler() |
203 | { |
204 | notifyPropertyChanged(QStringLiteral("EntryIsLocked"), entryIsLocked()); |
205 | Q_EMIT entryIsLockedChanged(); |
206 | |
207 | === modified file 'plugins/LightDM/DBusGreeterList.h' |
208 | --- plugins/LightDM/DBusGreeterList.h 2015-09-11 13:38:45 +0000 |
209 | +++ plugins/LightDM/DBusGreeterList.h 2017-01-25 16:04:08 +0000 |
210 | @@ -47,8 +47,8 @@ |
211 | void entryIsLockedChanged(); |
212 | |
213 | private Q_SLOTS: |
214 | - void authenticationUserChangedHandler(const QString &user); |
215 | - void promptlessChangedHandler(); |
216 | + void authenticationUserChangedHandler(); |
217 | + void authenticatedChangedHandler(); |
218 | |
219 | private: |
220 | Greeter *m_greeter; |
221 | |
222 | === modified file 'plugins/LightDM/Greeter.cpp' |
223 | --- plugins/LightDM/Greeter.cpp 2016-07-28 15:34:29 +0000 |
224 | +++ plugins/LightDM/Greeter.cpp 2017-01-25 16:04:08 +0000 |
225 | @@ -1,5 +1,5 @@ |
226 | /* |
227 | - * Copyright (C) 2013, 2015 Canonical, Ltd. |
228 | + * Copyright (C) 2013-2017 Canonical, Ltd. |
229 | * |
230 | * This program is free software; you can redistribute it and/or modify |
231 | * it under the terms of the GNU General Public License as published by |
232 | @@ -17,13 +17,16 @@ |
233 | |
234 | #include "Greeter.h" |
235 | #include "GreeterPrivate.h" |
236 | +#include <QCoreApplication> |
237 | #include <libintl.h> |
238 | |
239 | +static Greeter *singleton = nullptr; |
240 | + |
241 | GreeterPrivate::GreeterPrivate(Greeter* parent) |
242 | : m_greeter(new QLightDM::Greeter(parent)), |
243 | m_active(false), |
244 | - wasPrompted(false), |
245 | - promptless(false), |
246 | + responded(false), |
247 | + everResponded(false), |
248 | q_ptr(parent) |
249 | { |
250 | } |
251 | @@ -41,9 +44,32 @@ |
252 | connect(d->m_greeter, &QLightDM::Greeter::authenticationComplete, |
253 | this, &Greeter::authenticationCompleteFilter); |
254 | |
255 | + // Don't get stuck waiting for PAM as we shut down. |
256 | + connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, |
257 | + d->m_greeter, &QLightDM::Greeter::cancelAuthentication); |
258 | + |
259 | d->m_greeter->connectSync(); |
260 | } |
261 | |
262 | +Greeter::~Greeter() |
263 | +{ |
264 | + singleton = nullptr; |
265 | +} |
266 | + |
267 | +Greeter *Greeter::instance() |
268 | +{ |
269 | + if (!singleton) { |
270 | + singleton = new Greeter; |
271 | + } |
272 | + return singleton; |
273 | +} |
274 | + |
275 | +PromptsModel *Greeter::promptsModel() |
276 | +{ |
277 | + Q_D(Greeter); |
278 | + return &d->prompts; |
279 | +} |
280 | + |
281 | bool Greeter::isActive() const |
282 | { |
283 | Q_D(const Greeter); |
284 | @@ -68,7 +94,16 @@ |
285 | QString Greeter::authenticationUser() const |
286 | { |
287 | Q_D(const Greeter); |
288 | - return d->m_greeter->authenticationUser(); |
289 | + return d->cachedAuthUser; |
290 | +} |
291 | + |
292 | +void Greeter::checkAuthenticationUser() |
293 | +{ |
294 | + Q_D(Greeter); |
295 | + if (d->cachedAuthUser != d->m_greeter->authenticationUser()) { |
296 | + d->cachedAuthUser = d->m_greeter->authenticationUser(); |
297 | + Q_EMIT authenticationUserChanged(); |
298 | + } |
299 | } |
300 | |
301 | QString Greeter::defaultSessionHint() const |
302 | @@ -77,35 +112,64 @@ |
303 | return d->m_greeter->defaultSessionHint(); |
304 | } |
305 | |
306 | -bool Greeter::promptless() const |
307 | -{ |
308 | - Q_D(const Greeter); |
309 | - return d->promptless; |
310 | -} |
311 | - |
312 | QString Greeter::selectUser() const |
313 | { |
314 | Q_D(const Greeter); |
315 | - return d->m_greeter->selectUserHint(); |
316 | + if (hasGuestAccount() && d->m_greeter->selectGuestHint()) { |
317 | + return QStringLiteral("*guest"); |
318 | + } else { |
319 | + return d->m_greeter->selectUserHint(); |
320 | + } |
321 | +} |
322 | + |
323 | +bool Greeter::hasGuestAccount() const |
324 | +{ |
325 | + Q_D(const Greeter); |
326 | + return d->m_greeter->hasGuestAccountHint(); |
327 | +} |
328 | + |
329 | +bool Greeter::showManualLoginHint() const |
330 | +{ |
331 | + Q_D(const Greeter); |
332 | + return d->m_greeter->showManualLoginHint(); |
333 | +} |
334 | + |
335 | +bool Greeter::hideUsersHint() const |
336 | +{ |
337 | + Q_D(const Greeter); |
338 | + return d->m_greeter->hideUsersHint(); |
339 | } |
340 | |
341 | void Greeter::authenticate(const QString &username) |
342 | { |
343 | Q_D(Greeter); |
344 | - d->wasPrompted = false; |
345 | - if (d->promptless) { |
346 | - d->promptless = false; |
347 | - Q_EMIT promptlessChanged(); |
348 | - } |
349 | - |
350 | - d->m_greeter->authenticate(username); |
351 | + d->prompts.clear(); |
352 | + d->responded = false; |
353 | + d->everResponded = false; |
354 | + |
355 | + if (authenticationUser() == username) { |
356 | + d->prompts = d->leftovers; |
357 | + } |
358 | + d->leftovers.clear(); |
359 | + |
360 | + if (username == QStringLiteral("*guest")) { |
361 | + d->m_greeter->authenticateAsGuest(); |
362 | + } else if (username == QStringLiteral("*other")) { |
363 | + d->m_greeter->authenticate(nullptr); |
364 | + } else { |
365 | + d->m_greeter->authenticate(username); |
366 | + } |
367 | + |
368 | + Q_EMIT authenticationStarted(); |
369 | Q_EMIT isAuthenticatedChanged(); |
370 | - Q_EMIT authenticationUserChanged(username); |
371 | + checkAuthenticationUser(); |
372 | } |
373 | |
374 | void Greeter::respond(const QString &response) |
375 | { |
376 | Q_D(Greeter); |
377 | + d->responded = true; |
378 | + d->everResponded = true; |
379 | d->m_greeter->respond(response); |
380 | } |
381 | |
382 | @@ -118,32 +182,82 @@ |
383 | void Greeter::showPromptFilter(const QString &text, QLightDM::Greeter::PromptType type) |
384 | { |
385 | Q_D(Greeter); |
386 | - d->wasPrompted = true; |
387 | + |
388 | + checkAuthenticationUser(); // may have changed in liblightdm |
389 | |
390 | bool isDefaultPrompt = (text == dgettext("Linux-PAM", "Password: ")); |
391 | + bool isSecret = type == QLightDM::Greeter::PromptTypeSecret; |
392 | + |
393 | + QString trimmedText; |
394 | + if (!isDefaultPrompt) |
395 | + trimmedText = text.trimmed(); |
396 | |
397 | // Strip prompt of any colons at the end |
398 | - QString trimmedText = text.trimmed(); |
399 | if (trimmedText.endsWith(':') || trimmedText.endsWith(QStringLiteral(":"))) { |
400 | trimmedText.chop(1); |
401 | } |
402 | |
403 | - Q_EMIT showPrompt(trimmedText, type == QLightDM::Greeter::PromptTypeSecret, isDefaultPrompt); |
404 | + if (trimmedText == "login") { |
405 | + // 'login' is provided untranslated by LightDM when asking for a manual |
406 | + // login username. |
407 | + trimmedText = gettext("Username"); |
408 | + } |
409 | + |
410 | + if (d->responded) { |
411 | + d->prompts.clear(); |
412 | + d->responded = false; |
413 | + } |
414 | + |
415 | + d->prompts.append(trimmedText, isSecret ? PromptsModel::Secret : PromptsModel::Question); |
416 | } |
417 | |
418 | void Greeter::showMessageFilter(const QString &text, QLightDM::Greeter::MessageType type) |
419 | { |
420 | - Q_EMIT showMessage(text, type == QLightDM::Greeter::MessageTypeError); |
421 | + Q_D(Greeter); |
422 | + |
423 | + checkAuthenticationUser(); // may have changed in liblightdm |
424 | + |
425 | + bool isError = type == QLightDM::Greeter::MessageTypeError; |
426 | + |
427 | + if (d->responded) { |
428 | + d->prompts.clear(); |
429 | + d->responded = false; |
430 | + } |
431 | + d->prompts.append(text, isError? PromptsModel::Error : PromptsModel::Message); |
432 | } |
433 | |
434 | void Greeter::authenticationCompleteFilter() |
435 | { |
436 | Q_D(Greeter); |
437 | - if (!d->wasPrompted) { |
438 | - d->promptless = true; |
439 | - Q_EMIT promptlessChanged(); |
440 | - } |
441 | |
442 | Q_EMIT isAuthenticatedChanged(); |
443 | - Q_EMIT authenticationComplete(); |
444 | + |
445 | + bool automatic = !d->everResponded; |
446 | + bool pamHasLeftoverMessages = !d->prompts.hasPrompt() && d->prompts.rowCount() > 0; |
447 | + |
448 | + if (!isAuthenticated()) { |
449 | + if (pamHasLeftoverMessages) { |
450 | + d->leftovers = d->prompts; // Prefer PAM's messages |
451 | + } else if (automatic) { |
452 | + d->leftovers.append(gettext("Failed to authenticate"), PromptsModel::Error); |
453 | + } else { |
454 | + d->leftovers.append(gettext("Invalid password, please try again"), PromptsModel::Error); |
455 | + } |
456 | + } else if (pamHasLeftoverMessages) { |
457 | + automatic = true; // treat this successful login as automatic, so user sees message |
458 | + d->leftovers = d->prompts; |
459 | + } |
460 | + |
461 | + if (automatic) { |
462 | + d->prompts = d->leftovers; // OK, we'll just use these now |
463 | + d->leftovers.clear(); |
464 | + d->prompts.append(isAuthenticated() ? gettext("Log In") : gettext("Retry"), |
465 | + PromptsModel::Button); |
466 | + } |
467 | + |
468 | + if (isAuthenticated()) { |
469 | + Q_EMIT loginSuccess(automatic); |
470 | + } else { |
471 | + Q_EMIT loginError(automatic); |
472 | + } |
473 | } |
474 | |
475 | === modified file 'plugins/LightDM/Greeter.h' |
476 | --- plugins/LightDM/Greeter.h 2016-07-28 15:34:29 +0000 |
477 | +++ plugins/LightDM/Greeter.h 2017-01-25 16:04:08 +0000 |
478 | @@ -1,5 +1,5 @@ |
479 | /* |
480 | - * Copyright (C) 2012,2013,2015 Canonical, Ltd. |
481 | + * Copyright (C) 2012-2016 Canonical, Ltd. |
482 | * |
483 | * This program is free software; you can redistribute it and/or modify |
484 | * it under the terms of the GNU General Public License as published by |
485 | @@ -21,13 +21,13 @@ |
486 | such edits in the future, and by inserting ourselves here, we have more |
487 | control. */ |
488 | |
489 | -#ifndef UNITY_GREETER_H |
490 | -#define UNITY_GREETER_H |
491 | +#pragma once |
492 | |
493 | #include <QLightDM/Greeter> |
494 | #include <QtCore/QObject> |
495 | |
496 | class GreeterPrivate; |
497 | +class PromptsModel; |
498 | |
499 | class Greeter : public QObject |
500 | { |
501 | @@ -37,18 +37,22 @@ |
502 | Q_PROPERTY(bool authenticated READ isAuthenticated NOTIFY isAuthenticatedChanged) |
503 | Q_PROPERTY(QString authenticationUser READ authenticationUser NOTIFY authenticationUserChanged) |
504 | Q_PROPERTY(QString defaultSession READ defaultSessionHint CONSTANT) |
505 | - Q_PROPERTY(bool promptless READ promptless NOTIFY promptlessChanged) |
506 | Q_PROPERTY(QString selectUser READ selectUser CONSTANT) |
507 | |
508 | public: |
509 | - explicit Greeter(QObject* parent=0); |
510 | + static Greeter *instance(); |
511 | + virtual ~Greeter(); |
512 | |
513 | bool isActive() const; |
514 | bool isAuthenticated() const; |
515 | QString authenticationUser() const; |
516 | QString defaultSessionHint() const; |
517 | - bool promptless() const; |
518 | QString selectUser() const; |
519 | + bool hasGuestAccount() const; |
520 | + bool showManualLoginHint() const; |
521 | + bool hideUsersHint() const; |
522 | + |
523 | + PromptsModel *promptsModel(); |
524 | |
525 | public Q_SLOTS: |
526 | void authenticate(const QString &username=QString()); |
527 | @@ -57,21 +61,22 @@ |
528 | void setIsActive(bool isActive); |
529 | |
530 | Q_SIGNALS: |
531 | - void showMessage(const QString &text, bool isError); |
532 | - void showPrompt(const QString &text, bool isSecret, bool isDefaultPrompt); |
533 | - void authenticationComplete(); |
534 | - void authenticationUserChanged(const QString &user); |
535 | + void authenticationUserChanged(); |
536 | void isActiveChanged(); |
537 | void isAuthenticatedChanged(); |
538 | - void promptlessChanged(); |
539 | void showGreeter(); |
540 | void hideGreeter(); |
541 | + void loginError(bool automatic); |
542 | + void loginSuccess(bool automatic); |
543 | + void authenticationStarted(); // useful for testing |
544 | |
545 | // This signal is emitted by external agents like indicators, and the UI |
546 | // should switch to this user if possible. |
547 | void requestAuthenticationUser(const QString &user); |
548 | |
549 | protected: |
550 | + explicit Greeter(QObject* parent=0); |
551 | + |
552 | GreeterPrivate * const d_ptr; |
553 | |
554 | Q_DECLARE_PRIVATE(Greeter) |
555 | @@ -80,6 +85,5 @@ |
556 | void showMessageFilter(const QString &text, QLightDM::Greeter::MessageType type); |
557 | void showPromptFilter(const QString &text, QLightDM::Greeter::PromptType type); |
558 | void authenticationCompleteFilter(); |
559 | + void checkAuthenticationUser(); |
560 | }; |
561 | - |
562 | -#endif |
563 | |
564 | === modified file 'plugins/LightDM/GreeterPrivate.h' |
565 | --- plugins/LightDM/GreeterPrivate.h 2015-09-11 13:38:45 +0000 |
566 | +++ plugins/LightDM/GreeterPrivate.h 2017-01-25 16:04:08 +0000 |
567 | @@ -1,5 +1,5 @@ |
568 | /* |
569 | - * Copyright (C) 2014 Canonical, Ltd. |
570 | + * Copyright (C) 2015-2017 Canonical, Ltd. |
571 | * |
572 | * This program is free software; you can redistribute it and/or modify |
573 | * it under the terms of the GNU General Public License as published by |
574 | @@ -14,10 +14,14 @@ |
575 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
576 | */ |
577 | |
578 | -#ifndef UNITY_GREETER_PRIVATE_H |
579 | -#define UNITY_GREETER_PRIVATE_H |
580 | - |
581 | -#include <QLightDM/Greeter> |
582 | +#pragma once |
583 | + |
584 | +#include "PromptsModel.h" |
585 | +#include <QObject> |
586 | + |
587 | +namespace QLightDM { |
588 | + class Greeter; |
589 | +} |
590 | |
591 | class GreeterPrivate |
592 | { |
593 | @@ -26,8 +30,11 @@ |
594 | |
595 | QLightDM::Greeter *m_greeter; |
596 | bool m_active; |
597 | - bool wasPrompted; |
598 | - bool promptless; |
599 | + PromptsModel prompts; |
600 | + PromptsModel leftovers; // prompts to show during next auth for same user |
601 | + bool responded; |
602 | + bool everResponded; |
603 | + QString cachedAuthUser; |
604 | |
605 | protected: |
606 | Greeter * const q_ptr; |
607 | @@ -35,5 +42,3 @@ |
608 | private: |
609 | Q_DECLARE_PUBLIC(Greeter) |
610 | }; |
611 | - |
612 | -#endif // UNITY_GREETER_PRIVATE_H |
613 | |
614 | === modified file 'plugins/LightDM/IntegratedLightDM/liblightdm/Greeter.cpp' |
615 | --- plugins/LightDM/IntegratedLightDM/liblightdm/Greeter.cpp 2016-07-28 15:34:29 +0000 |
616 | +++ plugins/LightDM/IntegratedLightDM/liblightdm/Greeter.cpp 2017-01-25 16:04:08 +0000 |
617 | @@ -49,7 +49,7 @@ |
618 | |
619 | bool Greeter::hasGuestAccountHint() const |
620 | { |
621 | - return true; |
622 | + return false; |
623 | } |
624 | |
625 | QString Greeter::getHint(const QString &name) const |
626 | @@ -70,12 +70,12 @@ |
627 | |
628 | bool Greeter::showManualLoginHint() const |
629 | { |
630 | - return true; |
631 | + return false; |
632 | } |
633 | |
634 | bool Greeter::showRemoteLoginHint() const |
635 | { |
636 | - return true; |
637 | + return false; |
638 | } |
639 | |
640 | QString Greeter::selectUserHint() const |
641 | @@ -146,7 +146,10 @@ |
642 | } |
643 | |
644 | void Greeter::cancelAuthentication() |
645 | -{} |
646 | +{ |
647 | + Q_D(Greeter); |
648 | + d->cancelAuthentication(); |
649 | +} |
650 | |
651 | void Greeter::setLanguage (const QString &language) |
652 | { |
653 | |
654 | === modified file 'plugins/LightDM/IntegratedLightDM/liblightdm/GreeterPrivate.cpp' |
655 | --- plugins/LightDM/IntegratedLightDM/liblightdm/GreeterPrivate.cpp 2015-11-02 17:22:30 +0000 |
656 | +++ plugins/LightDM/IntegratedLightDM/liblightdm/GreeterPrivate.cpp 2017-01-25 16:04:08 +0000 |
657 | @@ -206,6 +206,28 @@ |
658 | } |
659 | } |
660 | |
661 | + void cancelPam() |
662 | + { |
663 | + if (pamHandle != nullptr) { |
664 | + QFuture<int> pamFuture = futureWatcher.future(); |
665 | + pam_handle *handle = pamHandle; |
666 | + pamHandle = nullptr; // to disable normal finishPam() handling |
667 | + pamFuture.cancel(); |
668 | + |
669 | + // Note the empty loop, we just want to clear the futures queue. |
670 | + // Any further prompts from the pam thread will be immediately |
671 | + // responded to/dismissed in handlePrompt(). |
672 | + while (respond(QString())); |
673 | + |
674 | + // Now let signal/slot handling happen so the thread can finish |
675 | + while (!pamFuture.isFinished()) { |
676 | + QCoreApplication::processEvents(); |
677 | + } |
678 | + |
679 | + pam_end(handle, PAM_CONV_ERR); |
680 | + } |
681 | + } |
682 | + |
683 | Q_SIGNALS: |
684 | void showMessage(pam_handle *handle, QString text, QLightDM::Greeter::MessageType type); |
685 | void showPrompt(pam_handle *handle, QString text, QLightDM::Greeter::PromptType type, QLightDM::GreeterImpl::ResponseFuture response); |
686 | @@ -247,28 +269,6 @@ |
687 | } |
688 | |
689 | private: |
690 | - void cancelPam() |
691 | - { |
692 | - if (pamHandle != nullptr) { |
693 | - QFuture<int> pamFuture = futureWatcher.future(); |
694 | - pam_handle *handle = pamHandle; |
695 | - pamHandle = nullptr; // to disable normal finishPam() handling |
696 | - pamFuture.cancel(); |
697 | - |
698 | - // Note the empty loop, we just want to clear the futures queue. |
699 | - // Any further prompts from the pam thread will be immediately |
700 | - // responded to/dismissed in handlePrompt(). |
701 | - while (respond(QString())); |
702 | - |
703 | - // Now let signal/slot handling happen so the thread can finish |
704 | - while (!pamFuture.isFinished()) { |
705 | - QCoreApplication::processEvents(); |
706 | - } |
707 | - |
708 | - pam_end(handle, PAM_CONV_ERR); |
709 | - } |
710 | - } |
711 | - |
712 | Greeter *greeter; |
713 | GreeterPrivate *greeterPrivate; |
714 | pam_handle* pamHandle; |
715 | @@ -299,6 +299,11 @@ |
716 | m_impl->respond(response); |
717 | } |
718 | |
719 | +void GreeterPrivate::cancelAuthentication() |
720 | +{ |
721 | + m_impl->cancelPam(); |
722 | +} |
723 | + |
724 | } |
725 | |
726 | #include "GreeterPrivate.moc" |
727 | |
728 | === modified file 'plugins/LightDM/IntegratedLightDM/liblightdm/GreeterPrivate.h' |
729 | --- plugins/LightDM/IntegratedLightDM/liblightdm/GreeterPrivate.h 2015-09-11 13:38:45 +0000 |
730 | +++ plugins/LightDM/IntegratedLightDM/liblightdm/GreeterPrivate.h 2017-01-25 16:04:08 +0000 |
731 | @@ -38,6 +38,7 @@ |
732 | |
733 | void handleAuthenticate(); |
734 | void handleRespond(const QString &response); |
735 | + void cancelAuthentication(); |
736 | |
737 | protected: |
738 | GreeterImpl *m_impl; // if the backend needs more private data |
739 | |
740 | === added file 'plugins/LightDM/PromptsModel.cpp' |
741 | --- plugins/LightDM/PromptsModel.cpp 1970-01-01 00:00:00 +0000 |
742 | +++ plugins/LightDM/PromptsModel.cpp 2017-01-25 16:04:08 +0000 |
743 | @@ -0,0 +1,97 @@ |
744 | +/* |
745 | + * Copyright (C) 2017 Canonical, Ltd. |
746 | + * |
747 | + * This program is free software; you can redistribute it and/or modify |
748 | + * it under the terms of the GNU General Public License as published by |
749 | + * the Free Software Foundation; version 3. |
750 | + * |
751 | + * This program is distributed in the hope that it will be useful, |
752 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
753 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
754 | + * GNU General Public License for more details. |
755 | + * |
756 | + * You should have received a copy of the GNU General Public License |
757 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
758 | + * |
759 | + */ |
760 | + |
761 | +#include "PromptsModel.h" |
762 | + |
763 | +PromptsModel::PromptsModel(QObject* parent) |
764 | + : QAbstractListModel(parent) |
765 | +{ |
766 | + m_roleNames[TypeRole] = "type"; |
767 | + m_roleNames[TextRole] = "text"; |
768 | +} |
769 | + |
770 | +PromptsModel& PromptsModel::operator=(const PromptsModel &other) |
771 | +{ |
772 | + beginResetModel(); |
773 | + m_prompts = other.m_prompts; |
774 | + endResetModel(); |
775 | + Q_EMIT countChanged(); |
776 | + return *this; |
777 | +} |
778 | + |
779 | +int PromptsModel::rowCount(const QModelIndex &parent) const |
780 | +{ |
781 | + if (parent.isValid()) |
782 | + return 0; |
783 | + |
784 | + return m_prompts.size(); |
785 | +} |
786 | + |
787 | +QVariant PromptsModel::data(const QModelIndex &index, int role) const |
788 | +{ |
789 | + if (!index.isValid() || index.column() > 0 || index.row() >= m_prompts.size()) |
790 | + return QVariant(); |
791 | + |
792 | + switch (role) { |
793 | + case Qt::DisplayRole: // fallthrough |
794 | + case TextRole: return m_prompts[index.row()].prompt; |
795 | + case TypeRole: return m_prompts[index.row()].type; |
796 | + default: return QVariant(); |
797 | + } |
798 | +} |
799 | + |
800 | +QHash<int, QByteArray> PromptsModel::roleNames() const |
801 | +{ |
802 | + return m_roleNames; |
803 | +} |
804 | + |
805 | +void PromptsModel::prepend(const QString &text, PromptType type) |
806 | +{ |
807 | + beginInsertRows(QModelIndex(), 0, 0); |
808 | + m_prompts.prepend(PromptInfo{text, type}); |
809 | + endInsertRows(); |
810 | + |
811 | + Q_EMIT countChanged(); |
812 | +} |
813 | + |
814 | +void PromptsModel::append(const QString &text, PromptType type) |
815 | +{ |
816 | + beginInsertRows(QModelIndex(), m_prompts.size(), m_prompts.size()); |
817 | + m_prompts.append(PromptInfo{text, type}); |
818 | + endInsertRows(); |
819 | + |
820 | + Q_EMIT countChanged(); |
821 | +} |
822 | + |
823 | +void PromptsModel::clear() |
824 | +{ |
825 | + beginResetModel(); |
826 | + m_prompts.clear(); |
827 | + endResetModel(); |
828 | + |
829 | + Q_EMIT countChanged(); |
830 | +} |
831 | + |
832 | +bool PromptsModel::hasPrompt() const |
833 | +{ |
834 | + Q_FOREACH(const PromptInfo &info, m_prompts) { |
835 | + if (info.type == PromptType::Secret || info.type == PromptType::Question) { |
836 | + return true; |
837 | + } |
838 | + } |
839 | + return false; |
840 | +} |
841 | |
842 | === added file 'plugins/LightDM/PromptsModel.h' |
843 | --- plugins/LightDM/PromptsModel.h 1970-01-01 00:00:00 +0000 |
844 | +++ plugins/LightDM/PromptsModel.h 2017-01-25 16:04:08 +0000 |
845 | @@ -0,0 +1,70 @@ |
846 | +/* |
847 | + * Copyright (C) 2017 Canonical, Ltd. |
848 | + * |
849 | + * This program is free software; you can redistribute it and/or modify |
850 | + * it under the terms of the GNU General Public License as published by |
851 | + * the Free Software Foundation; version 3. |
852 | + * |
853 | + * This program is distributed in the hope that it will be useful, |
854 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
855 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
856 | + * GNU General Public License for more details. |
857 | + * |
858 | + * You should have received a copy of the GNU General Public License |
859 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
860 | + * |
861 | + */ |
862 | + |
863 | +#pragma once |
864 | + |
865 | +#include <QAbstractListModel> |
866 | + |
867 | +class PromptsModel : public QAbstractListModel |
868 | +{ |
869 | + Q_OBJECT |
870 | + |
871 | + Q_PROPERTY(int count READ rowCount NOTIFY countChanged) |
872 | + |
873 | +public: |
874 | + enum PromptsModelRoles { |
875 | + TypeRole = Qt::UserRole, |
876 | + TextRole, |
877 | + }; |
878 | + Q_ENUM(PromptsModelRoles) |
879 | + |
880 | + enum PromptType { |
881 | + Message, |
882 | + Error, |
883 | + Secret, |
884 | + Question, |
885 | + Button, |
886 | + }; |
887 | + Q_ENUM(PromptType) |
888 | + |
889 | + explicit PromptsModel(QObject* parent=0); |
890 | + |
891 | + PromptsModel& operator=(const PromptsModel &other); |
892 | + |
893 | + int rowCount(const QModelIndex &parent = QModelIndex()) const override; |
894 | + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; |
895 | + QHash<int, QByteArray> roleNames() const override; |
896 | + |
897 | + Q_INVOKABLE void prepend(const QString &text, PromptType type); |
898 | + Q_INVOKABLE void append(const QString &text, PromptType type); |
899 | + |
900 | + void clear(); |
901 | + |
902 | + bool hasPrompt() const; |
903 | + |
904 | +Q_SIGNALS: |
905 | + void countChanged(); |
906 | + |
907 | +private: |
908 | + struct PromptInfo { |
909 | + QString prompt; |
910 | + PromptType type; |
911 | + }; |
912 | + |
913 | + QList<PromptInfo> m_prompts; |
914 | + QHash<int, QByteArray> m_roleNames; |
915 | +}; |
916 | |
917 | === modified file 'plugins/LightDM/SessionsModel.cpp' |
918 | --- plugins/LightDM/SessionsModel.cpp 2016-10-07 17:24:55 +0000 |
919 | +++ plugins/LightDM/SessionsModel.cpp 2017-01-25 16:04:08 +0000 |
920 | @@ -1,5 +1,5 @@ |
921 | /* |
922 | - * Copyright (C) 2015 Canonical, Ltd. |
923 | + * Copyright (C) 2015-2016 Canonical, Ltd. |
924 | * |
925 | * This program is free software; you can redistribute it and/or modify |
926 | * it under the terms of the GNU General Public License as published by |
927 | @@ -128,7 +128,6 @@ |
928 | } |
929 | |
930 | setModel(m_model); |
931 | - setSourceModel(m_model); |
932 | setSortCaseSensitivity(Qt::CaseInsensitive); |
933 | setSortLocaleAware(true); |
934 | setSortRole(Qt::DisplayRole); |
935 | |
936 | === modified file 'plugins/LightDM/SessionsModel.h' |
937 | --- plugins/LightDM/SessionsModel.h 2016-12-23 11:04:53 +0000 |
938 | +++ plugins/LightDM/SessionsModel.h 2017-01-25 16:04:08 +0000 |
939 | @@ -1,5 +1,5 @@ |
940 | /* |
941 | - * Copyright (C) 2015 Canonical, Ltd. |
942 | + * Copyright (C) 2015-2016 Canonical, Ltd. |
943 | * |
944 | * This program is free software; you can redistribute it and/or modify |
945 | * it under the terms of the GNU General Public License as published by |
946 | @@ -15,8 +15,7 @@ |
947 | * |
948 | */ |
949 | |
950 | -#ifndef UNITY_SESSIONSMODEL_H |
951 | -#define UNITY_SESSIONSMODEL_H |
952 | +#pragma once |
953 | |
954 | #include <unitysortfilterproxymodelqml.h> |
955 | #include <QLightDM/SessionsModel> |
956 | @@ -30,6 +29,7 @@ |
957 | |
958 | Q_PROPERTY(QList<QUrl> iconSearchDirectories READ iconSearchDirectories |
959 | WRITE setIconSearchDirectories NOTIFY iconSearchDirectoriesChanged) |
960 | + |
961 | Q_SIGNALS: |
962 | void iconSearchDirectoriesChanged(); |
963 | |
964 | @@ -54,6 +54,7 @@ |
965 | Q_INVOKABLE QUrl iconUrl(const QString sessionName) const; |
966 | |
967 | void setIconSearchDirectories(const QList<QUrl> searchDirectories); |
968 | + |
969 | private: |
970 | QLightDM::SessionsModel* m_model; |
971 | QHash<int, QByteArray> m_roleNames; |
972 | @@ -63,5 +64,3 @@ |
973 | QUrl("/usr/share/unity-greeter/")}; |
974 | |
975 | }; |
976 | - |
977 | -#endif // UNITY_SESSIONSMODEL_H |
978 | |
979 | === modified file 'plugins/LightDM/UsersModel.cpp' |
980 | --- plugins/LightDM/UsersModel.cpp 2015-10-26 20:15:08 +0000 |
981 | +++ plugins/LightDM/UsersModel.cpp 2017-01-25 16:04:08 +0000 |
982 | @@ -1,5 +1,5 @@ |
983 | /* |
984 | - * Copyright (C) 2013 Canonical, Ltd. |
985 | + * Copyright (C) 2013,2015-2016 Canonical, Ltd. |
986 | * |
987 | * This program is free software; you can redistribute it and/or modify |
988 | * it under the terms of the GNU General Public License as published by |
989 | @@ -12,20 +12,22 @@ |
990 | * |
991 | * You should have received a copy of the GNU General Public License |
992 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
993 | - * |
994 | - * Author: Michael Terry <michael.terry@canonical.com> |
995 | */ |
996 | |
997 | +#include "Greeter.h" |
998 | #include "UsersModel.h" |
999 | +#include <QIdentityProxyModel> |
1000 | #include <QLightDM/UsersModel> |
1001 | -#include <QtCore/QSortFilterProxyModel> |
1002 | + |
1003 | +#include <libintl.h> |
1004 | |
1005 | // First, we define an internal class that wraps LightDM's UsersModel. This |
1006 | // class will modify some of the data coming from LightDM. For example, we |
1007 | -// modify any empty Real Names into just normal Names. |
1008 | +// modify any empty Real Names into just normal Names. We also add optional |
1009 | +// rows, depending on configuration. |
1010 | // (We can't modify the data directly in UsersModel below because it won't sort |
1011 | // using the modified data.) |
1012 | -class MangleModel : public QSortFilterProxyModel |
1013 | +class MangleModel : public QIdentityProxyModel |
1014 | { |
1015 | Q_OBJECT |
1016 | |
1017 | @@ -33,21 +35,75 @@ |
1018 | explicit MangleModel(QObject* parent=0); |
1019 | |
1020 | QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; |
1021 | + int rowCount(const QModelIndex &parent = QModelIndex()) const override; |
1022 | + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; |
1023 | + |
1024 | +private: |
1025 | + struct CustomRow { |
1026 | + QString name; |
1027 | + QString realName; |
1028 | + }; |
1029 | + |
1030 | + int sourceRowCount() const; |
1031 | + |
1032 | + void updateGuestRow(); |
1033 | + void updateManualRow(); |
1034 | + void updateCustomRows(); |
1035 | + |
1036 | + void addCustomRow(const CustomRow &newRow); |
1037 | + void removeCustomRow(const QString &rowName); |
1038 | + |
1039 | + QList<CustomRow> m_customRows; |
1040 | + bool m_updatingCustomRows; |
1041 | }; |
1042 | |
1043 | MangleModel::MangleModel(QObject* parent) |
1044 | - : QSortFilterProxyModel(parent) |
1045 | + : QIdentityProxyModel(parent) |
1046 | + , m_updatingCustomRows(false) |
1047 | { |
1048 | setSourceModel(new QLightDM::UsersModel(this)); |
1049 | + |
1050 | + updateCustomRows(); |
1051 | + |
1052 | + // Would be nice if there were a rowCountChanged signal in the base class. |
1053 | + // We redo all custom rows on any row count change, because (A) some of |
1054 | + // custom rows (manual login) use row count information and (B) when |
1055 | + // testing, we use a modelReset signal as a way to indicate that a custom |
1056 | + // row has been toggled off or on. |
1057 | + connect(this, &QIdentityProxyModel::modelReset, |
1058 | + this, &MangleModel::updateCustomRows); |
1059 | + connect(this, &QIdentityProxyModel::rowsInserted, |
1060 | + this, &MangleModel::updateCustomRows); |
1061 | + connect(this, &QIdentityProxyModel::rowsRemoved, |
1062 | + this, &MangleModel::updateCustomRows); |
1063 | } |
1064 | |
1065 | QVariant MangleModel::data(const QModelIndex &index, int role) const |
1066 | { |
1067 | - QVariant variantData = QSortFilterProxyModel::data(index, role); |
1068 | + QVariant variantData; |
1069 | + |
1070 | + if (index.row() >= rowCount()) |
1071 | + return QVariant(); |
1072 | + |
1073 | + bool isCustomRow = index.row() >= sourceRowCount(); |
1074 | + if (isCustomRow && index.column() == 0) { |
1075 | + int customIndex = index.row() - sourceRowCount(); |
1076 | + if (role == QLightDM::UsersModel::NameRole) { |
1077 | + variantData = m_customRows[customIndex].name; |
1078 | + } else if (role == QLightDM::UsersModel::RealNameRole) { |
1079 | + variantData = m_customRows[customIndex].realName; |
1080 | + } else if (role == QLightDM::UsersModel::LoggedInRole) { |
1081 | + variantData = false; |
1082 | + } else if (role == QLightDM::UsersModel::SessionRole) { |
1083 | + variantData = Greeter::instance()->defaultSessionHint(); |
1084 | + } |
1085 | + } else { |
1086 | + variantData = QIdentityProxyModel::data(index, role); |
1087 | + } |
1088 | |
1089 | // If user's real name is empty, switch to unix name |
1090 | if (role == QLightDM::UsersModel::RealNameRole && variantData.toString().isEmpty()) { |
1091 | - variantData = QSortFilterProxyModel::data(index, QLightDM::UsersModel::NameRole); |
1092 | + variantData = data(index, QLightDM::UsersModel::NameRole); |
1093 | } else if (role == QLightDM::UsersModel::BackgroundPathRole && variantData.toString().startsWith('#')) { |
1094 | const QString stringData = "data:image/svg+xml,<svg><rect width='100%' height='100%' fill='" + variantData.toString() + "'/></svg>"; |
1095 | variantData = stringData; |
1096 | @@ -56,6 +112,95 @@ |
1097 | return variantData; |
1098 | } |
1099 | |
1100 | +void MangleModel::addCustomRow(const CustomRow &newRow) |
1101 | +{ |
1102 | + for (int i = 0; i < m_customRows.size(); i++) { |
1103 | + if (m_customRows[i].name == newRow.name) { |
1104 | + return; // we don't have custom rows that change content yet |
1105 | + } |
1106 | + } |
1107 | + |
1108 | + beginInsertRows(QModelIndex(), rowCount(), rowCount()); |
1109 | + m_customRows << newRow; |
1110 | + endInsertRows(); |
1111 | +} |
1112 | + |
1113 | +void MangleModel::removeCustomRow(const QString &rowName) |
1114 | +{ |
1115 | + for (int i = 0; i < m_customRows.size(); i++) { |
1116 | + if (m_customRows[i].name == rowName) { |
1117 | + int rowNum = sourceRowCount() + i; |
1118 | + beginRemoveRows(QModelIndex(), rowNum, rowNum); |
1119 | + m_customRows.removeAt(i); |
1120 | + endRemoveRows(); |
1121 | + break; |
1122 | + } |
1123 | + } |
1124 | +} |
1125 | + |
1126 | +void MangleModel::updateManualRow() |
1127 | +{ |
1128 | + bool hasAnotherEntry = sourceRowCount() > 0; |
1129 | + for (int i = 0; !hasAnotherEntry && i < m_customRows.size(); i++) { |
1130 | + if (m_customRows[i].name != QStringLiteral("*other")) { |
1131 | + hasAnotherEntry = true; |
1132 | + } |
1133 | + } |
1134 | + |
1135 | + // Show manual login if we are asked to OR if no other entry exists |
1136 | + if (Greeter::instance()->showManualLoginHint() || !hasAnotherEntry) |
1137 | + addCustomRow({QStringLiteral("*other"), gettext("Login")}); |
1138 | + else |
1139 | + removeCustomRow(QStringLiteral("*other")); |
1140 | +} |
1141 | + |
1142 | +void MangleModel::updateGuestRow() |
1143 | +{ |
1144 | + if (Greeter::instance()->hasGuestAccount()) |
1145 | + addCustomRow({QStringLiteral("*guest"), gettext("Guest Session")}); |
1146 | + else |
1147 | + removeCustomRow(QStringLiteral("*guest")); |
1148 | +} |
1149 | + |
1150 | +void MangleModel::updateCustomRows() |
1151 | +{ |
1152 | + // We update when rowCount changes, but we also insert/remove rows here. |
1153 | + // So guard this function to avoid recursion. |
1154 | + if (m_updatingCustomRows) |
1155 | + return; |
1156 | + |
1157 | + m_updatingCustomRows = true; |
1158 | + updateGuestRow(); |
1159 | + updateManualRow(); |
1160 | + m_updatingCustomRows = false; |
1161 | +} |
1162 | + |
1163 | +int MangleModel::rowCount(const QModelIndex &parent) const |
1164 | +{ |
1165 | + if (parent.isValid()) |
1166 | + return 0; |
1167 | + else |
1168 | + return sourceRowCount() + m_customRows.size(); |
1169 | +} |
1170 | + |
1171 | +int MangleModel::sourceRowCount() const |
1172 | +{ |
1173 | + return Greeter::instance()->hideUsersHint() ? 0 : sourceModel()->rowCount(); |
1174 | +} |
1175 | + |
1176 | +QModelIndex MangleModel::index(int row, int column, const QModelIndex &parent) const |
1177 | +{ |
1178 | + if (row >= rowCount()) |
1179 | + return QModelIndex(); |
1180 | + |
1181 | + bool isCustomRow = row >= sourceRowCount(); |
1182 | + if (isCustomRow && !parent.isValid()) { |
1183 | + return createIndex(row, column); |
1184 | + } else { |
1185 | + return QIdentityProxyModel::index(row, column, parent); |
1186 | + } |
1187 | +} |
1188 | + |
1189 | // **** Now we continue with actual UsersModel class **** |
1190 | |
1191 | UsersModel::UsersModel(QObject* parent) |
1192 | @@ -68,4 +213,21 @@ |
1193 | sort(0); |
1194 | } |
1195 | |
1196 | +bool UsersModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const |
1197 | +{ |
1198 | + auto leftName = source_left.data(QLightDM::UsersModel::NameRole); |
1199 | + auto rightName = source_right.data(QLightDM::UsersModel::NameRole); |
1200 | + |
1201 | + if (leftName == QStringLiteral("*guest")) |
1202 | + return false; |
1203 | + if (rightName == QStringLiteral("*guest")) |
1204 | + return true; |
1205 | + if (leftName == QStringLiteral("*other")) |
1206 | + return false; |
1207 | + if (rightName == QStringLiteral("*other")) |
1208 | + return true; |
1209 | + |
1210 | + return UnitySortFilterProxyModelQML::lessThan(source_left, source_right); |
1211 | +} |
1212 | + |
1213 | #include "UsersModel.moc" |
1214 | |
1215 | === modified file 'plugins/LightDM/UsersModel.h' |
1216 | --- plugins/LightDM/UsersModel.h 2015-09-11 13:38:45 +0000 |
1217 | +++ plugins/LightDM/UsersModel.h 2017-01-25 16:04:08 +0000 |
1218 | @@ -1,5 +1,5 @@ |
1219 | /* |
1220 | - * Copyright (C) 2012,2013 Canonical, Ltd. |
1221 | + * Copyright (C) 2012-2013,2015-2016 Canonical, Ltd. |
1222 | * |
1223 | * This program is free software; you can redistribute it and/or modify |
1224 | * it under the terms of the GNU General Public License as published by |
1225 | @@ -12,19 +12,16 @@ |
1226 | * |
1227 | * You should have received a copy of the GNU General Public License |
1228 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1229 | - * |
1230 | - * Authors: Michael Terry <michael.terry@canonical.com> |
1231 | */ |
1232 | |
1233 | /* This class is a really tiny filter around QLightDM::UsersModel. There are |
1234 | some operations that we want to edit a bit for the benefit of Qml. |
1235 | Specifically, we want to sort users according to realName. */ |
1236 | |
1237 | -#ifndef UNITY_USERSMODEL_H |
1238 | -#define UNITY_USERSMODEL_H |
1239 | +#pragma once |
1240 | |
1241 | #include <unitysortfilterproxymodelqml.h> |
1242 | -#include <QtCore/QObject> |
1243 | +#include <QObject> |
1244 | |
1245 | class UsersModel : public UnitySortFilterProxyModelQML |
1246 | { |
1247 | @@ -32,6 +29,7 @@ |
1248 | |
1249 | public: |
1250 | explicit UsersModel(QObject* parent=0); |
1251 | + |
1252 | +protected: |
1253 | + bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override; |
1254 | }; |
1255 | - |
1256 | -#endif |
1257 | |
1258 | === modified file 'plugins/LightDM/plugin.cpp' |
1259 | --- plugins/LightDM/plugin.cpp 2016-06-16 17:23:24 +0000 |
1260 | +++ plugins/LightDM/plugin.cpp 2017-01-25 16:04:08 +0000 |
1261 | @@ -19,6 +19,7 @@ |
1262 | #include "DBusGreeter.h" |
1263 | #include "DBusGreeterList.h" |
1264 | #include "Greeter.h" |
1265 | +#include "PromptsModel.h" |
1266 | #include "SessionsModel.h" |
1267 | #include "UsersModel.h" |
1268 | #include <libusermetricsoutput/ColorTheme.h> |
1269 | @@ -32,16 +33,24 @@ |
1270 | |
1271 | static QObject *greeter_provider(QQmlEngine *engine, QJSEngine *scriptEngine) |
1272 | { |
1273 | - Q_UNUSED(engine) |
1274 | Q_UNUSED(scriptEngine) |
1275 | |
1276 | - Greeter *greeter = new Greeter(); |
1277 | + Greeter *greeter = Greeter::instance(); |
1278 | new DBusGreeter(greeter, QStringLiteral("/")); |
1279 | new DBusGreeterList(greeter, QStringLiteral("/list")); |
1280 | |
1281 | + engine->setObjectOwnership(greeter, QQmlEngine::CppOwnership); |
1282 | + |
1283 | return greeter; |
1284 | } |
1285 | |
1286 | +static QObject *prompts_provider(QQmlEngine *engine, QJSEngine *) |
1287 | +{ |
1288 | + auto model = Greeter::instance()->promptsModel(); |
1289 | + engine->setObjectOwnership(model, QQmlEngine::CppOwnership); |
1290 | + return model; |
1291 | +} |
1292 | + |
1293 | static QObject *sessions_provider(QQmlEngine *engine, QJSEngine *scriptEngine) |
1294 | { |
1295 | Q_UNUSED(engine) |
1296 | @@ -78,8 +87,7 @@ |
1297 | #error No library defined in LightDM plugin |
1298 | #endif |
1299 | |
1300 | - qRegisterMetaType<QLightDM::Greeter::MessageType>("QLightDM::Greeter::MessageType"); |
1301 | - qRegisterMetaType<QLightDM::Greeter::PromptType>("QLightDM::Greeter::PromptType"); |
1302 | + qmlRegisterSingletonType<PromptsModel>(uri, 0, 1, "Prompts", prompts_provider); |
1303 | |
1304 | qmlRegisterSingletonType<SessionsModel>(uri, 0, 1, "Sessions", sessions_provider); |
1305 | qmlRegisterUncreatableType<QLightDM::SessionsModel>(uri, 0, 1, "SessionRoles", QStringLiteral("Type is not instantiable")); |
1306 | |
1307 | === modified file 'plugins/Unity/Launcher/launchermodel.cpp' |
1308 | --- plugins/Unity/Launcher/launchermodel.cpp 2016-11-01 18:18:56 +0000 |
1309 | +++ plugins/Unity/Launcher/launchermodel.cpp 2017-01-25 16:04:08 +0000 |
1310 | @@ -214,7 +214,6 @@ |
1311 | void LauncherModel::setUser(const QString &username) |
1312 | { |
1313 | Q_UNUSED(username) |
1314 | - qWarning() << "This backend doesn't support multiple users"; |
1315 | } |
1316 | |
1317 | QString LauncherModel::getUrlForAppId(const QString &appId) const |
1318 | |
1319 | === modified file 'plugins/Utils/CMakeLists.txt' |
1320 | --- plugins/Utils/CMakeLists.txt 2016-12-07 13:43:25 +0000 |
1321 | +++ plugins/Utils/CMakeLists.txt 2017-01-25 16:04:08 +0000 |
1322 | @@ -34,6 +34,7 @@ |
1323 | deviceconfigparser.cpp |
1324 | globalfunctions.cpp |
1325 | URLDispatcher.cpp |
1326 | + tabfocusfence.cpp |
1327 | plugin.cpp |
1328 | ) |
1329 | |
1330 | |
1331 | === modified file 'plugins/Utils/appdrawerproxymodel.cpp' |
1332 | --- plugins/Utils/appdrawerproxymodel.cpp 2016-12-23 11:04:53 +0000 |
1333 | +++ plugins/Utils/appdrawerproxymodel.cpp 2017-01-25 16:04:08 +0000 |
1334 | @@ -185,5 +185,5 @@ |
1335 | return adpm->appId(sourceIndex.row()); |
1336 | } |
1337 | } |
1338 | - return nullptr; |
1339 | + return QString(); |
1340 | } |
1341 | |
1342 | === modified file 'plugins/Utils/plugin.cpp' |
1343 | --- plugins/Utils/plugin.cpp 2016-11-01 18:18:56 +0000 |
1344 | +++ plugins/Utils/plugin.cpp 2017-01-25 16:04:08 +0000 |
1345 | @@ -1,5 +1,5 @@ |
1346 | /* |
1347 | - * Copyright (C) 2012-2015 Canonical, Ltd. |
1348 | + * Copyright (C) 2012-2017 Canonical, Ltd. |
1349 | * |
1350 | * This program is free software; you can redistribute it and/or modify |
1351 | * it under the terms of the GNU General Public License as published by |
1352 | @@ -40,6 +40,7 @@ |
1353 | #include "globalfunctions.h" |
1354 | #include "URLDispatcher.h" |
1355 | #include "appdrawerproxymodel.h" |
1356 | +#include "tabfocusfence.h" |
1357 | |
1358 | static QObject *createWindowStateStorage(QQmlEngine *engine, QJSEngine *scriptEngine) |
1359 | { |
1360 | @@ -84,4 +85,5 @@ |
1361 | qmlRegisterSingletonType<GlobalFunctions>(uri, 0, 1, "Functions", createGlobalFunctions); |
1362 | qmlRegisterType<URLDispatcher>(uri, 0, 1, "URLDispatcher"); |
1363 | qmlRegisterType<AppDrawerProxyModel>(uri, 0, 1, "AppDrawerProxyModel"); |
1364 | + qmlRegisterType<TabFocusFenceItem>(uri, 0, 1, "TabFocusFence"); |
1365 | } |
1366 | |
1367 | === added file 'plugins/Utils/tabfocusfence.cpp' |
1368 | --- plugins/Utils/tabfocusfence.cpp 1970-01-01 00:00:00 +0000 |
1369 | +++ plugins/Utils/tabfocusfence.cpp 2017-01-25 16:04:08 +0000 |
1370 | @@ -0,0 +1,72 @@ |
1371 | +/* |
1372 | + * Copyright 2017 Canonical Ltd. |
1373 | + * |
1374 | + * This program is free software; you can redistribute it and/or modify |
1375 | + * it under the terms of the GNU Lesser General Public License as published by |
1376 | + * the Free Software Foundation; version 3. |
1377 | + * |
1378 | + * This program is distributed in the hope that it will be useful, |
1379 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1380 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1381 | + * GNU Lesser General Public License for more details. |
1382 | + * |
1383 | + * You should have received a copy of the GNU Lesser General Public License |
1384 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1385 | +*/ |
1386 | + |
1387 | +#include "tabfocusfence.h" |
1388 | + |
1389 | +#include <private/qquickitem_p.h> |
1390 | + |
1391 | +TabFocusFenceItem::TabFocusFenceItem(QQuickItem *parent) : QQuickItem(parent) |
1392 | +{ |
1393 | + QQuickItemPrivate *d = QQuickItemPrivate::get(this); |
1394 | + d->isTabFence = true; |
1395 | + setFlag(ItemIsFocusScope); |
1396 | +} |
1397 | + |
1398 | +bool TabFocusFenceItem::focusNext() |
1399 | +{ |
1400 | + QQuickItem * current = scopedFocusItem(); |
1401 | + if (current) { |
1402 | + QQuickItem * next = current->nextItemInFocusChain(true); |
1403 | + if (next) { |
1404 | + next->setFocus(true, Qt::TabFocusReason); |
1405 | + return true; |
1406 | + } |
1407 | + } |
1408 | + return false; |
1409 | +} |
1410 | + |
1411 | +bool TabFocusFenceItem::focusPrev() |
1412 | +{ |
1413 | + QQuickItem * current = scopedFocusItem(); |
1414 | + if (current) { |
1415 | + QQuickItem * prev = current->nextItemInFocusChain(false); |
1416 | + if (prev) { |
1417 | + prev->setFocus(true, Qt::BacktabFocusReason); |
1418 | + return true; |
1419 | + } |
1420 | + } |
1421 | + return false; |
1422 | +} |
1423 | + |
1424 | +void TabFocusFenceItem::keyPressEvent(QKeyEvent *event) |
1425 | +{ |
1426 | + // Needed so we eat Tab keys when there's only one item inside the fence |
1427 | + if (event->key() == Qt::Key_Tab) { |
1428 | + event->accept(); |
1429 | + } else { |
1430 | + QQuickItem::keyPressEvent(event); |
1431 | + } |
1432 | +} |
1433 | + |
1434 | +void TabFocusFenceItem::keyReleaseEvent(QKeyEvent *event) |
1435 | +{ |
1436 | + // Needed so we eat Tab keys when there's only one item inside the fence |
1437 | + if (event->key() == Qt::Key_Tab) { |
1438 | + event->accept(); |
1439 | + } else { |
1440 | + QQuickItem::keyReleaseEvent(event); |
1441 | + } |
1442 | +} |
1443 | |
1444 | === added file 'plugins/Utils/tabfocusfence.h' |
1445 | --- plugins/Utils/tabfocusfence.h 1970-01-01 00:00:00 +0000 |
1446 | +++ plugins/Utils/tabfocusfence.h 2017-01-25 16:04:08 +0000 |
1447 | @@ -0,0 +1,38 @@ |
1448 | +/* |
1449 | + * Copyright 2017 Canonical Ltd. |
1450 | + * |
1451 | + * This program is free software; you can redistribute it and/or modify |
1452 | + * it under the terms of the GNU Lesser General Public License as published by |
1453 | + * the Free Software Foundation; version 3. |
1454 | + * |
1455 | + * This program is distributed in the hope that it will be useful, |
1456 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1457 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1458 | + * GNU Lesser General Public License for more details. |
1459 | + * |
1460 | + * You should have received a copy of the GNU Lesser General Public License |
1461 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1462 | +*/ |
1463 | + |
1464 | +#ifndef TABFOCUSFENCE_H |
1465 | +#define TABFOCUSFENCE_H |
1466 | + |
1467 | +#include <QQuickItem> |
1468 | + |
1469 | +// An item that restricts focus Tab travelling |
1470 | +// to its children |
1471 | +class TabFocusFenceItem : public QQuickItem |
1472 | +{ |
1473 | +Q_OBJECT |
1474 | + |
1475 | +public: |
1476 | + TabFocusFenceItem(QQuickItem *parent = nullptr); |
1477 | + |
1478 | + Q_INVOKABLE bool focusNext(); |
1479 | + Q_INVOKABLE bool focusPrev(); |
1480 | + |
1481 | + void keyPressEvent(QKeyEvent *event) override; |
1482 | + void keyReleaseEvent(QKeyEvent *event) override; |
1483 | +}; |
1484 | + |
1485 | +#endif |
1486 | |
1487 | === modified file 'po/unity8.pot' |
1488 | --- po/unity8.pot 2017-01-10 14:48:43 +0000 |
1489 | +++ po/unity8.pot 2017-01-25 16:04:08 +0000 |
1490 | @@ -8,7 +8,7 @@ |
1491 | msgstr "" |
1492 | "Project-Id-Version: unity8\n" |
1493 | "Report-Msgid-Bugs-To: \n" |
1494 | -"POT-Creation-Date: 2017-01-10 14:48+0000\n" |
1495 | +"POT-Creation-Date: 2017-01-24 07:46+0000\n" |
1496 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" |
1497 | "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" |
1498 | "Language-Team: LANGUAGE <LL@li.org>\n" |
1499 | @@ -18,10 +18,38 @@ |
1500 | "Content-Transfer-Encoding: 8bit\n" |
1501 | "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" |
1502 | |
1503 | -#: plugins/LightDM/Greeter.cpp:123 |
1504 | +#: plugins/LightDM/Greeter.cpp:188 |
1505 | msgid "Password: " |
1506 | msgstr "" |
1507 | |
1508 | +#: plugins/LightDM/Greeter.cpp:203 |
1509 | +msgid "Username" |
1510 | +msgstr "" |
1511 | + |
1512 | +#: plugins/LightDM/Greeter.cpp:242 |
1513 | +msgid "Failed to authenticate" |
1514 | +msgstr "" |
1515 | + |
1516 | +#: plugins/LightDM/Greeter.cpp:244 |
1517 | +msgid "Invalid password, please try again" |
1518 | +msgstr "" |
1519 | + |
1520 | +#: plugins/LightDM/Greeter.cpp:254 |
1521 | +msgid "Log In" |
1522 | +msgstr "" |
1523 | + |
1524 | +#: plugins/LightDM/Greeter.cpp:254 |
1525 | +msgid "Retry" |
1526 | +msgstr "" |
1527 | + |
1528 | +#: plugins/LightDM/UsersModel.cpp:152 |
1529 | +msgid "Login" |
1530 | +msgstr "" |
1531 | + |
1532 | +#: plugins/LightDM/UsersModel.cpp:160 |
1533 | +msgid "Guest Session" |
1534 | +msgstr "" |
1535 | + |
1536 | #: plugins/Unity/Launcher/launcheritem.cpp:50 |
1537 | #: plugins/Unity/Launcher/launcheritem.cpp:122 |
1538 | msgid "Pin shortcut" |
1539 | @@ -35,66 +63,66 @@ |
1540 | msgid "Unpin shortcut" |
1541 | msgstr "" |
1542 | |
1543 | -#: qml/Components/Dialogs.qml:149 |
1544 | +#: qml/Components/Dialogs.qml:177 |
1545 | msgctxt "Title: Lock/Log out dialog" |
1546 | msgid "Log out" |
1547 | msgstr "" |
1548 | |
1549 | -#: qml/Components/Dialogs.qml:150 |
1550 | +#: qml/Components/Dialogs.qml:178 |
1551 | msgid "Are you sure you want to log out?" |
1552 | msgstr "" |
1553 | |
1554 | -#: qml/Components/Dialogs.qml:152 |
1555 | +#: qml/Components/Dialogs.qml:181 |
1556 | msgctxt "Button: Lock the system" |
1557 | msgid "Lock" |
1558 | msgstr "" |
1559 | |
1560 | -#: qml/Components/Dialogs.qml:160 |
1561 | +#: qml/Components/Dialogs.qml:191 |
1562 | msgctxt "Button: Log out from the system" |
1563 | msgid "Log Out" |
1564 | msgstr "" |
1565 | |
1566 | -#: qml/Components/Dialogs.qml:167 qml/Components/Dialogs.qml:225 |
1567 | -#: qml/Dash/DashPageHeader.qml:324 qml/Greeter/NarrowView.qml:232 |
1568 | +#: qml/Components/Dialogs.qml:199 qml/Components/Dialogs.qml:264 |
1569 | +#: qml/Dash/DashPageHeader.qml:324 qml/Greeter/NarrowView.qml:222 |
1570 | #: qml/Wizard/Pages/passcode-confirm.qml:32 |
1571 | #: qml/Wizard/Pages/passcode-set.qml:32 |
1572 | msgid "Cancel" |
1573 | msgstr "" |
1574 | |
1575 | -#: qml/Components/Dialogs.qml:179 |
1576 | +#: qml/Components/Dialogs.qml:211 |
1577 | msgctxt "Title: Reboot dialog" |
1578 | msgid "Reboot" |
1579 | msgstr "" |
1580 | |
1581 | -#: qml/Components/Dialogs.qml:180 |
1582 | +#: qml/Components/Dialogs.qml:212 |
1583 | msgid "Are you sure you want to reboot?" |
1584 | msgstr "" |
1585 | |
1586 | -#: qml/Components/Dialogs.qml:182 |
1587 | +#: qml/Components/Dialogs.qml:215 |
1588 | msgid "No" |
1589 | msgstr "" |
1590 | |
1591 | -#: qml/Components/Dialogs.qml:189 |
1592 | +#: qml/Components/Dialogs.qml:223 |
1593 | msgid "Yes" |
1594 | msgstr "" |
1595 | |
1596 | -#: qml/Components/Dialogs.qml:204 |
1597 | +#: qml/Components/Dialogs.qml:239 |
1598 | msgctxt "Title: Power off/Restart dialog" |
1599 | msgid "Power" |
1600 | msgstr "" |
1601 | |
1602 | -#: qml/Components/Dialogs.qml:205 |
1603 | +#: qml/Components/Dialogs.qml:240 |
1604 | msgid "" |
1605 | "Are you sure you would like\n" |
1606 | "to power off?" |
1607 | msgstr "" |
1608 | |
1609 | -#: qml/Components/Dialogs.qml:208 |
1610 | +#: qml/Components/Dialogs.qml:244 |
1611 | msgctxt "Button: Power off the system" |
1612 | msgid "Power off" |
1613 | msgstr "" |
1614 | |
1615 | -#: qml/Components/Dialogs.qml:217 |
1616 | +#: qml/Components/Dialogs.qml:255 |
1617 | msgctxt "Button: Restart the system" |
1618 | msgid "Restart" |
1619 | msgstr "" |
1620 | @@ -120,7 +148,7 @@ |
1621 | msgstr "" |
1622 | |
1623 | #: qml/Components/KeyboardShortcutsOverlay.qml:80 |
1624 | -msgid "Takes a screenshot of a window." |
1625 | +msgid "Takes a screenshot of the current window." |
1626 | msgstr "" |
1627 | |
1628 | #: qml/Components/KeyboardShortcutsOverlay.qml:88 |
1629 | @@ -267,7 +295,7 @@ |
1630 | msgid "Closes the current window." |
1631 | msgstr "" |
1632 | |
1633 | -#: qml/Components/Lockscreen.qml:212 qml/Greeter/NarrowView.qml:252 |
1634 | +#: qml/Components/Lockscreen.qml:212 qml/Greeter/NarrowView.qml:242 |
1635 | msgid "Return to Call" |
1636 | msgstr "" |
1637 | |
1638 | @@ -275,7 +303,7 @@ |
1639 | msgid "Emergency Call" |
1640 | msgstr "" |
1641 | |
1642 | -#: qml/Components/Lockscreen.qml:244 |
1643 | +#: qml/Components/Lockscreen.qml:245 |
1644 | msgid "OK" |
1645 | msgstr "" |
1646 | |
1647 | @@ -289,29 +317,29 @@ |
1648 | msgid "%1:%2" |
1649 | msgstr "" |
1650 | |
1651 | -#: qml/Components/ModeSwitchWarningDialog.qml:32 |
1652 | +#: qml/Components/ModeSwitchWarningDialog.qml:33 |
1653 | msgid "Apps may have unsaved data:" |
1654 | msgstr "" |
1655 | |
1656 | -#: qml/Components/ModeSwitchWarningDialog.qml:57 |
1657 | +#: qml/Components/ModeSwitchWarningDialog.qml:60 |
1658 | msgctxt "" |
1659 | "Re-dock means connect the device again to an external screen/mouse/keyboard" |
1660 | msgid "Re-dock, save your work and close these apps to continue." |
1661 | msgstr "" |
1662 | |
1663 | -#: qml/Components/ModeSwitchWarningDialog.qml:63 |
1664 | +#: qml/Components/ModeSwitchWarningDialog.qml:67 |
1665 | msgid "Or force close now (unsaved data will be lost)." |
1666 | msgstr "" |
1667 | |
1668 | -#: qml/Components/ModeSwitchWarningDialog.qml:75 |
1669 | +#: qml/Components/ModeSwitchWarningDialog.qml:80 |
1670 | msgid "OK, I will reconnect" |
1671 | msgstr "" |
1672 | |
1673 | -#: qml/Components/ModeSwitchWarningDialog.qml:76 |
1674 | +#: qml/Components/ModeSwitchWarningDialog.qml:81 |
1675 | msgid "Reconnect now!" |
1676 | msgstr "" |
1677 | |
1678 | -#: qml/Components/ModeSwitchWarningDialog.qml:88 |
1679 | +#: qml/Components/ModeSwitchWarningDialog.qml:94 |
1680 | msgid "Close all" |
1681 | msgstr "" |
1682 | |
1683 | @@ -461,44 +489,44 @@ |
1684 | msgstr[0] "" |
1685 | msgstr[1] "" |
1686 | |
1687 | -#: qml/Greeter/Greeter.qml:592 |
1688 | +#: qml/Greeter/Greeter.qml:561 |
1689 | msgid "Try again" |
1690 | msgstr "" |
1691 | |
1692 | -#: qml/Greeter/LoginList.qml:70 |
1693 | +#: qml/Greeter/Greeter.qml:562 |
1694 | +msgid "Enter passphrase to unlock" |
1695 | +msgstr "" |
1696 | + |
1697 | +#: qml/Greeter/Greeter.qml:563 |
1698 | +msgid "Enter passcode to unlock" |
1699 | +msgstr "" |
1700 | + |
1701 | +#: qml/Greeter/NarrowView.qml:242 |
1702 | +msgid "Emergency" |
1703 | +msgstr "" |
1704 | + |
1705 | +#: qml/Greeter/PromptList.qml:126 |
1706 | msgid "Passphrase" |
1707 | msgstr "" |
1708 | |
1709 | -#: qml/Greeter/LoginList.qml:71 |
1710 | +#: qml/Greeter/PromptList.qml:126 |
1711 | msgid "Passcode" |
1712 | msgstr "" |
1713 | |
1714 | -#: qml/Greeter/LoginList.qml:98 |
1715 | -msgid "Retry" |
1716 | -msgstr "" |
1717 | - |
1718 | -#: qml/Greeter/LoginList.qml:99 |
1719 | -msgid "Log In" |
1720 | -msgstr "" |
1721 | - |
1722 | -#: qml/Greeter/NarrowView.qml:252 |
1723 | -msgid "Emergency" |
1724 | -msgstr "" |
1725 | - |
1726 | #: qml/Greeter/SessionsList.qml:122 |
1727 | msgid "Select desktop environment" |
1728 | msgstr "" |
1729 | |
1730 | -#: qml/Launcher/Drawer.qml:80 |
1731 | +#: qml/Launcher/Drawer.qml:92 |
1732 | msgid "Search…" |
1733 | msgstr "" |
1734 | |
1735 | -#: qml/Launcher/Drawer.qml:101 |
1736 | +#: qml/Launcher/Drawer.qml:123 |
1737 | msgctxt "Apps sorted alphabetically" |
1738 | msgid "A-Z" |
1739 | msgstr "" |
1740 | |
1741 | -#: qml/Launcher/MoreAppsHeader.qml:32 |
1742 | +#: qml/Launcher/MoreAppsHeader.qml:39 |
1743 | msgid "More apps in the store" |
1744 | msgstr "" |
1745 | |
1746 | |
1747 | === modified file 'qml/ApplicationMenus/MenuBar.qml' |
1748 | --- qml/ApplicationMenus/MenuBar.qml 2017-01-09 15:26:05 +0000 |
1749 | +++ qml/ApplicationMenus/MenuBar.qml 2017-01-25 16:04:08 +0000 |
1750 | @@ -57,6 +57,20 @@ |
1751 | onReleased: d.stopSHortcutTimer() |
1752 | } |
1753 | |
1754 | + GlobalShortcut { |
1755 | + shortcut: Qt.AltModifier | Qt.Key_F10 |
1756 | + active: enableKeyFilter && d.currentItem == null |
1757 | + onTriggered: { |
1758 | + for (var i = 0; i < rowRepeater.count; i++) { |
1759 | + var item = rowRepeater.itemAt(i); |
1760 | + if (item.enabled) { |
1761 | + item.show(); |
1762 | + break; |
1763 | + } |
1764 | + } |
1765 | + } |
1766 | + } |
1767 | + |
1768 | InverseMouseArea { |
1769 | acceptedButtons: Qt.LeftButton | Qt.MiddleButton | Qt.RightButton |
1770 | anchors.fill: parent |
1771 | |
1772 | === modified file 'qml/ApplicationMenus/MenuPopup.qml' |
1773 | --- qml/ApplicationMenus/MenuPopup.qml 2017-01-09 15:26:05 +0000 |
1774 | +++ qml/ApplicationMenus/MenuPopup.qml 2017-01-25 16:04:08 +0000 |
1775 | @@ -26,19 +26,7 @@ |
1776 | objectName: "menu" |
1777 | backgroundColor: theme.palette.normal.overlay |
1778 | |
1779 | - property alias unityMenuModel: listView.model |
1780 | - |
1781 | - readonly property real __ajustedMinimumHeight: { |
1782 | - if (listView.contentHeight > __minimumHeight) { |
1783 | - return units.gu(30); |
1784 | - } |
1785 | - return Math.max(listView.contentHeight, units.gu(2)); |
1786 | - } |
1787 | - |
1788 | - readonly property real __minimumWidth: units.gu(20) |
1789 | - readonly property real __minimumHeight: units.gu(30) |
1790 | - readonly property real __maximumWidth: Screen.width * 0.7 |
1791 | - readonly property real __maximumHeight: Screen.height * 0.7 |
1792 | + property alias unityMenuModel: repeater.model |
1793 | |
1794 | function show() { |
1795 | visible = true; |
1796 | @@ -63,18 +51,23 @@ |
1797 | d.dismissAll(); |
1798 | } |
1799 | |
1800 | - implicitWidth: container.width |
1801 | - implicitHeight: MathUtils.clamp(listView.contentHeight, __ajustedMinimumHeight, __maximumHeight) |
1802 | + implicitWidth: focusScope.width |
1803 | + implicitHeight: focusScope.height |
1804 | |
1805 | MenuNavigator { |
1806 | id: d |
1807 | objectName: "d" |
1808 | - itemView: listView |
1809 | + itemView: repeater |
1810 | |
1811 | property Item currentItem: null |
1812 | property Item hoveredItem: null |
1813 | readonly property int currentIndex: currentItem ? currentItem.__ownIndex : -1 |
1814 | |
1815 | + property real __minimumWidth: units.gu(20) |
1816 | + property real __maximumWidth: Screen.width * 0.7 |
1817 | + property real __minimumHeight: units.gu(2) |
1818 | + property real __maximumHeight: Screen.height * 0.7 |
1819 | + |
1820 | signal dismissAll() |
1821 | |
1822 | onCurrentItemChanged: { |
1823 | @@ -86,13 +79,26 @@ |
1824 | } |
1825 | |
1826 | onSelect: { |
1827 | - currentItem = listView.itemAt(index); |
1828 | + currentItem = repeater.itemAt(index); |
1829 | + if (currentItem) { |
1830 | + if (currentItem.y < listView.contentY) { |
1831 | + listView.contentY = currentItem.y; |
1832 | + } else if (currentItem.y + currentItem.height > listView.contentY + listView.height) { |
1833 | + listView.contentY = currentItem.y + currentItem.height - listView.height; |
1834 | + } |
1835 | + } |
1836 | } |
1837 | } |
1838 | |
1839 | + MouseArea { |
1840 | + // Eat events. |
1841 | + anchors.fill: parent |
1842 | + } |
1843 | + |
1844 | Item { |
1845 | id: focusScope |
1846 | - anchors.fill: parent |
1847 | + width: container.width |
1848 | + height: container.height |
1849 | focus: visible |
1850 | |
1851 | Keys.onUpPressed: d.selectPrevious(d.currentIndex) |
1852 | @@ -108,17 +114,17 @@ |
1853 | id: container |
1854 | objectName: "container" |
1855 | |
1856 | - width: listView.contentWidth |
1857 | - height: parent.height |
1858 | + height: MathUtils.clamp(listView.contentHeight, d.__minimumHeight, d.__maximumHeight) |
1859 | + width: menuColumn.width |
1860 | spacing: 0 |
1861 | |
1862 | - // FIXME use ListView.header - tried but was flaky with positionViewAtIndex. |
1863 | + // Header - scroll up |
1864 | Item { |
1865 | - Layout.fillWidth: true; |
1866 | - Layout.maximumHeight: units.gu(3) |
1867 | - Layout.minimumHeight: units.gu(3) |
1868 | + Layout.fillWidth: true |
1869 | + height: units.gu(3) |
1870 | visible: listView.contentHeight > root.height |
1871 | enabled: !listView.atYBeginning |
1872 | + z: 1 |
1873 | |
1874 | Rectangle { |
1875 | color: enabled ? theme.palette.normal.overlayText : |
1876 | @@ -143,47 +149,35 @@ |
1877 | MouseArea { |
1878 | anchors.fill: parent |
1879 | onPressed: { |
1880 | - var index = listView.indexAt(0, listView.contentY); |
1881 | - listView.positionViewAtIndex(index-1, ListView.Beginning); |
1882 | + var item = menuColumn.childAt(0, listView.contentY); |
1883 | + if (item) { |
1884 | + var previousItem = item; |
1885 | + do { |
1886 | + previousItem = repeater.itemAt(previousItem.__ownIndex-1); |
1887 | + if (!previousItem) { |
1888 | + listView.contentY = 0; |
1889 | + return; |
1890 | + } |
1891 | + } while (previousItem.__isSeparator); |
1892 | + |
1893 | + listView.contentY = previousItem.y |
1894 | + } |
1895 | } |
1896 | } |
1897 | } |
1898 | |
1899 | - ListView { |
1900 | + // Menu Items |
1901 | + Flickable { |
1902 | id: listView |
1903 | - objectName: "listView" |
1904 | + clip: interactive |
1905 | + |
1906 | Layout.fillHeight: true |
1907 | Layout.fillWidth: true |
1908 | - contentWidth: MathUtils.clamp(contentItem.childrenRect.width, |
1909 | - __minimumWidth, |
1910 | - __maximumWidth) |
1911 | - |
1912 | - orientation: Qt.Vertical |
1913 | - interactive: contentHeight > height |
1914 | - clip: interactive |
1915 | - highlightFollowsCurrentItem: false |
1916 | - |
1917 | - highlight: Rectangle { |
1918 | - color: "transparent" |
1919 | - border.width: units.dp(1) |
1920 | - border.color: UbuntuColors.orange |
1921 | - z: 1 |
1922 | - |
1923 | - width: listView.width |
1924 | - height: d.currentItem ? d.currentItem.height : 0 |
1925 | - y: d.currentItem ? d.currentItem.y : 0 |
1926 | - visible: d.currentItem |
1927 | - } |
1928 | - |
1929 | - function itemAt(index) { |
1930 | - if (index > count || index < 0) return null; |
1931 | - currentIndex = index; |
1932 | - return currentItem; |
1933 | - } |
1934 | + contentHeight: menuColumn.height |
1935 | + interactive: height < contentHeight |
1936 | |
1937 | MouseArea { |
1938 | - id: menuMouseArea |
1939 | - anchors.fill: listView |
1940 | + anchors.fill: parent |
1941 | hoverEnabled: true |
1942 | propagateComposedEvents: true // propogate events so we send clicks to children. |
1943 | z: 1 // on top so we override any other hovers |
1944 | @@ -195,7 +189,7 @@ |
1945 | |
1946 | if (!d.hoveredItem || !d.currentItem || |
1947 | !d.hoveredItem.contains(Qt.point(pos.x - d.currentItem.x, pos.y - d.currentItem.y))) { |
1948 | - d.hoveredItem = listView.itemAt(listView.indexAt(pos.x, pos.y)); |
1949 | + d.hoveredItem = menuColumn.childAt(pos.x, pos.y) |
1950 | if (!d.hoveredItem || !d.hoveredItem.enabled) |
1951 | return false; |
1952 | d.currentItem = d.hoveredItem; |
1953 | @@ -216,89 +210,123 @@ |
1954 | } |
1955 | } |
1956 | |
1957 | - delegate: Loader { |
1958 | - id: loader |
1959 | - objectName: root.objectName + "-item" + __ownIndex |
1960 | - |
1961 | - property int __ownIndex: index |
1962 | - |
1963 | - width: root.width |
1964 | - enabled: model.isSeparator ? false : model.sensitive |
1965 | - |
1966 | - sourceComponent: { |
1967 | - if (model.isSeparator) { |
1968 | - return separatorComponent; |
1969 | - } |
1970 | - return menuItemComponent; |
1971 | - } |
1972 | - |
1973 | - property Item popup: null |
1974 | - |
1975 | - Component { |
1976 | - id: menuItemComponent |
1977 | - MenuItem { |
1978 | - id: menuItem |
1979 | - menuData: model |
1980 | - objectName: loader.objectName + "-actionItem" |
1981 | - |
1982 | - action.onTriggered: { |
1983 | - d.currentItem = loader; |
1984 | - |
1985 | - if (hasSubmenu) { |
1986 | - if (!popup) { |
1987 | - var model = root.unityMenuModel.submenu(__ownIndex); |
1988 | - popup = submenuComponent.createObject(focusScope, { |
1989 | - objectName: loader.objectName + "-", |
1990 | - unityMenuModel: model, |
1991 | - x: Qt.binding(function() { return root.width }), |
1992 | - y: Qt.binding(function() { return loader.y }) |
1993 | - }); |
1994 | - } else if (popup) { |
1995 | - popup.visible = true; |
1996 | - } |
1997 | - popup.retreat.connect(function() { |
1998 | - popup.destroy(); |
1999 | - popup = null; |
2000 | - menuItem.forceActiveFocus(); |
2001 | - }) |
2002 | - } else { |
2003 | - root.unityMenuModel.activate(__ownIndex); |
2004 | - } |
2005 | - } |
2006 | - |
2007 | - Connections { |
2008 | - target: d |
2009 | - onCurrentIndexChanged: { |
2010 | - if (popup && d.currentIndex != __ownIndex) { |
2011 | - popup.visible = false; |
2012 | - } |
2013 | - } |
2014 | - onDismissAll: { |
2015 | - if (popup) { |
2016 | - popup.destroy(); |
2017 | - popup = null; |
2018 | - } |
2019 | - } |
2020 | - } |
2021 | - } |
2022 | - } |
2023 | - |
2024 | - Component { |
2025 | - id: separatorComponent |
2026 | - ListItems.ThinDivider { |
2027 | - objectName: loader.objectName + "-separator" |
2028 | - } |
2029 | - } |
2030 | - } |
2031 | - } // ListView |
2032 | - |
2033 | - // FIXME use ListView.footer - tried but was flaky with positionViewAtIndex. |
2034 | + ColumnLayout { |
2035 | + id: menuColumn |
2036 | + spacing: 0 |
2037 | + |
2038 | + width: MathUtils.clamp(implicitWidth, d.__minimumWidth, d.__maximumWidth) |
2039 | + |
2040 | + Repeater { |
2041 | + id: repeater |
2042 | + |
2043 | + Loader { |
2044 | + id: loader |
2045 | + objectName: root.objectName + "-item" + __ownIndex |
2046 | + |
2047 | + property int __ownIndex: index |
2048 | + property bool __isSeparator: model.isSeparator |
2049 | + |
2050 | + enabled: __isSeparator ? false : model.sensitive |
2051 | + |
2052 | + sourceComponent: { |
2053 | + if (model.isSeparator) { |
2054 | + return separatorComponent; |
2055 | + } |
2056 | + return menuItemComponent; |
2057 | + } |
2058 | + |
2059 | + property Item popup: null |
2060 | + |
2061 | + Layout.fillWidth: true |
2062 | + |
2063 | + Component { |
2064 | + id: menuItemComponent |
2065 | + MenuItem { |
2066 | + id: menuItem |
2067 | + menuData: model |
2068 | + objectName: loader.objectName + "-actionItem" |
2069 | + |
2070 | + width: MathUtils.clamp(implicitWidth, d.__minimumWidth, d.__maximumWidth) |
2071 | + |
2072 | + action.onTriggered: { |
2073 | + d.currentItem = loader; |
2074 | + |
2075 | + if (hasSubmenu) { |
2076 | + if (!popup) { |
2077 | + var model = root.unityMenuModel.submenu(__ownIndex); |
2078 | + popup = submenuComponent.createObject(focusScope, { |
2079 | + objectName: loader.objectName + "-", |
2080 | + unityMenuModel: model, |
2081 | + x: Qt.binding(function() { return root.width }), |
2082 | + y: Qt.binding(function() { |
2083 | + var dummy = listView.contentY; // force a recalc on contentY change. |
2084 | + return mapToItem(container, 0, y).y; |
2085 | + }) |
2086 | + }); |
2087 | + } else if (popup) { |
2088 | + popup.visible = true; |
2089 | + } |
2090 | + popup.retreat.connect(function() { |
2091 | + popup.destroy(); |
2092 | + popup = null; |
2093 | + menuItem.forceActiveFocus(); |
2094 | + }) |
2095 | + } else { |
2096 | + root.unityMenuModel.activate(__ownIndex); |
2097 | + } |
2098 | + } |
2099 | + |
2100 | + Connections { |
2101 | + target: d |
2102 | + onCurrentIndexChanged: { |
2103 | + if (popup && d.currentIndex != __ownIndex) { |
2104 | + popup.visible = false; |
2105 | + } |
2106 | + } |
2107 | + onDismissAll: { |
2108 | + if (popup) { |
2109 | + popup.destroy(); |
2110 | + popup = null; |
2111 | + } |
2112 | + } |
2113 | + } |
2114 | + } |
2115 | + } |
2116 | + |
2117 | + Component { |
2118 | + id: separatorComponent |
2119 | + ListItems.ThinDivider { |
2120 | + objectName: loader.objectName + "-separator" |
2121 | + implicitHeight: units.dp(2) |
2122 | + } |
2123 | + } |
2124 | + } |
2125 | + |
2126 | + } |
2127 | + } |
2128 | + |
2129 | + // Highlight |
2130 | + Rectangle { |
2131 | + color: "transparent" |
2132 | + border.width: units.dp(1) |
2133 | + border.color: UbuntuColors.orange |
2134 | + z: 1 |
2135 | + |
2136 | + width: listView.width |
2137 | + height: d.currentItem ? d.currentItem.height : 0 |
2138 | + y: d.currentItem ? d.currentItem.y : 0 |
2139 | + visible: d.currentItem |
2140 | + } |
2141 | + |
2142 | + } // Flickable |
2143 | + |
2144 | + // Header - scroll down |
2145 | Item { |
2146 | - Layout.fillWidth: true; |
2147 | - Layout.maximumHeight: units.gu(3) |
2148 | - Layout.minimumHeight: units.gu(3) |
2149 | + Layout.fillWidth: true |
2150 | + height: units.gu(3) |
2151 | visible: listView.contentHeight > root.height |
2152 | enabled: !listView.atYEnd |
2153 | + z: 1 |
2154 | |
2155 | Rectangle { |
2156 | color: enabled ? theme.palette.normal.overlayText : |
2157 | @@ -323,8 +351,19 @@ |
2158 | MouseArea { |
2159 | anchors.fill: parent |
2160 | onPressed: { |
2161 | - var index = listView.indexAt(0, listView.contentY); |
2162 | - listView.positionViewAtIndex(index+1, ListView.Beginning); |
2163 | + var item = menuColumn.childAt(0, listView.contentY + listView.height); |
2164 | + if (item) { |
2165 | + var nextItem = item; |
2166 | + do { |
2167 | + nextItem = repeater.itemAt(nextItem.__ownIndex+1); |
2168 | + if (!nextItem) { |
2169 | + listView.contentY = listView.contentHeight - listView.height; |
2170 | + return; |
2171 | + } |
2172 | + } while (nextItem.__isSeparator); |
2173 | + |
2174 | + listView.contentY = nextItem.y - listView.height |
2175 | + } |
2176 | } |
2177 | } |
2178 | } |
2179 | |
2180 | === modified file 'qml/Components/Dialogs.qml' |
2181 | --- qml/Components/Dialogs.qml 2016-12-20 08:55:23 +0000 |
2182 | +++ qml/Components/Dialogs.qml 2017-01-25 16:04:08 +0000 |
2183 | @@ -1,5 +1,5 @@ |
2184 | /* |
2185 | - * Copyright (C) 2014-2016 Canonical, Ltd. |
2186 | + * Copyright (C) 2014-2017 Canonical, Ltd. |
2187 | * |
2188 | * This program is free software; you can redistribute it and/or modify |
2189 | * it under the terms of the GNU General Public License as published by |
2190 | @@ -24,10 +24,13 @@ |
2191 | import Utils 0.1 |
2192 | import "../Greeter" |
2193 | |
2194 | -Item { |
2195 | +MouseArea { |
2196 | id: root |
2197 | + acceptedButtons: Qt.AllButtons |
2198 | + hoverEnabled: true |
2199 | + onWheel: wheel.accepted = true |
2200 | |
2201 | - readonly property alias hasActiveDialog: dialogLoader.active |
2202 | + readonly property bool hasActiveDialog: dialogLoader.active || d.modeSwitchWarningPopup |
2203 | |
2204 | // to be set from outside, useful mostly for testing purposes |
2205 | property var unitySessionService: DBusUnitySessionService |
2206 | @@ -42,6 +45,7 @@ |
2207 | } |
2208 | property string usageScenario |
2209 | property size screenSize: Qt.size(Screen.width, Screen.height) |
2210 | + property bool hasKeyboard: false |
2211 | |
2212 | signal powerOffClicked(); |
2213 | |
2214 | @@ -120,6 +124,11 @@ |
2215 | onTriggered: LightDMService.greeter.showGreeter() |
2216 | } |
2217 | |
2218 | + GlobalShortcut { // lock screen |
2219 | + shortcut: Qt.MetaModifier|Qt.Key_L |
2220 | + onTriggered: LightDMService.greeter.showGreeter() |
2221 | + } |
2222 | + |
2223 | QtObject { |
2224 | id: d // private stuff |
2225 | objectName: "dialogsPrivate" |
2226 | @@ -140,6 +149,25 @@ |
2227 | objectName: "dialogLoader" |
2228 | anchors.fill: parent |
2229 | active: false |
2230 | + onActiveChanged: { |
2231 | + if (!active) { |
2232 | + if (previousFocusedItem) { |
2233 | + previousFocusedItem.forceActiveFocus(Qt.OtherFocusReason); |
2234 | + previousFocusedItem = undefined; |
2235 | + } |
2236 | + previousSourceComponent = undefined; |
2237 | + sourceComponent = undefined; |
2238 | + } |
2239 | + } |
2240 | + onSourceComponentChanged: { |
2241 | + if (previousSourceComponent !== sourceComponent) { |
2242 | + previousSourceComponent = sourceComponent; |
2243 | + previousFocusedItem = window.activeFocusItem; |
2244 | + } |
2245 | + } |
2246 | + |
2247 | + property var previousSourceComponent: undefined |
2248 | + property var previousFocusedItem: undefined |
2249 | } |
2250 | |
2251 | Component { |
2252 | @@ -149,13 +177,16 @@ |
2253 | title: i18n.ctr("Title: Lock/Log out dialog", "Log out") |
2254 | text: i18n.tr("Are you sure you want to log out?") |
2255 | Button { |
2256 | + width: parent.width |
2257 | text: i18n.ctr("Button: Lock the system", "Lock") |
2258 | onClicked: { |
2259 | LightDMService.greeter.showGreeter() |
2260 | logoutDialog.hide(); |
2261 | } |
2262 | + Component.onCompleted: if (root.hasKeyboard) forceActiveFocus(Qt.TabFocusReason) |
2263 | } |
2264 | Button { |
2265 | + width: parent.width |
2266 | focus: true |
2267 | text: i18n.ctr("Button: Log out from the system", "Log Out") |
2268 | onClicked: { |
2269 | @@ -164,6 +195,7 @@ |
2270 | } |
2271 | } |
2272 | Button { |
2273 | + width: parent.width |
2274 | text: i18n.tr("Cancel") |
2275 | onClicked: { |
2276 | logoutDialog.hide(); |
2277 | @@ -179,12 +211,14 @@ |
2278 | title: i18n.ctr("Title: Reboot dialog", "Reboot") |
2279 | text: i18n.tr("Are you sure you want to reboot?") |
2280 | Button { |
2281 | + width: parent.width |
2282 | text: i18n.tr("No") |
2283 | onClicked: { |
2284 | rebootDialog.hide(); |
2285 | } |
2286 | } |
2287 | Button { |
2288 | + width: parent.width |
2289 | focus: true |
2290 | text: i18n.tr("Yes") |
2291 | onClicked: { |
2292 | @@ -193,6 +227,7 @@ |
2293 | rebootDialog.hide(); |
2294 | } |
2295 | color: theme.palette.normal.negative |
2296 | + Component.onCompleted: if (root.hasKeyboard) forceActiveFocus(Qt.TabFocusReason) |
2297 | } |
2298 | } |
2299 | } |
2300 | @@ -204,6 +239,7 @@ |
2301 | title: i18n.ctr("Title: Power off/Restart dialog", "Power") |
2302 | text: i18n.tr("Are you sure you would like\nto power off?") |
2303 | Button { |
2304 | + width: parent.width |
2305 | focus: true |
2306 | text: i18n.ctr("Button: Power off the system", "Power off") |
2307 | onClicked: { |
2308 | @@ -212,8 +248,10 @@ |
2309 | root.powerOffClicked(); |
2310 | } |
2311 | color: theme.palette.normal.negative |
2312 | + Component.onCompleted: if (root.hasKeyboard) forceActiveFocus(Qt.TabFocusReason) |
2313 | } |
2314 | Button { |
2315 | + width: parent.width |
2316 | text: i18n.ctr("Button: Restart the system", "Restart") |
2317 | onClicked: { |
2318 | root.closeAllApps(); |
2319 | @@ -222,6 +260,7 @@ |
2320 | } |
2321 | } |
2322 | Button { |
2323 | + width: parent.width |
2324 | text: i18n.tr("Cancel") |
2325 | onClicked: { |
2326 | powerDialog.hide(); |
2327 | |
2328 | === modified file 'qml/Components/KeyboardShortcutsOverlay.qml' |
2329 | --- qml/Components/KeyboardShortcutsOverlay.qml 2016-11-29 10:35:21 +0000 |
2330 | +++ qml/Components/KeyboardShortcutsOverlay.qml 2017-01-25 16:04:08 +0000 |
2331 | @@ -77,7 +77,7 @@ |
2332 | font.weight: Font.Medium |
2333 | } |
2334 | Label { |
2335 | - text: i18n.tr("Takes a screenshot of a window.") |
2336 | + text: i18n.tr("Takes a screenshot of the current window.") |
2337 | fontSize: "small" |
2338 | font.weight: Font.Light |
2339 | wrapMode: Text.Wrap |
2340 | |
2341 | === modified file 'qml/Components/KeymapSwitcher.qml' |
2342 | --- qml/Components/KeymapSwitcher.qml 2016-11-30 19:24:02 +0000 |
2343 | +++ qml/Components/KeymapSwitcher.qml 2017-01-25 16:04:08 +0000 |
2344 | @@ -51,6 +51,9 @@ |
2345 | nextIndex = currentKeymapIndex + 1; |
2346 | } |
2347 | currentKeymapIndex = nextIndex; |
2348 | + if (actionGroup.currentAction.valid) { |
2349 | + actionGroup.currentAction.updateState(currentKeymapIndex); |
2350 | + } |
2351 | } |
2352 | |
2353 | function previousKeymap() { |
2354 | @@ -60,30 +63,35 @@ |
2355 | prevIndex = currentKeymapIndex - 1; |
2356 | } |
2357 | currentKeymapIndex = prevIndex; |
2358 | + if (actionGroup.currentAction.valid) { |
2359 | + actionGroup.currentAction.updateState(currentKeymapIndex); |
2360 | + } |
2361 | } |
2362 | |
2363 | - property Binding surfaceKeymapBinding: Binding { |
2364 | + property Binding surfaceKeymapBinding: Binding { // NB: needed mainly for xmir & libertine apps |
2365 | target: root.focusedSurface |
2366 | property: "keymap" |
2367 | value: root.currentKeymap |
2368 | } |
2369 | |
2370 | + property Binding unityKeymapBinding: Binding { |
2371 | + target: Mir |
2372 | + property: "currentKeymap" |
2373 | + value: root.currentKeymap |
2374 | + } |
2375 | + |
2376 | // indicator |
2377 | property QDBusActionGroup actionGroup: QDBusActionGroup { |
2378 | busType: DBus.SessionBus |
2379 | busName: "com.canonical.indicator.keyboard" |
2380 | objectPath: "/com/canonical/indicator/keyboard" |
2381 | |
2382 | - property variant currentAction: action("current") |
2383 | - property variant activeAction: action("active") |
2384 | + property variant currentAction: action("current") // the one that's checked by the indicator |
2385 | + property variant activeAction: action("active") // the one that we clicked |
2386 | |
2387 | Component.onCompleted: actionGroup.start(); |
2388 | } |
2389 | |
2390 | - onCurrentKeymapIndexChanged: { |
2391 | - actionGroup.currentAction.updateState(currentKeymapIndex); |
2392 | - } |
2393 | - |
2394 | readonly property int activeActionState: actionGroup.activeAction.valid ? actionGroup.activeAction.state : -1 |
2395 | |
2396 | onActiveActionStateChanged: { |
2397 | |
2398 | === modified file 'qml/Components/Lockscreen.qml' |
2399 | --- qml/Components/Lockscreen.qml 2016-08-30 14:06:47 +0000 |
2400 | +++ qml/Components/Lockscreen.qml 2017-01-25 16:04:08 +0000 |
2401 | @@ -1,5 +1,5 @@ |
2402 | /* |
2403 | - * Copyright (C) 2013 Canonical, Ltd. |
2404 | + * Copyright (C) 2013-2017 Canonical, Ltd. |
2405 | * |
2406 | * This program is free software; you can redistribute it and/or modify |
2407 | * it under the terms of the GNU General Public License as published by |
2408 | @@ -240,8 +240,10 @@ |
2409 | property var dialogLoader // dummy to satisfy ShellDialog's context dependent prop |
2410 | |
2411 | Button { |
2412 | + width: parent.width |
2413 | objectName: "infoPopupOkButton" |
2414 | text: i18n.tr("OK") |
2415 | + focus: true |
2416 | onClicked: { |
2417 | PopupUtils.close(dialog) |
2418 | root.infoPopupConfirmed(); |
2419 | |
2420 | === modified file 'qml/Components/ModeSwitchWarningDialog.qml' |
2421 | --- qml/Components/ModeSwitchWarningDialog.qml 2016-05-17 20:46:51 +0000 |
2422 | +++ qml/Components/ModeSwitchWarningDialog.qml 2017-01-25 16:04:08 +0000 |
2423 | @@ -1,5 +1,5 @@ |
2424 | /* |
2425 | - * Copyright (C) 2015 Canonical, Ltd. |
2426 | + * Copyright (C) 2015-2017 Canonical, Ltd. |
2427 | * |
2428 | * This program is free software; you can redistribute it and/or modify |
2429 | * it under the terms of the GNU General Public License as published by |
2430 | @@ -29,6 +29,7 @@ |
2431 | signal forceClose(); |
2432 | |
2433 | Label { |
2434 | + width: parent.width |
2435 | text: i18n.tr("Apps may have unsaved data:") |
2436 | fontSize: "large" |
2437 | color: "#5D5D5D" |
2438 | @@ -37,6 +38,7 @@ |
2439 | Repeater { |
2440 | id: appRepeater |
2441 | RowLayout { |
2442 | + width: parent.width |
2443 | spacing: units.gu(2) |
2444 | Image { |
2445 | Layout.preferredHeight: units.gu(2) |
2446 | @@ -54,20 +56,23 @@ |
2447 | } |
2448 | |
2449 | Label { |
2450 | + width: parent.width |
2451 | 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.") |
2452 | wrapMode: Text.WordWrap |
2453 | color: "#888888" |
2454 | } |
2455 | |
2456 | Label { |
2457 | + width: parent.width |
2458 | text: i18n.tr("Or force close now (unsaved data will be lost).") |
2459 | wrapMode: Text.WordWrap |
2460 | color: "#888888" |
2461 | } |
2462 | |
2463 | - ThinDivider {} |
2464 | + ThinDivider { width: parent.width } |
2465 | |
2466 | RowLayout { |
2467 | + width: parent.width |
2468 | Label { |
2469 | objectName: "reconnectLabel" |
2470 | Layout.fillWidth: true |
2471 | @@ -84,6 +89,7 @@ |
2472 | } |
2473 | |
2474 | Button { |
2475 | + focus: true |
2476 | objectName: "forceCloseButton" |
2477 | text: i18n.tr("Close all") |
2478 | color: theme.palette.normal.negative |
2479 | |
2480 | === modified file 'qml/Components/ShellDialog.qml' |
2481 | --- qml/Components/ShellDialog.qml 2016-03-29 03:47:39 +0000 |
2482 | +++ qml/Components/ShellDialog.qml 2017-01-25 16:04:08 +0000 |
2483 | @@ -1,5 +1,5 @@ |
2484 | /* |
2485 | - * Copyright (C) 2014 Canonical, Ltd. |
2486 | + * Copyright (C) 2014-2017 Canonical, Ltd. |
2487 | * |
2488 | * This program is free software; you can redistribute it and/or modify |
2489 | * it under the terms of the GNU General Public License as published by |
2490 | @@ -19,6 +19,7 @@ |
2491 | import Ubuntu.Components 1.3 |
2492 | import Ubuntu.Components.Themes 1.3 |
2493 | import Ubuntu.Components.Popups 1.3 |
2494 | +import Utils 0.1 |
2495 | |
2496 | /* |
2497 | A Dialog configured for use as a proper in-scene Dialog |
2498 | @@ -32,10 +33,10 @@ |
2499 | // NB: PopupBase, Dialog's superclass, will check for the existence of this property |
2500 | property bool reparentToRootItem: false |
2501 | |
2502 | + default property alias columnContents: column.data |
2503 | + |
2504 | onVisibleChanged: { if (!visible && dialogLoader) { dialogLoader.active = false; } } |
2505 | |
2506 | - Keys.onEscapePressed: hide() |
2507 | - |
2508 | focus: true |
2509 | |
2510 | // FIXME: this is a hack because Dialog subtheming seems broken atm |
2511 | @@ -50,4 +51,21 @@ |
2512 | __foreground.theme = themeHack |
2513 | show(); |
2514 | } |
2515 | + |
2516 | + TabFocusFence { |
2517 | + width: parent.width |
2518 | + height: column.height |
2519 | + focus: true |
2520 | + Column { |
2521 | + id: column |
2522 | + width: parent.width |
2523 | + spacing: units.gu(2) |
2524 | + } |
2525 | + Keys.onDownPressed: { |
2526 | + event.accepted = focusNext(); |
2527 | + } |
2528 | + Keys.onUpPressed: { |
2529 | + event.accepted = focusPrev(); |
2530 | + } |
2531 | + } |
2532 | } |
2533 | |
2534 | === modified file 'qml/Greeter/Circle.qml' |
2535 | --- qml/Greeter/Circle.qml 2016-08-30 14:10:12 +0000 |
2536 | +++ qml/Greeter/Circle.qml 2017-01-25 16:04:08 +0000 |
2537 | @@ -27,7 +27,7 @@ |
2538 | onCenterCircleChanged: requestPaint() |
2539 | |
2540 | onPaint: { |
2541 | - if (circleScale <= 0) { |
2542 | + if (circleScale <= 0 || width <= 0 || height <= 0) { |
2543 | return; |
2544 | } |
2545 | |
2546 | |
2547 | === modified file 'qml/Greeter/FullLightDMImpl.qml' |
2548 | --- qml/Greeter/FullLightDMImpl.qml 2015-11-19 21:47:32 +0000 |
2549 | +++ qml/Greeter/FullLightDMImpl.qml 2017-01-25 16:04:08 +0000 |
2550 | @@ -22,6 +22,7 @@ |
2551 | |
2552 | property var greeter: LightDM.Greeter |
2553 | property var infographic: LightDM.Infographic |
2554 | + property var prompts: LightDM.Prompts |
2555 | property var sessions: LightDM.Sessions |
2556 | property var sessionRoles: LightDM.SessionRoles |
2557 | property var users: LightDM.Users |
2558 | |
2559 | === modified file 'qml/Greeter/Greeter.qml' |
2560 | --- qml/Greeter/Greeter.qml 2016-11-29 00:13:45 +0000 |
2561 | +++ qml/Greeter/Greeter.qml 2017-01-25 16:04:08 +0000 |
2562 | @@ -74,11 +74,11 @@ |
2563 | forcedUnlock = false; |
2564 | if (required) { |
2565 | if (loader.item) { |
2566 | - loader.item.reset(true /* forceShow */); |
2567 | + loader.item.forceShow(); |
2568 | } |
2569 | // Normally loader.onLoaded will select a user, but if we're |
2570 | // already shown, do it manually. |
2571 | - d.selectUser(d.currentIndex, false); |
2572 | + d.selectUser(d.currentIndex); |
2573 | } |
2574 | |
2575 | // Even though we may already be shown, we want to call show() for its |
2576 | @@ -154,7 +154,7 @@ |
2577 | readonly property bool multiUser: LightDMService.users.count > 1 |
2578 | readonly property int selectUserIndex: d.getUserIndex(LightDMService.greeter.selectUser) |
2579 | property int currentIndex: Math.max(selectUserIndex, 0) |
2580 | - property bool waiting |
2581 | + readonly property bool waiting: LightDMService.prompts.count == 0 && !root.forcedUnlock |
2582 | property bool isLockscreen // true when we are locking an active session, rather than first user login |
2583 | readonly property bool secureFingerprint: isLockscreen && |
2584 | AccountsService.failedFingerprintLogins < |
2585 | @@ -189,13 +189,9 @@ |
2586 | return -1; |
2587 | } |
2588 | |
2589 | - function selectUser(index, reset) { |
2590 | + function selectUser(index) { |
2591 | if (index < 0 || index >= LightDMService.users.count) |
2592 | return; |
2593 | - d.waiting = true; |
2594 | - if (reset) { |
2595 | - loader.item.reset(false /* forceShow */); |
2596 | - } |
2597 | currentIndex = index; |
2598 | var user = LightDMService.users.data(index, LightDMService.userRoles.NameRole); |
2599 | AccountsService.user = user; |
2600 | @@ -206,20 +202,17 @@ |
2601 | function hideView() { |
2602 | if (loader.item) { |
2603 | loader.item.enabled = false; // drop OSK and prevent interaction |
2604 | - loader.item.notifyAuthenticationSucceeded(false /* showFakePassword */); |
2605 | loader.item.hide(); |
2606 | } |
2607 | } |
2608 | |
2609 | function login() { |
2610 | - d.waiting = true; |
2611 | if (LightDMService.greeter.startSessionSync(root.sessionToStart())) { |
2612 | sessionStarted(); |
2613 | hideView(); |
2614 | } else if (loader.item) { |
2615 | loader.item.notifyAuthenticationFailed(); |
2616 | } |
2617 | - d.waiting = false; |
2618 | } |
2619 | |
2620 | function startUnlock(toTheRight) { |
2621 | @@ -239,27 +232,13 @@ |
2622 | } |
2623 | } |
2624 | |
2625 | - function showPromptMessage(text, isError) { |
2626 | - // inefficient, but we only rarely deal with messages |
2627 | - var html = text.replace(/&/g, "&") |
2628 | - .replace(/</g, "<") |
2629 | - .replace(/>/g, ">") |
2630 | - .replace(/\n/g, "<br>"); |
2631 | - if (isError) { |
2632 | - html = "<font color=\"#df382c\">" + html + "</font>"; |
2633 | - } |
2634 | - |
2635 | - if (loader.item) { |
2636 | - loader.item.showMessage(html); |
2637 | - } |
2638 | - } |
2639 | - |
2640 | function showFingerprintMessage(msg) { |
2641 | + d.selectUser(d.currentIndex); |
2642 | + LightDMService.prompts.prepend(msg, LightDMService.prompts.Error); |
2643 | if (loader.item) { |
2644 | - loader.item.reset(false /* forceShow */); |
2645 | loader.item.showErrorMessage(msg); |
2646 | + loader.item.notifyAuthenticationFailed(); |
2647 | } |
2648 | - showPromptMessage(msg, true); |
2649 | } |
2650 | } |
2651 | |
2652 | @@ -285,7 +264,6 @@ |
2653 | |
2654 | onRequiredChanged: { |
2655 | if (required) { |
2656 | - d.waiting = true; |
2657 | lockedApp = ""; |
2658 | } |
2659 | } |
2660 | @@ -377,14 +355,14 @@ |
2661 | onLoaded: { |
2662 | root.lockedApp = ""; |
2663 | item.forceActiveFocus(); |
2664 | - d.selectUser(d.currentIndex, true); |
2665 | + d.selectUser(d.currentIndex); |
2666 | LightDMService.infographic.readyForDataChange(); |
2667 | } |
2668 | |
2669 | Connections { |
2670 | target: loader.item |
2671 | onSelected: { |
2672 | - d.selectUser(index, true); |
2673 | + d.selectUser(index); |
2674 | } |
2675 | onResponded: { |
2676 | if (root.locked) { |
2677 | @@ -481,27 +459,15 @@ |
2678 | onShowGreeter: root.forceShow() |
2679 | onHideGreeter: root.forcedUnlock = true |
2680 | |
2681 | - onShowMessage: d.showPromptMessage(text, isError) |
2682 | - |
2683 | - onShowPrompt: { |
2684 | - if (loader.item) { |
2685 | - loader.item.showPrompt(text, isSecret, isDefaultPrompt); |
2686 | + onLoginError: { |
2687 | + if (!loader.item) { |
2688 | + return; |
2689 | } |
2690 | |
2691 | - d.waiting = false; |
2692 | - } |
2693 | - |
2694 | - onAuthenticationComplete: { |
2695 | - d.waiting = false; |
2696 | - |
2697 | - if (LightDMService.greeter.authenticated) { |
2698 | - if (!LightDMService.greeter.promptless) { |
2699 | - d.login(); |
2700 | - } |
2701 | - } else { |
2702 | - if (!LightDMService.greeter.promptless) { |
2703 | - AccountsService.failedLogins++; |
2704 | - } |
2705 | + loader.item.notifyAuthenticationFailed(); |
2706 | + |
2707 | + if (!automatic) { |
2708 | + AccountsService.failedLogins++; |
2709 | |
2710 | // Check if we should initiate a factory reset |
2711 | if (maxFailedLogins >= 2) { // require at least a warning |
2712 | @@ -519,14 +485,17 @@ |
2713 | forcedDelayTimer.forceDelay(); |
2714 | } |
2715 | |
2716 | - loader.item.notifyAuthenticationFailed(); |
2717 | - if (!LightDMService.greeter.promptless) { |
2718 | - d.selectUser(d.currentIndex, false); |
2719 | - } |
2720 | - } |
2721 | - } |
2722 | - |
2723 | - onRequestAuthenticationUser: d.selectUser(d.getUserIndex(user), true) |
2724 | + d.selectUser(d.currentIndex); |
2725 | + } |
2726 | + } |
2727 | + |
2728 | + onLoginSuccess: { |
2729 | + if (!automatic) { |
2730 | + d.login(); |
2731 | + } |
2732 | + } |
2733 | + |
2734 | + onRequestAuthenticationUser: d.selectUser(d.getUserIndex(user)) |
2735 | } |
2736 | |
2737 | Connections { |
2738 | @@ -589,7 +558,9 @@ |
2739 | if (!d.secureFingerprint) { |
2740 | d.startUnlock(false /* toTheRight */); // use normal login instead |
2741 | } |
2742 | - var msg = d.secureFingerprint ? i18n.tr("Try again") : ""; |
2743 | + var msg = d.secureFingerprint ? i18n.tr("Try again") : |
2744 | + d.alphanumeric ? i18n.tr("Enter passphrase to unlock") : |
2745 | + i18n.tr("Enter passcode to unlock"); |
2746 | d.showFingerprintMessage(msg); |
2747 | } |
2748 | |
2749 | @@ -609,8 +580,7 @@ |
2750 | } |
2751 | console.log("Identified user by fingerprint:", result); |
2752 | if (loader.item) { |
2753 | - loader.item.enabled = false; |
2754 | - loader.item.notifyAuthenticationSucceeded(true /* showFakePassword */); |
2755 | + loader.item.showFakePassword(); |
2756 | } |
2757 | if (root.active) |
2758 | root.forcedUnlock = true; |
2759 | |
2760 | === modified file 'qml/Greeter/GreeterPrompt.qml' |
2761 | --- qml/Greeter/GreeterPrompt.qml 2016-09-22 10:33:39 +0000 |
2762 | +++ qml/Greeter/GreeterPrompt.qml 2017-01-25 16:04:08 +0000 |
2763 | @@ -27,31 +27,25 @@ |
2764 | property bool isAlphanumeric |
2765 | property string text |
2766 | property bool isSecret |
2767 | + property bool interactive: true |
2768 | + readonly property alias enteredText: passwordInput.text |
2769 | |
2770 | signal clicked() |
2771 | signal canceled() |
2772 | - signal responded(string text) |
2773 | - |
2774 | - function reset() { |
2775 | - passwordInput.text = ""; |
2776 | - fakeLabel.text = ""; |
2777 | - d.enabled = true; |
2778 | - } |
2779 | + signal accepted() |
2780 | |
2781 | function showFakePassword() { |
2782 | // Just a silly hack for looking like 4 pin numbers got entered, if |
2783 | // a fingerprint was used and we happen to be using a pin. This was |
2784 | // a request from Design. |
2785 | if (isSecret && isPrompt && !isAlphanumeric) { |
2786 | - d.enabled = false; |
2787 | - text = "...."; // actual text doesn't matter |
2788 | + passwordInput.text = "...."; // actual text doesn't matter |
2789 | } |
2790 | } |
2791 | |
2792 | StyledItem { |
2793 | id: d |
2794 | |
2795 | - property bool enabled: true |
2796 | readonly property color textColor: passwordInput.enabled ? theme.palette.normal.raisedText |
2797 | : theme.palette.disabled.raisedText |
2798 | readonly property color selectedColor: passwordInput.enabled ? theme.palette.normal.raised |
2799 | @@ -60,12 +54,6 @@ |
2800 | : theme.palette.disabled.raisedSecondaryText |
2801 | readonly property color errorColor: passwordInput.enabled ? theme.palette.normal.negative |
2802 | : theme.palette.disabled.negative |
2803 | - |
2804 | - onEnabledChanged: { |
2805 | - if (!enabled) { |
2806 | - fakeLabel.text = passwordInput.displayText; |
2807 | - } |
2808 | - } |
2809 | } |
2810 | |
2811 | Rectangle { |
2812 | @@ -86,15 +74,17 @@ |
2813 | } |
2814 | } |
2815 | |
2816 | - Rectangle { |
2817 | + StyledItem { |
2818 | id: promptButton |
2819 | objectName: "promptButton" |
2820 | anchors.fill: parent |
2821 | visible: !root.isPrompt |
2822 | + activeFocusOnTab: true |
2823 | + |
2824 | + styleName: "FocusShape" |
2825 | |
2826 | function triggered() { |
2827 | - if (d.enabled) { |
2828 | - d.enabled = false; |
2829 | + if (root.interactive) { |
2830 | root.clicked(); |
2831 | } |
2832 | } |
2833 | @@ -109,6 +99,7 @@ |
2834 | } |
2835 | } |
2836 | |
2837 | + Keys.onSpacePressed: triggered(); |
2838 | Keys.onReturnPressed: triggered(); |
2839 | Keys.onEnterPressed: triggered(); |
2840 | MouseArea { |
2841 | @@ -129,6 +120,7 @@ |
2842 | anchors.fill: parent |
2843 | visible: root.isPrompt |
2844 | opacity: fakeLabel.visible ? 0 : 1 |
2845 | + activeFocusOnTab: true |
2846 | |
2847 | validator: RegExpValidator { |
2848 | regExp: root.isAlphanumeric ? /^.*$/ : /^\d{4}$/ |
2849 | @@ -142,15 +134,23 @@ |
2850 | |
2851 | readonly property real frameSpacing: units.gu(0.5) |
2852 | |
2853 | - style: Item { |
2854 | - property color color: d.textColor |
2855 | - property color selectedTextColor: d.selectedColor |
2856 | - property color selectionColor: d.textColor |
2857 | - property color borderColor: "transparent" |
2858 | - property color backgroundColor: "transparent" |
2859 | - property color errorColor: d.errorColor |
2860 | - property real frameSpacing: passwordInput.frameSpacing |
2861 | + style: StyledItem { |
2862 | anchors.fill: parent |
2863 | + styleName: "FocusShape" |
2864 | + |
2865 | + // Properties needed by TextField |
2866 | + readonly property color color: d.textColor |
2867 | + readonly property color selectedTextColor: d.selectedColor |
2868 | + readonly property color selectionColor: d.textColor |
2869 | + readonly property color borderColor: "transparent" |
2870 | + readonly property color backgroundColor: "transparent" |
2871 | + readonly property color errorColor: d.errorColor |
2872 | + readonly property real frameSpacing: styledItem.frameSpacing |
2873 | + |
2874 | + // Properties needed by FocusShape |
2875 | + readonly property bool enabled: styledItem.enabled |
2876 | + readonly property bool keyNavigationFocus: styledItem.keyNavigationFocus |
2877 | + property bool activeFocusOnTab |
2878 | } |
2879 | |
2880 | secondaryItem: [ |
2881 | @@ -178,9 +178,8 @@ |
2882 | onAccepted: respond() |
2883 | |
2884 | function respond() { |
2885 | - if (d.enabled) { |
2886 | - d.enabled = false; |
2887 | - root.responded(text); |
2888 | + if (root.interactive) { |
2889 | + root.accepted(); |
2890 | } |
2891 | } |
2892 | |
2893 | @@ -194,6 +193,7 @@ |
2894 | // palette color, whereas we want raisedSecondaryText. |
2895 | Label { |
2896 | id: hint |
2897 | + objectName: "promptHint" |
2898 | anchors { |
2899 | left: parent.left |
2900 | right: parent.right |
2901 | @@ -223,6 +223,7 @@ |
2902 | anchors.leftMargin: passwordInput.frameSpacing * 2 |
2903 | anchors.rightMargin: passwordInput.frameSpacing * 2 + capsIcon.visibleWidth |
2904 | color: d.drawColor |
2905 | - visible: root.isPrompt && !d.enabled |
2906 | + text: passwordInput.displayText |
2907 | + visible: root.isPrompt && !root.interactive |
2908 | } |
2909 | } |
2910 | |
2911 | === modified file 'qml/Greeter/IntegratedLightDMImpl.qml' |
2912 | --- qml/Greeter/IntegratedLightDMImpl.qml 2015-11-19 21:47:32 +0000 |
2913 | +++ qml/Greeter/IntegratedLightDMImpl.qml 2017-01-25 16:04:08 +0000 |
2914 | @@ -22,6 +22,7 @@ |
2915 | |
2916 | property var greeter: LightDM.Greeter |
2917 | property var infographic: LightDM.Infographic |
2918 | + property var prompts: LightDM.Prompts |
2919 | property var sessions: LightDM.Sessions |
2920 | property var sessionRoles: LightDM.SessionRoles |
2921 | property var users: LightDM.Users |
2922 | |
2923 | === modified file 'qml/Greeter/LightDMService.qml' |
2924 | --- qml/Greeter/LightDMService.qml 2015-11-19 21:47:32 +0000 |
2925 | +++ qml/Greeter/LightDMService.qml 2017-01-25 16:04:08 +0000 |
2926 | @@ -27,6 +27,7 @@ |
2927 | |
2928 | property var greeter: d.valid ? loader.item.greeter : null |
2929 | property var infographic: d.valid ? loader.item.infographic : null |
2930 | + property var prompts: d.valid ? loader.item.prompts : null |
2931 | property var sessions: d.valid ? loader.item.sessions : null |
2932 | property var sessionRoles: d.valid ? loader.item.sessionRoles : null |
2933 | property var users: d.valid ? loader.item.users : null |
2934 | @@ -35,10 +36,9 @@ |
2935 | // This trickery handles cases where applicationArguments aren't provided |
2936 | // such as during testing |
2937 | property var fullLightDM: { |
2938 | - if (typeof applicationArguments !== "undefined") { |
2939 | - if (applicationArguments.mode === "greeter") { |
2940 | - return true; |
2941 | - } |
2942 | + if (typeof applicationArguments === "undefined" || |
2943 | + applicationArguments.mode === "greeter") { |
2944 | + return true; |
2945 | } |
2946 | return false; |
2947 | } |
2948 | |
2949 | === modified file 'qml/Greeter/LoginList.qml' |
2950 | --- qml/Greeter/LoginList.qml 2016-11-29 00:13:45 +0000 |
2951 | +++ qml/Greeter/LoginList.qml 2017-01-25 16:04:08 +0000 |
2952 | @@ -25,95 +25,50 @@ |
2953 | focus: true |
2954 | |
2955 | property alias model: userList.model |
2956 | - property bool alphanumeric: true |
2957 | + property alias alphanumeric: promptList.alphanumeric |
2958 | property int currentIndex |
2959 | property bool locked |
2960 | property bool waiting |
2961 | property alias boxVerticalOffset: highlightItem.y |
2962 | |
2963 | - readonly property alias passwordInput: passwordInput |
2964 | readonly property int numAboveBelow: 4 |
2965 | readonly property int cellHeight: units.gu(5) |
2966 | - readonly property int highlightedHeight: units.gu(15) |
2967 | + readonly property int highlightedHeight: highlightItem.height |
2968 | readonly property int moveDuration: UbuntuAnimation.FastDuration |
2969 | - property string selectedSession |
2970 | property string currentSession |
2971 | readonly property string currentUser: userList.currentItem.username |
2972 | - property bool wasPrompted: false |
2973 | |
2974 | - signal loginListSessionChanged(string session) |
2975 | signal responded(string response) |
2976 | signal selected(int index) |
2977 | signal sessionChooserButtonClicked() |
2978 | |
2979 | function tryToUnlock() { |
2980 | - if (wasPrompted) { |
2981 | - passwordInput.forceActiveFocus(); |
2982 | - } else { |
2983 | - if (root.locked) { |
2984 | - root.selected(currentIndex); |
2985 | - } else { |
2986 | - root.responded(""); |
2987 | - } |
2988 | - } |
2989 | - } |
2990 | - |
2991 | - function showMessage(html) { |
2992 | - if (infoLabel.text === "") { |
2993 | - infoLabel.text = html; |
2994 | - } else { |
2995 | - infoLabel.text += "<br>" + html; |
2996 | - } |
2997 | - } |
2998 | - |
2999 | - function showPrompt(text, isSecret, isDefaultPrompt) { |
3000 | - passwordInput.text = isDefaultPrompt ? alphanumeric ? i18n.tr("Passphrase") |
3001 | - : i18n.tr("Passcode") |
3002 | - : text; |
3003 | - passwordInput.isPrompt = true; |
3004 | - passwordInput.isSecret = isSecret; |
3005 | - passwordInput.reset(); |
3006 | - wasPrompted = true; |
3007 | + promptList.forceActiveFocus(); |
3008 | } |
3009 | |
3010 | function showError() { |
3011 | wrongPasswordAnimation.start(); |
3012 | - root.resetAuthentication(); |
3013 | - } |
3014 | - |
3015 | - function reset() { |
3016 | - root.resetAuthentication(); |
3017 | } |
3018 | |
3019 | function showFakePassword() { |
3020 | - passwordInput.showFakePassword(); |
3021 | - } |
3022 | - |
3023 | - QtObject { |
3024 | - id: d |
3025 | - |
3026 | - function checkIfPromptless() { |
3027 | - if (!waiting && !wasPrompted) { |
3028 | - passwordInput.isPrompt = false; |
3029 | - passwordInput.text = root.locked ? i18n.tr("Retry") |
3030 | - : i18n.tr("Log In") |
3031 | - } |
3032 | - } |
3033 | - } |
3034 | - |
3035 | - onWaitingChanged: d.checkIfPromptless() |
3036 | - onLockedChanged: d.checkIfPromptless() |
3037 | + promptList.interactive = false; |
3038 | + promptList.showFakePassword(); |
3039 | + } |
3040 | |
3041 | theme: ThemeSettings { |
3042 | name: "Ubuntu.Components.Themes.Ambiance" |
3043 | } |
3044 | |
3045 | Keys.onUpPressed: { |
3046 | - selected(currentIndex - 1); |
3047 | + if (currentIndex > 0) { |
3048 | + selected(currentIndex - 1); |
3049 | + } |
3050 | event.accepted = true; |
3051 | } |
3052 | Keys.onDownPressed: { |
3053 | - selected(currentIndex + 1); |
3054 | + if (currentIndex + 1 < model.count) { |
3055 | + selected(currentIndex + 1); |
3056 | + } |
3057 | event.accepted = true; |
3058 | } |
3059 | Keys.onEscapePressed: { |
3060 | @@ -135,7 +90,8 @@ |
3061 | rightMargin: units.gu(2) |
3062 | } |
3063 | |
3064 | - height: root.highlightedHeight |
3065 | + height: Math.max(units.gu(15), promptList.height + units.gu(8)) |
3066 | + Behavior on height { NumberAnimation { duration: root.moveDuration; easing.type: Easing.InOutQuad; } } |
3067 | } |
3068 | |
3069 | ListView { |
3070 | @@ -153,14 +109,8 @@ |
3071 | interactive: count > 1 |
3072 | |
3073 | readonly property bool movingInternally: moveTimer.running || userList.moving |
3074 | - onMovingInternallyChanged: { |
3075 | - if (!movingInternally) { |
3076 | - root.selected(currentIndex); |
3077 | - } |
3078 | - } |
3079 | |
3080 | onCurrentIndexChanged: { |
3081 | - root.resetAuthentication(); |
3082 | moveTimer.start(); |
3083 | } |
3084 | |
3085 | @@ -203,7 +153,10 @@ |
3086 | // Add an offset to bottomMargin for any items below the highlight |
3087 | bottomMargin: -(units.gu(4) + (parent.belowHighlight ? parent.belowOffset : 0)) |
3088 | } |
3089 | - text: realName |
3090 | + text: userList.currentIndex === index |
3091 | + && name === "*other" |
3092 | + && LightDMService.greeter.authenticationUser !== "" |
3093 | + ? LightDMService.greeter.authenticationUser : realName |
3094 | color: userList.currentIndex !== index ? theme.palette.normal.raised |
3095 | : theme.palette.normal.raisedText |
3096 | |
3097 | @@ -309,64 +262,42 @@ |
3098 | } |
3099 | } |
3100 | |
3101 | - FadingLabel { |
3102 | - id: infoLabel |
3103 | - objectName: "infoLabel" |
3104 | - anchors { |
3105 | - bottom: passwordInput.top |
3106 | - left: highlightItem.left |
3107 | - topMargin: units.gu(1) |
3108 | - bottomMargin: units.gu(1) |
3109 | - leftMargin: units.gu(2) |
3110 | - rightMargin: units.gu(1) |
3111 | - } |
3112 | - |
3113 | - color: theme.palette.normal.raisedText |
3114 | - width: root.width - anchors.leftMargin - anchors.rightMargin |
3115 | - fontSize: "small" |
3116 | - textFormat: Text.StyledText |
3117 | - |
3118 | - opacity: (userList.movingInternally || text == "") ? 0 : 1 |
3119 | - Behavior on opacity { |
3120 | - NumberAnimation { duration: 100 } |
3121 | - } |
3122 | - } |
3123 | - |
3124 | - GreeterPrompt { |
3125 | - id: passwordInput |
3126 | - objectName: "passwordInput" |
3127 | + PromptList { |
3128 | + id: promptList |
3129 | + objectName: "promptList" |
3130 | anchors { |
3131 | bottom: highlightItem.bottom |
3132 | horizontalCenter: highlightItem.horizontalCenter |
3133 | margins: units.gu(2) |
3134 | } |
3135 | width: highlightItem.width - anchors.margins * 2 |
3136 | - opacity: userList.movingInternally ? 0 : 1 |
3137 | - |
3138 | - activeFocusOnTab: true |
3139 | - isAlphanumeric: root.alphanumeric |
3140 | - |
3141 | - onClicked: root.tryToUnlock() |
3142 | - onResponded: root.responded(text) |
3143 | - onCanceled: root.selected(currentIndex) |
3144 | - |
3145 | - Behavior on opacity { |
3146 | - NumberAnimation { duration: 100 } |
3147 | - } |
3148 | - |
3149 | - WrongPasswordAnimation { |
3150 | - id: wrongPasswordAnimation |
3151 | - objectName: "wrongPasswordAnimation" |
3152 | - target: passwordInput |
3153 | + |
3154 | + onClicked: { |
3155 | + interactive = false; |
3156 | + if (root.locked) { |
3157 | + root.selected(currentIndex); |
3158 | + } else { |
3159 | + root.responded(""); |
3160 | + } |
3161 | + } |
3162 | + onResponded: { |
3163 | + interactive = false; |
3164 | + root.responded(text); |
3165 | + } |
3166 | + onCanceled: { |
3167 | + interactive = false; |
3168 | + root.selected(currentIndex); |
3169 | + } |
3170 | + |
3171 | + Connections { |
3172 | + target: LightDMService.prompts |
3173 | + onModelReset: promptList.interactive = true |
3174 | } |
3175 | } |
3176 | |
3177 | - function resetAuthentication() { |
3178 | - if (!userList.currentItem) { |
3179 | - return; |
3180 | - } |
3181 | - infoLabel.text = ""; |
3182 | - passwordInput.reset(); |
3183 | - root.wasPrompted = false; |
3184 | + WrongPasswordAnimation { |
3185 | + id: wrongPasswordAnimation |
3186 | + objectName: "wrongPasswordAnimation" |
3187 | + target: promptList |
3188 | } |
3189 | } |
3190 | |
3191 | === modified file 'qml/Greeter/NarrowView.qml' |
3192 | --- qml/Greeter/NarrowView.qml 2016-08-30 20:23:15 +0000 |
3193 | +++ qml/Greeter/NarrowView.qml 2017-01-25 16:04:08 +0000 |
3194 | @@ -48,14 +48,6 @@ |
3195 | signal tease() |
3196 | signal emergencyCall() |
3197 | |
3198 | - function showMessage(html) { |
3199 | - loginList.showMessage(html); |
3200 | - } |
3201 | - |
3202 | - function showPrompt(text, isSecret, isDefaultPrompt) { |
3203 | - loginList.showPrompt(text, isSecret, isDefaultPrompt); |
3204 | - } |
3205 | - |
3206 | function showLastChance() { |
3207 | /* TODO: when we finish support for resetting device after too many |
3208 | failed logins, we should re-add this popup. |
3209 | @@ -75,10 +67,8 @@ |
3210 | coverPage.hide(); |
3211 | } |
3212 | |
3213 | - function notifyAuthenticationSucceeded(showFakePassword) { |
3214 | - if (showFakePassword) { |
3215 | - loginList.showFakePassword(); |
3216 | - } |
3217 | + function showFakePassword() { |
3218 | + loginList.showFakePassword(); |
3219 | } |
3220 | |
3221 | function notifyAuthenticationFailed() { |
3222 | @@ -89,11 +79,8 @@ |
3223 | coverPage.showErrorMessage(msg); |
3224 | } |
3225 | |
3226 | - function reset(forceShow) { |
3227 | - loginList.reset(); |
3228 | - if (forceShow) { |
3229 | - coverPage.show(); |
3230 | - } |
3231 | + function forceShow() { |
3232 | + coverPage.show(); |
3233 | } |
3234 | |
3235 | function tryToUnlock(toTheRight) { |
3236 | @@ -120,6 +107,7 @@ |
3237 | objectName: "lockscreen" |
3238 | anchors.fill: parent |
3239 | shown: false |
3240 | + opacity: 0 |
3241 | |
3242 | showAnimation: StandardAnimation { property: "opacity"; to: 1 } |
3243 | hideAnimation: StandardAnimation { property: "opacity"; to: 0 } |
3244 | @@ -195,10 +183,12 @@ |
3245 | onClicked: hide() |
3246 | |
3247 | onShowProgressChanged: { |
3248 | - if (showProgress === 1) { |
3249 | - loginList.reset(); |
3250 | - } else if (showProgress === 0) { |
3251 | - loginList.tryToUnlock(); |
3252 | + if (showProgress === 0) { |
3253 | + if (lockscreen.shown) { |
3254 | + loginList.tryToUnlock(); |
3255 | + } else { |
3256 | + root.responded(""); |
3257 | + } |
3258 | } |
3259 | } |
3260 | |
3261 | |
3262 | === added file 'qml/Greeter/PromptList.qml' |
3263 | --- qml/Greeter/PromptList.qml 1970-01-01 00:00:00 +0000 |
3264 | +++ qml/Greeter/PromptList.qml 2017-01-25 16:04:08 +0000 |
3265 | @@ -0,0 +1,148 @@ |
3266 | +/* |
3267 | + * Copyright (C) 2017 Canonical, Ltd. |
3268 | + * |
3269 | + * This program is free software; you can redistribute it and/or modify |
3270 | + * it under the terms of the GNU General Public License as published by |
3271 | + * the Free Software Foundation; version 3. |
3272 | + * |
3273 | + * This program is distributed in the hope that it will be useful, |
3274 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3275 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3276 | + * GNU General Public License for more details. |
3277 | + * |
3278 | + * You should have received a copy of the GNU General Public License |
3279 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3280 | + */ |
3281 | + |
3282 | +import QtQuick 2.4 |
3283 | +import Ubuntu.Components 1.3 |
3284 | +import "../Components" |
3285 | +import "." 0.1 |
3286 | + |
3287 | +FocusScope { |
3288 | + id: root |
3289 | + height: childrenRect.height |
3290 | + |
3291 | + property bool alphanumeric: true |
3292 | + property bool interactive: true |
3293 | + |
3294 | + signal responded(string text) |
3295 | + signal clicked() |
3296 | + signal canceled() |
3297 | + |
3298 | + function showFakePassword() { |
3299 | + for (var i = 0; i < repeater.count; i++) { |
3300 | + var item = repeater.itemAt(i).item; |
3301 | + if (item.isPrompt) { |
3302 | + item.showFakePassword(); |
3303 | + } |
3304 | + } |
3305 | + } |
3306 | + |
3307 | + QtObject { |
3308 | + id: d |
3309 | + |
3310 | + function sendResponse() { |
3311 | + for (var i = 0; i < repeater.count; i++) { |
3312 | + var item = repeater.itemAt(i).item; |
3313 | + if (item.isPrompt) { |
3314 | + root.responded(item.enteredText); |
3315 | + } |
3316 | + } |
3317 | + } |
3318 | + } |
3319 | + |
3320 | + Column { |
3321 | + width: parent.width |
3322 | + spacing: units.gu(0.5) |
3323 | + |
3324 | + Repeater { |
3325 | + id: repeater |
3326 | + model: LightDMService.prompts |
3327 | + |
3328 | + delegate: Loader { |
3329 | + id: loader |
3330 | + |
3331 | + readonly property bool isLabel: model.type == LightDMService.prompts.Message || |
3332 | + model.type == LightDMService.prompts.Error |
3333 | + readonly property var modelData: model |
3334 | + |
3335 | + sourceComponent: isLabel ? infoLabel : greeterPrompt |
3336 | + |
3337 | + onLoaded: { |
3338 | + for (var i = 0; i < repeater.count; i++) { |
3339 | + var item = repeater.itemAt(i); |
3340 | + if (item && !item.isLabel) { |
3341 | + item.focus = true; |
3342 | + break; |
3343 | + } |
3344 | + } |
3345 | + loader.item.opacity = 1; |
3346 | + } |
3347 | + |
3348 | + Binding { |
3349 | + target: loader.item |
3350 | + property: "model" |
3351 | + value: loader.modelData |
3352 | + } |
3353 | + } |
3354 | + } |
3355 | + } |
3356 | + |
3357 | + Component { |
3358 | + id: infoLabel |
3359 | + |
3360 | + FadingLabel { |
3361 | + objectName: "infoLabel" + model.index |
3362 | + width: root.width |
3363 | + |
3364 | + property var model |
3365 | + readonly property bool isPrompt: false |
3366 | + |
3367 | + color: model.type === LightDMService.prompts.Message ? theme.palette.normal.raisedText |
3368 | + : theme.palette.normal.negative |
3369 | + fontSize: "small" |
3370 | + textFormat: Text.PlainText |
3371 | + text: model.text |
3372 | + |
3373 | + Behavior on opacity { UbuntuNumberAnimation {} } |
3374 | + opacity: 0 |
3375 | + } |
3376 | + } |
3377 | + |
3378 | + Component { |
3379 | + id: greeterPrompt |
3380 | + |
3381 | + GreeterPrompt { |
3382 | + objectName: "greeterPrompt" + model.index |
3383 | + width: root.width |
3384 | + |
3385 | + property var model |
3386 | + |
3387 | + interactive: root.interactive |
3388 | + isAlphanumeric: model.text !== "" || root.alphanumeric |
3389 | + isPrompt: model.type !== LightDMService.prompts.Button |
3390 | + isSecret: model.type === LightDMService.prompts.Secret |
3391 | + text: model.text ? model.text : (isAlphanumeric ? i18n.tr("Passphrase") : i18n.tr("Passcode")) |
3392 | + |
3393 | + onClicked: root.clicked() |
3394 | + onAccepted: { |
3395 | + // If there is another GreeterPrompt, focus it. |
3396 | + for (var i = model.index + 1; i < repeater.count; i++) { |
3397 | + var item = repeater.itemAt(i).item; |
3398 | + if (item.isPrompt) { |
3399 | + item.forceActiveFocus(); |
3400 | + return; |
3401 | + } |
3402 | + } |
3403 | + |
3404 | + // Nope we're the last one; just send our response. |
3405 | + d.sendResponse(); |
3406 | + } |
3407 | + onCanceled: root.canceled() |
3408 | + |
3409 | + Behavior on opacity { UbuntuNumberAnimation {} } |
3410 | + opacity: 0 |
3411 | + } |
3412 | + } |
3413 | +} |
3414 | |
3415 | === modified file 'qml/Greeter/WideView.qml' |
3416 | --- qml/Greeter/WideView.qml 2016-11-29 00:13:45 +0000 |
3417 | +++ qml/Greeter/WideView.qml 2017-01-25 16:04:08 +0000 |
3418 | @@ -52,16 +52,8 @@ |
3419 | loginList.showError(); |
3420 | } |
3421 | |
3422 | - function reset(forceShow) { |
3423 | - loginList.reset(); |
3424 | - } |
3425 | - |
3426 | - function showMessage(html) { |
3427 | - loginList.showMessage(html); |
3428 | - } |
3429 | - |
3430 | - function showPrompt(text, isSecret, isDefaultPrompt) { |
3431 | - loginList.showPrompt(text, isSecret, isDefaultPrompt); |
3432 | + function forceShow() { |
3433 | + // Nothing to do, we are always fully shown |
3434 | } |
3435 | |
3436 | function tryToUnlock(toTheRight) { |
3437 | @@ -84,10 +76,8 @@ |
3438 | coverPage.hide(); |
3439 | } |
3440 | |
3441 | - function notifyAuthenticationSucceeded(showFakePassword) { |
3442 | - if (showFakePassword) { |
3443 | - loginList.showFakePassword(); |
3444 | - } |
3445 | + function showFakePassword() { |
3446 | + loginList.showFakePassword(); |
3447 | } |
3448 | |
3449 | function showLastChance() { |
3450 | @@ -125,8 +115,6 @@ |
3451 | id: loginList |
3452 | objectName: "loginList" |
3453 | |
3454 | - property int selectedUserIndex: 0 |
3455 | - |
3456 | width: units.gu(40) |
3457 | anchors { |
3458 | left: parent.left |
3459 | @@ -141,15 +129,18 @@ |
3460 | Behavior on boxVerticalOffset { UbuntuNumberAnimation {} } |
3461 | |
3462 | model: root.userModel |
3463 | - currentSession: LightDMService.users.data(selectedUserIndex, LightDMService.userRoles.SessionRole); |
3464 | onResponded: root.responded(response) |
3465 | - onSelected: { |
3466 | - root.selected(index) |
3467 | - loginList.selectedUserIndex = index; |
3468 | - } |
3469 | + onSelected: root.selected(index) |
3470 | onSessionChooserButtonClicked: parent.state = "SessionsList" |
3471 | + onCurrentIndexChanged: setCurrentSession() |
3472 | |
3473 | Keys.forwardTo: [sessionChooserLoader.item] |
3474 | + |
3475 | + Component.onCompleted: setCurrentSession() |
3476 | + |
3477 | + function setCurrentSession() { |
3478 | + currentSession = LightDMService.users.data(currentIndex, LightDMService.userRoles.SessionRole); |
3479 | + } |
3480 | } |
3481 | |
3482 | Loader { |
3483 | @@ -177,7 +168,7 @@ |
3484 | onSessionSelected: loginList.currentSession = sessionKey |
3485 | onShowLoginList: { |
3486 | coverPage.state = "LoginList" |
3487 | - loginList.passwordInput.forceActiveFocus(); |
3488 | + loginList.tryToUnlock(); |
3489 | } |
3490 | ignoreUnknownSignals: true |
3491 | } |
3492 | |
3493 | === modified file 'qml/Launcher/Drawer.qml' |
3494 | --- qml/Launcher/Drawer.qml 2016-11-28 10:17:22 +0000 |
3495 | +++ qml/Launcher/Drawer.qml 2017-01-25 16:04:08 +0000 |
3496 | @@ -20,6 +20,7 @@ |
3497 | import Utils 0.1 |
3498 | import "../Components" |
3499 | import Qt.labs.settings 1.0 |
3500 | +import GSettings 1.0 |
3501 | |
3502 | FocusScope { |
3503 | id: root |
3504 | @@ -43,6 +44,16 @@ |
3505 | searchField.focus = true; |
3506 | } |
3507 | |
3508 | + Keys.onPressed: { |
3509 | + if (event.text.trim() !== "") { |
3510 | + focusInput(); |
3511 | + searchField.text = event.text; |
3512 | + } |
3513 | + // Catch all presses here in case the navigation lets something through |
3514 | + // We never want to end up in the launcher with focus |
3515 | + event.accepted = true; |
3516 | + } |
3517 | + |
3518 | Settings { |
3519 | property alias selectedTab: sections.selectedIndex |
3520 | } |
3521 | @@ -76,9 +87,13 @@ |
3522 | |
3523 | TextField { |
3524 | id: searchField |
3525 | + objectName: "searchField" |
3526 | anchors { left: parent.left; top: parent.top; right: parent.right; margins: units.gu(1) } |
3527 | placeholderText: i18n.tr("Search…") |
3528 | focus: true |
3529 | + |
3530 | + KeyNavigation.down: sections |
3531 | + |
3532 | onAccepted: { |
3533 | if (searchField.displayText != "" && listLoader.item && listLoader.item.currentItem) { |
3534 | root.applicationSelected(listLoader.item.getFirstAppId()); |
3535 | @@ -95,7 +110,14 @@ |
3536 | |
3537 | Sections { |
3538 | id: sections |
3539 | + objectName: "drawerSections" |
3540 | width: parent.width |
3541 | + |
3542 | + KeyNavigation.up: searchField |
3543 | + KeyNavigation.down: headerFocusScope |
3544 | + KeyNavigation.backtab: searchField |
3545 | + KeyNavigation.tab: headerFocusScope |
3546 | + |
3547 | actions: [ |
3548 | Action { |
3549 | text: i18n.ctr("Apps sorted alphabetically", "A-Z") |
3550 | @@ -115,9 +137,45 @@ |
3551 | } |
3552 | } |
3553 | |
3554 | + FocusScope { |
3555 | + id: headerFocusScope |
3556 | + objectName: "headerFocusScope" |
3557 | + KeyNavigation.up: sections |
3558 | + KeyNavigation.down: listLoader.item |
3559 | + KeyNavigation.backtab: sections |
3560 | + KeyNavigation.tab: listLoader.item |
3561 | + activeFocusOnTab: true |
3562 | + |
3563 | + GSettings { |
3564 | + id: settings |
3565 | + schema.id: "com.canonical.Unity8" |
3566 | + } |
3567 | + |
3568 | + Keys.onPressed: { |
3569 | + switch (event.key) { |
3570 | + case Qt.Key_Return: |
3571 | + case Qt.Key_Enter: |
3572 | + case Qt.Key_Space: |
3573 | + trigger(); |
3574 | + event.accepted = true; |
3575 | + } |
3576 | + } |
3577 | + |
3578 | + function trigger() { |
3579 | + Qt.openUrlExternally(settings.appstoreUri) |
3580 | + } |
3581 | + } |
3582 | + |
3583 | Loader { |
3584 | id: listLoader |
3585 | - anchors { left: parent.left; top: sectionsContainer.bottom; right: parent.right; bottom: parent.bottom; leftMargin: units.gu(1); rightMargin: units.gu(1) } |
3586 | + objectName: "drawerListLoader" |
3587 | + anchors { left: parent.left; top: sectionsContainer.bottom; right: parent.right; bottom: parent.bottom } |
3588 | + |
3589 | + KeyNavigation.up: headerFocusScope |
3590 | + KeyNavigation.down: searchField |
3591 | + KeyNavigation.backtab: headerFocusScope |
3592 | + KeyNavigation.tab: searchField |
3593 | + |
3594 | sourceComponent: { |
3595 | switch (sections.selectedIndex) { |
3596 | case 0: return aToZComponent; |
3597 | @@ -167,10 +225,13 @@ |
3598 | Component { |
3599 | id: mostUsedComponent |
3600 | DrawerListView { |
3601 | + id: mostUsedListView |
3602 | |
3603 | header: MoreAppsHeader { |
3604 | width: parent.width |
3605 | height: units.gu(6) |
3606 | + highlighted: headerFocusScope.activeFocus |
3607 | + onClicked: headerFocusScope.trigger(); |
3608 | } |
3609 | |
3610 | model: AppDrawerProxyModel { |
3611 | @@ -180,7 +241,8 @@ |
3612 | } |
3613 | |
3614 | delegate: UbuntuShape { |
3615 | - width: parent.width |
3616 | + width: parent.width - units.gu(2) |
3617 | + anchors.horizontalCenter: parent.horizontalCenter |
3618 | color: "#20ffffff" |
3619 | aspect: UbuntuShape.Flat |
3620 | // NOTE: Cannot use gridView.rows here as it would evaluate to 0 at first and only update later, |
3621 | @@ -196,6 +258,9 @@ |
3622 | bottomMargin: units.gu(1) |
3623 | clip: true |
3624 | |
3625 | + interactive: true |
3626 | + focus: index == mostUsedListView.currentIndex |
3627 | + |
3628 | model: sortProxyModel |
3629 | |
3630 | delegateWidth: units.gu(8) |
3631 | @@ -209,10 +274,13 @@ |
3632 | Component { |
3633 | id: aToZComponent |
3634 | DrawerListView { |
3635 | + id: aToZListView |
3636 | |
3637 | header: MoreAppsHeader { |
3638 | width: parent.width |
3639 | height: units.gu(6) |
3640 | + highlighted: headerFocusScope.activeFocus |
3641 | + onClicked: headerFocusScope.trigger(); |
3642 | } |
3643 | |
3644 | model: AppDrawerProxyModel { |
3645 | @@ -222,7 +290,8 @@ |
3646 | } |
3647 | |
3648 | delegate: UbuntuShape { |
3649 | - width: parent.width |
3650 | + width: parent.width - units.gu(2) |
3651 | + anchors.horizontalCenter: parent.horizontalCenter |
3652 | color: "#20ffffff" |
3653 | aspect: UbuntuShape.Flat |
3654 | |
3655 | @@ -244,7 +313,8 @@ |
3656 | anchors { left: parent.left; top: categoryNameLabel.bottom; right: parent.right; topMargin: units.gu(1) } |
3657 | height: rows * delegateHeight |
3658 | |
3659 | - interactive: false |
3660 | + interactive: true |
3661 | + focus: index == aToZListView.currentIndex |
3662 | |
3663 | model: AppDrawerProxyModel { |
3664 | id: categoryModel |
3665 | @@ -263,10 +333,13 @@ |
3666 | Component { |
3667 | id: drawerDelegateComponent |
3668 | AbstractButton { |
3669 | + id: drawerDelegate |
3670 | width: GridView.view.cellWidth |
3671 | height: units.gu(10) |
3672 | objectName: "drawerItem_" + model.appId |
3673 | |
3674 | + readonly property bool focused: index === GridView.view.currentIndex && GridView.view.activeFocus |
3675 | + |
3676 | onClicked: root.applicationSelected(model.appId) |
3677 | |
3678 | Column { |
3679 | @@ -290,6 +363,15 @@ |
3680 | source: model.icon |
3681 | } |
3682 | sourceFillMode: UbuntuShape.PreserveAspectCrop |
3683 | + |
3684 | + StyledItem { |
3685 | + styleName: "FocusShape" |
3686 | + anchors.fill: parent |
3687 | + StyleHints { |
3688 | + visible: drawerDelegate.focused |
3689 | + radius: units.gu(2.55) |
3690 | + } |
3691 | + } |
3692 | } |
3693 | |
3694 | Label { |
3695 | |
3696 | === modified file 'qml/Launcher/DrawerGridView.qml' |
3697 | --- qml/Launcher/DrawerGridView.qml 2016-11-10 14:39:18 +0000 |
3698 | +++ qml/Launcher/DrawerGridView.qml 2017-01-25 16:04:08 +0000 |
3699 | @@ -17,7 +17,7 @@ |
3700 | import QtQuick 2.4 |
3701 | import "../Components" |
3702 | |
3703 | -Item { |
3704 | +FocusScope { |
3705 | id: root |
3706 | |
3707 | property int delegateWidth: units.gu(10) |
3708 | @@ -25,6 +25,7 @@ |
3709 | property alias delegate: gridView.delegate |
3710 | property alias model: gridView.model |
3711 | property alias interactive: gridView.interactive |
3712 | + property alias currentIndex: gridView.currentIndex |
3713 | |
3714 | property alias header: gridView.header |
3715 | property alias topMargin: gridView.topMargin |
3716 | @@ -37,6 +38,7 @@ |
3717 | id: gridView |
3718 | anchors.fill: parent |
3719 | leftMargin: spacing |
3720 | + focus: true |
3721 | |
3722 | readonly property int overflow: width - (root.columns * root.delegateWidth) |
3723 | readonly property real spacing: overflow / (root.columns) |
3724 | |
3725 | === modified file 'qml/Launcher/DrawerListView.qml' |
3726 | --- qml/Launcher/DrawerListView.qml 2016-11-28 14:56:02 +0000 |
3727 | +++ qml/Launcher/DrawerListView.qml 2017-01-25 16:04:08 +0000 |
3728 | @@ -25,6 +25,12 @@ |
3729 | bottomMargin: units.gu(1) |
3730 | spacing: units.gu(1) |
3731 | clip: true |
3732 | + focus: true |
3733 | + |
3734 | + onActiveFocusChanged: { |
3735 | + currentIndex = -1; |
3736 | + currentIndex = 0; |
3737 | + } |
3738 | |
3739 | function getFirstAppId() { |
3740 | return model.appId(0); |
3741 | |
3742 | === modified file 'qml/Launcher/Launcher.qml' |
3743 | --- qml/Launcher/Launcher.qml 2016-12-07 13:47:15 +0000 |
3744 | +++ qml/Launcher/Launcher.qml 2017-01-25 16:04:08 +0000 |
3745 | @@ -74,7 +74,7 @@ |
3746 | } else { |
3747 | superPressTimer.stop(); |
3748 | superLongPressTimer.stop(); |
3749 | - launcher.switchToNextState(""); |
3750 | + switchToNextState(""); |
3751 | panel.shortcutHintsShown = false; |
3752 | } |
3753 | } |
3754 | @@ -87,14 +87,14 @@ |
3755 | superPressTimer.stop(); |
3756 | superLongPressTimer.stop(); |
3757 | } else { |
3758 | + switchToNextState(""); |
3759 | + root.focus = false; |
3760 | if (panel.highlightIndex == -1) { |
3761 | - showDashHome(); |
3762 | + root.showDashHome(); |
3763 | } else if (panel.highlightIndex >= 0){ |
3764 | launcherApplicationSelected(LauncherModel.get(panel.highlightIndex).appId); |
3765 | } |
3766 | panel.highlightIndex = -2; |
3767 | - switchToNextState(""); |
3768 | - root.focus = false; |
3769 | } |
3770 | } |
3771 | |
3772 | @@ -108,6 +108,10 @@ |
3773 | } |
3774 | } |
3775 | |
3776 | + onPanelWidthChanged: { |
3777 | + hint(); |
3778 | + } |
3779 | + |
3780 | function hide(flags) { |
3781 | if ((flags & ignoreHideIfMouseOverLauncher) && Utils.Functions.itemUnderMouse(panel)) { |
3782 | return; |
3783 | @@ -206,7 +210,7 @@ |
3784 | case Qt.Key_Return: |
3785 | case Qt.Key_Space: |
3786 | if (panel.highlightIndex == -1) { |
3787 | - showDashHome(); |
3788 | + root.showDashHome(); |
3789 | } else if (panel.highlightIndex >= 0) { |
3790 | launcherApplicationSelected(LauncherModel.get(panel.highlightIndex).appId); |
3791 | } |
3792 | @@ -469,7 +473,7 @@ |
3793 | } |
3794 | onPassed: { |
3795 | if (root.drawerEnabled) { |
3796 | - root.switchToNextState("drawer"); |
3797 | + root.openDrawer() |
3798 | } |
3799 | } |
3800 | |
3801 | @@ -545,8 +549,7 @@ |
3802 | if (!dragging) { |
3803 | if (distance > panel.width / 2) { |
3804 | if (root.drawerEnabled && distance > panel.width * 3 && dragDirection() !== "left") { |
3805 | - root.switchToNextState("drawer"); |
3806 | - root.focus = true; |
3807 | + root.openDrawer(false) |
3808 | } else { |
3809 | root.switchToNextState("visible"); |
3810 | } |
3811 | |
3812 | === modified file 'qml/Launcher/LauncherDelegate.qml' |
3813 | --- qml/Launcher/LauncherDelegate.qml 2016-09-26 11:58:56 +0000 |
3814 | +++ qml/Launcher/LauncherDelegate.qml 2017-01-25 16:04:08 +0000 |
3815 | @@ -124,15 +124,14 @@ |
3816 | height: parent.itemHeight + units.gu(1) |
3817 | anchors.centerIn: parent |
3818 | |
3819 | - Image { |
3820 | - objectName: "focusRing" |
3821 | - anchors.centerIn: iconShape |
3822 | - height: width * 15 / 16 |
3823 | - width: iconShape.width + units.gu(1) |
3824 | - source: "graphics/launcher-app-focus-ring.svg" |
3825 | - sourceSize.width: width |
3826 | - sourceSize.height: height |
3827 | - visible: root.highlighted |
3828 | + StyledItem { |
3829 | + styleName: "FocusShape" |
3830 | + anchors.fill: iconShape |
3831 | + activeFocusOnTab: true |
3832 | + StyleHints { |
3833 | + visible: root.highlighted |
3834 | + radius: units.gu(2.55) |
3835 | + } |
3836 | } |
3837 | |
3838 | ProportionalShape { |
3839 | |
3840 | === modified file 'qml/Launcher/LauncherPanel.qml' |
3841 | --- qml/Launcher/LauncherPanel.qml 2017-01-10 14:44:00 +0000 |
3842 | +++ qml/Launcher/LauncherPanel.qml 2017-01-25 16:04:08 +0000 |
3843 | @@ -80,6 +80,7 @@ |
3844 | } |
3845 | |
3846 | Rectangle { |
3847 | + id: bfb |
3848 | objectName: "buttonShowDashHome" |
3849 | width: parent.width |
3850 | height: width * .9 |
3851 | @@ -100,13 +101,15 @@ |
3852 | activeFocusOnPress: false |
3853 | onClicked: root.showDashHome() |
3854 | } |
3855 | - Rectangle { |
3856 | - objectName: "bfbFocusHighlight" |
3857 | + |
3858 | + StyledItem { |
3859 | + styleName: "FocusShape" |
3860 | anchors.fill: parent |
3861 | - border.color: "white" |
3862 | - border.width: units.dp(1) |
3863 | - color: "transparent" |
3864 | - visible: parent.highlighted |
3865 | + anchors.margins: units.gu(.5) |
3866 | + StyleHints { |
3867 | + visible: bfb.highlighted |
3868 | + radius: 0 |
3869 | + } |
3870 | } |
3871 | } |
3872 | |
3873 | @@ -788,6 +791,8 @@ |
3874 | quickList.model = launcherListView.model.get(index).quickList; |
3875 | quickList.appId = launcherListView.model.get(index).appId; |
3876 | quickList.state = "open"; |
3877 | + root.highlightIndex = index; |
3878 | + quickList.forceActiveFocus(); |
3879 | } |
3880 | |
3881 | Item { |
3882 | |
3883 | === modified file 'qml/Launcher/MoreAppsHeader.qml' |
3884 | --- qml/Launcher/MoreAppsHeader.qml 2016-11-04 10:10:26 +0000 |
3885 | +++ qml/Launcher/MoreAppsHeader.qml 2017-01-25 16:04:08 +0000 |
3886 | @@ -4,17 +4,24 @@ |
3887 | AbstractButton { |
3888 | id: root |
3889 | |
3890 | - onClicked: { |
3891 | - // TODO: Make this point to the snappy store as soon as we stop landing to vivid |
3892 | - Qt.openUrlExternally("scope://com.canonical.scopes.clickstore") |
3893 | - } |
3894 | + property bool highlighted: false |
3895 | |
3896 | UbuntuShape { |
3897 | - width: parent.width |
3898 | + width: parent.width - units.gu(2) |
3899 | + anchors.horizontalCenter: parent.horizontalCenter |
3900 | height: parent.height - units.gu(1) |
3901 | color: "#20ffffff" |
3902 | aspect: UbuntuShape.Flat |
3903 | |
3904 | + StyledItem { |
3905 | + styleName: "FocusShape" |
3906 | + anchors.fill: parent |
3907 | + activeFocusOnTab: true |
3908 | + StyleHints { |
3909 | + visible: root.highlighted |
3910 | + } |
3911 | + } |
3912 | + |
3913 | Row { |
3914 | anchors.fill: parent |
3915 | anchors.margins: units.gu(1) |
3916 | |
3917 | === removed file 'qml/Launcher/graphics/launcher-app-focus-ring.svg' |
3918 | --- qml/Launcher/graphics/launcher-app-focus-ring.svg 2015-12-15 12:53:53 +0000 |
3919 | +++ qml/Launcher/graphics/launcher-app-focus-ring.svg 1970-01-01 00:00:00 +0000 |
3920 | @@ -1,12 +0,0 @@ |
3921 | -<?xml version="1.0" encoding="UTF-8" standalone="no"?> |
3922 | -<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"> |
3923 | - <!-- Generator: Sketch 3.4.4 (17249) - http://www.bohemiancoding.com/sketch --> |
3924 | - <title>Shape</title> |
3925 | - <desc>Created with Sketch.</desc> |
3926 | - <defs></defs> |
3927 | - <g id="•-Launcher" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage"> |
3928 | - <g id="Artboard-9" sketch:type="MSArtboardGroup" transform="translate(-163.000000, -1436.000000)" fill="#E95420"> |
3929 | - <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> |
3930 | - </g> |
3931 | - </g> |
3932 | -</svg> |
3933 | \ No newline at end of file |
3934 | |
3935 | === modified file 'qml/OrientedShell.qml' |
3936 | --- qml/OrientedShell.qml 2016-12-13 09:56:20 +0000 |
3937 | +++ qml/OrientedShell.qml 2017-01-25 16:04:08 +0000 |
3938 | @@ -266,7 +266,8 @@ |
3939 | nativeWidth: root.width |
3940 | nativeHeight: root.height |
3941 | mode: applicationArguments.mode |
3942 | - hasMouse: miceModel.count + touchPadModel.count > 0 |
3943 | + hasMouse: pointerInputDevices > 0 |
3944 | + hasKeyboard: keyboardsModel.count > 0 |
3945 | // TODO: Factor in if the current screen is a touch screen and if the user wants to |
3946 | // have multiple keyboards around. For now we only enable one keyboard at a time |
3947 | // thus hiding it here if there is a physical one around or if we have a second |
3948 | |
3949 | === modified file 'qml/Panel/PanelBar.qml' |
3950 | --- qml/Panel/PanelBar.qml 2016-09-30 14:27:30 +0000 |
3951 | +++ qml/Panel/PanelBar.qml 2017-01-25 16:04:08 +0000 |
3952 | @@ -49,6 +49,7 @@ |
3953 | row.resetCurrentItem(); |
3954 | } |
3955 | row.setCurrentItemIndex(index); |
3956 | + d.alignIndicators(); |
3957 | } |
3958 | |
3959 | function addScrollOffset(scrollAmmout) { |
3960 | |
3961 | === modified file 'qml/Panel/PanelMenu.qml' |
3962 | --- qml/Panel/PanelMenu.qml 2016-12-22 14:55:39 +0000 |
3963 | +++ qml/Panel/PanelMenu.qml 2017-01-25 16:04:08 +0000 |
3964 | @@ -140,6 +140,19 @@ |
3965 | visible: !root.fullyClosed |
3966 | } |
3967 | |
3968 | + Keys.onPressed: { |
3969 | + if (event.key === Qt.Key_Left) { |
3970 | + bar.setCurrentItemIndex(bar.currentItemIndex - 1); |
3971 | + event.accepted = true; |
3972 | + } else if (event.key === Qt.Key_Right) { |
3973 | + bar.setCurrentItemIndex(bar.currentItemIndex + 1); |
3974 | + event.accepted = true; |
3975 | + } else if (event.key === Qt.Key_Escape) { |
3976 | + root.hide(); |
3977 | + event.accepted = true; |
3978 | + } |
3979 | + } |
3980 | + |
3981 | PanelBar { |
3982 | id: bar |
3983 | objectName: "indicatorsBar" |
3984 | @@ -368,6 +381,7 @@ |
3985 | State { |
3986 | name: "commit" |
3987 | extend: "locked" |
3988 | + PropertyChanges { target: root; focus: true } |
3989 | PropertyChanges { target: bar; interactive: true } |
3990 | PropertyChanges { |
3991 | target: d; |
3992 | |
3993 | === modified file 'qml/Shell.qml' |
3994 | --- qml/Shell.qml 2017-01-03 12:04:08 +0000 |
3995 | +++ qml/Shell.qml 2017-01-25 16:04:08 +0000 |
3996 | @@ -68,6 +68,7 @@ |
3997 | stage.updateFocusedAppOrientationAnimated(); |
3998 | } |
3999 | property bool hasMouse: false |
4000 | + property bool hasKeyboard: false |
4001 | |
4002 | // to be read from outside |
4003 | readonly property int mainAppWindowOrientationAngle: stage.mainAppWindowOrientationAngle |
4004 | @@ -240,7 +241,7 @@ |
4005 | // Ignore when greeter is active, to avoid pocket presses |
4006 | if (!greeter.active) { |
4007 | launcher.fadeOut(); |
4008 | - shell.showHome(); |
4009 | + ApplicationManager.requestFocusApplication("unity8-dash"); |
4010 | } |
4011 | } |
4012 | onTouchBegun: { cursor.opacity = 0; } |
4013 | @@ -319,6 +320,35 @@ |
4014 | panel.applicationMenus.hide(); |
4015 | } |
4016 | } |
4017 | + |
4018 | + TouchGestureArea { |
4019 | + anchors.fill: stage |
4020 | + |
4021 | + minimumTouchPoints: 4 |
4022 | + maximumTouchPoints: minimumTouchPoints |
4023 | + |
4024 | + readonly property bool recognisedPress: status == TouchGestureArea.Recognized && |
4025 | + touchPoints.length >= minimumTouchPoints && |
4026 | + touchPoints.length <= maximumTouchPoints |
4027 | + property bool wasPressed: false |
4028 | + |
4029 | + onRecognisedPressChanged: { |
4030 | + if (recognisedPress) { |
4031 | + wasPressed = true; |
4032 | + } |
4033 | + } |
4034 | + |
4035 | + onStatusChanged: { |
4036 | + if (status !== TouchGestureArea.Recognized) { |
4037 | + if (status === TouchGestureArea.WaitingForTouch) { |
4038 | + if (wasPressed && !dragging) { |
4039 | + launcher.openDrawer(true); |
4040 | + } |
4041 | + } |
4042 | + wasPressed = false; |
4043 | + } |
4044 | + } |
4045 | + } |
4046 | } |
4047 | |
4048 | InputMethod { |
4049 | @@ -343,6 +373,16 @@ |
4050 | onLoaded: { |
4051 | item.objectName = "greeter" |
4052 | } |
4053 | + property bool openDrawerAfterUnlock: false |
4054 | + Connections { |
4055 | + target: greeter |
4056 | + onActiveChanged: { |
4057 | + if (!greeter.active && greeterLoader.openDrawerAfterUnlock) { |
4058 | + launcher.openDrawer(false); |
4059 | + greeterLoader.openDrawerAfterUnlock = false; |
4060 | + } |
4061 | + } |
4062 | + } |
4063 | } |
4064 | |
4065 | Component { |
4066 | @@ -434,9 +474,11 @@ |
4067 | if (shell.mode === "greeter") { |
4068 | SessionBroadcast.requestHomeShown(AccountsService.user); |
4069 | } else { |
4070 | - var animate = !LightDMService.greeter.active && !stages.shown; |
4071 | - dash.setCurrentScope(0, animate, false); |
4072 | - ApplicationManager.requestFocusApplication("unity8-dash"); |
4073 | + if (!greeter.active) { |
4074 | + launcher.openDrawer(false); |
4075 | + } else { |
4076 | + greeterLoader.openDrawerAfterUnlock = true; |
4077 | + } |
4078 | } |
4079 | } |
4080 | |
4081 | @@ -512,7 +554,7 @@ |
4082 | lockedVisible: shell.usageScenario == "desktop" && !settings.autohideLauncher && !panel.fullscreenMode |
4083 | blurSource: greeter.shown ? greeter : stages |
4084 | topPanelHeight: panel.panelHeight |
4085 | - drawerEnabled: !greeter.shown |
4086 | + drawerEnabled: !greeter.active |
4087 | |
4088 | onShowDashHome: showHome() |
4089 | onLauncherApplicationSelected: { |
4090 | @@ -657,8 +699,10 @@ |
4091 | id: dialogs |
4092 | objectName: "dialogs" |
4093 | anchors.fill: parent |
4094 | + visible: hasActiveDialog |
4095 | z: overlay.z + 10 |
4096 | usageScenario: shell.usageScenario |
4097 | + hasKeyboard: shell.hasKeyboard |
4098 | onPowerOffClicked: { |
4099 | shutdownFadeOutRectangle.enabled = true; |
4100 | shutdownFadeOutRectangle.visible = true; |
4101 | |
4102 | === modified file 'qml/Stage/DecoratedWindow.qml' |
4103 | --- qml/Stage/DecoratedWindow.qml 2016-12-12 11:16:47 +0000 |
4104 | +++ qml/Stage/DecoratedWindow.qml 2017-01-25 16:04:08 +0000 |
4105 | @@ -196,18 +196,19 @@ |
4106 | ] |
4107 | } |
4108 | |
4109 | - MouseArea { |
4110 | + WindowDecoration { |
4111 | + id: decoration |
4112 | + closeButtonVisible: root.application.appId !== "unity8-dash" |
4113 | + objectName: "appWindowDecoration" |
4114 | + |
4115 | anchors { left: parent.left; top: parent.top; right: parent.right } |
4116 | height: units.gu(3) |
4117 | |
4118 | + title: applicationWindow.title |
4119 | + |
4120 | opacity: root.hasDecoration ? Math.min(1, root.showDecoration) : 0 |
4121 | - |
4122 | Behavior on opacity { UbuntuNumberAnimation { } } |
4123 | |
4124 | - drag.target: Item {} |
4125 | - drag.filterChildren: true |
4126 | - drag.threshold: 0 |
4127 | - |
4128 | onPressed: root.decorationPressed(); |
4129 | onPressedChanged: moveHandler.handlePressedChanged(pressed, pressedButtons, mouseX, mouseY) |
4130 | onPositionChanged: moveHandler.handlePositionChanged(mouse) |
4131 | @@ -216,47 +217,39 @@ |
4132 | moveHandler.handleReleased(); |
4133 | } |
4134 | |
4135 | - WindowDecoration { |
4136 | - id: decoration |
4137 | - closeButtonVisible: root.application.appId !== "unity8-dash" |
4138 | - objectName: "appWindowDecoration" |
4139 | - anchors.fill: parent |
4140 | - title: applicationWindow.title |
4141 | - |
4142 | - onCloseClicked: root.closeClicked(); |
4143 | - onMaximizeClicked: { root.decorationPressed(); root.maximizeClicked(); } |
4144 | - onMaximizeHorizontallyClicked: { root.decorationPressed(); root.maximizeHorizontallyClicked(); } |
4145 | - onMaximizeVerticallyClicked: { root.decorationPressed(); root.maximizeVerticallyClicked(); } |
4146 | - onMinimizeClicked: root.minimizeClicked(); |
4147 | - |
4148 | - enableMenus: { |
4149 | - return active && |
4150 | - surface && |
4151 | - (PanelState.focusedPersistentSurfaceId === surface.persistentId && !PanelState.decorationsVisible) |
4152 | - } |
4153 | - menu: sharedAppModel.model |
4154 | - |
4155 | - Indicators.SharedUnityMenuModel { |
4156 | - id: sharedAppModel |
4157 | - property var menus: surface ? ApplicationMenuRegistry.getMenusForSurface(surface.persistentId) : [] |
4158 | - property var menuService: menus.length > 0 ? menus[0] : undefined |
4159 | - |
4160 | - busName: menuService ? menuService.service : "" |
4161 | - menuObjectPath: menuService && menuService.menuPath ? menuService.menuPath : "" |
4162 | - actions: menuService && menuService.actionPath ? { "unity": menuService.actionPath } : {} |
4163 | - } |
4164 | - |
4165 | - Connections { |
4166 | - target: ApplicationMenuRegistry |
4167 | - onSurfaceMenuRegistered: { |
4168 | - if (surface && surfaceId === surface.persistentId) { |
4169 | - sharedAppModel.menus = Qt.binding(function() { return surface ? ApplicationMenuRegistry.getMenusForSurface(surface.persistentId) : [] }); |
4170 | - } |
4171 | + onCloseClicked: root.closeClicked(); |
4172 | + onMaximizeClicked: { root.decorationPressed(); root.maximizeClicked(); } |
4173 | + onMaximizeHorizontallyClicked: { root.decorationPressed(); root.maximizeHorizontallyClicked(); } |
4174 | + onMaximizeVerticallyClicked: { root.decorationPressed(); root.maximizeVerticallyClicked(); } |
4175 | + onMinimizeClicked: root.minimizeClicked(); |
4176 | + |
4177 | + enableMenus: { |
4178 | + return active && |
4179 | + surface && |
4180 | + (PanelState.focusedPersistentSurfaceId === surface.persistentId && !PanelState.decorationsVisible) |
4181 | + } |
4182 | + menu: sharedAppModel.model |
4183 | + |
4184 | + Indicators.SharedUnityMenuModel { |
4185 | + id: sharedAppModel |
4186 | + property var menus: surface ? ApplicationMenuRegistry.getMenusForSurface(surface.persistentId) : [] |
4187 | + property var menuService: menus.length > 0 ? menus[0] : undefined |
4188 | + |
4189 | + busName: menuService ? menuService.service : "" |
4190 | + menuObjectPath: menuService && menuService.menuPath ? menuService.menuPath : "" |
4191 | + actions: menuService && menuService.actionPath ? { "unity": menuService.actionPath } : {} |
4192 | + } |
4193 | + |
4194 | + Connections { |
4195 | + target: ApplicationMenuRegistry |
4196 | + onSurfaceMenuRegistered: { |
4197 | + if (surface && surfaceId === surface.persistentId) { |
4198 | + sharedAppModel.menus = Qt.binding(function() { return surface ? ApplicationMenuRegistry.getMenusForSurface(surface.persistentId) : [] }); |
4199 | } |
4200 | - onSurfaceMenuUnregistered: { |
4201 | - if (surface && surfaceId === surface.persistentId) { |
4202 | - sharedAppModel.menus = Qt.binding(function() { return surface ? ApplicationMenuRegistry.getMenusForSurface(surface.persistentId) : [] }); |
4203 | - } |
4204 | + } |
4205 | + onSurfaceMenuUnregistered: { |
4206 | + if (surface && surfaceId === surface.persistentId) { |
4207 | + sharedAppModel.menus = Qt.binding(function() { return surface ? ApplicationMenuRegistry.getMenusForSurface(surface.persistentId) : [] }); |
4208 | } |
4209 | } |
4210 | } |
4211 | |
4212 | === modified file 'qml/Stage/WindowDecoration.qml' |
4213 | --- qml/Stage/WindowDecoration.qml 2016-12-12 11:16:47 +0000 |
4214 | +++ qml/Stage/WindowDecoration.qml 2017-01-25 16:04:08 +0000 |
4215 | @@ -38,6 +38,10 @@ |
4216 | acceptedButtons: Qt.AllButtons // prevent leaking unhandled mouse events |
4217 | hoverEnabled: true |
4218 | |
4219 | + drag.target: Item {} |
4220 | + drag.filterChildren: true |
4221 | + drag.threshold: 0 |
4222 | + |
4223 | signal closeClicked() |
4224 | signal minimizeClicked() |
4225 | signal maximizeClicked() |
4226 | @@ -64,8 +68,8 @@ |
4227 | (menuBar.showRequested || root.containsMouse) |
4228 | } |
4229 | |
4230 | - // We dont want touch events to fall through to parent, |
4231 | - // otherwise the containsMouse will not work. |
4232 | + // We dont want touch events to fall through to parent as it expect some child MouseArea to have them |
4233 | + // If not some MouseArea in the menu bar, it will be this one. |
4234 | MouseArea { |
4235 | anchors.fill: parent |
4236 | propagateComposedEvents: true |
4237 | |
4238 | === modified file 'src/MouseTouchAdaptor.cpp' |
4239 | --- src/MouseTouchAdaptor.cpp 2016-03-29 03:47:39 +0000 |
4240 | +++ src/MouseTouchAdaptor.cpp 2017-01-25 16:04:08 +0000 |
4241 | @@ -72,6 +72,7 @@ |
4242 | MouseTouchAdaptor *g_instance = nullptr; |
4243 | |
4244 | const Qt::KeyboardModifiers TRI_PRESS_MODIFIER = Qt::ShiftModifier|Qt::ControlModifier|Qt::AltModifier; |
4245 | +const Qt::KeyboardModifiers QUAD_PRESS_MODIFIER = TRI_PRESS_MODIFIER|Qt::MetaModifier; |
4246 | |
4247 | Qt::MouseButton translateMouseButton(xcb_button_t detail) |
4248 | { |
4249 | @@ -91,7 +92,7 @@ |
4250 | if (mod & 0x01) qtMod |= Qt::ShiftModifier; |
4251 | if (mod & 0x04) qtMod |= Qt::ControlModifier; |
4252 | if (mod & 0x08) qtMod |= Qt::AltModifier; |
4253 | - if (mod & 0x80) qtMod |= Qt::MetaModifier; |
4254 | + if (mod & 0x40) qtMod |= Qt::MetaModifier; |
4255 | |
4256 | return qtMod; |
4257 | } |
4258 | @@ -101,6 +102,7 @@ |
4259 | : QObject(nullptr) |
4260 | , m_leftButtonIsPressed(false) |
4261 | , m_triPressModifier(false) |
4262 | + , m_quadPressModifier(false) |
4263 | , m_enabled(true) |
4264 | { |
4265 | QCoreApplication::instance()->installNativeEventFilter(this); |
4266 | @@ -297,6 +299,13 @@ |
4267 | touchEvent.press(2, windowPos); |
4268 | m_triPressModifier = true; |
4269 | } |
4270 | + if (qtMod == QUAD_PRESS_MODIFIER) { |
4271 | + touchEvent.press(1, windowPos); |
4272 | + touchEvent.press(2, windowPos); |
4273 | + touchEvent.press(3, windowPos); |
4274 | + m_quadPressModifier = true; |
4275 | + } |
4276 | + |
4277 | touchEvent.commit(false /* processEvents */); |
4278 | |
4279 | m_leftButtonIsPressed = true; |
4280 | @@ -322,10 +331,16 @@ |
4281 | touchEvent.release(1, windowPos); |
4282 | touchEvent.release(2, windowPos); |
4283 | } |
4284 | + if (m_quadPressModifier) { |
4285 | + touchEvent.release(1, windowPos); |
4286 | + touchEvent.release(2, windowPos); |
4287 | + touchEvent.release(3, windowPos); |
4288 | + } |
4289 | touchEvent.commit(false /* processEvents */); |
4290 | |
4291 | m_leftButtonIsPressed = false; |
4292 | m_triPressModifier = false; |
4293 | + m_quadPressModifier = false; |
4294 | return true; |
4295 | } |
4296 | |
4297 | @@ -354,6 +369,18 @@ |
4298 | m_triPressModifier = false; |
4299 | } |
4300 | } |
4301 | + if (m_quadPressModifier) { |
4302 | + if (qtMod == QUAD_PRESS_MODIFIER) { |
4303 | + touchEvent.move(1, windowPos); |
4304 | + touchEvent.move(2, windowPos); |
4305 | + touchEvent.move(3, windowPos); |
4306 | + } else { |
4307 | + touchEvent.release(1, windowPos); |
4308 | + touchEvent.release(2, windowPos); |
4309 | + touchEvent.release(3, windowPos); |
4310 | + m_quadPressModifier = false; |
4311 | + } |
4312 | + } |
4313 | touchEvent.commit(false /* processEvents */); |
4314 | |
4315 | return true; |
4316 | |
4317 | === modified file 'src/MouseTouchAdaptor.h' |
4318 | --- src/MouseTouchAdaptor.h 2016-03-29 03:47:39 +0000 |
4319 | +++ src/MouseTouchAdaptor.h 2017-01-25 16:04:08 +0000 |
4320 | @@ -59,6 +59,7 @@ |
4321 | QTouchDevice *m_touchDevice; |
4322 | bool m_leftButtonIsPressed; |
4323 | bool m_triPressModifier; |
4324 | + bool m_quadPressModifier; |
4325 | |
4326 | |
4327 | bool m_enabled; |
4328 | |
4329 | === modified file 'tests/CMakeLists.txt' |
4330 | --- tests/CMakeLists.txt 2016-12-06 20:16:56 +0000 |
4331 | +++ tests/CMakeLists.txt 2017-01-25 16:04:08 +0000 |
4332 | @@ -7,8 +7,10 @@ |
4333 | add_meta_test(uitests) |
4334 | add_meta_test(xvfbuitests) |
4335 | |
4336 | -add_meta_test(alltests DEPENDS unittests uitests) |
4337 | -add_meta_test(xvfballtests DEPENDS unittests xvfbuitests) |
4338 | +# Run our meta-meta tests serially because we don't need to nest |
4339 | +# parallelized tests. |
4340 | +add_meta_test(alltests SERIAL DEPENDS unittests uitests) |
4341 | +add_meta_test(xvfballtests SERIAL DEPENDS unittests xvfbuitests) |
4342 | |
4343 | # Support libraries and plugins |
4344 | add_subdirectory(mocks) |
4345 | @@ -42,8 +44,8 @@ |
4346 | |
4347 | set(ld_paths) |
4348 | list(APPEND ld_paths |
4349 | + ${UNITY_MOCKPATH}/liblightdm |
4350 | ${UNITY_MOCKPATH}/libusermetrics |
4351 | - ${UNITY_MOCKPATH}/LightDM/IntegratedLightDM/liblightdm |
4352 | ) |
4353 | |
4354 | string(REPLACE ";" ":" ld_library_path "${ld_paths}") |
4355 | |
4356 | === modified file 'tests/autopilot/unity8/fixture_setup.py' |
4357 | --- tests/autopilot/unity8/fixture_setup.py 2015-10-26 20:15:08 +0000 |
4358 | +++ tests/autopilot/unity8/fixture_setup.py 2017-01-25 16:04:08 +0000 |
4359 | @@ -74,7 +74,7 @@ |
4360 | def _get_lightdm_mock_path(self): |
4361 | lib_path = get_mocks_library_path() |
4362 | lightdm_mock_path = os.path.abspath( |
4363 | - os.path.join(lib_path, "LightDM" ,"IntegratedLightDM", "liblightdm") |
4364 | + os.path.join(lib_path, "liblightdm") |
4365 | ) |
4366 | |
4367 | if not os.path.exists(lightdm_mock_path): |
4368 | |
4369 | === modified file 'tests/autopilot/unity8/shell/tests/__init__.py' |
4370 | --- tests/autopilot/unity8/shell/tests/__init__.py 2016-10-25 13:11:19 +0000 |
4371 | +++ tests/autopilot/unity8/shell/tests/__init__.py 2017-01-25 16:04:08 +0000 |
4372 | @@ -264,7 +264,7 @@ |
4373 | def _get_lightdm_mock_path(self): |
4374 | lib_path = get_mocks_library_path() |
4375 | lightdm_mock_path = os.path.abspath( |
4376 | - os.path.join(lib_path, "LightDM" ,"IntegratedLightDM", "liblightdm") |
4377 | + os.path.join(lib_path, "liblightdm") |
4378 | ) |
4379 | |
4380 | if not os.path.exists(lightdm_mock_path): |
4381 | |
4382 | === modified file 'tests/mocks/AccountsService/AccountsService.cpp' |
4383 | --- tests/mocks/AccountsService/AccountsService.cpp 2016-08-04 14:05:54 +0000 |
4384 | +++ tests/mocks/AccountsService/AccountsService.cpp 2017-01-25 16:04:08 +0000 |
4385 | @@ -15,7 +15,7 @@ |
4386 | */ |
4387 | |
4388 | #include "AccountsService.h" |
4389 | -#include "MockUsersModel.h" |
4390 | +#include "UsersModel.h" |
4391 | |
4392 | #include <QLightDM/UsersModel> |
4393 | #include <paths.h> |
4394 | @@ -33,9 +33,8 @@ |
4395 | m_demoEdgesCompleted(), |
4396 | m_hereEnabled(false), |
4397 | m_hereLicensePath(""), |
4398 | - m_usersModel(new MockUsersModel(this)) |
4399 | + m_usersModel(new UsersModel(this)) |
4400 | { |
4401 | - m_usersModel->setMockMode("full"); |
4402 | } |
4403 | |
4404 | QString AccountsService::user() const |
4405 | |
4406 | === modified file 'tests/mocks/AccountsService/AccountsService.h' |
4407 | --- tests/mocks/AccountsService/AccountsService.h 2016-12-23 11:04:53 +0000 |
4408 | +++ tests/mocks/AccountsService/AccountsService.h 2017-01-25 16:04:08 +0000 |
4409 | @@ -14,15 +14,14 @@ |
4410 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
4411 | */ |
4412 | |
4413 | -#ifndef UNITY_MOCK_ACCOUNTSSERVICE_H |
4414 | -#define UNITY_MOCK_ACCOUNTSSERVICE_H |
4415 | +#pragma once |
4416 | |
4417 | #include <QObject> |
4418 | #include <QString> |
4419 | #include <QStringList> |
4420 | #include <QVariant> |
4421 | |
4422 | -class MockUsersModel; |
4423 | +class UsersModel; |
4424 | |
4425 | class AccountsService: public QObject |
4426 | { |
4427 | @@ -165,7 +164,5 @@ |
4428 | QString m_realName; |
4429 | QStringList m_kbdMap; |
4430 | QString m_email; |
4431 | - MockUsersModel *m_usersModel; |
4432 | + UsersModel *m_usersModel; |
4433 | }; |
4434 | - |
4435 | -#endif |
4436 | |
4437 | === modified file 'tests/mocks/AccountsService/CMakeLists.txt' |
4438 | --- tests/mocks/AccountsService/CMakeLists.txt 2016-12-06 20:16:56 +0000 |
4439 | +++ tests/mocks/AccountsService/CMakeLists.txt 2017-01-25 16:04:08 +0000 |
4440 | @@ -1,14 +1,15 @@ |
4441 | include_directories( |
4442 | ${CMAKE_CURRENT_BINARY_DIR} |
4443 | ${CMAKE_SOURCE_DIR}/plugins/LightDM |
4444 | + ${CMAKE_SOURCE_DIR}/plugins/LightDM/IntegratedLightDM |
4445 | ${CMAKE_SOURCE_DIR}/plugins/Utils |
4446 | - ${CMAKE_SOURCE_DIR}/tests/mocks/LightDM/IntegratedLightDM |
4447 | ) |
4448 | |
4449 | add_library(MockAccountsService-qml MODULE |
4450 | + ${CMAKE_SOURCE_DIR}/plugins/LightDM/Greeter.cpp |
4451 | + ${CMAKE_SOURCE_DIR}/plugins/LightDM/PromptsModel.cpp |
4452 | ${CMAKE_SOURCE_DIR}/plugins/LightDM/UsersModel.cpp |
4453 | ${CMAKE_SOURCE_DIR}/plugins/Utils/unitysortfilterproxymodelqml.cpp |
4454 | - ${CMAKE_SOURCE_DIR}/tests/mocks/LightDM/IntegratedLightDM/MockUsersModel.cpp |
4455 | AccountsService.cpp |
4456 | plugin.cpp |
4457 | ) |
4458 | |
4459 | === modified file 'tests/mocks/CMakeLists.txt' |
4460 | --- tests/mocks/CMakeLists.txt 2016-12-06 20:16:56 +0000 |
4461 | +++ tests/mocks/CMakeLists.txt 2017-01-25 16:04:08 +0000 |
4462 | @@ -32,8 +32,9 @@ |
4463 | add_subdirectory(Cursor) |
4464 | add_subdirectory(GSettings.1.0) |
4465 | add_subdirectory(indicator-service) |
4466 | +add_subdirectory(liblightdm) |
4467 | add_subdirectory(libusermetrics) |
4468 | -add_subdirectory(LightDM) |
4469 | +add_subdirectory(LightDMController) |
4470 | add_subdirectory(Lights) |
4471 | add_subdirectory(MeeGo) |
4472 | add_subdirectory(Powerd) |
4473 | |
4474 | === modified file 'tests/mocks/GSettings.1.0/fake_gsettings.cpp' |
4475 | --- tests/mocks/GSettings.1.0/fake_gsettings.cpp 2016-11-16 05:54:50 +0000 |
4476 | +++ tests/mocks/GSettings.1.0/fake_gsettings.cpp 2017-01-25 16:04:08 +0000 |
4477 | @@ -29,6 +29,7 @@ |
4478 | , m_edgeDragWidth(2) |
4479 | , m_enableLauncher(true) |
4480 | , m_enableIndicatorMenu(true) |
4481 | + , m_appstoreUri("http://uappexplorer.com") |
4482 | { |
4483 | } |
4484 | |
4485 | @@ -173,6 +174,11 @@ |
4486 | } |
4487 | } |
4488 | |
4489 | +QString GSettingsControllerQml::appstoreUri() const |
4490 | +{ |
4491 | + return m_appstoreUri; |
4492 | +} |
4493 | + |
4494 | GSettingsSchemaQml::GSettingsSchemaQml(QObject *parent): QObject(parent) { |
4495 | } |
4496 | |
4497 | @@ -376,6 +382,14 @@ |
4498 | } |
4499 | } |
4500 | |
4501 | +QVariant GSettingsQml::appstoreUri() const |
4502 | +{ |
4503 | + if (m_valid && m_schema->id() == "com.canonical.Unity8") { |
4504 | + return GSettingsControllerQml::instance()->appstoreUri(); |
4505 | + } |
4506 | + return QVariant(); |
4507 | +} |
4508 | + |
4509 | void GSettingsQml::setLifecycleExemptAppids(const QVariant &appIds) |
4510 | { |
4511 | if (m_valid && m_schema->id() == "com.canonical.qtmir") { |
4512 | |
4513 | === modified file 'tests/mocks/GSettings.1.0/fake_gsettings.h' |
4514 | --- tests/mocks/GSettings.1.0/fake_gsettings.h 2016-11-16 05:54:50 +0000 |
4515 | +++ tests/mocks/GSettings.1.0/fake_gsettings.h 2017-01-25 16:04:08 +0000 |
4516 | @@ -59,6 +59,7 @@ |
4517 | Q_PROPERTY(QVariant edgeDragWidth READ edgeDragWidth WRITE setEdgeDragWidth NOTIFY edgeDragWidthChanged) |
4518 | Q_PROPERTY(QVariant enableLauncher READ enableLauncher WRITE setEnableLauncher NOTIFY enableLauncherChanged) |
4519 | Q_PROPERTY(QVariant enableIndicatorMenu READ enableIndicatorMenu WRITE setEnableIndicatorMenu NOTIFY enableIndicatorMenuChanged) |
4520 | + Q_PROPERTY(QVariant appstoreUri READ appstoreUri NOTIFY appstoreUriChanged) |
4521 | |
4522 | public: |
4523 | GSettingsQml(QObject *parent = nullptr); |
4524 | @@ -77,6 +78,7 @@ |
4525 | QVariant edgeDragWidth() const; |
4526 | QVariant enableLauncher() const; |
4527 | QVariant enableIndicatorMenu() const; |
4528 | + QVariant appstoreUri() const; |
4529 | |
4530 | void setDisableHeight(const QVariant &val); |
4531 | void setPictureUri(const QVariant &str); |
4532 | @@ -101,6 +103,7 @@ |
4533 | void edgeDragWidthChanged(); |
4534 | void enableLauncherChanged(); |
4535 | void enableIndicatorMenuChanged(); |
4536 | + void appstoreUriChanged(); |
4537 | |
4538 | private: |
4539 | GSettingsSchemaQml* m_schema; |
4540 | @@ -147,6 +150,8 @@ |
4541 | bool enableIndicatorMenu() const; |
4542 | Q_INVOKABLE void setEnableIndicatorMenu(bool enableIndicatorMenu); |
4543 | |
4544 | + QString appstoreUri() const; |
4545 | + |
4546 | Q_SIGNALS: |
4547 | void disableHeightChanged(); |
4548 | void pictureUriChanged(const QString&); |
4549 | @@ -158,6 +163,7 @@ |
4550 | void edgeDragWidthChanged(uint edgeDragWidth); |
4551 | void enableLauncherChanged(bool enableLauncher); |
4552 | void enableIndicatorMenuChanged(bool enableIndicatorMenu); |
4553 | + void appstoreUriChanged(const QString &appstoreUri); |
4554 | |
4555 | private: |
4556 | GSettingsControllerQml(); |
4557 | @@ -172,6 +178,7 @@ |
4558 | uint m_edgeDragWidth; |
4559 | bool m_enableLauncher; |
4560 | bool m_enableIndicatorMenu; |
4561 | + QString m_appstoreUri; |
4562 | |
4563 | static GSettingsControllerQml* s_controllerInstance; |
4564 | QList<GSettingsQml *> m_registeredGSettings; |
4565 | |
4566 | === removed directory 'tests/mocks/LightDM' |
4567 | === removed file 'tests/mocks/LightDM/CMakeLists.txt' |
4568 | --- tests/mocks/LightDM/CMakeLists.txt 2015-09-25 13:01:00 +0000 |
4569 | +++ tests/mocks/LightDM/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
4570 | @@ -1,1 +0,0 @@ |
4571 | -add_subdirectory(IntegratedLightDM) |
4572 | |
4573 | === removed directory 'tests/mocks/LightDM/IntegratedLightDM' |
4574 | === removed file 'tests/mocks/LightDM/IntegratedLightDM/CMakeLists.txt' |
4575 | --- tests/mocks/LightDM/IntegratedLightDM/CMakeLists.txt 2016-07-14 13:04:10 +0000 |
4576 | +++ tests/mocks/LightDM/IntegratedLightDM/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
4577 | @@ -1,50 +0,0 @@ |
4578 | -# This is a copy of the normal LightDM plugin, but instead of statically |
4579 | -# linking in the lightdm bits, this one uses shared libraries so we can swap |
4580 | -# out different sets of users for different tests. When we finally switch to |
4581 | -# actually using the system liblightdm in the normal plugin, this version can |
4582 | -# be deleted. |
4583 | - |
4584 | -add_subdirectory(liblightdm) |
4585 | - |
4586 | -include_directories( |
4587 | - ${CMAKE_CURRENT_SOURCE_DIR} |
4588 | - ${CMAKE_CURRENT_BINARY_DIR} |
4589 | - ${CMAKE_SOURCE_DIR}/plugins/Utils |
4590 | - ${CMAKE_SOURCE_DIR}/plugins/LightDM |
4591 | - ${CMAKE_SOURCE_DIR}/tests/mocks/libusermetrics |
4592 | - ${libunity8-private_SOURCE_DIR} |
4593 | -) |
4594 | - |
4595 | -set(QMLPLUGIN_SRC |
4596 | - ${CMAKE_SOURCE_DIR}/plugins/LightDM/DBusGreeter.cpp |
4597 | - ${CMAKE_SOURCE_DIR}/plugins/LightDM/DBusGreeterList.cpp |
4598 | - ${CMAKE_SOURCE_DIR}/plugins/LightDM/Greeter.cpp |
4599 | - ${CMAKE_SOURCE_DIR}/plugins/LightDM/SessionsModel.cpp |
4600 | - ${CMAKE_SOURCE_DIR}/plugins/LightDM/UsersModel.cpp |
4601 | - ${CMAKE_SOURCE_DIR}/plugins/Utils/unitysortfilterproxymodelqml.cpp |
4602 | - MockGreeter.cpp |
4603 | - MockSessionsModel.cpp |
4604 | - MockUsersModel.cpp |
4605 | - plugin.cpp |
4606 | - ) |
4607 | - |
4608 | -add_library(MockLightDM-qml MODULE |
4609 | - ${QMLPLUGIN_SRC} |
4610 | - ) |
4611 | - |
4612 | -# We want to link to liblightdm-qt5-3, but we don't want to depend on it being |
4613 | -# installed on the system. So we make sure we link to our full fake version |
4614 | -# At run time, we can point to whichever version we happen to be using via |
4615 | -# LD_LIBRARY_PATH. |
4616 | -target_link_libraries(MockLightDM-qml |
4617 | - MockLightDM |
4618 | - MockUserMetrics |
4619 | - unity8-private |
4620 | - ) |
4621 | - |
4622 | -qt5_use_modules(MockLightDM-qml DBus Gui Qml) |
4623 | - |
4624 | -add_unity8_mock(LightDM.IntegratedLightDM 0.1 LightDM/IntegratedLightDM |
4625 | - TARGETS MockLightDM-qml |
4626 | - ENVIRONMENT "LD_LIBRARY_PATH=${CMAKE_CURRENT_BINARY_DIR}/liblightdm" |
4627 | -) |
4628 | |
4629 | === removed file 'tests/mocks/LightDM/IntegratedLightDM/MockGreeter.cpp' |
4630 | --- tests/mocks/LightDM/IntegratedLightDM/MockGreeter.cpp 2016-06-09 21:45:09 +0000 |
4631 | +++ tests/mocks/LightDM/IntegratedLightDM/MockGreeter.cpp 1970-01-01 00:00:00 +0000 |
4632 | @@ -1,51 +0,0 @@ |
4633 | -/* |
4634 | - * Copyright (C) 2014 Canonical, Ltd. |
4635 | - * |
4636 | - * This program is free software; you can redistribute it and/or modify |
4637 | - * it under the terms of the GNU General Public License as published by |
4638 | - * the Free Software Foundation; version 3. |
4639 | - * |
4640 | - * This program is distributed in the hope that it will be useful, |
4641 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
4642 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4643 | - * GNU General Public License for more details. |
4644 | - * |
4645 | - * You should have received a copy of the GNU General Public License |
4646 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
4647 | - * |
4648 | - */ |
4649 | - |
4650 | -#include "MockGreeter.h" |
4651 | -#include <GreeterPrivate.h> |
4652 | - |
4653 | -QString MockGreeter::mockMode() const |
4654 | -{ |
4655 | - Q_D(const Greeter); |
4656 | - return d->m_greeter->mockMode(); |
4657 | -} |
4658 | - |
4659 | -void MockGreeter::setMockMode(QString mockMode) |
4660 | -{ |
4661 | - Q_D(Greeter); |
4662 | - |
4663 | - if (d->m_greeter->mockMode() != mockMode) { |
4664 | - d->m_greeter->setMockMode(mockMode); |
4665 | - Q_EMIT mockModeChanged(mockMode); |
4666 | - } |
4667 | -} |
4668 | - |
4669 | -QString MockGreeter::selectUserHint() const |
4670 | -{ |
4671 | - Q_D(const Greeter); |
4672 | - return d->m_greeter->selectUserHint(); |
4673 | -} |
4674 | - |
4675 | -void MockGreeter::setSelectUserHint(const QString &selectUserHint) |
4676 | -{ |
4677 | - Q_D(Greeter); |
4678 | - |
4679 | - if (d->m_greeter->selectUserHint() != selectUserHint) { |
4680 | - d->m_greeter->setSelectUserHint(selectUserHint); |
4681 | - Q_EMIT selectUserHintChanged(); |
4682 | - } |
4683 | -} |
4684 | |
4685 | === removed file 'tests/mocks/LightDM/IntegratedLightDM/MockGreeter.h' |
4686 | --- tests/mocks/LightDM/IntegratedLightDM/MockGreeter.h 2016-06-09 21:45:09 +0000 |
4687 | +++ tests/mocks/LightDM/IntegratedLightDM/MockGreeter.h 1970-01-01 00:00:00 +0000 |
4688 | @@ -1,42 +0,0 @@ |
4689 | -/* |
4690 | - * Copyright (C) 2014 Canonical, Ltd. |
4691 | - * |
4692 | - * This program is free software; you can redistribute it and/or modify |
4693 | - * it under the terms of the GNU General Public License as published by |
4694 | - * the Free Software Foundation; version 3. |
4695 | - * |
4696 | - * This program is distributed in the hope that it will be useful, |
4697 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
4698 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4699 | - * GNU General Public License for more details. |
4700 | - * |
4701 | - * You should have received a copy of the GNU General Public License |
4702 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
4703 | - * |
4704 | - */ |
4705 | - |
4706 | -// The real, production, Greeter |
4707 | -#include <Greeter.h> |
4708 | - |
4709 | -#ifndef MOCK_UNITY_GREETER_H |
4710 | -#define MOCK_UNITY_GREETER_H |
4711 | - |
4712 | -class MockGreeter : public Greeter { |
4713 | - Q_OBJECT |
4714 | - |
4715 | - Q_PROPERTY(QString mockMode READ mockMode WRITE setMockMode NOTIFY mockModeChanged) |
4716 | - Q_PROPERTY(QString selectUser READ selectUserHint WRITE setSelectUserHint NOTIFY selectUserHintChanged) |
4717 | - |
4718 | -public: |
4719 | - QString mockMode() const; |
4720 | - void setMockMode(QString mockMode); |
4721 | - |
4722 | - QString selectUserHint() const; |
4723 | - void setSelectUserHint(const QString &selectUserHint); |
4724 | - |
4725 | -Q_SIGNALS: |
4726 | - void mockModeChanged(QString mode); |
4727 | - void selectUserHintChanged(); |
4728 | -}; |
4729 | - |
4730 | -#endif // MOCK_UNITY_GREETER_H |
4731 | |
4732 | === removed file 'tests/mocks/LightDM/IntegratedLightDM/MockSessionsModel.cpp' |
4733 | --- tests/mocks/LightDM/IntegratedLightDM/MockSessionsModel.cpp 2016-07-13 20:24:24 +0000 |
4734 | +++ tests/mocks/LightDM/IntegratedLightDM/MockSessionsModel.cpp 1970-01-01 00:00:00 +0000 |
4735 | @@ -1,66 +0,0 @@ |
4736 | -/* |
4737 | - * Copyright (C) 2015 Canonical, Ltd. |
4738 | - * |
4739 | - * This program is free software; you can redistribute it and/or modify |
4740 | - * it under the terms of the GNU General Public License as published by |
4741 | - * the Free Software Foundation; version 3. |
4742 | - * |
4743 | - * This program is distributed in the hope that it will be useful, |
4744 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
4745 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4746 | - * GNU General Public License for more details. |
4747 | - * |
4748 | - * You should have received a copy of the GNU General Public License |
4749 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
4750 | - * |
4751 | - */ |
4752 | - |
4753 | -#include "MockSessionsModel.h" |
4754 | -#include <QLightDM/SessionsModel> |
4755 | - |
4756 | - |
4757 | -int MockSessionsModel::numSessions() const |
4758 | -{ |
4759 | - QLightDM::SessionsModel* qSessionsModel = |
4760 | - static_cast<QLightDM::SessionsModel*>(sourceModel()); |
4761 | - |
4762 | - return qSessionsModel->numSessions(); |
4763 | -} |
4764 | - |
4765 | -int MockSessionsModel::numAvailableSessions() const |
4766 | -{ |
4767 | - QLightDM::SessionsModel* qSessionsModel = |
4768 | - static_cast<QLightDM::SessionsModel*>(sourceModel()); |
4769 | - |
4770 | - return qSessionsModel->numAvailableSessions(); |
4771 | -} |
4772 | - |
4773 | -QString MockSessionsModel::testScenario() const |
4774 | -{ |
4775 | - QLightDM::SessionsModel* qSessionsModel = |
4776 | - static_cast<QLightDM::SessionsModel*>(sourceModel()); |
4777 | - |
4778 | - return qSessionsModel->testScenario(); |
4779 | -} |
4780 | - |
4781 | -void MockSessionsModel::setNumSessions(const int numSessions) |
4782 | -{ |
4783 | - QLightDM::SessionsModel* qSessionsModel = |
4784 | - static_cast<QLightDM::SessionsModel*>(sourceModel()); |
4785 | - |
4786 | - if (qSessionsModel->numSessions() != numSessions) { |
4787 | - qSessionsModel->setNumSessions(numSessions); |
4788 | - Q_EMIT numSessionsChanged(); |
4789 | - } |
4790 | -} |
4791 | - |
4792 | -void MockSessionsModel::setTestScenario(const QString testScenario) |
4793 | -{ |
4794 | - QLightDM::SessionsModel* qSessionsModel = |
4795 | - static_cast<QLightDM::SessionsModel*>(sourceModel()); |
4796 | - |
4797 | - if (qSessionsModel->testScenario() != testScenario) { |
4798 | - qSessionsModel->setTestScenario(testScenario); |
4799 | - Q_EMIT testScenarioChanged(); |
4800 | - } |
4801 | -} |
4802 | |
4803 | === removed file 'tests/mocks/LightDM/IntegratedLightDM/MockSessionsModel.h' |
4804 | --- tests/mocks/LightDM/IntegratedLightDM/MockSessionsModel.h 2016-07-13 20:24:24 +0000 |
4805 | +++ tests/mocks/LightDM/IntegratedLightDM/MockSessionsModel.h 1970-01-01 00:00:00 +0000 |
4806 | @@ -1,44 +0,0 @@ |
4807 | -/* |
4808 | - * Copyright (C) 2015 Canonical, Ltd. |
4809 | - * |
4810 | - * This program is free software; you can redistribute it and/or modify |
4811 | - * it under the terms of the GNU General Public License as published by |
4812 | - * the Free Software Foundation; version 3. |
4813 | - * |
4814 | - * This program is distributed in the hope that it will be useful, |
4815 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
4816 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4817 | - * GNU General Public License for more details. |
4818 | - * |
4819 | - * You should have received a copy of the GNU General Public License |
4820 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
4821 | - * |
4822 | - */ |
4823 | - |
4824 | -#ifndef MOCK_UNITY_SESSIONSMODEL_H |
4825 | -#define MOCK_UNITY_SESSIONSMODEL_H |
4826 | - |
4827 | -#include <SessionsModel.h> |
4828 | - |
4829 | -class MockSessionsModel : public SessionsModel |
4830 | -{ |
4831 | - Q_OBJECT |
4832 | - |
4833 | - Q_PROPERTY(int numAvailableSessions READ numAvailableSessions CONSTANT) |
4834 | - Q_PROPERTY(int numSessions READ numSessions WRITE setNumSessions NOTIFY numSessionsChanged) |
4835 | - Q_PROPERTY(QString testScenario READ testScenario WRITE setTestScenario NOTIFY testScenarioChanged) |
4836 | - |
4837 | -public: |
4838 | - int numAvailableSessions() const; |
4839 | - int numSessions() const; |
4840 | - QString testScenario() const; |
4841 | - void setNumSessions(const int numSessions); |
4842 | - void setTestScenario(const QString testScenario); |
4843 | - |
4844 | -Q_SIGNALS: |
4845 | - void numSessionsChanged(); |
4846 | - void testScenarioChanged(); |
4847 | - |
4848 | -}; |
4849 | - |
4850 | -#endif // MOCK_UNITY_SESSIONSMODEL_H |
4851 | |
4852 | === removed file 'tests/mocks/LightDM/IntegratedLightDM/MockUsersModel.cpp' |
4853 | --- tests/mocks/LightDM/IntegratedLightDM/MockUsersModel.cpp 2016-08-04 14:05:54 +0000 |
4854 | +++ tests/mocks/LightDM/IntegratedLightDM/MockUsersModel.cpp 1970-01-01 00:00:00 +0000 |
4855 | @@ -1,44 +0,0 @@ |
4856 | -/* |
4857 | - * Copyright (C) 2014 Canonical, Ltd. |
4858 | - * |
4859 | - * This program is free software; you can redistribute it and/or modify |
4860 | - * it under the terms of the GNU General Public License as published by |
4861 | - * the Free Software Foundation; version 3. |
4862 | - * |
4863 | - * This program is distributed in the hope that it will be useful, |
4864 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
4865 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4866 | - * GNU General Public License for more details. |
4867 | - * |
4868 | - * You should have received a copy of the GNU General Public License |
4869 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
4870 | - * |
4871 | - */ |
4872 | - |
4873 | -#include "MockUsersModel.h" |
4874 | -#include <QLightDM/UsersModel> |
4875 | -#include <QSortFilterProxyModel> |
4876 | - |
4877 | -MockUsersModel::MockUsersModel(QObject* parent) |
4878 | - : UsersModel(parent) |
4879 | -{ |
4880 | -} |
4881 | - |
4882 | -QString MockUsersModel::mockMode() const |
4883 | -{ |
4884 | - QLightDM::UsersModel* qUsersModel = |
4885 | - static_cast<QLightDM::UsersModel*>(static_cast<QSortFilterProxyModel*>(sourceModel())->sourceModel()); |
4886 | - |
4887 | - return qUsersModel->mockMode(); |
4888 | -} |
4889 | - |
4890 | -void MockUsersModel::setMockMode(QString mockMode) |
4891 | -{ |
4892 | - QLightDM::UsersModel* qUsersModel = |
4893 | - static_cast<QLightDM::UsersModel*>(static_cast<QSortFilterProxyModel*>(sourceModel())->sourceModel()); |
4894 | - |
4895 | - if (qUsersModel->mockMode() != mockMode) { |
4896 | - qUsersModel->setMockMode(mockMode); |
4897 | - Q_EMIT mockModeChanged(mockMode); |
4898 | - } |
4899 | -} |
4900 | |
4901 | === removed file 'tests/mocks/LightDM/IntegratedLightDM/MockUsersModel.h' |
4902 | --- tests/mocks/LightDM/IntegratedLightDM/MockUsersModel.h 2016-08-04 14:05:54 +0000 |
4903 | +++ tests/mocks/LightDM/IntegratedLightDM/MockUsersModel.h 1970-01-01 00:00:00 +0000 |
4904 | @@ -1,39 +0,0 @@ |
4905 | -/* |
4906 | - * Copyright (C) 2014 Canonical, Ltd. |
4907 | - * |
4908 | - * This program is free software; you can redistribute it and/or modify |
4909 | - * it under the terms of the GNU General Public License as published by |
4910 | - * the Free Software Foundation; version 3. |
4911 | - * |
4912 | - * This program is distributed in the hope that it will be useful, |
4913 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
4914 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4915 | - * GNU General Public License for more details. |
4916 | - * |
4917 | - * You should have received a copy of the GNU General Public License |
4918 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
4919 | - * |
4920 | - */ |
4921 | - |
4922 | -#ifndef MOCK_UNITY_USERSMODEL_H |
4923 | -#define MOCK_UNITY_USERSMODEL_H |
4924 | - |
4925 | -#include <UsersModel.h> |
4926 | - |
4927 | -class MockUsersModel : public UsersModel |
4928 | -{ |
4929 | - Q_OBJECT |
4930 | - |
4931 | - Q_PROPERTY(QString mockMode READ mockMode WRITE setMockMode NOTIFY mockModeChanged) |
4932 | - |
4933 | -public: |
4934 | - explicit MockUsersModel(QObject* parent=0); |
4935 | - |
4936 | - QString mockMode() const; |
4937 | - void setMockMode(QString mockMode); |
4938 | - |
4939 | -Q_SIGNALS: |
4940 | - void mockModeChanged(QString mode); |
4941 | -}; |
4942 | - |
4943 | -#endif // MOCK_UNITY_USERSMODEL_H |
4944 | |
4945 | === removed directory 'tests/mocks/LightDM/IntegratedLightDM/QLightDM' |
4946 | === removed file 'tests/mocks/LightDM/IntegratedLightDM/QLightDM/Greeter' |
4947 | --- tests/mocks/LightDM/IntegratedLightDM/QLightDM/Greeter 2015-01-20 11:50:19 +0000 |
4948 | +++ tests/mocks/LightDM/IntegratedLightDM/QLightDM/Greeter 1970-01-01 00:00:00 +0000 |
4949 | @@ -1,17 +0,0 @@ |
4950 | -/* |
4951 | - * Copyright (C) 2014 Canonical, Ltd. |
4952 | - * |
4953 | - * This program is free software; you can redistribute it and/or modify |
4954 | - * it under the terms of the GNU General Public License as published by |
4955 | - * the Free Software Foundation; version 3. |
4956 | - * |
4957 | - * This program is distributed in the hope that it will be useful, |
4958 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
4959 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4960 | - * GNU General Public License for more details. |
4961 | - * |
4962 | - * You should have received a copy of the GNU General Public License |
4963 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
4964 | - */ |
4965 | - |
4966 | -#include "../liblightdm/Greeter.h" |
4967 | |
4968 | === removed file 'tests/mocks/LightDM/IntegratedLightDM/QLightDM/SessionsModel' |
4969 | --- tests/mocks/LightDM/IntegratedLightDM/QLightDM/SessionsModel 2015-11-18 03:52:01 +0000 |
4970 | +++ tests/mocks/LightDM/IntegratedLightDM/QLightDM/SessionsModel 1970-01-01 00:00:00 +0000 |
4971 | @@ -1,17 +0,0 @@ |
4972 | -/* |
4973 | - * Copyright (C) 2015 Canonical, Ltd. |
4974 | - * |
4975 | - * This program is free software; you can redistribute it and/or modify |
4976 | - * it under the terms of the GNU General Public License as published by |
4977 | - * the Free Software Foundation; version 3. |
4978 | - * |
4979 | - * This program is distributed in the hope that it will be useful, |
4980 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
4981 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4982 | - * GNU General Public License for more details. |
4983 | - * |
4984 | - * You should have received a copy of the GNU General Public License |
4985 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
4986 | - */ |
4987 | - |
4988 | -#include "../liblightdm/SessionsModel.h" |
4989 | |
4990 | === removed file 'tests/mocks/LightDM/IntegratedLightDM/QLightDM/UsersModel' |
4991 | --- tests/mocks/LightDM/IntegratedLightDM/QLightDM/UsersModel 2015-01-20 11:50:19 +0000 |
4992 | +++ tests/mocks/LightDM/IntegratedLightDM/QLightDM/UsersModel 1970-01-01 00:00:00 +0000 |
4993 | @@ -1,17 +0,0 @@ |
4994 | -/* |
4995 | - * Copyright (C) 2014 Canonical, Ltd. |
4996 | - * |
4997 | - * This program is free software; you can redistribute it and/or modify |
4998 | - * it under the terms of the GNU General Public License as published by |
4999 | - * the Free Software Foundation; version 3. |
5000 | - * |
The diff has been truncated for viewing.