Merge lp:~lukas-kde/unity8/wizardKeyboardPage into lp:unity8
- wizardKeyboardPage
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Albert Astals Cid |
Approved revision: | 2427 |
Merged at revision: | 2591 |
Proposed branch: | lp:~lukas-kde/unity8/wizardKeyboardPage |
Merge into: | lp:unity8 |
Diff against target: |
816 lines (+596/-12) 15 files modified
CMakeLists.txt (+1/-0) debian/control (+1/-0) plugins/AccountsService/AccountsService.cpp (+18/-0) plugins/AccountsService/AccountsService.h (+2/-3) plugins/Wizard/CMakeLists.txt (+6/-3) plugins/Wizard/keyboardLayoutsModel.cpp (+163/-0) plugins/Wizard/keyboardLayoutsModel.h (+66/-0) plugins/Wizard/plugin.cpp (+2/-0) plugins/Wizard/qmldir (+0/-1) qml/Wizard/Pages/20-keyboard.qml (+160/-0) qml/Wizard/WizardItemSelector.qml (+128/-0) tests/mocks/AccountsService/AccountsService.h (+1/-1) tests/mocks/Wizard/CMakeLists.txt (+6/-3) tests/mocks/Wizard/mockplugin.cpp (+2/-0) tests/qmltests/Wizard/tst_Wizard.qml (+40/-1) |
To merge this branch: | bzr merge lp:~lukas-kde/unity8/wizardKeyboardPage |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Unity8 CI Bot | continuous-integration | Needs Fixing | |
Albert Astals Cid (community) | Approve | ||
Review via email: mp+295060@code.launchpad.net |
Commit message
Implement a page for choosing HW keyboard layout in OOBE wizard
Description of the change
Implement a page for choosing HW keyboard layout in OOBE wizard
* Are there any related MPs required for this MP to build/function as expected? Please list.
No, although will likely conflict with https:/
* Did you perform an exploratory manual test run of your code change and any related functionality?
Yes
* If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?
N/A
* If you changed the UI, has there been a design review?
Yes (paty+grazina)
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2422
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2423
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Albert Astals Cid (aacid) wrote : | # |
The AccountsService
Albert Astals Cid (aacid) wrote : | # |
In AccountsService
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2424
https:/
Executed test runs:
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
UNSTABLE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Albert Astals Cid (aacid) wrote : | # |
Looks good, some *very* minor comments.
Do you think it makes sense to move m_xkbInfo to be declared inside KeyboardLayouts
This allows us to "hide" the
#define GNOME_DESKTOP_
#include <libgnome-
to the .cpp file to instead of the .h
Suggestion when reading the code, feel free to ignore, In KeyboardLayouts
Minor silly optimization
KeyboardLayoutInfo layout = m_layouts.at(row);
to
const KeyboardLayoutInfo &layout = m_layouts.at(row);
Remove
Component.
print("Selected language:", selectedLanguage)
print("Saving keymap:", selectedKeymap);
?
make this readonly
property int maxVisibleItems: 6
?
Lukáš Tinkl (lukas-kde) wrote : | # |
Addressed all the review comments
Albert Astals Cid (aacid) wrote : | # |
* Did you perform an exploratory manual test run of the code change and any related functionality?
Yes
* Did CI run pass? If not, please explain why.
Yes
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2425
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
UNSTABLE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Albert Astals Cid (aacid) wrote : | # |
Note: was top approved already
Text conflict in debian/control
1 conflicts encountered.
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2426
https:/
Executed test runs:
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
- 2427. By Lukáš Tinkl
-
merge trunk
Lukáš Tinkl (lukas-kde) wrote : | # |
Merged trunk, should be back to normal
Albert Astals Cid (aacid) wrote : | # |
Merges fine, was already top approved.
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2427
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Preview Diff
1 | === modified file 'CMakeLists.txt' |
2 | --- CMakeLists.txt 2016-07-14 07:09:00 +0000 |
3 | +++ CMakeLists.txt 2016-08-08 10:10:54 +0000 |
4 | @@ -62,6 +62,7 @@ |
5 | pkg_check_modules(GIO REQUIRED gio-2.0>=2.32) |
6 | pkg_check_modules(GLIB REQUIRED glib-2.0>=2.32) |
7 | pkg_check_modules(QMENUMODEL REQUIRED qmenumodel) |
8 | +pkg_check_modules(GD3 REQUIRED gnome-desktop-3.0) |
9 | |
10 | pkg_check_modules(UBUNTUGESTURES REQUIRED UbuntuGestures) |
11 | |
12 | |
13 | === modified file 'debian/control' |
14 | --- debian/control 2016-08-03 15:53:00 +0000 |
15 | +++ debian/control 2016-08-08 10:10:54 +0000 |
16 | @@ -17,6 +17,7 @@ |
17 | libconnectivity-qt1-dev (>= 0.7.1), |
18 | libevdev-dev, |
19 | libgeonames-dev (>= 0.2), |
20 | + libgnome-desktop-3-dev, |
21 | libgl1-mesa-dev[!arm64 !armhf] | libgl-dev[!arm64 !armhf], |
22 | libgl1-mesa-dri, |
23 | libgles2-mesa-dev[arm64 armhf], |
24 | |
25 | === modified file 'plugins/AccountsService/AccountsService.cpp' |
26 | --- plugins/AccountsService/AccountsService.cpp 2016-07-08 18:39:55 +0000 |
27 | +++ plugins/AccountsService/AccountsService.cpp 2016-08-08 10:10:54 +0000 |
28 | @@ -284,6 +284,24 @@ |
29 | return {QStringLiteral("us")}; |
30 | } |
31 | |
32 | +void AccountsService::setKeymaps(const QStringList &keymaps) |
33 | +{ |
34 | + if (keymaps.isEmpty()) { |
35 | + qWarning() << "Setting empty keymaps is not supported"; |
36 | + return; |
37 | + } |
38 | + |
39 | + StringMapList result; |
40 | + Q_FOREACH(const QString &keymap, keymaps) { |
41 | + StringMap map; |
42 | + map.insert(QStringLiteral("xkb"), keymap); |
43 | + result.append(map); |
44 | + } |
45 | + |
46 | + setProperty(IFACE_ACCOUNTS_USER, PROP_INPUT_SOURCES, QVariant::fromValue(result)); |
47 | + Q_EMIT keymapsChanged(); |
48 | +} |
49 | + |
50 | uint AccountsService::failedFingerprintLogins() const |
51 | { |
52 | return getProperty(IFACE_UNITY_PRIVATE, PROP_FAILED_FINGERPRINT_LOGINS).toUInt(); |
53 | |
54 | === modified file 'plugins/AccountsService/AccountsService.h' |
55 | --- plugins/AccountsService/AccountsService.h 2016-07-08 18:39:55 +0000 |
56 | +++ plugins/AccountsService/AccountsService.h 2016-08-08 10:10:54 +0000 |
57 | @@ -79,9 +79,7 @@ |
58 | NOTIFY hereLicensePathChanged) |
59 | Q_PROPERTY(QString realName READ realName WRITE setRealName NOTIFY realNameChanged) |
60 | Q_PROPERTY(QString email READ email WRITE setEmail NOTIFY emailChanged) |
61 | - Q_PROPERTY(QStringList keymaps |
62 | - READ keymaps |
63 | - NOTIFY keymapsChanged) |
64 | + Q_PROPERTY(QStringList keymaps READ keymaps WRITE setKeymaps NOTIFY keymapsChanged) |
65 | |
66 | public: |
67 | enum PasswordDisplayHint { |
68 | @@ -117,6 +115,7 @@ |
69 | QString email() const; |
70 | void setEmail(const QString &email); |
71 | QStringList keymaps() const; |
72 | + void setKeymaps(const QStringList &keymaps); |
73 | |
74 | Q_SIGNALS: |
75 | void userChanged(); |
76 | |
77 | === modified file 'plugins/Wizard/CMakeLists.txt' |
78 | --- plugins/Wizard/CMakeLists.txt 2016-03-29 03:47:39 +0000 |
79 | +++ plugins/Wizard/CMakeLists.txt 2016-08-08 10:10:54 +0000 |
80 | @@ -1,4 +1,6 @@ |
81 | -include_directories(${GLIB_INCLUDE_DIRS} ${GEONAMES_INCLUDE_DIRS}) |
82 | +pkg_search_module(GD3 REQUIRED gnome-desktop-3.0) |
83 | + |
84 | +include_directories(SYSTEM ${GD3_INCLUDE_DIRS} ${GLIB_INCLUDE_DIRS} ${GEONAMES_INCLUDE_DIRS}) |
85 | |
86 | add_library(Wizard-qml MODULE |
87 | plugin.cpp |
88 | @@ -7,10 +9,11 @@ |
89 | LocalePlugin.cpp |
90 | timezonemodel.cpp |
91 | Status.cpp |
92 | + keyboardLayoutsModel.cpp |
93 | ) |
94 | |
95 | -qt5_use_modules(Wizard-qml DBus Qml Concurrent) |
96 | -target_link_libraries(Wizard-qml ${GLIB_LDFLAGS} ${GEONAMES_LDFLAGS}) |
97 | +qt5_use_modules(Wizard-qml DBus Qml) |
98 | +target_link_libraries(Wizard-qml ${GD3_LDFLAGS} ${GLIB_LDFLAGS} ${GEONAMES_LDFLAGS}) |
99 | add_unity8_plugin(Wizard 0.1 Wizard TARGETS Wizard-qml) |
100 | |
101 | set(POLKIT_LIB_DIR "${CMAKE_INSTALL_LOCALSTATEDIR}/lib/polkit-1") |
102 | |
103 | === added file 'plugins/Wizard/keyboardLayoutsModel.cpp' |
104 | --- plugins/Wizard/keyboardLayoutsModel.cpp 1970-01-01 00:00:00 +0000 |
105 | +++ plugins/Wizard/keyboardLayoutsModel.cpp 2016-08-08 10:10:54 +0000 |
106 | @@ -0,0 +1,163 @@ |
107 | +/* |
108 | + * Copyright (C) 2016 Canonical Ltd. |
109 | + * |
110 | + * This program is free software: you can redistribute it and/or modify it |
111 | + * under the terms of the GNU General Public License version 3, as published |
112 | + * by the Free Software Foundation. |
113 | + * |
114 | + * This program is distributed in the hope that it will be useful, but |
115 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
116 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
117 | + * PURPOSE. See the GNU General Public License for more details. |
118 | + * |
119 | + * You should have received a copy of the GNU General Public License along |
120 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
121 | + */ |
122 | + |
123 | +#include <QDBusMetaType> |
124 | +#include <QDebug> |
125 | + |
126 | +#define GNOME_DESKTOP_USE_UNSTABLE_API |
127 | +#include <libgnome-desktop/gnome-xkb-info.h> |
128 | + |
129 | +#include "keyboardLayoutsModel.h" |
130 | + |
131 | +typedef QList<QMap<QString, QString>> StringMapList; |
132 | +Q_DECLARE_METATYPE(StringMapList) |
133 | + |
134 | +KeyboardLayoutsModel::KeyboardLayoutsModel(QObject *parent) |
135 | + : QAbstractListModel(parent) |
136 | +{ |
137 | + m_roleNames = { |
138 | + {LayoutIdRole, "layoutId"}, |
139 | + {DisplayNameRole, "displayName"}, |
140 | + {LanguageRole, "language"} |
141 | + }; |
142 | + |
143 | + qDBusRegisterMetaType<StringMapList>(); |
144 | + |
145 | + buildModel(); |
146 | + connect(this, &KeyboardLayoutsModel::languageChanged, this, &KeyboardLayoutsModel::updateModel); |
147 | +} |
148 | + |
149 | +QString KeyboardLayoutsModel::language() const |
150 | +{ |
151 | + return m_language; |
152 | +} |
153 | + |
154 | +void KeyboardLayoutsModel::setLanguage(const QString &language) |
155 | +{ |
156 | + if (m_language == language) |
157 | + return; |
158 | + |
159 | + m_language = language; |
160 | + Q_EMIT languageChanged(language); |
161 | +} |
162 | + |
163 | +static bool compareLayouts(const KeyboardLayoutInfo &layout0, const KeyboardLayoutInfo &layout1) |
164 | +{ |
165 | + QString name0(layout0.id); |
166 | + QString name1(layout1.id); |
167 | + |
168 | + if (name0 == name1) { |
169 | + name0 = layout0.displayName; |
170 | + name1 = layout1.displayName; |
171 | + |
172 | + if (name0 == name1) { |
173 | + name0 = layout0.language; |
174 | + name1 = layout1.language; |
175 | + } |
176 | + } |
177 | + |
178 | + return QString::localeAwareCompare(name0, name1) < 0; |
179 | +} |
180 | + |
181 | +void KeyboardLayoutsModel::buildModel() |
182 | +{ |
183 | + GList *sources, *tmp; |
184 | + const gchar *display_name; |
185 | + const gchar *short_name; |
186 | + const gchar *xkb_layout; |
187 | + const gchar *xkb_variant; |
188 | + |
189 | + GnomeXkbInfo * xkbInfo(gnome_xkb_info_new()); |
190 | + |
191 | + sources = gnome_xkb_info_get_all_layouts(xkbInfo); |
192 | + |
193 | + for (tmp = sources; tmp != NULL; tmp = tmp->next) { |
194 | + gboolean result = gnome_xkb_info_get_layout_info(xkbInfo, (const gchar *)tmp->data, |
195 | + &display_name, &short_name, &xkb_layout, &xkb_variant); |
196 | + if (!result) { |
197 | + qWarning() << "Skipping invalid layout"; |
198 | + continue; |
199 | + } |
200 | + |
201 | + KeyboardLayoutInfo layout; |
202 | + layout.id = QString::fromUtf8((const gchar *)tmp->data); |
203 | + layout.language = QString::fromUtf8(short_name); |
204 | + layout.displayName = QString::fromUtf8(display_name); |
205 | + |
206 | + m_db.append(layout); |
207 | + } |
208 | + g_list_free(sources); |
209 | + g_object_unref(xkbInfo); |
210 | + |
211 | + std::sort(m_db.begin(), m_db.end(), compareLayouts); |
212 | +} |
213 | + |
214 | +void KeyboardLayoutsModel::updateModel() |
215 | +{ |
216 | + beginResetModel(); |
217 | + m_layouts.clear(); |
218 | + |
219 | + const QString lang = m_language.section("_", 0, 0); |
220 | + const QString country = m_language.section("_", 1, 1).toLower(); |
221 | + |
222 | + Q_FOREACH(const KeyboardLayoutInfo & info, m_db) { |
223 | + const QString kbdCountry = info.id.section("+", 0, 0); |
224 | + if (info.language == lang && // filter by language |
225 | + (kbdCountry.startsWith(country) || kbdCountry.length() > 2)) { // and by known country, also insert anything that doesn't match the country |
226 | + m_layouts.append(info); |
227 | + } |
228 | + } |
229 | + |
230 | + std::sort(m_layouts.begin(), m_layouts.end(), compareLayouts); |
231 | + endResetModel(); |
232 | +} |
233 | + |
234 | +int KeyboardLayoutsModel::rowCount(const QModelIndex &parent) const |
235 | +{ |
236 | + Q_UNUSED(parent) |
237 | + return m_layouts.count(); |
238 | +} |
239 | + |
240 | +QVariant KeyboardLayoutsModel::data(const QModelIndex &index, int role) const |
241 | +{ |
242 | + const int row = index.row(); |
243 | + |
244 | + if (row >= m_layouts.count()) { |
245 | + qWarning() << Q_FUNC_INFO << "index out of bounds"; |
246 | + return QVariant(); |
247 | + } |
248 | + |
249 | + const KeyboardLayoutInfo &layout = m_layouts.at(row); |
250 | + |
251 | + switch (role) { |
252 | + case Qt::DisplayRole: |
253 | + case DisplayNameRole: |
254 | + return layout.displayName; |
255 | + case LayoutIdRole: |
256 | + return layout.id; |
257 | + case LanguageRole: |
258 | + return layout.language; |
259 | + default: { |
260 | + qWarning() << Q_FUNC_INFO << "unsupported data role"; |
261 | + return QVariant(); |
262 | + } |
263 | + } |
264 | +} |
265 | + |
266 | +QHash<int, QByteArray> KeyboardLayoutsModel::roleNames() const |
267 | +{ |
268 | + return m_roleNames; |
269 | +} |
270 | |
271 | === added file 'plugins/Wizard/keyboardLayoutsModel.h' |
272 | --- plugins/Wizard/keyboardLayoutsModel.h 1970-01-01 00:00:00 +0000 |
273 | +++ plugins/Wizard/keyboardLayoutsModel.h 2016-08-08 10:10:54 +0000 |
274 | @@ -0,0 +1,66 @@ |
275 | +/* |
276 | + * Copyright (C) 2016 Canonical Ltd. |
277 | + * |
278 | + * This program is free software: you can redistribute it and/or modify it |
279 | + * under the terms of the GNU General Public License version 3, as published |
280 | + * by the Free Software Foundation. |
281 | + * |
282 | + * This program is distributed in the hope that it will be useful, but |
283 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
284 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
285 | + * PURPOSE. See the GNU General Public License for more details. |
286 | + * |
287 | + * You should have received a copy of the GNU General Public License along |
288 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
289 | + */ |
290 | + |
291 | +#ifndef KEYBOARDLAYOUTSMODEL_H |
292 | +#define KEYBOARDLAYOUTSMODEL_H |
293 | + |
294 | +#include <QAbstractListModel> |
295 | + |
296 | +struct KeyboardLayoutInfo { |
297 | + QString id; |
298 | + QString displayName; |
299 | + QString language; |
300 | +}; |
301 | + |
302 | +class KeyboardLayoutsModel: public QAbstractListModel |
303 | +{ |
304 | + Q_OBJECT |
305 | + |
306 | + Q_PROPERTY(QString language READ language WRITE setLanguage NOTIFY languageChanged) |
307 | + |
308 | +public: |
309 | + explicit KeyboardLayoutsModel(QObject * parent = nullptr); |
310 | + ~KeyboardLayoutsModel() = default; |
311 | + |
312 | + enum Roles { |
313 | + LayoutIdRole = Qt::UserRole + 1, |
314 | + DisplayNameRole, |
315 | + LanguageRole |
316 | + }; |
317 | + |
318 | + QString language() const; |
319 | + void setLanguage(const QString &language); |
320 | + |
321 | + int rowCount(const QModelIndex &parent = QModelIndex()) const override; |
322 | + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; |
323 | + QHash<int, QByteArray> roleNames() const override; |
324 | + |
325 | +Q_SIGNALS: |
326 | + void languageChanged(const QString &language); |
327 | + |
328 | +private Q_SLOTS: |
329 | + void updateModel(); |
330 | + |
331 | +private: |
332 | + void buildModel(); |
333 | + |
334 | + QString m_language; |
335 | + QHash<int, QByteArray> m_roleNames; |
336 | + QVector<KeyboardLayoutInfo> m_layouts; |
337 | + QVector<KeyboardLayoutInfo> m_db; |
338 | +}; |
339 | + |
340 | +#endif |
341 | |
342 | === modified file 'plugins/Wizard/plugin.cpp' |
343 | --- plugins/Wizard/plugin.cpp 2016-03-29 03:47:39 +0000 |
344 | +++ plugins/Wizard/plugin.cpp 2016-08-08 10:10:54 +0000 |
345 | @@ -20,6 +20,7 @@ |
346 | #include "timezonemodel.h" |
347 | #include "LocalePlugin.h" |
348 | #include "Status.h" |
349 | +#include "keyboardLayoutsModel.h" |
350 | |
351 | #include <QtQml/qqml.h> |
352 | |
353 | @@ -30,5 +31,6 @@ |
354 | qmlRegisterSingletonType<System>(uri, 0, 1, "System", [](QQmlEngine*, QJSEngine*) -> QObject* { return new System; }); |
355 | qmlRegisterSingletonType<Status>(uri, 0, 1, "Status", [](QQmlEngine*, QJSEngine*) -> QObject* { return new Status; }); |
356 | qmlRegisterType<TimeZoneLocationModel>(uri, 0, 1, "TimeZoneModel"); |
357 | + qmlRegisterType<KeyboardLayoutsModel>(uri, 0, 1, "KeyboardLayoutsModel"); |
358 | qmlRegisterType<LocalePlugin>(uri, 0, 1, "LocalePlugin"); |
359 | } |
360 | |
361 | === modified file 'plugins/Wizard/qmldir' |
362 | --- plugins/Wizard/qmldir 2014-11-14 17:47:00 +0000 |
363 | +++ plugins/Wizard/qmldir 2016-08-08 10:10:54 +0000 |
364 | @@ -1,3 +1,2 @@ |
365 | module Wizard |
366 | plugin Wizard-qml |
367 | -typeinfo Wizard.qmltypes |
368 | |
369 | === added file 'qml/Wizard/Pages/20-keyboard.qml' |
370 | --- qml/Wizard/Pages/20-keyboard.qml 1970-01-01 00:00:00 +0000 |
371 | +++ qml/Wizard/Pages/20-keyboard.qml 2016-08-08 10:10:54 +0000 |
372 | @@ -0,0 +1,160 @@ |
373 | +/* |
374 | + * Copyright (C) 2016 Canonical, Ltd. |
375 | + * |
376 | + * This program is free software; you can redistribute it and/or modify |
377 | + * it under the terms of the GNU General Public License as published by |
378 | + * the Free Software Foundation; version 3. |
379 | + * |
380 | + * This program is distributed in the hope that it will be useful, |
381 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
382 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
383 | + * GNU General Public License for more details. |
384 | + * |
385 | + * You should have received a copy of the GNU General Public License |
386 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
387 | + */ |
388 | + |
389 | +import QtQuick 2.4 |
390 | +import Ubuntu.Components 1.3 |
391 | +import Ubuntu.Components.ListItems 1.3 |
392 | +import Ubuntu.SystemSettings.LanguagePlugin 1.0 |
393 | +import Wizard 0.1 |
394 | +import AccountsService 0.1 |
395 | +import Unity.InputInfo 0.1 |
396 | +import Unity.Application 0.1 |
397 | +import ".." as LocalComponents |
398 | + |
399 | +LocalComponents.Page { |
400 | + objectName: "keyboardPage" |
401 | + |
402 | + title: i18n.tr("Select Keyboard") |
403 | + forwardButtonSourceComponent: forwardButton |
404 | + |
405 | + skip: keyboardsModel.count == 0 |
406 | + skipValid: false |
407 | + |
408 | + UbuntuLanguagePlugin { |
409 | + id: langPlugin |
410 | + } |
411 | + |
412 | + KeyboardLayoutsModel { |
413 | + id: layoutsModel |
414 | + language: selectedLanguage |
415 | + } |
416 | + |
417 | + InputDeviceModel { |
418 | + id: keyboardsModel |
419 | + deviceFilter: InputInfo.Keyboard |
420 | + Component.onCompleted: skipValid = true; |
421 | + } |
422 | + |
423 | + readonly property string selectedLanguage: langPlugin.languageCodes[langSelector.selectedIndex].split(".")[0] // chop off the codeset (.UTF-8) |
424 | + |
425 | + property string selectedKeymap: "" |
426 | + |
427 | + Column { |
428 | + id: column |
429 | + spacing: units.gu(2) |
430 | + |
431 | + anchors { |
432 | + fill: content |
433 | + leftMargin: wideMode ? parent.leftMargin : staticMargin |
434 | + rightMargin: wideMode ? parent.rightMargin : staticMargin |
435 | + topMargin: staticMargin |
436 | + } |
437 | + |
438 | + Label { |
439 | + id: label1 |
440 | + anchors.left: parent.left |
441 | + anchors.right: parent.right |
442 | + text: i18n.tr("Keyboard language") |
443 | + font.weight: Font.Normal |
444 | + color: textColor |
445 | + } |
446 | + |
447 | + LocalComponents.WizardItemSelector { |
448 | + id: langSelector |
449 | + objectName: "langSelector" |
450 | + anchors.left: parent.left |
451 | + anchors.right: parent.right |
452 | + model: langPlugin.languageNames |
453 | + selectedIndex: langPlugin.languageCodes.indexOf(i18n.language) |
454 | + onSelectedIndexChanged: { |
455 | + keyboardListView.currentIndex = -1; |
456 | + selectedKeymap = ""; |
457 | + } |
458 | + } |
459 | + |
460 | + Label { |
461 | + id: label2 |
462 | + anchors.left: parent.left |
463 | + anchors.right: parent.right |
464 | + text: i18n.tr("Keyboard layout") |
465 | + font.weight: Font.Normal |
466 | + color: textColor |
467 | + } |
468 | + |
469 | + ListView { |
470 | + id: keyboardListView |
471 | + clip: true |
472 | + anchors.left: parent.left |
473 | + anchors.right: parent.right |
474 | + snapMode: ListView.SnapToItem |
475 | + model: layoutsModel |
476 | + currentIndex: -1 |
477 | + opacity: langSelector.expanded ? 0.5 : 1 |
478 | + height: column.height - label1.height - langSelector.height - label2.height - column.spacing * 3 |
479 | + enabled: !langSelector.expanded |
480 | + Behavior on opacity { |
481 | + UbuntuNumberAnimation {} |
482 | + } |
483 | + |
484 | + delegate: ListItem { |
485 | + id: itemDelegate |
486 | + objectName: "kbdDelegate" + index |
487 | + height: layout.height + (divider.visible ? divider.height : 0) |
488 | + readonly property bool isCurrent: index === ListView.view.currentIndex |
489 | + highlightColor: backgroundColor |
490 | + divider.colorFrom: dividerColor |
491 | + divider.colorTo: backgroundColor |
492 | + |
493 | + ListItemLayout { |
494 | + id: layout |
495 | + title.text: displayName |
496 | + title.color: textColor |
497 | + subtitle.text: layoutId |
498 | + subtitle.color: textColor |
499 | + padding.leading: -units.gu(1) |
500 | + padding.trailing: -units.gu(1) |
501 | + Image { |
502 | + SlotsLayout.position: SlotsLayout.Trailing |
503 | + SlotsLayout.overrideVerticalPositioning: true |
504 | + fillMode: Image.PreserveAspectFit |
505 | + anchors.verticalCenter: parent.verticalCenter |
506 | + height: units.gu(1.5) |
507 | + source: "data/Tick@30.png" |
508 | + visible: itemDelegate.isCurrent |
509 | + } |
510 | + } |
511 | + |
512 | + onClicked: { |
513 | + keyboardListView.currentIndex = index; |
514 | + selectedKeymap = layoutId; |
515 | + } |
516 | + } |
517 | + } |
518 | + } |
519 | + |
520 | + Component { |
521 | + id: forwardButton |
522 | + LocalComponents.StackButton { |
523 | + text: keyboardListView.currentIndex != -1 ? i18n.tr("Next") : i18n.tr("Skip") |
524 | + onClicked: { |
525 | + if (keyboardListView.currentIndex != -1) { |
526 | + AccountsService.keymaps = selectedKeymap; |
527 | + } |
528 | + pageStack.next(); |
529 | + } |
530 | + } |
531 | + } |
532 | +} |
533 | |
534 | === added file 'qml/Wizard/WizardItemSelector.qml' |
535 | --- qml/Wizard/WizardItemSelector.qml 1970-01-01 00:00:00 +0000 |
536 | +++ qml/Wizard/WizardItemSelector.qml 2016-08-08 10:10:54 +0000 |
537 | @@ -0,0 +1,128 @@ |
538 | +/* |
539 | + * Copyright (C) 2016 Canonical, Ltd. |
540 | + * |
541 | + * This program is free software; you can redistribute it and/or modify |
542 | + * it under the terms of the GNU General Public License as published by |
543 | + * the Free Software Foundation; version 3. |
544 | + * |
545 | + * This program is distributed in the hope that it will be useful, |
546 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
547 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
548 | + * GNU General Public License for more details. |
549 | + * |
550 | + * You should have received a copy of the GNU General Public License |
551 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
552 | + */ |
553 | + |
554 | +import QtQuick 2.4 |
555 | +import Ubuntu.Components 1.3 |
556 | +import Ubuntu.Components.ListItems 1.3 as ListItem |
557 | + |
558 | +Rectangle { |
559 | + id: optionToggle |
560 | + |
561 | + property bool expanded |
562 | + property var model |
563 | + property int selectedIndex: -1 |
564 | + readonly property double itemHeight: units.gu(4) |
565 | + readonly property int maxVisibleItems: 6 |
566 | + |
567 | + color: theme.palette.normal.foreground |
568 | + height: expanded ? maxVisibleItems * itemHeight : itemHeight |
569 | + Behavior on height { |
570 | + UbuntuNumberAnimation { id: heightAnimation } |
571 | + } |
572 | + |
573 | + width: parent.width |
574 | + radius: units.gu(0.6) |
575 | + clip: true |
576 | + border.width: units.dp(1) |
577 | + border.color: theme.palette.normal.base |
578 | + |
579 | + Flickable { |
580 | + id: flickable |
581 | + interactive: expanded |
582 | + flickableDirection: Flickable.VerticalFlick |
583 | + width: parent.width |
584 | + height: parent.height |
585 | + contentHeight: optionToggleRepeater.count * itemHeight |
586 | + |
587 | + Column { |
588 | + id: optionToggleContent |
589 | + width: parent.width |
590 | + |
591 | + Repeater { |
592 | + id: optionToggleRepeater |
593 | + model: optionToggle.model |
594 | + |
595 | + delegate: Loader { |
596 | + asynchronous: true |
597 | + visible: status === Loader.Ready |
598 | + |
599 | + Component { |
600 | + id: optionToggleEntry |
601 | + |
602 | + AbstractButton { |
603 | + width: optionToggleContent.width |
604 | + height: optionToggle.itemHeight |
605 | + onClicked: { |
606 | + if (expanded) { |
607 | + selectedIndex = index; |
608 | + } |
609 | + expanded = !expanded |
610 | + } |
611 | + |
612 | + ListItem.ThinDivider { |
613 | + visible: expanded && index != 0 |
614 | + } |
615 | + |
616 | + Label { |
617 | + id: delegateLabel |
618 | + anchors { |
619 | + left: parent.left |
620 | + leftMargin: units.gu(1) |
621 | + right: parent.right |
622 | + rightMargin: units.gu(3) |
623 | + verticalCenter: parent.verticalCenter |
624 | + } |
625 | + |
626 | + width: parent.width |
627 | + text: expanded ? modelData : optionToggle.model[selectedIndex] |
628 | + color: textColor |
629 | + font.weight: Font.Light |
630 | + maximumLineCount: 1 |
631 | + elide: Text.ElideRight |
632 | + } |
633 | + |
634 | + Icon { |
635 | + anchors { |
636 | + right: parent.right |
637 | + rightMargin: units.gu(1) |
638 | + verticalCenter: parent.verticalCenter |
639 | + } |
640 | + |
641 | + visible: (index == 0 || !expanded) && !heightAnimation.running |
642 | + name: expanded ? "up" : "down" |
643 | + width: units.gu(1.5) |
644 | + height: width |
645 | + } |
646 | + |
647 | + Image { |
648 | + anchors { |
649 | + right: parent.right |
650 | + rightMargin: units.gu(1) |
651 | + verticalCenter: parent.verticalCenter |
652 | + } |
653 | + visible: expanded && index == optionToggle.selectedIndex && index != 0 |
654 | + height: units.gu(1.5) |
655 | + fillMode: Image.PreserveAspectFit |
656 | + source: Qt.resolvedUrl("Pages/data/Tick@30.png") |
657 | + } |
658 | + } |
659 | + } |
660 | + sourceComponent: optionToggleEntry |
661 | + } |
662 | + } |
663 | + } |
664 | + } |
665 | +} |
666 | |
667 | === modified file 'tests/mocks/AccountsService/AccountsService.h' |
668 | --- tests/mocks/AccountsService/AccountsService.h 2016-06-20 15:24:27 +0000 |
669 | +++ tests/mocks/AccountsService/AccountsService.h 2016-08-08 10:10:54 +0000 |
670 | @@ -84,7 +84,7 @@ |
671 | Q_PROPERTY(QString email READ email WRITE setEmail NOTIFY emailChanged) |
672 | Q_PROPERTY(QStringList keymaps |
673 | READ keymaps |
674 | - WRITE setKeymaps // only in mock |
675 | + WRITE setKeymaps |
676 | NOTIFY keymapsChanged) |
677 | |
678 | public: |
679 | |
680 | === modified file 'tests/mocks/Wizard/CMakeLists.txt' |
681 | --- tests/mocks/Wizard/CMakeLists.txt 2016-03-29 03:47:39 +0000 |
682 | +++ tests/mocks/Wizard/CMakeLists.txt 2016-08-08 10:10:54 +0000 |
683 | @@ -1,5 +1,7 @@ |
684 | +pkg_search_module(GD3 REQUIRED gnome-desktop-3.0) |
685 | + |
686 | include_directories( |
687 | - ${GLIB_INCLUDE_DIRS} ${GEONAMES_INCLUDE_DIRS} |
688 | + ${GD3_INCLUDE_DIRS} ${GLIB_INCLUDE_DIRS} ${GEONAMES_INCLUDE_DIRS} |
689 | ${CMAKE_SOURCE_DIR}/plugins/Wizard |
690 | ) |
691 | |
692 | @@ -10,8 +12,9 @@ |
693 | ${CMAKE_SOURCE_DIR}/plugins/Wizard/timezonemodel.cpp |
694 | ${CMAKE_SOURCE_DIR}/plugins/Wizard/LocalePlugin.cpp |
695 | ${CMAKE_SOURCE_DIR}/plugins/Wizard/Status.cpp |
696 | + ${CMAKE_SOURCE_DIR}/plugins/Wizard/keyboardLayoutsModel.cpp |
697 | ) |
698 | |
699 | -qt5_use_modules(MockWizard-qml DBus Qml Concurrent) |
700 | -target_link_libraries(MockWizard-qml ${GLIB_LDFLAGS} ${GEONAMES_LDFLAGS}) |
701 | +qt5_use_modules(MockWizard-qml DBus Qml) |
702 | +target_link_libraries(MockWizard-qml ${GD3_LDFLAGS} ${GLIB_LDFLAGS} ${GEONAMES_LDFLAGS}) |
703 | add_unity8_mock(Wizard 0.1 Wizard TARGETS MockWizard-qml) |
704 | |
705 | === modified file 'tests/mocks/Wizard/mockplugin.cpp' |
706 | --- tests/mocks/Wizard/mockplugin.cpp 2016-03-29 03:47:39 +0000 |
707 | +++ tests/mocks/Wizard/mockplugin.cpp 2016-08-08 10:10:54 +0000 |
708 | @@ -20,6 +20,7 @@ |
709 | #include "timezonemodel.h" |
710 | #include "LocalePlugin.h" |
711 | #include "Status.h" |
712 | +#include "keyboardLayoutsModel.h" |
713 | |
714 | #include <QtQml/qqml.h> |
715 | |
716 | @@ -30,5 +31,6 @@ |
717 | qmlRegisterSingletonType<MockSystem>(uri, 0, 1, "System", [](QQmlEngine*, QJSEngine*) -> QObject* { return new MockSystem; }); |
718 | qmlRegisterSingletonType<Status>(uri, 0, 1, "Status", [](QQmlEngine*, QJSEngine*) -> QObject* { return new Status; }); |
719 | qmlRegisterType<TimeZoneLocationModel>(uri, 0, 1, "TimeZoneModel"); |
720 | + qmlRegisterType<KeyboardLayoutsModel>(uri, 0, 1, "KeyboardLayoutsModel"); |
721 | qmlRegisterType<LocalePlugin>(uri, 0, 1, "LocalePlugin"); |
722 | } |
723 | |
724 | === modified file 'tests/qmltests/Wizard/tst_Wizard.qml' |
725 | --- tests/qmltests/Wizard/tst_Wizard.qml 2016-06-29 14:44:41 +0000 |
726 | +++ tests/qmltests/Wizard/tst_Wizard.qml 2016-08-08 10:10:54 +0000 |
727 | @@ -24,6 +24,7 @@ |
728 | import Ubuntu.SystemSettings.TimeDate 1.1 |
729 | import Unity.Test 0.1 as UT |
730 | import Wizard 0.1 |
731 | +import Unity.InputInfo 0.1 |
732 | import "../../../qml/Wizard" |
733 | |
734 | Item { |
735 | @@ -41,7 +42,12 @@ |
736 | id: wizard |
737 | anchors.fill: parent |
738 | |
739 | + Component.onCompleted: { |
740 | + MockInputDeviceBackend.addMockDevice("/test", InputInfo.Keyboard); |
741 | + } |
742 | + |
743 | Component.onDestruction: { |
744 | + MockInputDeviceBackend.removeDevice("/test"); |
745 | wizardLoader.itemDestroyed = true; |
746 | } |
747 | } |
748 | @@ -74,6 +80,12 @@ |
749 | signalName: "timeZoneChangedCalled" |
750 | } |
751 | |
752 | + SignalSpy { |
753 | + id: kbdLayoutSpy |
754 | + target: AccountsService |
755 | + signalName: "keymapsChanged" |
756 | + } |
757 | + |
758 | function setup() { |
759 | AccountsService.hereEnabled = false; |
760 | AccountsService.hereLicensePath = Qt.resolvedUrl("licenses"); |
761 | @@ -88,6 +100,7 @@ |
762 | activateLocationSpy.clear(); |
763 | activateGPSSpy.clear(); |
764 | timezoneSpy.clear(); |
765 | + kbdLayoutSpy.clear(); |
766 | |
767 | ActionData.data = { |
768 | "location-detection-enabled": { |
769 | @@ -176,6 +189,10 @@ |
770 | tap(findChild(page, "forwardButton")); |
771 | } |
772 | |
773 | + page = waitForPage("keyboardPage"); |
774 | + if (name === page.objectName) return page; |
775 | + tap(findChild(page, "forwardButton")); |
776 | + |
777 | page = waitForPage("wifiPage"); |
778 | if (name === page.objectName) return page; |
779 | tap(findChild(page, "forwardButton")); |
780 | @@ -281,7 +298,7 @@ |
781 | |
782 | // Make sure that moving from sim page lands on wifi page |
783 | tap(findChild(page, "forwardButton")); |
784 | - waitForPage("wifiPage"); // thus skipping passwdPage |
785 | + waitForPage("keyboardPage"); // thus skipping passwdPage |
786 | } |
787 | |
788 | function verifyAnimationsNotRunning(page) { |
789 | @@ -480,5 +497,27 @@ |
790 | tap(findChild(page, "nameInput")); |
791 | typeString("foobar"); |
792 | } |
793 | + |
794 | + function test_keyboardPage() { |
795 | + var page = goToPage("keyboardPage"); |
796 | + var forwardButton = findChild(page, "forwardButton"); |
797 | + |
798 | + // change language |
799 | + var langSelector = findChild(page, "langSelector"); |
800 | + verify(langSelector); |
801 | + langSelector.selectedIndex = 1; // should be fr_FR |
802 | + print("Selected language:", page.selectedLanguage); |
803 | + |
804 | + // pick some layout |
805 | + var kbdDelegate = findChild(page, "kbdDelegate1"); |
806 | + verify(kbdDelegate); |
807 | + mouseClick(kbdDelegate); |
808 | + verify(kbdDelegate.isCurrent); |
809 | + print("Selected keymap:", page.selectedKeymap); |
810 | + |
811 | + // verify the keymapsChanged signal got fired |
812 | + tap(findChild(page, "forwardButton")); |
813 | + tryCompare(kbdLayoutSpy, "count", 1); |
814 | + } |
815 | } |
816 | } |
FAILED: Continuous integration, rev:2420 /unity8- jenkins. ubuntu. com/job/ lp-unity8- ci/1815/ /unity8- jenkins. ubuntu. com/job/ build/2372/ console /unity8- jenkins. ubuntu. com/job/ build-0- fetch/2400 /unity8- jenkins. ubuntu. com/job/ build-1- sourcepkg/ release= vivid+overlay/ 2287 /unity8- jenkins. ubuntu. com/job/ build-1- sourcepkg/ release= xenial+ overlay/ 2287 /unity8- jenkins. ubuntu. com/job/ build-1- sourcepkg/ release= yakkety/ 2287 /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=amd64, release= vivid+overlay/ 2280/console /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=amd64, release= xenial+ overlay/ 2280/console /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=amd64, release= yakkety/ 2280/console /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=armhf, release= vivid+overlay/ 2280/console /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=armhf, release= xenial+ overlay/ 2280/console /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=armhf, release= yakkety/ 2280/console /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=i386, release= vivid+overlay/ 2280/console /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=i386, release= xenial+ overlay/ 2280/console /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=i386, release= yakkety/ 2280/console
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild: /unity8- jenkins. ubuntu. com/job/ lp-unity8- ci/1815/ rebuild
https:/