on 4dc2b58 help sign in
58 files changed+2687−3429
since splitsnapshots
unifiedsplit
README.md+3−3 clickable.json+158 clickable.yaml−153 plugins/Lights/CMakeLists.txt+1−5 plugins/Lights/HfdLights.cpp−62 plugins/Lights/HfdLights.h−42 plugins/Lights/LegacyLights.cpp−91 plugins/Lights/LegacyLights.h−45 plugins/Lights/Lights.cpp+65 plugins/Lights/Lights.h+8−4 plugins/Lights/plugin.cpp+1−20 plugins/WindowManager/TopLevelWindowModel.cpp+3−4 po/am.po+6−4 po/ar.po+25−20 po/cs.po+12−12 po/el.po+6−4 po/es.po+22−22 po/he.po+11−9 po/hu.po+11−9 po/it.po+6−6 po/km.po+14−11 po/nb.po+4−2 po/nl.po+3−3 po/pt.po+4−4 po/sc.po+6−6 po/sv.po+52−25 po/th.po+20−20 po/ur.po+28−31 qml/Greeter/ButtonPrompt.qml−51 qml/Greeter/CoverPage.qml+8−27 qml/Greeter/Greeter.qml+2−37 qml/Greeter/GreeterPrompt.qml+187−64 qml/Greeter/GreeterView.qml−534 qml/Greeter/LoginAreaContainer.qml+3−3 qml/Greeter/LoginList.qml+98−71 qml/Greeter/NarrowView.qml+274 qml/Greeter/PinPrompt.qml−184 qml/Greeter/PromptList.qml+18−21 qml/Greeter/SessionsList.qml+2−3 qml/Greeter/TextPrompt.qml−198 qml/Greeter/WideView.qml+210 qml/Launcher/BackgroundBlur.qml−81 qml/Launcher/Drawer.qml+2−8 qml/Launcher/Launcher.qml+1−23 qml/OrientedShell.qml−1 qml/Panel/Indicators/IndicatorItem.qml−6 qml/Rotation/RotationStates.qml+3 qml/Shell.qml+3−10 qml/Stage/ApplicationWindow.qml+3−12 src/ApplicationArguments.h−5 src/ShellApplication.cpp−6 tests/qmltests/CMakeLists.txt+2−1 tests/qmltests/Greeter/TestView.qml−7 tests/qmltests/Greeter/tst_GreeterView.qml−1366 tests/qmltests/Greeter/tst_NarrowView.qml+565 tests/qmltests/Greeter/tst_WideView.qml+781 tests/qmltests/tst_OrientedShell.qml+41−45 tests/qmltests/tst_Shell.qml+15−48
README.md+3−3
@@ -22,10 +22,10 @@ Now, clone this repository to your computer: `git clone https://github.com/ubpor
2222
2323Move into this directory: `cd unity8`
2424
25−Now you can use the full suite of tools provided by this repository's [clickable.yaml](clickable.yaml). For example:
25+Now you can use the full suite of tools provided by this repository's [clickable.json](clickable.json). For example:
2626
2727* `clickable ide qtcreator` will open QtCreator with this repository open as a project. You can edit and build the project this way, but running the tests will be a bit difficult.
28−* `clickable build --libs --verbose` will build Lomiri in the same environment as it would receive in Ubuntu Touch.
29−* `clickable test --libs` will run the entire Lomiri test suite. This test suite includes graphical tests (which are run on a virtual, invisible desktop) and non-graphical unit tests. These tests make sure Lomiri functions as prescribed and prevents new bugs from being added. You should run them before you create a PR on this repository. This command takes about 7 minutes on an Intel i7-8550U, so plan your time accordingly and use the next two options to reduce the number of times you need to run the whole test suite...
28+* `clickable build-libs --debug` will build Lomiri in the same environment as it would receive in Ubuntu Touch.
29+* `clickable test-libs` will run the entire Lomiri test suite. This test suite includes graphical tests (which are run on a virtual, invisible desktop) and non-graphical unit tests. These tests make sure Lomiri functions as prescribed and prevents new bugs from being added. You should run them before you create a PR on this repository. This command takes about 7 minutes on an Intel i7-8550U, so plan your time accordingly and use the next two options to reduce the number of times you need to run the whole test suite...
3030* `clickable ide 'cd build/x86_64-linux-gnu/unity8/ && make tryShell'` and similar commands that replace the `Shell` with another test target defined in [tests/qmltests/CMakeLists.txt](tests/qmltests/CMakeLists.txt) allow trying out some Lomiri components in a mock environment. You can use this to try out your changes to Lomiri as you develop.
3131* `clickable ide 'cd build/x86_64-linux-gnu/unity8/ && make xvfbtestShell'` and similar commands that replace the `Shell` with another test target defined in [tests/qmltests/CMakeLists.txt](tests/qmltests/CMakeLists.txt) allow running a single TestCase. You can remove the `xvfb` part of the make target to run the test case graphically, which might help you see what is causing the test to fail.
clickable.jsonnew file+158
@@ -0,0 +1,158 @@
1+{
2+ "builder": "custom",
3+ "build": "echo Run \\'clickable build-libs --debug\\' to build && false",
4+ "restrict_arch": "amd64",
5+ "libraries": {
6+ "unity8": {
7+ "builder": "cmake",
8+ "src_dir": "${ROOT}",
9+ "test": "make xvfballtests -j${NUM_PROCS}",
10+ "dependencies_host": [
11+ "android-headers",
12+ "cmake",
13+ "cmake-extras",
14+ "dbus-test-runner",
15+ "debhelper",
16+ "dh-apparmor",
17+ "doxyqml",
18+ "g++:native",
19+ "gdb",
20+ "graphviz",
21+ "gsettings-ubuntu-schemas",
22+ "libandroid-properties-dev",
23+ "libconnectivity-qt1-dev",
24+ "libevdev-dev",
25+ "libgeonames-dev",
26+ "libgl1-mesa-dev",
27+ "libgl1-mesa-dri",
28+ "libgles2-mesa-dev",
29+ "libglib2.0-dev",
30+ "libgnome-desktop-3-12",
31+ "libgnome-desktop-3-dev",
32+ "libgsettings-qt-dev",
33+ "libhardware-dev",
34+ "liblightdm-qt5-3-dev",
35+ "libpam0g-dev",
36+ "libpulse-dev",
37+ "libqmenumodel-dev",
38+ "libqt5sql5-sqlite",
39+ "libqt5svg5-dev",
40+ "libqt5xmlpatterns5-dev",
41+ "libqtdbusmock1-dev",
42+ "libqtdbustest1-dev",
43+ "libsystemsettings-dev",
44+ "libubuntu-app-launch2-dev",
45+ "libubuntu-download-manager-common-dev",
46+ "libubuntugestures5-dev",
47+ "libubuntugestures5-private-dev",
48+ "libudev-dev",
49+ "libudm-common-dev",
50+ "libunity-api-dev",
51+ "libusermetricsoutput1-dev",
52+ "libx11-dev",
53+ "libxcb1-dev",
54+ "libxi-dev",
55+ "pkg-config",
56+ "python3-all:any",
57+ "python3-setuptools:native",
58+ "qml-module-qt-labs-folderlistmodel",
59+ "qml-module-qt-labs-settings",
60+ "qml-module-qtmultimedia",
61+ "qml-module-qtqml-statemachine",
62+ "qml-module-qtquick-layouts",
63+ "qml-module-qtquick-xmllistmodel",
64+ "qml-module-qtquick2",
65+ "qml-module-qtsysteminfo",
66+ "qml-module-qttest",
67+ "qml-module-ubuntu-components",
68+ "qml-module-ubuntu-layouts",
69+ "qml-module-ubuntu-settings-components",
70+ "qml-module-ubuntu-test",
71+ "qtbase5-dev",
72+ "qtbase5-dev-tools",
73+ "qtbase5-private-dev",
74+ "qtdbustest-runner",
75+ "qtdeclarative5-dev",
76+ "qtdeclarative5-dev-tools",
77+ "qtdeclarative5-private-dev",
78+ "qtdeclarative5-ubuntu-content1",
79+ "ttf-ubuntu-font-family",
80+ "ubports-wallpapers",
81+ "xvfb"
82+ ]
83+ }
84+ },
85+ "dependencies_target": [
86+ "android-headers",
87+ "cmake",
88+ "cmake-extras",
89+ "dbus-test-runner",
90+ "debhelper",
91+ "dh-apparmor",
92+ "doxyqml",
93+ "g++:native",
94+ "gdb",
95+ "graphviz",
96+ "gsettings-ubuntu-schemas",
97+ "libandroid-properties-dev",
98+ "libconnectivity-qt1-dev",
99+ "libevdev-dev",
100+ "libgeonames-dev",
101+ "libgl1-mesa-dev",
102+ "libgl1-mesa-dri",
103+ "libgles2-mesa-dev",
104+ "libglib2.0-dev",
105+ "libgnome-desktop-3-12",
106+ "libgnome-desktop-3-dev",
107+ "libgsettings-qt-dev",
108+ "libhardware-dev",
109+ "liblightdm-qt5-3-dev",
110+ "libpam0g-dev",
111+ "libpulse-dev",
112+ "libqmenumodel-dev",
113+ "libqt5sql5-sqlite",
114+ "libqt5svg5-dev",
115+ "libqt5xmlpatterns5-dev",
116+ "libqtdbusmock1-dev",
117+ "libqtdbustest1-dev",
118+ "libsystemsettings-dev",
119+ "libubuntu-app-launch2-dev",
120+ "libubuntu-download-manager-common-dev",
121+ "libubuntugestures5-dev",
122+ "libubuntugestures5-private-dev",
123+ "libudev-dev",
124+ "libudm-common-dev",
125+ "libunity-api-dev",
126+ "libusermetricsoutput1-dev",
127+ "libx11-dev",
128+ "libxcb1-dev",
129+ "libxi-dev",
130+ "pkg-config",
131+ "python3-all:any",
132+ "python3-setuptools:native",
133+ "qml-module-qt-labs-folderlistmodel",
134+ "qml-module-qt-labs-settings",
135+ "qml-module-qtmultimedia",
136+ "qml-module-qtqml-statemachine",
137+ "qml-module-qtquick-layouts",
138+ "qml-module-qtquick-xmllistmodel",
139+ "qml-module-qtquick2",
140+ "qml-module-qtsysteminfo",
141+ "qml-module-qttest",
142+ "qml-module-ubuntu-components",
143+ "qml-module-ubuntu-layouts",
144+ "qml-module-ubuntu-settings-components",
145+ "qml-module-ubuntu-test",
146+ "qtbase5-dev",
147+ "qtbase5-dev-tools",
148+ "qtbase5-private-dev",
149+ "qtdbustest-runner",
150+ "qtdeclarative5-dev",
151+ "qtdeclarative5-dev-tools",
152+ "qtdeclarative5-private-dev",
153+ "qtdeclarative5-ubuntu-content1",
154+ "ttf-ubuntu-font-family",
155+ "ubports-wallpapers",
156+ "xvfb"
157+ ]
158+ }
clickable.yamldeleted−153
@@ -1,153 +0,0 @@
1−clickable_minimum_required: '7'
2−builder: custom
3−build: echo Run \'clickable build --libs --verbose\' to build && false
4−restrict_arch: amd64
5−libraries:
6− unity8:
7− builder: cmake
8− src_dir: "${ROOT}"
9− test: make xvfballtests -j${NUM_PROCS}
10− dependencies_host:
11− - android-headers
12− - cmake
13− - cmake-extras
14− - dbus-test-runner
15− - debhelper
16− - dh-apparmor
17− - doxyqml
18− - g++:native
19− - gdb
20− - graphviz
21− - gsettings-ubuntu-schemas
22− - libandroid-properties-dev
23− - libconnectivity-qt1-dev
24− - libevdev-dev
25− - libgeonames-dev
26− - libgl1-mesa-dev
27− - libgl1-mesa-dri
28− - libgles2-mesa-dev
29− - libglib2.0-dev
30− - libgnome-desktop-3-12
31− - libgnome-desktop-3-dev
32− - libgsettings-qt-dev
33− - libhardware-dev
34− - liblightdm-qt5-3-dev
35− - libpam0g-dev
36− - libpulse-dev
37− - libqmenumodel-dev
38− - libqt5sql5-sqlite
39− - libqt5svg5-dev
40− - libqt5xmlpatterns5-dev
41− - libqtdbusmock1-dev
42− - libqtdbustest1-dev
43− - libsystemsettings-dev
44− - libubuntu-app-launch2-dev
45− - libubuntu-download-manager-common-dev
46− - libubuntugestures5-dev
47− - libubuntugestures5-private-dev
48− - libudev-dev
49− - libudm-common-dev
50− - libunity-api-dev
51− - libusermetricsoutput1-dev
52− - libx11-dev
53− - libxcb1-dev
54− - libxi-dev
55− - pkg-config
56− - python3-all:any
57− - python3-setuptools:native
58− - qml-module-qt-labs-folderlistmodel
59− - qml-module-qt-labs-settings
60− - qml-module-qtmultimedia
61− - qml-module-qtqml-statemachine
62− - qml-module-qtquick-layouts
63− - qml-module-qtquick-xmllistmodel
64− - qml-module-qtquick2
65− - qml-module-qtsysteminfo
66− - qml-module-qttest
67− - qml-module-ubuntu-components
68− - qml-module-ubuntu-layouts
69− - qml-module-ubuntu-settings-components
70− - qml-module-ubuntu-test
71− - qtbase5-dev
72− - qtbase5-dev-tools
73− - qtbase5-private-dev
74− - qtdbustest-runner
75− - qtdeclarative5-dev
76− - qtdeclarative5-dev-tools
77− - qtdeclarative5-private-dev
78− - qtdeclarative5-ubuntu-content1
79− - ttf-ubuntu-font-family
80− - ubports-wallpapers
81− - xvfb
82−dependencies_target:
83− - android-headers
84− - cmake
85− - cmake-extras
86− - dbus-test-runner
87− - debhelper
88− - dh-apparmor
89− - doxyqml
90− - g++:native
91− - gdb
92− - graphviz
93− - gsettings-ubuntu-schemas
94− - libandroid-properties-dev
95− - libconnectivity-qt1-dev
96− - libevdev-dev
97− - libgeonames-dev
98− - libgl1-mesa-dev
99− - libgl1-mesa-dri
100− - libgles2-mesa-dev
101− - libglib2.0-dev
102− - libgnome-desktop-3-12
103− - libgnome-desktop-3-dev
104− - libgsettings-qt-dev
105− - libhardware-dev
106− - liblightdm-qt5-3-dev
107− - libpam0g-dev
108− - libpulse-dev
109− - libqmenumodel-dev
110− - libqt5sql5-sqlite
111− - libqt5svg5-dev
112− - libqt5xmlpatterns5-dev
113− - libqtdbusmock1-dev
114− - libqtdbustest1-dev
115− - libsystemsettings-dev
116− - libubuntu-app-launch2-dev
117− - libubuntu-download-manager-common-dev
118− - libubuntugestures5-dev
119− - libubuntugestures5-private-dev
120− - libudev-dev
121− - libudm-common-dev
122− - libunity-api-dev
123− - libusermetricsoutput1-dev
124− - libx11-dev
125− - libxcb1-dev
126− - libxi-dev
127− - pkg-config
128− - python3-all:any
129− - python3-setuptools:native
130− - qml-module-qt-labs-folderlistmodel
131− - qml-module-qt-labs-settings
132− - qml-module-qtmultimedia
133− - qml-module-qtqml-statemachine
134− - qml-module-qtquick-layouts
135− - qml-module-qtquick-xmllistmodel
136− - qml-module-qtquick2
137− - qml-module-qtsysteminfo
138− - qml-module-qttest
139− - qml-module-ubuntu-components
140− - qml-module-ubuntu-layouts
141− - qml-module-ubuntu-settings-components
142− - qml-module-ubuntu-test
143− - qtbase5-dev
144− - qtbase5-dev-tools
145− - qtbase5-private-dev
146− - qtdbustest-runner
147− - qtdeclarative5-dev
148− - qtdeclarative5-dev-tools
149− - qtdeclarative5-private-dev
150− - qtdeclarative5-ubuntu-content1
151− - ttf-ubuntu-font-family
152− - ubports-wallpapers
153− - xvfb
plugins/Lights/CMakeLists.txt+1−5
@@ -1,6 +1,5 @@
11pkg_check_modules(ANDROID_HEADERS REQUIRED android-headers)
22pkg_check_modules(ANDROID_HARDWARE REQUIRED libhardware)
3−pkg_check_modules(ANDROID_PROPERTIES REQUIRED libandroid-properties)
43
54include_directories(
65 SYSTEM
@@ -10,14 +9,11 @@ include_directories(
109add_library(Lights-qml MODULE
1110 plugin.cpp
1211 Lights.cpp
13− LegacyLights.cpp
14− HfdLights.cpp
1512 )
1613
1714target_link_libraries(Lights-qml
18− Qt5::Qml Qt5::Gui Qt5::DBus
15+ Qt5::Qml Qt5::Gui
1916 ${ANDROID_HARDWARE_LIBRARIES}
20− ${ANDROID_PROPERTIES_LIBRARIES}
2117 )
2218
2319add_unity8_plugin(Lights 0.1 Lights TARGETS Lights-qml)
plugins/Lights/HfdLights.cppdeleted−62
@@ -1,62 +0,0 @@
1−/*
2− * Copyright (C) 2021 UBports Foundation.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Author: Florian Leeber <florian@ubports.com>
17− */
18−
19−#include "HfdLights.h"
20−#include <QtCore/QDebug>
21−#include <QDBusArgument>
22−#include <QDBusConnection>
23−#include <iostream>
24−
25−#define HFD_SERVICE_NAME "com.lomiri.hfd"
26−#define HFD_SERVICE_PATH "/com/lomiri/hfd"
27−#define HFD_SERVICE_INTERFACE "com.lomiri.hfd.Leds"
28−
29−HfdLights::HfdLights(QObject* parent)
30− : Lights(parent)
31−{
32− m_hfdInterface = new QDBusInterface(
33− QStringLiteral(HFD_SERVICE_NAME),
34− QStringLiteral(HFD_SERVICE_PATH),
35− QStringLiteral(HFD_SERVICE_INTERFACE),
36− QDBusConnection::systemBus(),
37− this
38− );
39−}
40−
41−bool HfdLights::init()
42−{
43− // hfd does not need any initalization to be used
44− return true;
45−}
46−
47−void HfdLights::turnOn()
48−{
49− m_hfdInterface->call(QStringLiteral("setState"), 0);
50− m_hfdInterface->call(QStringLiteral("setColor"), (quint32)m_color.rgba());
51− m_hfdInterface->call(QStringLiteral("setOnMs"), m_onMs);
52− m_hfdInterface->call(QStringLiteral("setOffMs"), m_offMs);
53− m_hfdInterface->call(QStringLiteral("setState"), 1);
54−}
55−
56−void HfdLights::turnOff()
57−{
58− m_hfdInterface->call(QStringLiteral("setState"), 0);
59− m_hfdInterface->call(QStringLiteral("setColor"), (quint32)0);
60− m_hfdInterface->call(QStringLiteral("setOnMs"), 0);
61− m_hfdInterface->call(QStringLiteral("setOffMs"), 0);
62−}
plugins/Lights/HfdLights.hdeleted−42
@@ -1,42 +0,0 @@
1−/*
2− * Copyright (C) 2021 UBports Foundation.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Author: Florian Leeber <florian@ubports.com>
17− */
18−
19−#ifndef UNITY_HFDLIGHTS_H
20−#define UNITY_HFDLIGHTS_H
21−
22−#include <QtCore/QObject>
23−#include <QDBusInterface>
24−#include "Lights.h"
25−
26−class HfdLights: public Lights
27−{
28− Q_OBJECT
29−
30−public:
31− explicit HfdLights(QObject *parent = 0);
32−
33−protected:
34− bool init() override;
35− void turnOff() override;
36− void turnOn() override;
37−
38−private:
39− QDBusInterface* m_hfdInterface;
40−};
41−
42−#endif
plugins/Lights/LegacyLights.cppdeleted−91
@@ -1,91 +0,0 @@
1−/*
2− * Copyright (C) 2014 Canonical, Ltd.
3− * Copyright (C) 2021 UBports Foundation.
4− *
5− * This program is free software; you can redistribute it and/or modify
6− * it under the terms of the GNU General Public License as published by
7− * the Free Software Foundation; version 3.
8− *
9− * This program is distributed in the hope that it will be useful,
10− * but WITHOUT ANY WARRANTY; without even the implied warranty of
11− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12− * GNU General Public License for more details.
13− *
14− * You should have received a copy of the GNU General Public License
15− * along with this program. If not, see <http://www.gnu.org/licenses/>.
16− *
17− * Author: Renato Araujo Oliveira Filho <renato.filho@canonical.com>
18− */
19−
20−#include "LegacyLights.h"
21−#include <QtCore/QDebug>
22−
23−extern "C" {
24−#include "android-hardware.h"
25−#include <string.h>
26−}
27−
28−LegacyLights::~LegacyLights()
29−{
30− if (m_lightDevice) {
31− hw_device_t* device = (hw_device_t*) m_lightDevice;
32− device->close(device);
33− }
34−}
35−
36−bool LegacyLights::init()
37−{
38− if (m_lightDevice) {
39− return true;
40− }
41−
42− int err;
43− hw_module_t* module;
44−
45− err = hw_get_module(LIGHTS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
46− if (err == 0) {
47− hw_device_t* device;
48− err = module->methods->open(module, LIGHT_ID_NOTIFICATIONS, &device);
49− if (err == 0) {
50− m_lightDevice = (light_device_t*)device;
51− turnOff();
52− return true;
53− } else {
54− qWarning() << "Failed to access notification lights";
55− }
56− } else {
57− qWarning() << "Failed to initialize lights hardware.";
58− }
59− return false;
60−}
61−
62−void LegacyLights::turnOn()
63−{
64− // pulse
65− light_state_t state;
66− memset(&state, 0, sizeof(light_state_t));
67− state.color = m_color.rgba();
68− state.flashMode = LIGHT_FLASH_TIMED;
69− state.flashOnMS = m_onMs;
70− state.flashOffMS = m_offMs;
71− state.brightnessMode = BRIGHTNESS_MODE_USER;
72−
73− if (m_lightDevice->set_light(m_lightDevice, &state) != 0) {
74− qWarning() << "Failed to turn the light off";
75− }
76−}
77−
78−void LegacyLights::turnOff()
79−{
80− light_state_t state;
81− memset(&state, 0, sizeof(light_state_t));
82− state.color = 0x00000000;
83− state.flashMode = LIGHT_FLASH_NONE;
84− state.flashOnMS = 0;
85− state.flashOffMS = 0;
86− state.brightnessMode = 0;
87−
88− if (m_lightDevice->set_light(m_lightDevice, &state) != 0) {
89− qWarning() << "Failed to turn the light off";
90− }
91−}
plugins/Lights/LegacyLights.hdeleted−45
@@ -1,45 +0,0 @@
1−/*
2− * Copyright (C) 2014 Canonical, Ltd.
3− * Copyright (C) 2021 UBports Foundation.
4− *
5− * This program is free software; you can redistribute it and/or modify
6− * it under the terms of the GNU General Public License as published by
7− * the Free Software Foundation; version 3.
8− *
9− * This program is distributed in the hope that it will be useful,
10− * but WITHOUT ANY WARRANTY; without even the implied warranty of
11− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12− * GNU General Public License for more details.
13− *
14− * You should have received a copy of the GNU General Public License
15− * along with this program. If not, see <http://www.gnu.org/licenses/>.
16− *
17− * Author: Renato Araujo Oliveira Filho <renato.filho@canonical.com>
18− */
19−
20−#ifndef UNITY_LEGACYLIGHTS_H
21−#define UNITY_LEGACYLIGHTS_H
22−
23−#include <QtCore/QObject>
24−#include "Lights.h"
25−
26−struct light_device_t;
27−
28−class LegacyLights: public Lights
29−{
30− Q_OBJECT
31−
32−public:
33− ~LegacyLights();
34−
35−protected:
36− bool init() override;
37− void turnOff() override;
38− void turnOn() override;
39−
40−private:
41− light_device_t* m_lightDevice;
42−
43−};
44−
45−#endif
plugins/Lights/Lights.cpp+65
@@ -26,12 +26,20 @@ extern "C" {
2626
2727Lights::Lights(QObject* parent)
2828 : QObject(parent),
29+ m_lightDevice(0),
2930 m_color("blue"),
3031 m_state(Lights::Off),
3132 m_onMs(1000),
3233 m_offMs(3000)
3334{
35+}
3436
37+Lights::~Lights()
38+{
39+ if (m_lightDevice) {
40+ hw_device_t* device = (hw_device_t*) m_lightDevice;
41+ device->close(device);
42+ }
3543}
3644
3745void Lights::setState(Lights::State newState)
@@ -99,3 +107,60 @@ void Lights::setOffMillisec(int offMs)
99107 // FIXME: update the property if the light is already on
100108 }
101109}
110+
111+bool Lights::init()
112+{
113+ if (m_lightDevice) {
114+ return true;
115+ }
116+
117+ int err;
118+ hw_module_t* module;
119+
120+ err = hw_get_module(LIGHTS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
121+ if (err == 0) {
122+ hw_device_t* device;
123+ err = module->methods->open(module, LIGHT_ID_NOTIFICATIONS, &device);
124+ if (err == 0) {
125+ m_lightDevice = (light_device_t*)device;
126+ turnOff();
127+ return true;
128+ } else {
129+ qWarning() << "Failed to access notification lights";
130+ }
131+ } else {
132+ qWarning() << "Failed to initialize lights hardware.";
133+ }
134+ return false;
135+}
136+
137+void Lights::turnOn()
138+{
139+ // pulse
140+ light_state_t state;
141+ memset(&state, 0, sizeof(light_state_t));
142+ state.color = m_color.rgba();
143+ state.flashMode = LIGHT_FLASH_TIMED;
144+ state.flashOnMS = m_onMs;
145+ state.flashOffMS = m_offMs;
146+ state.brightnessMode = BRIGHTNESS_MODE_USER;
147+
148+ if (m_lightDevice->set_light(m_lightDevice, &state) != 0) {
149+ qWarning() << "Failed to turn the light off";
150+ }
151+}
152+
153+void Lights::turnOff()
154+{
155+ light_state_t state;
156+ memset(&state, 0, sizeof(light_state_t));
157+ state.color = 0x00000000;
158+ state.flashMode = LIGHT_FLASH_NONE;
159+ state.flashOnMS = 0;
160+ state.flashOffMS = 0;
161+ state.brightnessMode = 0;
162+
163+ if (m_lightDevice->set_light(m_lightDevice, &state) != 0) {
164+ qWarning() << "Failed to turn the light off";
165+ }
166+}
plugins/Lights/Lights.h+8−4
@@ -22,6 +22,8 @@
2222#include <QtCore/QObject>
2323#include <QtGui/QColor>
2424
25+struct light_device_t;
26+
2527class Lights: public QObject
2628{
2729 Q_OBJECT
@@ -38,6 +40,7 @@ public:
3840 Q_ENUM(State)
3941
4042 explicit Lights(QObject *parent = 0);
43+ ~Lights();
4144
4245 void setState(State newState);
4346 State state() const;
@@ -57,15 +60,16 @@ Q_SIGNALS:
5760 void onMillisecChanged(int onMs);
5861 void offMillisecChanged(int offMs);
5962
60−protected:
63+private:
64+ light_device_t* m_lightDevice;
6165 QColor m_color;
6266 State m_state;
6367 int m_onMs;
6468 int m_offMs;
6569
66− virtual bool init() = 0;
67− virtual void turnOff() = 0;
68− virtual void turnOn() = 0;
70+ bool init();
71+ void turnOff();
72+ void turnOn();
6973};
7074
7175#endif
plugins/Lights/plugin.cpp+1−20
@@ -18,11 +18,6 @@
1818
1919#include "plugin.h"
2020#include "Lights.h"
21−#include "HfdLights.h"
22−#include "LegacyLights.h"
23−
24−// libandroid-properties
25−#include <hybris/properties/properties.h>
2621
2722#include <QtQml/qqml.h>
2823
@@ -30,21 +25,7 @@ static QObject *lights_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
3025{
3126 Q_UNUSED(engine)
3227 Q_UNUSED(scriptEngine)
33−
34− bool use_hfd = false;
35− char buffer[PROP_VALUE_MAX];
36− auto result = property_get("ro.build.version.sdk", buffer, "0");
37− if (result) {
38− auto sdkVersion = QString(buffer).toInt();
39− if (sdkVersion >= 27 || sdkVersion == 0) {
40− use_hfd = true;
41− }
42− }
43− if (use_hfd) {
44− return new HfdLights();
45− } else {
46− return new LegacyLights();
47− }
28+ return new Lights();
4829}
4930
5031void LightsPlugin::registerTypes(const char *uri)
plugins/WindowManager/TopLevelWindowModel.cpp+3−4
@@ -317,11 +317,10 @@ void TopLevelWindowModel::onSurfaceDied(unityapi::MirSurfaceInterface *surface)
317317
318318 auto application = m_windowModel[i].application;
319319
320− DEBUG_MSG << " application->name()=" << application->name()
321− << " application->state()=" << application->state();
320+ // can't be starting if it already has a surface
321+ Q_ASSERT(application->state() != unityapi::ApplicationInfoInterface::Starting);
322322
323− if (application->state() == unityapi::ApplicationInfoInterface::Running
324− || application->state() == unityapi::ApplicationInfoInterface::Starting) {
323+ if (application->state() == unityapi::ApplicationInfoInterface::Running) {
325324 m_windowModel[i].removeOnceSurfaceDestroyed = true;
326325 } else {
327326 // assume it got killed by the out-of-memory daemon.
po/am.po+6−4
@@ -8,10 +8,10 @@ msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: \n"
1010"POT-Creation-Date: 2021-01-29 11:42-0600\n"
11−"PO-Revision-Date: 2021-08-29 20:18+0000\n"
11+"PO-Revision-Date: 2021-01-31 01:03+0000\n"
1212"Last-Translator: Samson <sambelet@yahoo.com>\n"
13−"Language-Team: Amharic <https://translate.ubports.com/projects/ubports/"
14−"lomiri/am/>\n"
13+"Language-Team: Amharic <https://translate.ubports.com/projects/unity8/unity8/"
14+"am/>\n"
1515"Language: am\n"
1616"MIME-Version: 1.0\n"
1717"Content-Type: text/plain; charset=UTF-8\n"
@@ -131,6 +131,8 @@ msgid "Restart"
131131msgstr "እንደገና ማስጀመሪያ"
132132
133133#: qml/Components/Dialogs.qml:276
134+#, fuzzy
135+#| msgid "Takes a screenshot."
134136msgid "Screenshot"
135137msgstr "መመልከቻ ፎቶ ማንሻ"
136138
@@ -140,7 +142,7 @@ msgstr "የ ፊደል ገበታ አቋራጮች"
140142
141143#: qml/Components/KeyboardShortcutsOverlay.qml:55
142144msgid "Lomiri"
143−msgstr "Lomiri"
145+msgstr ""
144146
145147#: qml/Components/KeyboardShortcutsOverlay.qml:62
146148msgid "PrtScr"
po/ar.po+25−20
@@ -8,9 +8,9 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: \n"
1010"POT-Creation-Date: 2021-01-29 11:42-0600\n"
11−"PO-Revision-Date: 2021-10-12 07:49+0000\n"
12−"Last-Translator: HEROBRINE7GAMER <tapermeerkat77@gmail.com>\n"
13−"Language-Team: Arabic <https://translate.ubports.com/projects/ubports/lomiri/"
11+"PO-Revision-Date: 2020-12-07 19:09+0000\n"
12+"Last-Translator: abidin <abidin24@tutanota.com>\n"
13+"Language-Team: Arabic <https://translate.ubports.com/projects/unity8/unity8/"
1414"ar/>\n"
1515"Language: ar\n"
1616"MIME-Version: 1.0\n"
@@ -39,7 +39,7 @@ msgstr "كلمة المرور غير صحيحة، الرجاء المحاولة
3939
4040#: plugins/LightDM/Greeter.cpp:270
4141msgid "Log In"
42−msgstr "سجل الدخول"
42+msgstr "لِج"
4343
4444#: plugins/LightDM/Greeter.cpp:270
4545msgid "Retry"
@@ -47,7 +47,7 @@ msgstr "حاول مجدداً"
4747
4848#: plugins/LightDM/UsersModel.cpp:157
4949msgid "Login"
50−msgstr "سجل الدخول"
50+msgstr "لِج"
5151
5252#: plugins/LightDM/UsersModel.cpp:165
5353msgid "Guest Session"
@@ -73,7 +73,7 @@ msgstr "اخرج"
7373
7474#: qml/Components/Dialogs.qml:177
7575msgid "Are you sure you want to log out?"
76−msgstr "أمتأكد أنك تريد تسجيل الخروج ؟"
76+msgstr "أمتأكد أنك تريد الخروج ؟"
7777
7878#: qml/Components/Dialogs.qml:180
7979msgctxt "Button: Lock the system"
@@ -132,8 +132,10 @@ msgid "Restart"
132132msgstr "أعد التشغيل"
133133
134134#: qml/Components/Dialogs.qml:276
135+#, fuzzy
136+#| msgid "Takes a screenshot."
135137msgid "Screenshot"
136−msgstr "لقطة شاشة"
138+msgstr "خُذ لقطة للشاشة."
137139
138140#: qml/Components/KeyboardShortcutsOverlay.qml:41
139141msgid "Keyboard Shortcuts"
@@ -141,7 +143,7 @@ msgstr "اختصارات لوحة المفاتيح"
141143
142144#: qml/Components/KeyboardShortcutsOverlay.qml:55
143145msgid "Lomiri"
144−msgstr "لوميري"
146+msgstr ""
145147
146148#: qml/Components/KeyboardShortcutsOverlay.qml:62
147149msgid "PrtScr"
@@ -185,7 +187,7 @@ msgstr "شغِل الطرفية."
185187
186188#: qml/Components/KeyboardShortcutsOverlay.qml:131
187189msgid "Launcher"
188−msgstr "مطلق"
190+msgstr "مُطْلِق"
189191
190192#: qml/Components/KeyboardShortcutsOverlay.qml:138
191193msgid "Super (Hold)"
@@ -200,8 +202,9 @@ msgid "Alt + F1"
200202msgstr "Alt + F1"
201203
202204#: qml/Components/KeyboardShortcutsOverlay.qml:156
205+#, fuzzy
203206msgid "Opens launcher keyboard navigation mode."
204−msgstr "فتح وضع التنقل بلوحة المفاتيح المشغلة."
207+msgstr "افتح وضع التصفح باستخدام لوحة مفاتيح المطلق."
205208
206209#: qml/Components/KeyboardShortcutsOverlay.qml:164
207210msgid "Super + Tab"
@@ -348,6 +351,7 @@ msgid "Close all"
348351msgstr "أغلق الكل"
349352
350353#: qml/Components/SharingPicker.qml:54
354+#, fuzzy
351355msgid "Preview Share Item"
352356msgstr "معاينة الملف المشارك"
353357
@@ -420,7 +424,7 @@ msgstr "طوارئ"
420424
421425#: qml/Greeter/PromptList.qml:126
422426msgid "Passphrase"
423−msgstr "كلمة السر"
427+msgstr "العبارة السرية"
424428
425429#: qml/Greeter/PromptList.qml:126
426430msgid "Passcode"
@@ -436,11 +440,11 @@ msgstr "ابحث…"
436440
437441#: qml/Launcher/PullToRefreshScopeStyle.qml:56
438442msgid "Pull to refresh…"
439−msgstr "اسحب للتحديث…"
443+msgstr "اسحب للتحديث..."
440444
441445#: qml/Launcher/PullToRefreshScopeStyle.qml:61
442446msgid "Release to refresh…"
443−msgstr "حرّر للتحديث…"
447+msgstr "حرّر للتحديث..."
444448
445449#: qml/Notifications/NotificationMenuItemFactory.qml:124
446450msgid "Show password"
@@ -473,9 +477,9 @@ msgstr[5] "%1 ساعة"
473477#, qt-format
474478msgid "%1 minute"
475479msgid_plural "%1 minutes"
476−msgstr[0] "%1 دقيقة"
477−msgstr[1] "دقيقة واحدة"
478−msgstr[2] "دقيقتان"
480+msgstr[0] "أقل من دقيقة (%1)"
481+msgstr[1] "دقيقة واحدة (%1)"
482+msgstr[2] "دقيقتان (%1)"
479483msgstr[3] "%1 دقائق"
480484msgstr[4] "%1 دقيقة"
481485msgstr[5] "%1 دقيقة"
@@ -537,6 +541,7 @@ msgid "Reply"
537541msgstr "رد"
538542
539543#: qml/Panel/Indicators/MessageMenuItemFactory.qml:209
544+#, fuzzy
540545msgctxt "Button: Call back on phone"
541546msgid "Call back"
542547msgstr "عاود الاتصال"
@@ -625,11 +630,11 @@ msgstr "تخطيط لوحة المفاتيح"
625630#: qml/Wizard/Pages/60-account.qml:72 qml/Wizard/Pages/79-system-update.qml:144
626631#: qml/Wizard/Pages/sim.qml:101
627632msgid "Skip"
628−msgstr "تخط"
633+msgstr "تخطى"
629634
630635#: qml/Wizard/Pages/30-wifi.qml:31
631636msgid "Connect to Wi‑Fi"
632−msgstr "اتصل بالإنترنت"
637+msgstr "اتصل بالواي فاي"
633638
634639#: qml/Wizard/Pages/30-wifi.qml:139
635640msgid "Connected"
@@ -730,7 +735,7 @@ msgstr "ابدأ"
730735#: qml/Wizard/Pages/passcode-confirm.qml:43
731736#: qml/Wizard/Pages/passcode-desktop.qml:104
732737msgid "Confirm passcode"
733−msgstr "تأكيد كلمة السر"
738+msgstr "تأكيد الرمز السري"
734739
735740#: qml/Wizard/Pages/passcode-confirm.qml:45
736741msgid "Incorrect passcode."
@@ -752,7 +757,7 @@ msgstr "أدخل 4 أرقام لإعداد الرمز السري"
752757#: qml/Wizard/Pages/passcode-desktop.qml:84
753758#: qml/Wizard/Pages/passcode-set.qml:54
754759msgid "Choose passcode"
755−msgstr "اختر كلمة السر"
760+msgstr "اختر الرمز السري"
756761
757762#: qml/Wizard/Pages/passcode-desktop.qml:139
758763msgid "Passcode too short"
po/cs.po+12−12
@@ -8,7 +8,7 @@ msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: \n"
1010"POT-Creation-Date: 2021-01-29 11:42-0600\n"
11−"PO-Revision-Date: 2021-12-05 18:44+0000\n"
11+"PO-Revision-Date: 2021-04-30 20:44+0000\n"
1212"Last-Translator: Milan Korecký <milan.korecky@gmail.com>\n"
1313"Language-Team: Czech <https://translate.ubports.com/projects/ubports/lomiri/"
1414"cs/>\n"
@@ -30,11 +30,11 @@ msgstr "Uživatelské jméno"
3030
3131#: plugins/LightDM/Greeter.cpp:258
3232msgid "Failed to authenticate"
33−msgstr "Chyba při ověřování"
33+msgstr "Chyba při ověřování se"
3434
3535#: plugins/LightDM/Greeter.cpp:260
3636msgid "Invalid password, please try again"
37−msgstr "Nesprávné heslo, zkuste to znovu prosím"
37+msgstr "Nesprávné heslo – zkuste to znovu prosím"
3838
3939#: plugins/LightDM/Greeter.cpp:270
4040msgid "Log In"
@@ -42,11 +42,11 @@ msgstr "Přihlásit se"
4242
4343#: plugins/LightDM/Greeter.cpp:270
4444msgid "Retry"
45−msgstr "Zkusit znovu"
45+msgstr "Znovu"
4646
4747#: plugins/LightDM/UsersModel.cpp:157
4848msgid "Login"
49−msgstr "Přihlášení"
49+msgstr "Zahájit relaci"
5050
5151#: plugins/LightDM/UsersModel.cpp:165
5252msgid "Guest Session"
@@ -55,7 +55,7 @@ msgstr "Sezení hosta"
5555#: plugins/Unity/Launcher/launcheritem.cpp:48
5656#: plugins/Unity/Launcher/launcheritem.cpp:122
5757msgid "Pin shortcut"
58−msgstr "Připnout odkaz"
58+msgstr "Připnout zástupce"
5959
6060#: plugins/Unity/Launcher/launcheritem.cpp:54
6161msgid "Quit"
@@ -63,7 +63,7 @@ msgstr "Ukončit"
6363
6464#: plugins/Unity/Launcher/launcheritem.cpp:122
6565msgid "Unpin shortcut"
66−msgstr "Zrušit připnutý odkaz"
66+msgstr "Zrušit připnutí zástupce"
6767
6868#: qml/Components/Dialogs.qml:176
6969msgctxt "Title: Lock/Log out dialog"
@@ -308,7 +308,7 @@ msgstr "Tísňové volání"
308308
309309#: qml/Components/Lockscreen.qml:245
310310msgid "OK"
311−msgstr "OK"
311+msgstr "Přijmout"
312312
313313#: qml/Components/MediaServices/VideoPlayerControls.qml:44
314314#, qt-format
@@ -481,8 +481,8 @@ msgstr[2] "%1 minuty"
481481msgid "%1 second"
482482msgid_plural "%1 seconds"
483483msgstr[0] "%1 sekunda"
484−msgstr[1] "%1 sekund"
485−msgstr[2] ""
484+msgstr[1] "%1 sekundy"
485+msgstr[2] "%1 sekund"
486486
487487#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1009
488488msgid "0 seconds"
@@ -512,7 +512,7 @@ msgstr "Zrušeno"
512512
513513#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1027
514514msgid "Finished"
515−msgstr "Hotovo"
515+msgstr "Dokončeno"
516516
517517#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1029
518518msgid "Failed, tap to retry"
@@ -585,7 +585,7 @@ msgstr "Jazyk"
585585#: qml/Wizard/Pages/passcode-desktop.qml:154
586586#: qml/Wizard/Pages/password-set.qml:153
587587msgid "Next"
588−msgstr "Další"
588+msgstr "Následující"
589589
590590#: qml/Wizard/Pages/10-welcome-update.qml:91
591591msgid "Welcome to "
po/el.po+6−4
@@ -8,9 +8,9 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: \n"
1010"POT-Creation-Date: 2021-01-29 11:42-0600\n"
11−"PO-Revision-Date: 2021-09-20 23:03+0000\n"
11+"PO-Revision-Date: 2020-08-19 00:03+0000\n"
1212"Last-Translator: Efstathios Iosifidis <iefstathios@gmail.com>\n"
13−"Language-Team: Greek <https://translate.ubports.com/projects/ubports/lomiri/"
13+"Language-Team: Greek <https://translate.ubports.com/projects/unity8/unity8/"
1414"el/>\n"
1515"Language: el\n"
1616"MIME-Version: 1.0\n"
@@ -131,8 +131,10 @@ msgid "Restart"
131131msgstr "Επανεκκίνηση"
132132
133133#: qml/Components/Dialogs.qml:276
134+#, fuzzy
135+#| msgid "Takes a screenshot."
134136msgid "Screenshot"
135−msgstr "Στιγμιότυπο οθόνης"
137+msgstr "Λαμβάνει ένα στιγμιότυπο οθόνης."
136138
137139#: qml/Components/KeyboardShortcutsOverlay.qml:41
138140msgid "Keyboard Shortcuts"
@@ -140,7 +142,7 @@ msgstr "Συντομεύσεις Πληκτρολογίου"
140142
141143#: qml/Components/KeyboardShortcutsOverlay.qml:55
142144msgid "Lomiri"
143−msgstr "Lomiri"
145+msgstr ""
144146
145147#: qml/Components/KeyboardShortcutsOverlay.qml:62
146148msgid "PrtScr"
po/es.po+22−22
@@ -8,8 +8,8 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: \n"
1010"POT-Creation-Date: 2021-01-29 11:42-0600\n"
11−"PO-Revision-Date: 2021-10-04 12:20+0000\n"
12−"Last-Translator: Adolfo Jayme Barrientos <fitojb@ubuntu.com>\n"
11+"PO-Revision-Date: 2021-03-31 05:24+0000\n"
12+"Last-Translator: Josué <j2g2rp@gmail.com>\n"
1313"Language-Team: Spanish <https://translate.ubports.com/projects/ubports/"
1414"lomiri/es/>\n"
1515"Language: es\n"
@@ -34,11 +34,11 @@ msgstr "Fallo al autenticar"
3434
3535#: plugins/LightDM/Greeter.cpp:260
3636msgid "Invalid password, please try again"
37−msgstr "Contraseña incorrecta; inténtelo de nuevo"
37+msgstr "Contraseña incorrecta, por favor, inténtelo de nuevo"
3838
3939#: plugins/LightDM/Greeter.cpp:270
4040msgid "Log In"
41−msgstr "Acceder"
41+msgstr "Iniciar sesión"
4242
4343#: plugins/LightDM/Greeter.cpp:270
4444msgid "Retry"
@@ -46,7 +46,7 @@ msgstr "Reintentar"
4646
4747#: plugins/LightDM/UsersModel.cpp:157
4848msgid "Login"
49−msgstr "Acceder"
49+msgstr "Iniciar sesión"
5050
5151#: plugins/LightDM/UsersModel.cpp:165
5252msgid "Guest Session"
@@ -68,7 +68,7 @@ msgstr "Desanclar atajo"
6868#: qml/Components/Dialogs.qml:176
6969msgctxt "Title: Lock/Log out dialog"
7070msgid "Log out"
71−msgstr "Cerrar sesión"
71+msgstr "Cerrar la sesión"
7272
7373#: qml/Components/Dialogs.qml:177
7474msgid "Are you sure you want to log out?"
@@ -82,7 +82,7 @@ msgstr "Bloquear"
8282#: qml/Components/Dialogs.qml:191
8383msgctxt "Button: Log out from the system"
8484msgid "Log Out"
85−msgstr "Cerrar sesión"
85+msgstr "Cerrar la sesión"
8686
8787#: qml/Components/Dialogs.qml:199 qml/Components/Dialogs.qml:284
8888#: qml/Greeter/NarrowView.qml:215 qml/Wizard/Pages/passcode-confirm.qml:32
@@ -164,7 +164,7 @@ msgstr "Super + Espacio"
164164
165165#: qml/Components/KeyboardShortcutsOverlay.qml:93
166166msgid "Switches to next keyboard layout."
167−msgstr "Cambia a la disposición de teclado siguiente."
167+msgstr "Cambia a la siguiente disposición de teclado."
168168
169169#: qml/Components/KeyboardShortcutsOverlay.qml:101
170170msgid "Super + Shift + Space"
@@ -172,7 +172,7 @@ msgstr "Super + Mayús + Espacio"
172172
173173#: qml/Components/KeyboardShortcutsOverlay.qml:106
174174msgid "Switches to previous keyboard layout."
175−msgstr "Cambia a la disposición de teclado anterior."
175+msgstr "Cambia a la anterior disposición de teclado."
176176
177177#: qml/Components/KeyboardShortcutsOverlay.qml:114
178178msgid "Ctrl + Alt + T"
@@ -184,15 +184,15 @@ msgstr "Abre la aplicación Terminal."
184184
185185#: qml/Components/KeyboardShortcutsOverlay.qml:131
186186msgid "Launcher"
187−msgstr "Iniciador"
187+msgstr "Lanzador"
188188
189189#: qml/Components/KeyboardShortcutsOverlay.qml:138
190190msgid "Super (Hold)"
191−msgstr "Súper (mantener)"
191+msgstr "Super (Mantener)"
192192
193193#: qml/Components/KeyboardShortcutsOverlay.qml:143
194194msgid "Opens the launcher, displays shortcuts."
195−msgstr "Abre el iniciador y muestra los atajos."
195+msgstr "Abre el lanzador y muestra los atajos."
196196
197197#: qml/Components/KeyboardShortcutsOverlay.qml:151
198198msgid "Alt + F1"
@@ -200,7 +200,7 @@ msgstr "Alt + F1"
200200
201201#: qml/Components/KeyboardShortcutsOverlay.qml:156
202202msgid "Opens launcher keyboard navigation mode."
203−msgstr "Abre el iniciador en modo de navegación de teclado."
203+msgstr "Abre el Lanzador en modo de navegación de teclado."
204204
205205#: qml/Components/KeyboardShortcutsOverlay.qml:164
206206msgid "Super + Tab"
@@ -208,7 +208,7 @@ msgstr "Super + Tab"
208208
209209#: qml/Components/KeyboardShortcutsOverlay.qml:169
210210msgid "Switches applications via the launcher."
211−msgstr "Cambia de aplicación mediante el iniciador."
211+msgstr "Recorre las aplicaciones del Lanzador."
212212
213213#: qml/Components/KeyboardShortcutsOverlay.qml:177
214214msgid "Super + 1 to 0"
@@ -216,7 +216,7 @@ msgstr "Súper + 1 a 0"
216216
217217#: qml/Components/KeyboardShortcutsOverlay.qml:182
218218msgid "Same as clicking on a launcher icon."
219−msgstr "Lo mismo que pulsar en un icono del iniciador."
219+msgstr "Lo mismo que hacer clic en un icono del Lanzador."
220220
221221#: qml/Components/KeyboardShortcutsOverlay.qml:190
222222msgid "Super + A"
@@ -288,7 +288,7 @@ msgstr "Ctrl + Super + Arriba o Derecha"
288288
289289#: qml/Components/KeyboardShortcutsOverlay.qml:336
290290msgid "Semi-maximizes the current window."
291−msgstr "Semimaximiza la ventana actual."
291+msgstr "Semi-maximiza la ventana actual."
292292
293293#: qml/Components/KeyboardShortcutsOverlay.qml:344
294294msgid "Alt + F4"
@@ -373,7 +373,7 @@ msgstr "Deslice con dos dedos para desplazarse."
373373
374374#: qml/Components/VirtualTouchPad.qml:396
375375msgid "Find more settings in the system settings."
376−msgstr "Encuentre más ajustes en Configuración del sistema."
376+msgstr "Encuentre mas ajustes en la configuración del sistema."
377377
378378#: qml/Greeter/CoverPage.qml:127
379379msgid "Unlock"
@@ -549,21 +549,21 @@ msgstr ""
549549
550550#: qml/Tutorial/TutorialLeft.qml:47
551551msgid "Short swipe from the left edge to open the launcher"
552−msgstr "Deslice un poco desde el borde izquierdo para abrir el iniciador"
552+msgstr "Deslizar un poco desde el borde izquierdo para abrir el lanzador"
553553
554554#: qml/Tutorial/TutorialRight.qml:54
555555msgid "Push your mouse against the right edge to view your open apps"
556556msgstr ""
557−"Para ver las aplicaciones abiertas empuje el ratón contra el borde derecho"
557+"Para ver las aplicaciones abiertas empuja el ratón contra el borde derecho"
558558
559559#: qml/Tutorial/TutorialRight.qml:55
560560msgid "Swipe from the right edge to view your open apps"
561−msgstr "Deslice desde el borde derecho para ver las aplicaciones abiertas"
561+msgstr "Deslizar desde el borde derecho para ver las aplis abiertas"
562562
563563#: qml/Tutorial/TutorialTop.qml:53
564564msgid "Swipe from the top edge to access notifications and quick settings"
565565msgstr ""
566−"Deslice desde el borde superior para acceder a las notificaciones y "
566+"Deslizar desde el borde superior para acceder a las notificaciones y "
567567"configuración"
568568
569569#: qml/Wizard/Page.qml:59
@@ -583,7 +583,7 @@ msgstr "Idioma"
583583#: qml/Wizard/Pages/passcode-desktop.qml:154
584584#: qml/Wizard/Pages/password-set.qml:153
585585msgid "Next"
586−msgstr "Sig."
586+msgstr "Siguiente"
587587
588588#: qml/Wizard/Pages/10-welcome-update.qml:91
589589msgid "Welcome to "
po/he.po+11−9
@@ -8,9 +8,9 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: \n"
1010"POT-Creation-Date: 2021-01-29 11:42-0600\n"
11−"PO-Revision-Date: 2022-02-07 20:15+0000\n"
12−"Last-Translator: anonymous <noreply@weblate.org>\n"
13−"Language-Team: Hebrew <https://translate.ubports.com/projects/ubports/lomiri/"
11+"PO-Revision-Date: 2020-09-05 22:10+0000\n"
12+"Last-Translator: tamer dab <dabsantamer@yahoo.com>\n"
13+"Language-Team: Hebrew <https://translate.ubports.com/projects/unity8/unity8/"
1414"he/>\n"
1515"Language: he\n"
1616"MIME-Version: 1.0\n"
@@ -129,8 +129,10 @@ msgid "Restart"
129129msgstr "הפעלה מחדש"
130130
131131#: qml/Components/Dialogs.qml:276
132+#, fuzzy
133+#| msgid "Takes a screenshot."
132134msgid "Screenshot"
133−msgstr "צילום מסך"
135+msgstr "מצלם מסך."
134136
135137#: qml/Components/KeyboardShortcutsOverlay.qml:41
136138msgid "Keyboard Shortcuts"
@@ -138,7 +140,7 @@ msgstr "קיצורי דרך במקלדת"
138140
139141#: qml/Components/KeyboardShortcutsOverlay.qml:55
140142msgid "Lomiri"
141−msgstr "Lomiri"
143+msgstr ""
142144
143145#: qml/Components/KeyboardShortcutsOverlay.qml:62
144146msgid "PrtScr"
@@ -456,13 +458,13 @@ msgstr "שום דבר לא מתנגן"
456458msgid "%1 hour"
457459msgid_plural "%1 hours"
458460msgstr[0] "%1 שעה"
459−msgstr[1] "%1 שעת"
461+msgstr[1] "%1 שעות"
460462
461463#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1001
462464#, qt-format
463465msgid "%1 minute"
464466msgid_plural "%1 minutes"
465−msgstr[0] "%1 דקה"
467+msgstr[0] "דקה %1"
466468msgstr[1] "%1 דקות"
467469
468470#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1006
@@ -524,7 +526,7 @@ msgstr "התקשר חזרה"
524526
525527#: qml/Panel/PanelMenuPage.qml:92
526528msgid "Back"
527−msgstr "חזרה"
529+msgstr "חזור"
528530
529531#: qml/Stage/SideStage.qml:76
530532msgid "Drag using 3 fingers any application from one window to the other"
@@ -557,7 +559,7 @@ msgstr "החלק מהקצה העליון כדי לגשת להתראות ולהג
557559#: qml/Wizard/Page.qml:59
558560msgctxt "Button: Go back one page in the Wizard"
559561msgid "Back"
560−msgstr "חזרה"
562+msgstr "חזור"
561563
562564#: qml/Wizard/Pages/10-welcome.qml:29
563565msgid "Language"
po/hu.po+11−9
@@ -8,8 +8,8 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: \n"
1010"POT-Creation-Date: 2021-01-29 11:42-0600\n"
11−"PO-Revision-Date: 2022-02-06 00:18+0000\n"
12−"Last-Translator: Lundrin <berenyi.vilmos@tutanota.com>\n"
11+"PO-Revision-Date: 2021-03-17 10:43+0000\n"
12+"Last-Translator: David Farkas <dvdfrks@gmail.com>\n"
1313"Language-Team: Hungarian <https://translate.ubports.com/projects/ubports/"
1414"lomiri/hu/>\n"
1515"Language: hu\n"
@@ -88,7 +88,7 @@ msgstr "Kijelentkezés"
8888#: qml/Greeter/NarrowView.qml:215 qml/Wizard/Pages/passcode-confirm.qml:32
8989#: qml/Wizard/Pages/passcode-set.qml:32
9090msgid "Cancel"
91−msgstr "Mégse"
91+msgstr "Mégsem"
9292
9393#: qml/Components/Dialogs.qml:211
9494msgctxt "Title: Reboot dialog"
@@ -131,8 +131,10 @@ msgid "Restart"
131131msgstr "Újraindítás"
132132
133133#: qml/Components/Dialogs.qml:276
134+#, fuzzy
135+#| msgid "Takes a screenshot."
134136msgid "Screenshot"
135−msgstr "Képernyőmentés"
137+msgstr "Képernyőkép készítése."
136138
137139#: qml/Components/KeyboardShortcutsOverlay.qml:41
138140msgid "Keyboard Shortcuts"
@@ -140,7 +142,7 @@ msgstr "Gyorsbillentyűk"
140142
141143#: qml/Components/KeyboardShortcutsOverlay.qml:55
142144msgid "Lomiri"
143−msgstr "Lomiri"
145+msgstr ""
144146
145147#: qml/Components/KeyboardShortcutsOverlay.qml:62
146148msgid "PrtScr"
@@ -504,7 +506,7 @@ msgstr "Megszakítva"
504506
505507#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1027
506508msgid "Finished"
507−msgstr "Kész"
509+msgstr "Befejeződött"
508510
509511#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1029
510512msgid "Failed, tap to retry"
@@ -576,7 +578,7 @@ msgstr "Nyelv"
576578#: qml/Wizard/Pages/passcode-desktop.qml:154
577579#: qml/Wizard/Pages/password-set.qml:153
578580msgid "Next"
579−msgstr "Köv."
581+msgstr "Következő"
580582
581583#: qml/Wizard/Pages/10-welcome-update.qml:91
582584msgid "Welcome to "
@@ -618,7 +620,7 @@ msgstr "Csatlakozás Wi-Fi hálózathoz"
618620
619621#: qml/Wizard/Pages/30-wifi.qml:139
620622msgid "Connected"
621−msgstr "Csatlakozva"
623+msgstr "Kapcsolódva"
622624
623625#: qml/Wizard/Pages/30-wifi.qml:172
624626msgid "Available Wi-Fi networks"
@@ -739,7 +741,7 @@ msgstr "Adjon meg egy 4 számjegyű jelkódot"
739741#: qml/Wizard/Pages/passcode-desktop.qml:84
740742#: qml/Wizard/Pages/passcode-set.qml:54
741743msgid "Choose passcode"
742−msgstr "Válasszon jelkódot"
744+msgstr "Jelkód megadása"
743745
744746#: qml/Wizard/Pages/passcode-desktop.qml:139
745747msgid "Passcode too short"
po/it.po+6−6
@@ -8,8 +8,8 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: \n"
1010"POT-Creation-Date: 2021-01-29 11:42-0600\n"
11−"PO-Revision-Date: 2022-01-22 21:58+0000\n"
12−"Last-Translator: Matteo <mattbel10@hotmail.com>\n"
11+"PO-Revision-Date: 2021-07-18 22:26+0000\n"
12+"Last-Translator: Mike <miguel2000@livecom.it>\n"
1313"Language-Team: Italian <https://translate.ubports.com/projects/ubports/"
1414"lomiri/it/>\n"
1515"Language: it\n"
@@ -409,7 +409,7 @@ msgstr "Riprova"
409409
410410#: qml/Greeter/Greeter.qml:551
411411msgid "Enter passphrase to unlock"
412−msgstr "Inserire parola chiave per sbloccare"
412+msgstr "Inserire passphrase per sbloccare"
413413
414414#: qml/Greeter/Greeter.qml:552
415415msgid "Enter passcode to unlock"
@@ -421,7 +421,7 @@ msgstr "Emergenza"
421421
422422#: qml/Greeter/PromptList.qml:126
423423msgid "Passphrase"
424−msgstr "Parola chiave"
424+msgstr "Passphrase"
425425
426426#: qml/Greeter/PromptList.qml:126
427427msgid "Passcode"
@@ -540,12 +540,12 @@ msgstr "Trascinare con 3 dita qualsiasi applicazione da una finestra all'altra"
540540
541541#: qml/Stage/Stage.qml:671
542542msgid "No running apps"
543−msgstr "Nessuna applicazione in esecuzione"
543+msgstr "Nessun applicazione in esecuzione"
544544
545545#: qml/Tutorial/TutorialLeftLong.qml:49
546546msgid "Long swipe from the left edge to open the Application Drawer"
547547msgstr ""
548−"Scorrere lentamente dal bordo sinistro per aprire il Cassetto delle "
548+"Scorrere lentamente dal bordo sinistro per aprire il Drawer delle "
549549"Applicazioni"
550550
551551#: qml/Tutorial/TutorialLeft.qml:47
po/km.po+14−11
@@ -8,17 +8,16 @@ msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: \n"
1010"POT-Creation-Date: 2021-01-29 11:42-0600\n"
11−"PO-Revision-Date: 2022-03-14 13:38+0000\n"
12−"Last-Translator: ប៉ុកណូ រ៉ូយ៉ាល់ <poknoroyal@outlook.com>\n"
13−"Language-Team: Central Khmer <https://translate.ubports.com/projects/ubports/"
14−"lomiri/km/>\n"
15−"Language: km\n"
11+"PO-Revision-Date: 2014-08-20 02:48+0000\n"
12+"Last-Translator: Sophea Sok <sksophea@gmail.com>\n"
13+"Language-Team: Khmer <km@li.org>\n"
14+"Language: \n"
1615"MIME-Version: 1.0\n"
1716"Content-Type: text/plain; charset=UTF-8\n"
1817"Content-Transfer-Encoding: 8bit\n"
1918"Plural-Forms: nplurals=1; plural=0;\n"
20−"X-Generator: Weblate 3.11.3\n"
2119"X-Launchpad-Export-Date: 2016-11-23 06:35+0000\n"
20+"X-Generator: Launchpad (build 18269)\n"
2221
2322#: plugins/LightDM/Greeter.cpp:199
2423msgid "Password: "
@@ -26,7 +25,7 @@ msgstr "ពាក្យសម្ងាត់៖ "
2625
2726#: plugins/LightDM/Greeter.cpp:214
2827msgid "Username"
29−msgstr "អត្ថនាម"
28+msgstr ""
3029
3130#: plugins/LightDM/Greeter.cpp:258
3231msgid "Failed to authenticate"
@@ -376,7 +375,7 @@ msgstr ""
376375
377376#: qml/Greeter/CoverPage.qml:127
378377msgid "Unlock"
379−msgstr "ដោះសោ"
378+msgstr "ដោះ​សោ"
380379
381380#: qml/Greeter/DelayedLockscreen.qml:43
382381msgid "Device Locked"
@@ -524,8 +523,11 @@ msgid "Call back"
524523msgstr "ហៅ​មកវិញ"
525524
526525#: qml/Panel/PanelMenuPage.qml:92
526+#, fuzzy
527+#| msgctxt "Button: Go back one page in the Wizard"
528+#| msgid "Back"
527529msgid "Back"
528−msgstr "បកក្រោយ"
530+msgstr "ថយក្រោយ"
529531
530532#: qml/Stage/SideStage.qml:76
531533msgid "Drag using 3 fingers any application from one window to the other"
@@ -590,8 +592,9 @@ msgid "What's new"
590592msgstr ""
591593
592594#: qml/Wizard/Pages/11-changelog.qml:74
595+#, fuzzy
593596msgid "Loading..."
594−msgstr "កំពុងផ្ទុក…"
597+msgstr "កំពុង​និយាយ..."
595598
596599#: qml/Wizard/Pages/20-keyboard.qml:31
597600msgid "Select Keyboard"
@@ -603,7 +606,7 @@ msgstr ""
603606
604607#: qml/Wizard/Pages/20-keyboard.qml:93
605608msgid "Keyboard layout"
606−msgstr "ប្លង់ក្ដារចុច"
609+msgstr ""
607610
608611#: qml/Wizard/Pages/20-keyboard.qml:152 qml/Wizard/Pages/30-wifi.qml:214
609612#: qml/Wizard/Pages/60-account.qml:72 qml/Wizard/Pages/79-system-update.qml:144
po/nb.po+4−2
@@ -8,7 +8,7 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: \n"
1010"POT-Creation-Date: 2021-01-29 11:42-0600\n"
11−"PO-Revision-Date: 2021-10-15 10:11+0000\n"
11+"PO-Revision-Date: 2021-07-22 23:44+0000\n"
1212"Last-Translator: Allan Nordhøy <epost@anotheragency.no>\n"
1313"Language-Team: Norwegian Bokmål <https://translate.ubports.com/projects/"
1414"ubports/lomiri/nb/>\n"
@@ -131,8 +131,10 @@ msgid "Restart"
131131msgstr "Start på nytt"
132132
133133#: qml/Components/Dialogs.qml:276
134+#, fuzzy
135+#| msgid "Takes a screenshot."
134136msgid "Screenshot"
135−msgstr "Skjermavbildning"
137+msgstr "Tar bilde av skjermen."
136138
137139#: qml/Components/KeyboardShortcutsOverlay.qml:41
138140msgid "Keyboard Shortcuts"
po/nl.po+3−3
@@ -8,8 +8,8 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: \n"
1010"POT-Creation-Date: 2021-01-29 11:42-0600\n"
11−"PO-Revision-Date: 2021-09-21 19:30+0000\n"
12−"Last-Translator: Heimen Stoffels <vistausss@fastmail.com>\n"
11+"PO-Revision-Date: 2021-07-22 10:09+0000\n"
12+"Last-Translator: Jeroen Baten <jbaten@i2rs.nl>\n"
1313"Language-Team: Dutch <https://translate.ubports.com/projects/ubports/lomiri/"
1414"nl/>\n"
1515"Language: nl\n"
@@ -463,7 +463,7 @@ msgstr "Er wordt niets afgespeeld"
463463msgid "%1 hour"
464464msgid_plural "%1 hours"
465465msgstr[0] "%1 uur"
466−msgstr[1] "%1 uur"
466+msgstr[1] "%1 uren"
467467
468468#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1001
469469#, qt-format
po/pt.po+4−4
@@ -8,8 +8,8 @@ msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: \n"
1010"POT-Creation-Date: 2021-01-29 11:42-0600\n"
11−"PO-Revision-Date: 2021-11-19 22:39+0000\n"
12−"Last-Translator: Hugo Carvalho <hugokarvalho@hotmail.com>\n"
11+"PO-Revision-Date: 2021-06-01 14:51+0000\n"
12+"Last-Translator: Ivo Xavier <ivofernandes12@gmail.com>\n"
1313"Language-Team: Portuguese <https://translate.ubports.com/projects/ubports/"
1414"lomiri/pt/>\n"
1515"Language: pt\n"
@@ -531,7 +531,7 @@ msgstr "Ligar de volta"
531531
532532#: qml/Panel/PanelMenuPage.qml:92
533533msgid "Back"
534−msgstr "Voltar"
534+msgstr "Retroceder"
535535
536536#: qml/Stage/SideStage.qml:76
537537msgid "Drag using 3 fingers any application from one window to the other"
@@ -712,7 +712,7 @@ msgstr "Está pronto/a para usar o seu dispositivo"
712712
713713#: qml/Wizard/Pages/80-finished.qml:126
714714msgid "Continue"
715−msgstr "Continuar"
715+msgstr "Avançar"
716716
717717#: qml/Wizard/Pages/80-finished.qml:126
718718msgid "Get Started"
po/sc.po+6−6
@@ -8,7 +8,7 @@ msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: \n"
1010"POT-Creation-Date: 2021-01-29 11:42-0600\n"
11−"PO-Revision-Date: 2021-11-21 15:09+0000\n"
11+"PO-Revision-Date: 2021-04-22 10:44+0000\n"
1212"Last-Translator: Adria <adriamartinmor@gmail.com>\n"
1313"Language-Team: Sardinian <https://translate.ubports.com/projects/ubports/"
1414"lomiri/sc/>\n"
@@ -539,7 +539,7 @@ msgstr "Traga cun 3 ditos is aplicatziones dae una ventana a s'àtera"
539539
540540#: qml/Stage/Stage.qml:671
541541msgid "No running apps"
542−msgstr "Nissuna aplicatzione ativa"
542+msgstr "Peruna aplicatzione ativa"
543543
544544#: qml/Tutorial/TutorialLeftLong.qml:49
545545msgid "Long swipe from the left edge to open the Application Drawer"
@@ -634,7 +634,7 @@ msgstr "Retes Wi-Fi a disponimentu"
634634
635635#: qml/Wizard/Pages/30-wifi.qml:173
636636msgid "No available Wi-Fi networks"
637−msgstr "Nissuna rete Wi-Fi a disponimentu"
637+msgstr "Peruna rete Wi-Fi a disponimentu"
638638
639639#: qml/Wizard/Pages/50-timezone.qml:30
640640msgid "Time Zone"
@@ -669,7 +669,7 @@ msgstr "Crea unu còdighe de atzessu (nùmeros isceti)"
669669#: qml/Wizard/Pages/70-passwd-type.qml:107
670670msgctxt "Label: Type of security method"
671671msgid "No lock code"
672−msgstr "Nissunu còdighe de blocu"
672+msgstr "Perunu còdighe de blocu"
673673
674674#: qml/Wizard/Pages/79-system-update.qml:28
675675msgid "Update Device"
@@ -696,7 +696,7 @@ msgstr "Versione %1"
696696
697697#: qml/Wizard/Pages/79-system-update.qml:95
698698msgid "This could take a few minutes..."
699−msgstr "Podet trigare calicunu minutu..."
699+msgstr "B'at a pònnere carchi minutu..."
700700
701701#: qml/Wizard/Pages/79-system-update.qml:109
702702msgid "Install and restart now"
@@ -783,7 +783,7 @@ msgstr "Cunfirma sa crae"
783783
784784#: qml/Wizard/Pages/sim.qml:27
785785msgid "No SIM card installed"
786−msgstr "Nissuna SIM installada"
786+msgstr "Peruna SIM installada"
787787
788788#: qml/Wizard/Pages/sim.qml:54
789789msgid "SIM card added"
po/sv.po+52−25
@@ -8,8 +8,8 @@ msgstr ""
88"Project-Id-Version: unity\n"
99"Report-Msgid-Bugs-To: \n"
1010"POT-Creation-Date: 2021-01-29 11:42-0600\n"
11−"PO-Revision-Date: 2021-10-19 20:55+0000\n"
12−"Last-Translator: Jakob Fridesjö <jakob.fridesjo@tuta.io>\n"
11+"PO-Revision-Date: 2021-07-26 20:33+0000\n"
12+"Last-Translator: Rondy Andersson <rondarius@vivaldi.net>\n"
1313"Language-Team: Swedish <https://translate.ubports.com/projects/ubports/"
1414"lomiri/sv/>\n"
1515"Language: sv\n"
@@ -26,15 +26,15 @@ msgstr "Lösenord: "
2626
2727#: plugins/LightDM/Greeter.cpp:214
2828msgid "Username"
29−msgstr "Användarnamn"
29+msgstr ""
3030
3131#: plugins/LightDM/Greeter.cpp:258
3232msgid "Failed to authenticate"
33−msgstr "Misslyckades att autentisera"
33+msgstr ""
3434
3535#: plugins/LightDM/Greeter.cpp:260
3636msgid "Invalid password, please try again"
37−msgstr "Felaktigt lösenord, vänligen försök igen"
37+msgstr ""
3838
3939#: plugins/LightDM/Greeter.cpp:270
4040msgid "Log In"
@@ -46,11 +46,11 @@ msgstr "Försök igen"
4646
4747#: plugins/LightDM/UsersModel.cpp:157
4848msgid "Login"
49−msgstr "Inloggning"
49+msgstr ""
5050
5151#: plugins/LightDM/UsersModel.cpp:165
5252msgid "Guest Session"
53−msgstr "Gästsession"
53+msgstr ""
5454
5555#: plugins/Unity/Launcher/launcheritem.cpp:48
5656#: plugins/Unity/Launcher/launcheritem.cpp:122
@@ -131,8 +131,10 @@ msgid "Restart"
131131msgstr "Starta om"
132132
133133#: qml/Components/Dialogs.qml:276
134+#, fuzzy
135+#| msgid "Takes a screenshot."
134136msgid "Screenshot"
135−msgstr "Skärmdump"
137+msgstr "Tar en skärmbild."
136138
137139#: qml/Components/KeyboardShortcutsOverlay.qml:41
138140msgid "Keyboard Shortcuts"
@@ -140,7 +142,7 @@ msgstr "Tangentbordsgenvägar"
140142
141143#: qml/Components/KeyboardShortcutsOverlay.qml:55
142144msgid "Lomiri"
143−msgstr "Lomiri"
145+msgstr ""
144146
145147#: qml/Components/KeyboardShortcutsOverlay.qml:62
146148msgid "PrtScr"
@@ -155,8 +157,10 @@ msgid "Alt + PrtScr"
155157msgstr "Alt + PrtScr"
156158
157159#: qml/Components/KeyboardShortcutsOverlay.qml:80
160+#, fuzzy
161+#| msgid "Takes a screenshot of a window."
158162msgid "Takes a screenshot of the current window."
159−msgstr "Tar en skärmdump av det nuvarande fönstret."
163+msgstr "Tar en skärmbild av ett fönster."
160164
161165#: qml/Components/KeyboardShortcutsOverlay.qml:88
162166msgid "Super + Space"
@@ -175,12 +179,16 @@ msgid "Switches to previous keyboard layout."
175179msgstr "Byter till tidigare tangentbordslayout."
176180
177181#: qml/Components/KeyboardShortcutsOverlay.qml:114
182+#, fuzzy
183+#| msgid "Ctrl + Super + D"
178184msgid "Ctrl + Alt + T"
179−msgstr "Ctrl + Alt + T"
185+msgstr "Ctrl + Super + D"
180186
181187#: qml/Components/KeyboardShortcutsOverlay.qml:119
188+#, fuzzy
189+#| msgid "Switches between applications."
182190msgid "Starts the Terminal application."
183−msgstr "Startar terminalen."
191+msgstr "Växlar mellan program."
184192
185193#: qml/Components/KeyboardShortcutsOverlay.qml:131
186194msgid "Launcher"
@@ -211,20 +219,26 @@ msgid "Switches applications via the launcher."
211219msgstr "Byter program via Programstartaren."
212220
213221#: qml/Components/KeyboardShortcutsOverlay.qml:177
222+#, fuzzy
223+#| msgid "Super + 0 to 9"
214224msgid "Super + 1 to 0"
215−msgstr "Super + 1 till 0"
225+msgstr "Super + 0 till 9"
216226
217227#: qml/Components/KeyboardShortcutsOverlay.qml:182
218228msgid "Same as clicking on a launcher icon."
219229msgstr "Samma som att klicka på en ikon i Programstartaren."
220230
221231#: qml/Components/KeyboardShortcutsOverlay.qml:190
232+#, fuzzy
233+#| msgid "Super + W"
222234msgid "Super + A"
223−msgstr "Super + A"
235+msgstr "Super + W"
224236
225237#: qml/Components/KeyboardShortcutsOverlay.qml:195
238+#, fuzzy
239+#| msgid "Open the launcher"
226240msgid "Opens the Application Drawer."
227−msgstr "Öppnar programstartaren."
241+msgstr "Öppna programstartaren"
228242
229243#: qml/Components/KeyboardShortcutsOverlay.qml:235
230244msgid "Switching"
@@ -360,20 +374,22 @@ msgstr ""
360374"styrplatta för att interagera med pekaren."
361375
362376#: qml/Components/VirtualTouchPad.qml:327
377+#, fuzzy
378+#| msgid "Tap to return to call..."
363379msgid "Tap left button to click."
364−msgstr "Tryck på vänster knapp för att klicka."
380+msgstr "Tryck för att återgå till samtal…"
365381
366382#: qml/Components/VirtualTouchPad.qml:339
367383msgid "Tap right button to right click."
368−msgstr "Tryck på höger knapp för att högerklicka."
384+msgstr ""
369385
370386#: qml/Components/VirtualTouchPad.qml:351
371387msgid "Swipe with two fingers to scroll."
372−msgstr "Svep med två fingrar för att scrolla."
388+msgstr ""
373389
374390#: qml/Components/VirtualTouchPad.qml:396
375391msgid "Find more settings in the system settings."
376−msgstr "Hitta fler inställningar i systeminställningarna."
392+msgstr ""
377393
378394#: qml/Greeter/CoverPage.qml:127
379395msgid "Unlock"
@@ -403,12 +419,16 @@ msgid "Try again"
403419msgstr "Försök igen"
404420
405421#: qml/Greeter/Greeter.qml:551
422+#, fuzzy
423+#| msgid "Enter passphrase"
406424msgid "Enter passphrase to unlock"
407−msgstr "Ange lösefras för att låsa upp"
425+msgstr "Ange lösenfras"
408426
409427#: qml/Greeter/Greeter.qml:552
428+#, fuzzy
429+#| msgid "Enter passcode"
410430msgid "Enter passcode to unlock"
411−msgstr "Ange lösenkod för att låsa upp"
431+msgstr "Ange lösenkod"
412432
413433#: qml/Greeter/NarrowView.qml:235
414434msgid "Emergency"
@@ -427,8 +447,10 @@ msgid "Select desktop environment"
427447msgstr "Välj skrivbordsmiljö"
428448
429449#: qml/Launcher/Drawer.qml:250
450+#, fuzzy
451+#| msgid "Search"
430452msgid "Search…"
431−msgstr "Sök…"
453+msgstr "Sök"
432454
433455#: qml/Launcher/PullToRefreshScopeStyle.qml:56
434456msgid "Pull to refresh…"
@@ -535,11 +557,13 @@ msgstr "Dra ett program från ett fönster till det andra med tre fingrar"
535557
536558#: qml/Stage/Stage.qml:671
537559msgid "No running apps"
538−msgstr "Inga appar körs"
560+msgstr ""
539561
540562#: qml/Tutorial/TutorialLeftLong.qml:49
563+#, fuzzy
564+#| msgid "Long swipe from the left edge to open the Today scope"
541565msgid "Long swipe from the left edge to open the Application Drawer"
542−msgstr "Gör ett långt svep från vänstra kanten för öppna applikationsstartaren"
566+msgstr "Gör ett lång svep från vänstra kanten för öppna vyn Idag"
543567
544568#: qml/Tutorial/TutorialLeft.qml:47
545569msgid "Short swipe from the left edge to open the launcher"
@@ -675,9 +699,12 @@ msgstr ""
675699"kommer enheten att startas om automatiskt."
676700
677701#: qml/Wizard/Pages/79-system-update.qml:76
702+#, fuzzy
703+#| msgctxt "string identifying name of the update"
704+#| msgid "Ubuntu system"
678705msgctxt "string identifying name of the update"
679706msgid "Ubuntu Touch system"
680−msgstr "Ubuntu Touch system"
707+msgstr "Ubuntusystem"
681708
682709#: qml/Wizard/Pages/79-system-update.qml:83
683710#, qt-format
po/th.po+20−20
@@ -8,7 +8,7 @@ msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: \n"
1010"POT-Creation-Date: 2021-01-29 11:42-0600\n"
11−"PO-Revision-Date: 2021-10-14 16:02+0000\n"
11+"PO-Revision-Date: 2021-05-23 05:10+0000\n"
1212"Last-Translator: Wannaphong Phatthiyaphaibun <wannaphong@yahoo.com>\n"
1313"Language-Team: Thai <https://translate.ubports.com/projects/ubports/lomiri/"
1414"th/>\n"
@@ -22,31 +22,31 @@ msgstr ""
2222
2323#: plugins/LightDM/Greeter.cpp:199
2424msgid "Password: "
25−msgstr "รหัสผ่าน: "
25+msgstr ""
2626
2727#: plugins/LightDM/Greeter.cpp:214
2828msgid "Username"
29−msgstr "ชื่อผู้ใช้"
29+msgstr ""
3030
3131#: plugins/LightDM/Greeter.cpp:258
3232msgid "Failed to authenticate"
33−msgstr "ไม่สามารถตรวจสอบสิทธิ์ได้"
33+msgstr ""
3434
3535#: plugins/LightDM/Greeter.cpp:260
3636msgid "Invalid password, please try again"
37−msgstr "รหัสผ่านไม่ถูกต้อง กรุณาลองอีกครั้ง"
37+msgstr ""
3838
3939#: plugins/LightDM/Greeter.cpp:270
4040msgid "Log In"
41−msgstr "เข้าสู่ระบบ"
41+msgstr ""
4242
4343#: plugins/LightDM/Greeter.cpp:270
4444msgid "Retry"
45−msgstr "ลองอีกครั้ง"
45+msgstr ""
4646
4747#: plugins/LightDM/UsersModel.cpp:157
4848msgid "Login"
49−msgstr "เข้าสู่ระบบ"
49+msgstr ""
5050
5151#: plugins/LightDM/UsersModel.cpp:165
5252msgid "Guest Session"
@@ -68,7 +68,7 @@ msgstr ""
6868#: qml/Components/Dialogs.qml:176
6969msgctxt "Title: Lock/Log out dialog"
7070msgid "Log out"
71−msgstr "ลงชื่อออก"
71+msgstr ""
7272
7373#: qml/Components/Dialogs.qml:177
7474msgid "Are you sure you want to log out?"
@@ -77,7 +77,7 @@ msgstr ""
7777#: qml/Components/Dialogs.qml:180
7878msgctxt "Button: Lock the system"
7979msgid "Lock"
80−msgstr "ล็อก"
80+msgstr ""
8181
8282#: qml/Components/Dialogs.qml:191
8383msgctxt "Button: Log out from the system"
@@ -101,16 +101,16 @@ msgstr ""
101101
102102#: qml/Components/Dialogs.qml:215
103103msgid "No"
104−msgstr "ไม่"
104+msgstr ""
105105
106106#: qml/Components/Dialogs.qml:223
107107msgid "Yes"
108−msgstr "ใช่"
108+msgstr ""
109109
110110#: qml/Components/Dialogs.qml:243
111111msgctxt "Title: Power off/Restart dialog"
112112msgid "Power"
113−msgstr "พลังงาน"
113+msgstr ""
114114
115115#: qml/Components/Dialogs.qml:244
116116msgid ""
@@ -121,7 +121,7 @@ msgstr ""
121121#: qml/Components/Dialogs.qml:248
122122msgctxt "Button: Power off the system"
123123msgid "Power off"
124−msgstr "ปิดเครื่อง"
124+msgstr ""
125125
126126#: qml/Components/Dialogs.qml:263
127127msgctxt "Button: Restart the system"
@@ -138,7 +138,7 @@ msgstr ""
138138
139139#: qml/Components/KeyboardShortcutsOverlay.qml:55
140140msgid "Lomiri"
141−msgstr "Lomiri"
141+msgstr ""
142142
143143#: qml/Components/KeyboardShortcutsOverlay.qml:62
144144msgid "PrtScr"
@@ -395,7 +395,7 @@ msgstr[1] ""
395395
396396#: qml/Greeter/Greeter.qml:550
397397msgid "Try again"
398−msgstr "ลองอีกครั้ง"
398+msgstr ""
399399
400400#: qml/Greeter/Greeter.qml:551
401401msgid "Enter passphrase to unlock"
@@ -407,7 +407,7 @@ msgstr ""
407407
408408#: qml/Greeter/NarrowView.qml:235
409409msgid "Emergency"
410−msgstr "เหตุฉุกเฉิน"
410+msgstr ""
411411
412412#: qml/Greeter/PromptList.qml:126
413413msgid "Passphrase"
@@ -423,7 +423,7 @@ msgstr ""
423423
424424#: qml/Launcher/Drawer.qml:250
425425msgid "Search…"
426−msgstr "ค้นหา…"
426+msgstr ""
427427
428428#: qml/Launcher/PullToRefreshScopeStyle.qml:56
429429msgid "Pull to refresh…"
@@ -435,7 +435,7 @@ msgstr ""
435435
436436#: qml/Notifications/NotificationMenuItemFactory.qml:124
437437msgid "Show password"
438−msgstr "แสดงรหัสผ่าน"
438+msgstr ""
439439
440440#: qml/Panel/ActiveCallHint.qml:79
441441msgid "Tap to return to call..."
@@ -699,7 +699,7 @@ msgstr ""
699699
700700#: qml/Wizard/Pages/80-finished.qml:126
701701msgid "Continue"
702−msgstr "ดำเนินการต่อ"
702+msgstr ""
703703
704704#: qml/Wizard/Pages/80-finished.qml:126
705705msgid "Get Started"
po/ur.po+28−31
@@ -8,100 +8,99 @@ msgstr ""
88"Project-Id-Version: unity8\n"
99"Report-Msgid-Bugs-To: \n"
1010"POT-Creation-Date: 2021-01-29 11:42-0600\n"
11−"PO-Revision-Date: 2022-02-11 14:14+0000\n"
12−"Last-Translator: Muhammad <alimuhammad42620@gmail.com>\n"
13−"Language-Team: Urdu <https://translate.ubports.com/projects/ubports/lomiri/"
14−"ur/>\n"
11+"PO-Revision-Date: 2015-05-05 03:12+0000\n"
12+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
13+"Language-Team: Urdu <ur@li.org>\n"
1514"Language: ur\n"
1615"MIME-Version: 1.0\n"
1716"Content-Type: text/plain; charset=UTF-8\n"
1817"Content-Transfer-Encoding: 8bit\n"
1918"Plural-Forms: nplurals=2; plural=n != 1;\n"
20−"X-Generator: Weblate 3.11.3\n"
2119"X-Launchpad-Export-Date: 2016-11-23 06:36+0000\n"
20+"X-Generator: Launchpad (build 18269)\n"
2221
2322#: plugins/LightDM/Greeter.cpp:199
2423msgid "Password: "
25−msgstr "پاسورڈ "
24+msgstr ""
2625
2726#: plugins/LightDM/Greeter.cpp:214
2827msgid "Username"
29−msgstr "یوزر نیم"
28+msgstr ""
3029
3130#: plugins/LightDM/Greeter.cpp:258
3231msgid "Failed to authenticate"
33−msgstr "تصدیق میں ناکامی"
32+msgstr ""
3433
3534#: plugins/LightDM/Greeter.cpp:260
3635msgid "Invalid password, please try again"
37−msgstr "پاسورڈ غلط ھے، دوبارا کوشش کریں"
36+msgstr ""
3837
3938#: plugins/LightDM/Greeter.cpp:270
4039msgid "Log In"
41−msgstr "لاگ ان کریں"
40+msgstr ""
4241
4342#: plugins/LightDM/Greeter.cpp:270
4443msgid "Retry"
45−msgstr "دوبارا کوشش کریں"
44+msgstr ""
4645
4746#: plugins/LightDM/UsersModel.cpp:157
4847msgid "Login"
49−msgstr "لاگ ان کریں"
48+msgstr ""
5049
5150#: plugins/LightDM/UsersModel.cpp:165
5251msgid "Guest Session"
53−msgstr "مہمان سیشن"
52+msgstr ""
5453
5554#: plugins/Unity/Launcher/launcheritem.cpp:48
5655#: plugins/Unity/Launcher/launcheritem.cpp:122
5756msgid "Pin shortcut"
58−msgstr "شورت کٹ پن کیجیے"
57+msgstr ""
5958
6059#: plugins/Unity/Launcher/launcheritem.cpp:54
6160msgid "Quit"
62−msgstr "چھوڑیں"
61+msgstr ""
6362
6463#: plugins/Unity/Launcher/launcheritem.cpp:122
6564msgid "Unpin shortcut"
66−msgstr "شورت کٹ انپن کیجیے"
65+msgstr ""
6766
6867#: qml/Components/Dialogs.qml:176
6968msgctxt "Title: Lock/Log out dialog"
7069msgid "Log out"
71−msgstr "لاگ آؤٹ کیجیے"
70+msgstr ""
7271
7372#: qml/Components/Dialogs.qml:177
7473msgid "Are you sure you want to log out?"
75−msgstr "کیا آپ لاگ آؤٹ کرنا چاہتے ہیں؟"
74+msgstr ""
7675
7776#: qml/Components/Dialogs.qml:180
7877msgctxt "Button: Lock the system"
7978msgid "Lock"
80−msgstr "بند"
79+msgstr ""
8180
8281#: qml/Components/Dialogs.qml:191
8382msgctxt "Button: Log out from the system"
8483msgid "Log Out"
85−msgstr "لاگ آؤٹ"
84+msgstr ""
8685
8786#: qml/Components/Dialogs.qml:199 qml/Components/Dialogs.qml:284
8887#: qml/Greeter/NarrowView.qml:215 qml/Wizard/Pages/passcode-confirm.qml:32
8988#: qml/Wizard/Pages/passcode-set.qml:32
9089msgid "Cancel"
91−msgstr "نہیں"
90+msgstr ""
9291
9392#: qml/Components/Dialogs.qml:211
9493msgctxt "Title: Reboot dialog"
9594msgid "Reboot"
96−msgstr "ریبوٹ"
95+msgstr ""
9796
9897#: qml/Components/Dialogs.qml:212
9998msgid "Are you sure you want to reboot?"
100−msgstr "کیا آپ ریبوٹ کرنا چاہتے ہیں؟"
99+msgstr ""
101100
102101#: qml/Components/Dialogs.qml:215
103102msgid "No"
104−msgstr "نہیں"
103+msgstr ""
105104
106105#: qml/Components/Dialogs.qml:223
107106msgid "Yes"
@@ -117,22 +116,20 @@ msgid ""
117116"Are you sure you would like\n"
118117"to power off?"
119118msgstr ""
120−"کیا آپ موبائل کو\n"
121−" بند کرنا چاہتے ہیں؟"
122119
123120#: qml/Components/Dialogs.qml:248
124121msgctxt "Button: Power off the system"
125122msgid "Power off"
126−msgstr "بند"
123+msgstr ""
127124
128125#: qml/Components/Dialogs.qml:263
129126msgctxt "Button: Restart the system"
130127msgid "Restart"
131−msgstr "ریسارٹ"
128+msgstr ""
132129
133130#: qml/Components/Dialogs.qml:276
134131msgid "Screenshot"
135−msgstr "سکرین کی تصویر"
132+msgstr ""
136133
137134#: qml/Components/KeyboardShortcutsOverlay.qml:41
138135msgid "Keyboard Shortcuts"
@@ -524,7 +521,7 @@ msgstr ""
524521
525522#: qml/Panel/PanelMenuPage.qml:92
526523msgid "Back"
527−msgstr "پیچھے"
524+msgstr ""
528525
529526#: qml/Stage/SideStage.qml:76
530527msgid "Drag using 3 fingers any application from one window to the other"
@@ -780,7 +777,7 @@ msgstr ""
780777
781778#: qml/Wizard/Pages/sim.qml:59
782779msgid "Restart"
783−msgstr "دوباره شروع"
780+msgstr ""
784781
785782#: qml/Wizard/Pages/sim.qml:78
786783msgid "You won’t be able to make calls or use text messaging without a SIM."
qml/Greeter/ButtonPrompt.qmldeleted−51
@@ -1,51 +0,0 @@
1−import QtQuick 2.4
2−import Ubuntu.Components 1.3
3−import "../Components"
4−
5−FocusScope {
6− id: root
7− objectName: "promptButton"
8−
9− property alias text: buttonLabel.text
10− property alias interactive: root.enabled
11− property bool isSecret
12− property bool loginError: false
13− property bool hasKeyboard: false
14− property string enteredText: ""
15−
16− signal clicked()
17− signal canceled()
18− signal accepted(string response)
19−
20− Keys.onSpacePressed: clicked();
21− Keys.onReturnPressed: clicked();
22− Keys.onEnterPressed: clicked();
23−
24− anchors.fill: parent
25−
26− activeFocusOnTab: true
27−
28− Rectangle {
29− anchors.fill: parent
30− radius: units.gu(0.5)
31− color: "#7A111111"
32− Behavior on border.color {
33− ColorAnimation{}
34− }
35− border {
36− color: root.loginError ? theme.palette.normal.negative : theme.palette.normal.raisedSecondaryText
37− width: root.loginError ? units.dp(2): units.dp(1)
38− }
39− }
40−
41− MouseArea {
42− anchors.fill: parent
43− onClicked: parent.clicked();
44− }
45−
46− Label {
47− id: buttonLabel
48− anchors.centerIn: parent
49− color: theme.palette.normal.raisedSecondaryText
50− }
51−}
qml/Greeter/CoverPage.qml+8−27
@@ -16,7 +16,6 @@
1616 */
1717
1818import QtQuick 2.4
19−import QtGraphicalEffects 1.12
2019import Ubuntu.Components 1.3
2120import Ubuntu.Gestures 0.1
2221import "../Components"
@@ -29,16 +28,11 @@ Showable {
2928 property alias background: greeterBackground.source
3029 property alias backgroundSourceSize: greeterBackground.sourceSize
3130 property alias hasCustomBackground: backgroundShade.visible
32− property alias backgroundShadeOpacity: backgroundShade.opacity
3331 property real panelHeight
3432 property var infographicModel
3533 property bool draggable: true
3634
37− property bool showInfographic: false
38− property real infographicsLeftMargin: 0
39− property real infographicsTopMargin: 0
40− property real infographicsRightMargin: 0
41− property real infographicsBottomMargin: 0
35+ property alias infographics: infographics
4236
4337 readonly property real showProgress: MathUtils.clamp((width - Math.abs(x + launcherOffset)) / width, 0, 1)
4438
@@ -105,17 +99,18 @@ Showable {
10599 objectName: "backgroundShade"
106100 anchors.fill: parent
107101 color: "black"
102+ opacity: 0.4
108103 visible: false
109104 }
110105
111− Item {
112− id: infographicsArea
106+ Infographics {
107+ id: infographics
108+ objectName: "infographics"
109+ model: root.infographicModel
110+ clip: true // clip large data bubbles
113111
114112 anchors {
115− leftMargin: root.infographicsLeftMargin
116− topMargin: root.infographicsTopMargin ? root.infographicsTopMargin : root.panelHeight
117− rightMargin: root.infographicsRightMargin
118− bottomMargin: root.infographicsBottomMargin
113+ topMargin: root.panelHeight
119114 top: parent.top
120115 bottom: parent.bottom
121116 left: parent.left
@@ -123,20 +118,6 @@ Showable {
123118 }
124119 }
125120
126− Loader {
127− id: infographicsLoader
128− objectName: "infographicsLoader"
129− active: root.showInfographic && infographicsArea.width > units.gu(32)
130− anchors.fill: infographicsArea
131−
132− sourceComponent:Infographics {
133− id: infographics
134− objectName: "infographics"
135− model: root.infographicModel
136− clip: true // clip large data bubbles
137− }
138− }
139−
140121 Label {
141122 id: swipeHint
142123 objectName: "swipeHint"
qml/Greeter/Greeter.qml+2−37
@@ -58,7 +58,6 @@ Showable {
5858 readonly property bool locked: LightDMService.greeter.active && !LightDMService.greeter.authenticated && !forcedUnlock
5959
6060 property bool tabletMode
61− property string usageMode
6261 property url viewSource // only used for testing
6362
6463 property int failedLoginsDelayAttempts: 7 // number of failed logins
@@ -67,11 +66,6 @@ Showable {
6766
6867 readonly property bool animating: loader.item ? loader.item.animating : false
6968
70− property rect inputMethodRect
71−
72− property bool hasKeyboard: false
73− property int orientation
74−
7569 signal tease()
7670 signal sessionStarted()
7771 signal emergencyCall()
@@ -358,7 +352,8 @@ Showable {
358352 anchors.fill: parent
359353
360354 active: root.required
361− source: root.viewSource.toString() ? root.viewSource : "GreeterView.qml"
355+ source: root.viewSource.toString() ? root.viewSource :
356+ (d.multiUser || root.tabletMode) ? "WideView.qml" : "NarrowView.qml"
362357
363358 onLoaded: {
364359 root.lockedApp = "";
@@ -465,36 +460,6 @@ Showable {
465460 property: "infographicModel"
466461 value: LightDMService.infographic
467462 }
468−
469− Binding {
470− target: loader.item
471− property: "inputMethodRect"
472− value: root.inputMethodRect
473− }
474−
475− Binding {
476− target: loader.item
477− property: "hasKeyboard"
478− value: root.hasKeyboard
479− }
480−
481− Binding {
482− target: loader.item
483− property: "usageMode"
484− value: root.usageMode
485− }
486−
487− Binding {
488− target: loader.item
489− property: "multiUser"
490− value: d.multiUser
491− }
492−
493− Binding {
494− target: loader.item
495− property: "orientation"
496− value: root.orientation
497− }
498463 }
499464
500465 Connections {
qml/Greeter/GreeterPrompt.qml+187−64
@@ -25,103 +25,226 @@ FocusScope {
2525 focus: true
2626
2727 property bool isPrompt
28− property bool isPinPrompt
28+ property bool isAlphanumeric
2929 property string text
3030 property bool isSecret
3131 property bool interactive: true
32− property bool loginError: false
33− readonly property string enteredText: loader.item.enteredText
34− property bool hasKeyboard: false
35− property bool waitingToAccept: false
32+ readonly property alias enteredText: passwordInput.text
3633
3734 signal clicked()
3835 signal canceled()
3936 signal accepted()
4037
38+ function showFakePassword() {
39+ // Just a silly hack for looking like 4 pin numbers got entered, if
40+ // a fingerprint was used and we happen to be using a pin. This was
41+ // a request from Design.
42+ if (isSecret && isPrompt && !isAlphanumeric) {
43+ passwordInput.text = "...."; // actual text doesn't matter
44+ }
45+ }
46+
4147 GSettings {
4248 id: unity8Settings
4349 schema.id: "com.canonical.Unity8"
4450 }
4551
46− onEnteredTextChanged: if (waitingToAccept) root.accepted()
52+ StyledItem {
53+ id: d
54+
55+ readonly property color textColor: passwordInput.enabled ? theme.palette.normal.raisedText
56+ : theme.palette.disabled.raisedText
57+ readonly property color selectedColor: passwordInput.enabled ? theme.palette.normal.raised
58+ : theme.palette.disabled.raised
59+ readonly property color drawColor: passwordInput.enabled ? theme.palette.normal.raisedSecondaryText
60+ : theme.palette.disabled.raisedSecondaryText
61+ readonly property color errorColor: passwordInput.enabled ? theme.palette.normal.negative
62+ : theme.palette.disabled.negative
63+ }
4764
48− function showFakePassword() {
49− // Just a silly hack for looking like 4 pin numbers got entered, if
50− // a fingerprint was used and we happen to be using a pin. This was
51− // a request from Design.
52− if (isSecret) {
53− loader.item.enteredText = "...."; // actual text doesn't matter
65+ Rectangle {
66+ anchors.fill: parent
67+ border.width: units.dp(1)
68+ border.color: d.drawColor
69+ radius: units.gu(0.5)
70+ color: "transparent"
71+ }
72+
73+ Component.onCompleted: updateFocus()
74+ onIsPromptChanged: updateFocus()
75+ function updateFocus() {
76+ if (root.isPrompt) {
77+ passwordInput.focus = true;
78+ } else {
79+ promptButton.focus = true;
5480 }
5581 }
5682
57− Loader {
58− id: loader
59− objectName: "promptLoader"
83+ StyledItem {
84+ id: promptButton
85+ objectName: "promptButton"
86+ anchors.fill: parent
87+ visible: !root.isPrompt
88+ activeFocusOnTab: true
6089
61− focus: true
90+ styleName: "FocusShape"
6291
63− anchors.fill: parent
92+ function triggered() {
93+ if (root.interactive) {
94+ root.clicked();
95+ }
96+ }
6497
65− Connections {
66− target: loader.item
67− onClicked: root.clicked()
68− onCanceled: root.canceled()
69− onAccepted: {
70− if (response == enteredText)
71− root.accepted();
72− else
73− waitingToAccept = true;
98+ Rectangle {
99+ height: parent.height;
100+ width: parent.width
101+ color: "transparent"
102+ border {
103+ color: d.textColor
104+ width: units.dp(1)
74105 }
75106 }
76107
77− Binding {
78− target: loader.item
79− property: "text"
80− value: root.text
108+ Keys.onSpacePressed: triggered();
109+ Keys.onReturnPressed: triggered();
110+ Keys.onEnterPressed: triggered();
111+ MouseArea {
112+ anchors.fill: parent
113+ onClicked: parent.triggered();
81114 }
82115
83− Binding {
84− target: loader.item
85− property: "isSecret"
86− value: root.isSecret
116+ Label {
117+ anchors.centerIn: parent
118+ color: d.textColor
119+ text: root.text
87120 }
121+ }
122+
123+ TextField {
124+ id: passwordInput
125+ objectName: "promptField"
126+ anchors.fill: parent
127+ visible: root.isPrompt
128+ opacity: fakeLabel.visible ? 0 : 1
129+ activeFocusOnTab: true
88130
89− Binding {
90− target: loader.item
91− property: "interactive"
92− value: root.interactive
131+ validator: RegExpValidator {
132+ regExp: root.isAlphanumeric ? /^.*$/ : /^\d{4}$/
93133 }
94134
95− Binding {
96− target: loader.item
97− property: "loginError"
98− value: root.loginError
135+ inputMethodHints: Qt.ImhSensitiveData | Qt.ImhNoPredictiveText |
136+ Qt.ImhMultiLine | // so OSK doesn't close on Enter
137+ (root.isAlphanumeric ? Qt.ImhNone : Qt.ImhDigitsOnly)
138+ echoMode: root.isSecret ? TextInput.Password : TextInput.Normal
139+ hasClearButton: false
140+
141+ readonly property real frameSpacing: units.gu(0.5)
142+
143+ style: StyledItem {
144+ anchors.fill: parent
145+ styleName: "FocusShape"
146+
147+ // Properties needed by TextField
148+ readonly property color color: d.textColor
149+ readonly property color selectedTextColor: d.selectedColor
150+ readonly property color selectionColor: d.textColor
151+ readonly property color borderColor: "transparent"
152+ readonly property color backgroundColor: "transparent"
153+ readonly property color errorColor: d.errorColor
154+ readonly property real frameSpacing: styledItem.frameSpacing
155+
156+ // Properties needed by FocusShape
157+ readonly property bool enabled: styledItem.enabled
158+ readonly property bool keyNavigationFocus: styledItem.keyNavigationFocus
159+ property bool activeFocusOnTab
99160 }
100161
101− Binding {
102− target: loader.item
103− property: "hasKeyboard"
104− value: root.hasKeyboard
162+ secondaryItem: [
163+ Row {
164+ id: extraIcons
165+ spacing: passwordInput.frameSpacing
166+ Icon {
167+ name: "keyboard-caps-enabled"
168+ height: units.gu(3)
169+ width: units.gu(3)
170+ color: d.textColor
171+ visible: root.isSecret && false // TODO: detect when caps lock is on
172+ }
173+ Icon {
174+ name: "input-keyboard-symbolic"
175+ height: units.gu(3)
176+ width: units.gu(3)
177+ color: d.textColor
178+ visible: !unity8Settings.alwaysShowOsk
179+ MouseArea {
180+ anchors.fill: parent
181+ onClicked: unity8Settings.alwaysShowOsk = true
182+ }
183+ }
184+ }
185+ ]
186+
187+ onDisplayTextChanged: {
188+ // We use onDisplayTextChanged instead of onTextChanged because
189+ // displayText changes after text and if we did this before it
190+ // updated, we would use the wrong displayText for fakeLabel.
191+ if (!isAlphanumeric && text.length >= 4) {
192+ // hard limit of 4 for passcodes right now
193+ respond();
194+ }
105195 }
106196
107− onLoaded: loader.item.focus = true
108− }
197+ onAccepted: respond()
109198
110− states: [
111− State {
112− name: "ButtonPrompt"
113− when: !root.isPrompt
114− PropertyChanges { target: loader; source: "ButtonPrompt.qml" }
115− },
116− State {
117− name: "PinPrompt"
118− when: root.isPinPrompt
119− PropertyChanges { target: loader; source: "PinPrompt.qml" }
120− },
121− State {
122− name: "TextPrompt"
123− when: !root.isPinPrompt
124− PropertyChanges { target: loader; source: "TextPrompt.qml" }
199+ function respond() {
200+ if (root.interactive) {
201+ root.accepted();
202+ }
125203 }
126− ]
204+
205+ Keys.onEscapePressed: {
206+ root.canceled();
207+ event.accepted = true;
208+ }
209+
210+ // We use our own custom placeholder label instead of the standard
211+ // TextField one because the standard one hardcodes baseText as the
212+ // palette color, whereas we want raisedSecondaryText.
213+ Label {
214+ id: hint
215+ objectName: "promptHint"
216+ anchors {
217+ left: parent ? parent.left : undefined
218+ right: parent ? parent.right : undefined
219+ verticalCenter: parent ? parent.verticalCenter : undefined
220+ leftMargin: units.gu(1.5)
221+ rightMargin: anchors.leftMargin + extraIcons.width
222+ }
223+ text: root.text
224+ visible: passwordInput.text == "" && !passwordInput.inputMethodComposing
225+ enabled: visible
226+ color: d.drawColor
227+ elide: Text.ElideRight
228+ }
229+ }
230+
231+ // Have a fake label that covers the text field after the user presses
232+ // enter. What we *really* want is a disabled mode that doesn't lose OSK
233+ // focus. Because our goal here is simply to keep the OSK up while
234+ // we wait for PAM to get back to us, and while waiting, we don't want
235+ // the user to be able to edit the field (simply because it would look
236+ // weird if we allowed that). But until we have such a disabled mode,
237+ // we'll fake it by covering the real text field with a label.
238+ FadingLabel {
239+ id: fakeLabel
240+ anchors.verticalCenter: parent ? parent.verticalCenter : undefined
241+ anchors.left: parent ? parent.left : undefined
242+ anchors.right: parent ? parent.right : undefined
243+ anchors.leftMargin: passwordInput.frameSpacing * 2
244+ anchors.rightMargin: passwordInput.frameSpacing * 2 + extraIcons.width
245+ color: d.drawColor
246+ text: passwordInput.displayText
247+ visible: root.isPrompt && !root.interactive
248+ enabled: visible
249+ }
127250}
qml/Greeter/GreeterView.qmldeleted−534
@@ -1,534 +0,0 @@
1−/*
2− * Copyright (C) 2015-2016 Canonical, Ltd.
3− * Copyright (C) 2021 UBports Foundation
4− *
5− * This program is free software; you can redistribute it and/or modify
6− * it under the terms of the GNU General Public License as published by
7− * the Free Software Foundation; version 3.
8− *
9− * This program is distributed in the hope that it will be useful,
10− * but WITHOUT ANY WARRANTY; without even the implied warranty of
11− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12− * GNU General Public License for more details.
13− *
14− * You should have received a copy of the GNU General Public License
15− * along with this program. If not, see <http://www.gnu.org/licenses/>.
16− */
17−
18−import QtQuick 2.4
19−import QtQuick.Window 2.2
20−import QtGraphicalEffects 1.12
21−import Ubuntu.Components 1.3
22−import Ubuntu.Telephony 0.1 as Telephony
23−import "../Components"
24−
25−FocusScope {
26− id: root
27− objectName: "GreeterView"
28−
29− focus: true
30−
31− property url background
32− property real backgroundSourceSize
33− property real panelHeight
34− property bool hasCustomBackground
35− property alias dragHandleLeftMargin: coverPage.dragHandleLeftMargin
36− property var infographicModel
37− property alias launcherOffset: coverPage.launcherOffset
38− property alias currentIndex: loginList.currentIndex
39− property alias delayMinutes: delayedLockscreen.delayMinutes // TODO
40− property alias alphanumeric: loginList.alphanumeric
41− property alias hasKeyboard: loginList.hasKeyboard
42− property bool locked
43− property bool waiting
44− property var userModel // Set from outside
45− property bool multiUser: false
46− property int orientation
47− property bool isLandscape: root.orientation == Qt.LandscapeOrientation ||
48− root.orientation == Qt.InvertedLandscapeOrientation ||
49− usageMode == "desktop"
50− property bool isPortrait: (root.orientation == Qt.PortraitOrientation ||
51− root.orientation == Qt.InvertedPortraitOrientation) &&
52− usageMode != "desktop"
53−
54− property string usageMode
55−
56− readonly property bool animating: coverPage.showAnimation.running || coverPage.hideAnimation.running
57− readonly property bool fullyShown: coverPage.showProgress === 1 || lockscreen.shown
58− readonly property bool required: coverPage.required || lockscreen.required
59− readonly property alias sessionToStart: loginList.currentSession
60−
61− property rect inputMethodRect
62−
63− signal selected(int index)
64− signal responded(string response)
65− signal tease()
66− signal emergencyCall() // unused
67−
68− function notifyAuthenticationFailed() {
69− loginList.showError();
70− }
71−
72− function forceShow() {
73− coverPage.show();
74− }
75−
76− function tryToUnlock(toTheRight) {
77− var coverChanged = coverPage.shown;
78− if (toTheRight) {
79− coverPage.hideRight();
80− } else {
81− coverPage.hide();
82− }
83− if (root.locked) {
84− lockscreen.show();
85− loginList.tryToUnlock();
86− return false;
87− } else {
88− root.responded("");
89− return coverChanged;
90− }
91− }
92−
93− function hide() {
94− lockscreen.hide();
95− coverPage.hide();
96− }
97−
98− function showFakePassword() {
99− loginList.showFakePassword();
100− }
101−
102− function showErrorMessage(msg) {
103− coverPage.showErrorMessage(msg);
104− }
105−
106− onLockedChanged: changeLockscreenState()
107− onMultiUserChanged: changeLockscreenState()
108−
109− function changeLockscreenState() {
110− if (locked || multiUser) {
111− lockscreen.maybeShow();
112− } else {
113− lockscreen.hide();
114− }
115− }
116−
117− Keys.onSpacePressed: coverPage.hide();
118− Keys.onReturnPressed: coverPage.hide();
119− Keys.onEnterPressed: coverPage.hide();
120−
121− Rectangle {
122− anchors.fill: parent
123− color: "black"
124− opacity: lockscreen.showProgress * 0.8
125− }
126−
127− CoverPage {
128− id: lockscreen
129− objectName: "lockscreen"
130− height: parent.height
131− width: parent.width
132− draggable: false
133− state: "LoginList"
134−
135− background: root.background
136− backgroundSourceSize: root.backgroundSourceSize
137− panelHeight: root.panelHeight
138− hasCustomBackground: root.hasCustomBackground
139− backgroundShadeOpacity: 0.6
140−
141− showInfographic: isLandscape && root.usageMode != "phone" && (root.usageMode != "tablet" || root.multiUser) && !delayedLockscreen.visible
142− infographicModel: root.infographicModel
143−
144− shown: false
145− opacity: 0
146−
147− showAnimation: StandardAnimation { property: "opacity"; to: 1 }
148− hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
149−
150− infographicsTopMargin: parent.height * 0.125
151− infographicsBottomMargin: parent.height * 0.125
152− infographicsLeftMargin: loginList.x + loginList.width
153−
154− onTease: root.tease()
155−
156− onShowProgressChanged: {
157− if (showProgress === 0 && !root.locked) {
158− root.responded("");
159− }
160− }
161−
162− LoginList {
163− id: loginList
164− objectName: "loginList"
165−
166− width: units.gu(40)
167− anchors {
168− top: parent.top
169− bottom: parent.bottom
170− }
171−
172− boxVerticalOffset: (height - highlightedHeight -
173− inputMethodRect.height) / 2
174− Behavior on boxVerticalOffset { UbuntuNumberAnimation {} }
175−
176− enabled: !coverPage.shown && visible
177− visible: !delayedLockscreen.visible
178−
179− model: root.userModel
180− onResponded: root.responded(response)
181− onSelected: root.selected(index)
182− onSessionChooserButtonClicked: parent.state = "SessionsList"
183− onCurrentIndexChanged: setCurrentSession()
184−
185− locked: root.locked
186− waiting: root.waiting
187−
188− Keys.forwardTo: [sessionChooserLoader.item]
189−
190− Component.onCompleted: setCurrentSession()
191−
192− function setCurrentSession() {
193− currentSession = LightDMService.users.data(currentIndex, LightDMService.userRoles.SessionRole);
194− }
195− }
196−
197− DelayedLockscreen {
198− id: delayedLockscreen
199− objectName: "delayedLockscreen"
200− anchors.fill: parent
201− visible: delayMinutes > 0
202− alphaNumeric: loginList.alphanumeric
203− }
204−
205− function maybeShow() {
206− if ((root.locked || root.multiUser) && !shown) {
207− showNow();
208− }
209− }
210−
211− Loader {
212− id: sessionChooserLoader
213−
214− height: loginList.height
215− width: loginList.width
216−
217− anchors {
218− left: parent.left
219− leftMargin: Math.min(parent.width * 0.16, units.gu(20))
220− top: parent.top
221− }
222−
223− active: false
224−
225− onLoaded: sessionChooserLoader.item.forceActiveFocus();
226− onActiveChanged: {
227− if (!active) return;
228− item.updateHighlight(loginList.currentSession);
229− }
230−
231− Connections {
232− target: sessionChooserLoader.item
233− onSessionSelected: loginList.currentSession = sessionKey
234− onShowLoginList: {
235− lockscreen.state = "LoginList"
236− loginList.tryToUnlock();
237− }
238− ignoreUnknownSignals: true
239− }
240− }
241−
242− // Use an AbstractButton due to icon limitations with Button
243− AbstractButton {
244− id: sessionChooser
245− objectName: "sessionChooserButton"
246−
247− readonly property url icon: LightDMService.sessions.iconUrl(loginList.currentSession)
248−
249− visible: LightDMService.sessions.count > 1 &&
250− !LightDMService.users.data(loginList.currentUserIndex, LightDMService.userRoles.LoggedInRole)
251−
252− height: units.gu(3.5)
253− width: units.gu(3.5)
254−
255− activeFocusOnTab: true
256− anchors {
257− right: parent.right
258− rightMargin: units.gu(2)
259−
260− bottom: parent.bottom
261− bottomMargin: units.gu(1.5)
262− }
263−
264− Rectangle {
265− id: badgeHighlight
266−
267− anchors.fill: parent
268− visible: parent.activeFocus
269− color: "transparent"
270− border.color: theme.palette.normal.focus
271− border.width: units.dp(1)
272− radius: 3
273− }
274−
275− Icon {
276− id: badge
277− anchors.fill: parent
278− anchors.margins: units.dp(3)
279− keyColor: "#ffffff" // icon providers give us white icons
280− color: theme.palette.normal.raisedSecondaryText
281− source: sessionChooser.icon
282− }
283−
284− Keys.onReturnPressed: {
285− parent.state = "SessionsList";
286− }
287−
288− onClicked: {
289− parent.state = "SessionsList";
290− }
291−
292− // Refresh the icon path if looking at different places at runtime
293− // this is mainly for testing
294− Connections {
295− target: LightDMService.sessions
296− onIconSearchDirectoriesChanged: {
297− badge.source = LightDMService.sessions.iconUrl(root.currentSession)
298− }
299− }
300− }
301−
302− states: [
303− State {
304− name: "SessionsList"
305− PropertyChanges { target: loginList; opacity: 0 }
306− PropertyChanges { target: sessionChooserLoader;
307− active: true;
308− opacity: 1
309− source: "SessionsList.qml"
310− }
311− },
312−
313− State {
314− name: "LoginList"
315− PropertyChanges { target: loginList; opacity: 1 }
316− PropertyChanges { target: sessionChooserLoader;
317− active: false;
318− opacity: 0
319− source: "";
320− }
321− }
322− ]
323−
324− transitions: [
325− Transition {
326− from: "*"
327− to: "*"
328− UbuntuNumberAnimation {
329− property: "opacity";
330− }
331− }
332− ]
333−
334− Component.onCompleted: if (root.multiUser) showNow()
335− }
336−
337− Rectangle {
338− anchors.fill: parent
339− color: "black"
340− opacity: coverPage.showProgress * 0.8
341− }
342−
343− CoverPage {
344− id: coverPage
345− objectName: "coverPage"
346− height: parent.height
347− width: parent.width
348− background: root.background
349− hasCustomBackground: root.hasCustomBackground
350− backgroundShadeOpacity: 0.4
351− panelHeight: root.panelHeight
352− draggable: !root.waiting
353− onTease: root.tease()
354− onClicked: hide()
355− backgroundSourceSize: root.backgroundSourceSize
356− infographicModel: root.infographicModel
357−
358− showInfographic: !root.multiUser && root.usageMode != "desktop"
359−
360− onShowProgressChanged: {
361− if (showProgress === 0) {
362− if (lockscreen.shown) {
363− loginList.tryToUnlock();
364− } else {
365− root.responded("");
366− }
367− }
368− }
369−
370− Clock {
371− id: clock
372− anchors.centerIn: parent
373− }
374−
375− states: [
376− State {
377− name: "landscape-with-infographics"
378− when: isLandscape && coverPage.showInfographic
379− AnchorChanges {
380− target: clock
381− anchors.top: undefined
382− anchors.horizontalCenter: undefined
383− anchors.verticalCenter: undefined
384− }
385− PropertyChanges {
386− target: clock;
387− anchors.topMargin: undefined
388− anchors.centerIn: coverPage
389− anchors.horizontalCenterOffset: - coverPage.width / 2 + clock.width / 2 + units.gu(8)
390− }
391− PropertyChanges {
392− target: coverPage
393− infographicsLeftMargin: clock.width + units.gu(8)
394− }
395− },
396− State {
397− name: "portrait"
398− when: isPortrait && coverPage.showInfographic
399− AnchorChanges {
400− target: clock;
401− anchors.top: coverPage.top
402− anchors.horizontalCenter: coverPage.horizontalCenter
403− anchors.verticalCenter: undefined
404− }
405− PropertyChanges {
406− target: clock;
407− anchors.topMargin: units.gu(2) + panelHeight
408− anchors.centerIn: undefined
409− anchors.horizontalCenterOffset: 0
410− }
411− PropertyChanges {
412− target: coverPage
413− infographicsLeftMargin: 0
414− }
415− },
416− State {
417− name: "without-infographics"
418− when: !coverPage.showInfographic
419− AnchorChanges {
420− target: clock
421− anchors.top: undefined
422− anchors.horizontalCenter: coverPage.horizontalCenter
423− anchors.verticalCenter: coverPage.verticalCenter
424− }
425− PropertyChanges {
426− target: clock;
427− anchors.topMargin: 0
428− anchors.centerIn: undefined
429− anchors.horizontalCenterOffset: 0
430− }
431− PropertyChanges {
432− target: coverPage
433− infographicsLeftMargin: 0
434− }
435− }
436− ]
437− }
438−
439− StyledItem {
440− id: bottomBar
441− visible: usageMode == "phone" && lockscreen.shown
442− height: units.gu(4)
443−
444− anchors.left: parent.left
445− anchors.right: parent.right
446− anchors.top: parent.bottom
447− anchors.topMargin: - height * (1 - coverPage.showProgress)
448− - ( inputMethodRect.height )
449−
450− Label {
451− text: i18n.tr("Cancel")
452− anchors.left: parent.left
453− anchors.leftMargin: units.gu(2)
454− anchors.top: parent.top
455− anchors.bottom: parent.bottom
456− verticalAlignment: Text.AlignVCenter
457− font.weight: Font.Light
458− fontSize: "small"
459− color: theme.palette.normal.raisedSecondaryText
460−
461− AbstractButton {
462− anchors.fill: parent
463− anchors.leftMargin: -units.gu(2)
464− anchors.rightMargin: -units.gu(2)
465− onClicked: coverPage.show()
466− }
467− }
468−
469− Label {
470− objectName: "emergencyCallLabel"
471− text: callManager.hasCalls ? i18n.tr("Return to Call") : i18n.tr("Emergency")
472− anchors.right: parent.right
473− anchors.rightMargin: units.gu(2)
474− anchors.top: parent.top
475− anchors.bottom: parent.bottom
476− verticalAlignment: Text.AlignVCenter
477− font.weight: Font.Light
478− fontSize: "small"
479− color: theme.palette.normal.raisedSecondaryText
480− // TODO: uncomment once bug 1616538 is fixed
481− // visible: telepathyHelper.ready && telepathyHelper.emergencyCallsAvailable
482− enabled: visible
483−
484− AbstractButton {
485− anchors.fill: parent
486− anchors.leftMargin: -units.gu(2)
487− anchors.rightMargin: -units.gu(2)
488− onClicked: root.emergencyCall()
489− }
490− }
491− }
492−
493− states: [
494− State {
495− name: "phone"
496− when: root.usageMode == "phone" || (root.usageMode == "tablet" && isPortrait)
497− AnchorChanges {
498− target: loginList;
499− anchors.horizontalCenter: lockscreen.horizontalCenter;
500− anchors.left: undefined;
501− }
502− PropertyChanges {
503− target: loginList;
504− anchors.leftMargin: 0;
505− }
506− },
507− State {
508− name: "tablet"
509− when: root.usageMode == "tablet" && isLandscape
510− AnchorChanges {
511− target: loginList;
512− anchors.horizontalCenter: undefined;
513− anchors.left: lockscreen.left;
514− }
515− PropertyChanges {
516− target: loginList;
517− anchors.leftMargin: Math.min(lockscreen.width * 0.16, units.gu(8));
518− }
519− },
520− State {
521− name: "desktop"
522− when: root.usageMode == "desktop"
523− AnchorChanges {
524− target: loginList;
525− anchors.horizontalCenter: undefined;
526− anchors.left: lockscreen.left;
527− }
528− PropertyChanges {
529− target: loginList;
530− anchors.leftMargin: Math.min(lockscreen.width * 0.16, units.gu(20));
531− }
532− }
533− ]
534−}
qml/Greeter/LoginAreaContainer.qml+3−3
@@ -21,18 +21,18 @@ Item {
2121 BorderImage {
2222 anchors {
2323 fill: parent
24− topMargin: -units.gu(3)
24+ topMargin: -units.gu(1)
2525 leftMargin: -units.gu(1.5)
2626 rightMargin: -units.gu(1.5)
2727 bottomMargin: -units.gu(1.5)
2828 }
2929 source: "../graphics/dropshadow2gu.sci"
30− opacity: 0
30+ opacity: 0.35
3131 }
3232
3333 UbuntuShape {
3434 anchors.fill: parent
3535 aspect: UbuntuShape.Flat
36− backgroundColor: "transparent"
36+ backgroundColor: theme.palette.normal.raised
3737 }
3838}
qml/Greeter/LoginList.qml+98−71
@@ -26,7 +26,6 @@ StyledItem {
2626
2727 property alias model: userList.model
2828 property alias alphanumeric: promptList.alphanumeric
29− property alias hasKeyboard: promptList.hasKeyboard
3029 property int currentIndex
3130 property bool locked
3231 property bool waiting
@@ -40,8 +39,6 @@ StyledItem {
4039 property string currentSession // Initially set by LightDM
4140 readonly property string currentUser: userList.currentItem.username
4241
43− readonly property alias currentUserIndex: userList.currentIndex
44−
4542 signal responded(string response)
4643 signal selected(int index)
4744 signal sessionChooserButtonClicked()
@@ -51,7 +48,6 @@ StyledItem {
5148 }
5249
5350 function showError() {
54− promptList.loginError = true;
5551 wrongPasswordAnimation.start();
5652 }
5753
@@ -83,7 +79,6 @@ StyledItem {
8379
8480 onCurrentIndexChanged: {
8581 userList.currentIndex = currentIndex;
86− promptList.loginError = false;
8782 }
8883
8984 LoginAreaContainer {
@@ -98,6 +93,18 @@ StyledItem {
9893
9994 height: Math.max(units.gu(15), promptList.height + units.gu(8))
10095 Behavior on height { NumberAnimation { duration: root.moveDuration; easing.type: Easing.InOutQuad; } }
96+
97+ Label {
98+ // HACK: Work around https://github.com/ubports/unity8/issues/185
99+ text: _realName ? _realName : LightDMService.greeter.authenticationUser
100+ visible: userList.count == 1
101+ anchors {
102+ left: parent.left
103+ top: parent.top
104+ topMargin: units.gu(2)
105+ leftMargin: units.gu(2)
106+ }
107+ }
101108 }
102109
103110 ListView {
@@ -108,31 +115,26 @@ StyledItem {
108115 anchors.leftMargin: units.gu(2)
109116 anchors.rightMargin: units.gu(2)
110117
111− preferredHighlightBegin: highlightItem.y + units.gu(1.5)
112− preferredHighlightEnd: highlightItem.y + units.gu(1.5)
118+ preferredHighlightBegin: highlightItem.y
119+ preferredHighlightEnd: highlightItem.y
113120 highlightRangeMode: ListView.StrictlyEnforceRange
114121 highlightMoveDuration: root.moveDuration
115122 interactive: count > 1
116123
117124 readonly property bool movingInternally: moveTimer.running || userList.moving
118125
119− onMovingChanged: if (!moving) root.selected(currentIndex)
120−
121126 onCurrentIndexChanged: {
122127 moveTimer.start();
123128 }
124129
125− onCountChanged: if (root.currentIndex >= count) root.selected(0)
126−
127130 delegate: Item {
128131 width: userList.width
129132 height: root.cellHeight
130133
131134 readonly property bool belowHighlight: (userList.currentIndex < 0 && index > 0) || (userList.currentIndex >= 0 && index > userList.currentIndex)
132− readonly property bool aboveCurrent: (userList.currentIndex > 0 && index < 0) || (userList.currentIndex >= 0 && index < userList.currentIndex)
133135 readonly property int belowOffset: root.highlightedHeight - root.cellHeight
134136 readonly property string userSession: session
135− readonly property string username: name ? name : ""
137+ readonly property string username: name
136138
137139 opacity: {
138140 // The goal here is to make names less and less opaque as they
@@ -152,74 +154,40 @@ StyledItem {
152154 return 1 - Math.min(1, (Math.abs(highlightDist) + root.cellHeight) / ((root.numAboveBelow + 1) * root.cellHeight))
153155 }
154156
155− Row {
156− spacing: units.gu(1)
157−// visible: userList.count != 1 // HACK Hide username label until someone sorts out the anchoring with the keyboard-dismiss animation, Work around https://github.com/ubports/unity8/issues/185
157+ FadingLabel {
158+ objectName: "username" + index
159+ visible: userList.count != 1 // HACK Hide username label until someone sorts out the anchoring with the keyboard-dismiss animation, Work around https://github.com/ubports/unity8/issues/185
158160
159161 anchors {
162+ left: parent.left
160163 leftMargin: units.gu(2)
164+ right: parent.right
161165 rightMargin: units.gu(2)
162− horizontalCenter: parent.horizontalCenter
163166 bottom: parent.top
164167 // Add an offset to bottomMargin for any items below the highlight
165− bottomMargin: -(units.gu(4) + (parent.belowHighlight ? parent.belowOffset : parent.aboveCurrent ? -units.gu(5) : 0))
168+ bottomMargin: -(units.gu(4) + (parent.belowHighlight ? parent.belowOffset : 0))
166169 }
170+ text: userList.currentIndex === index
171+ && name === "*other"
172+ && LightDMService.greeter.authenticationUser !== ""
173+ ? LightDMService.greeter.authenticationUser : realName
174+ color: userList.currentIndex !== index ? theme.palette.normal.raised
175+ : theme.palette.normal.raisedText
176+
177+ Component.onCompleted: _realName = realName
178+
179+ Behavior on anchors.topMargin { NumberAnimation { duration: root.moveDuration; easing.type: Easing.InOutQuad; } }
167180
168181 Rectangle {
169182 id: activeIndicator
183+ anchors.horizontalCenter: parent.left
184+ anchors.horizontalCenterOffset: -units.gu(1)
170185 anchors.verticalCenter: parent.verticalCenter
171− color: theme.palette.normal.raised
186+ color: userList.currentIndex !== index ? theme.palette.normal.raised
187+ : theme.palette.normal.focus
172188 visible: userList.count > 1 && loggedIn
173− height: visible ? units.gu(0.5) : 0
174− width: height
175− }
176−
177− Icon {
178− id: userIcon
179− name: "account"
180− height: userList.currentIndex === index ? units.gu(4.5) : units.gu(3)
189+ height: units.gu(0.5)
181190 width: height
182− color: theme.palette.normal.raisedSecondaryText
183− anchors.verticalCenter: parent.verticalCenter
184− }
185−
186− Column {
187− anchors.verticalCenter: parent.verticalCenter
188− spacing: units.gu(0.25)
189−
190− FadingLabel {
191− objectName: "username" + index
192−
193− text: userList.currentIndex === index
194− && name === "*other"
195− && LightDMService.greeter.authenticationUser !== ""
196− ? LightDMService.greeter.authenticationUser : realName ? realName : ""
197− color: userList.currentIndex !== index ? theme.palette.normal.raised
198− : theme.palette.normal.raisedSecondaryText
199− font.weight: userList.currentIndex === index ? Font.Normal : Font.Light
200− font.pointSize: units.gu(2)
201−
202− width: highlightItem.width
203− && contentWidth > highlightItem.width - userIcon.width - units.gu(4)
204− ? highlightItem.width - userIcon.width - units.gu(4)
205− : contentWidth
206−
207− Component.onCompleted: _realName = realName
208−
209− Behavior on anchors.topMargin { NumberAnimation { duration: root.moveDuration; easing.type: Easing.InOutQuad; } }
210− }
211−
212− Row {
213− spacing: units.gu(1)
214−
215− FadingLabel {
216− text: root.alphanumeric ? "Login with password" : "Login with pin"
217− color: theme.palette.normal.raisedSecondaryText
218− visible: userList.currentIndex === index && false
219− font.weight: Font.Light
220− font.pointSize: units.gu(1.25)
221− }
222− }
223191 }
224192 }
225193
@@ -229,7 +197,7 @@ StyledItem {
229197 right: parent.right
230198 top: parent.top
231199 // Add an offset to topMargin for any items below the highlight
232− topMargin: parent.belowHighlight ? parent.belowOffset : parent.aboveCurrent ? -units.gu(5) : 0
200+ topMargin: parent.belowHighlight ? parent.belowOffset : 0
233201 }
234202 height: parent.height
235203 enabled: userList.currentIndex !== index && parent.opacity > 0
@@ -249,6 +217,67 @@ StyledItem {
249217 }
250218 }
251219
220+ // Use an AbstractButton due to icon limitations with Button
221+ AbstractButton {
222+ id: sessionChooser
223+ objectName: "sessionChooserButton"
224+
225+ readonly property alias icon: badge.source
226+
227+ visible: LightDMService.sessions.count > 1 &&
228+ !LightDMService.users.data(userList.currentIndex, LightDMService.userRoles.LoggedInRole)
229+
230+ height: units.gu(3.5)
231+ width: units.gu(3.5)
232+
233+ activeFocusOnTab: true
234+ anchors {
235+ right: highlightItem.right
236+ rightMargin: units.gu(2)
237+
238+ top: highlightItem.top
239+ topMargin: units.gu(1.5)
240+ }
241+
242+ Rectangle {
243+ id: badgeHighlight
244+
245+ anchors.fill: parent
246+ visible: parent.activeFocus
247+ color: "transparent"
248+ border.color: theme.palette.normal.focus
249+ border.width: units.dp(1)
250+ radius: 3
251+ }
252+
253+ Icon {
254+ id: badge
255+ anchors.fill: parent
256+ anchors.margins: units.dp(3)
257+ keyColor: "#ffffff" // icon providers give us white icons
258+ color: theme.palette.normal.raisedSecondaryText
259+ source: LightDMService.sessions.iconUrl(root.currentSession)
260+ }
261+
262+ Keys.onReturnPressed: {
263+ sessionChooserButtonClicked();
264+ event.accepted = true;
265+ }
266+
267+ onClicked: {
268+ sessionChooserButtonClicked();
269+ }
270+
271+ // Refresh the icon path if looking at different places at runtime
272+ // this is mainly for testing
273+ Connections {
274+ target: LightDMService.sessions
275+ onIconSearchDirectoriesChanged: {
276+ badge.source = LightDMService.sessions.iconUrl(root.currentSession)
277+ }
278+ }
279+ }
280+
252281 PromptList {
253282 id: promptList
254283 objectName: "promptList"
@@ -259,8 +288,6 @@ StyledItem {
259288 }
260289 width: highlightItem.width - anchors.margins * 2
261290
262− focus: true
263−
264291 onClicked: {
265292 interactive = false;
266293 if (root.locked) {
qml/Greeter/NarrowView.qmlnew file+274
@@ -0,0 +1,274 @@
1+/*
2+ * Copyright (C) 2015-2016 Canonical, Ltd.
3+ * Copyright (C) 2021 UBports Foundation
4+ *
5+ * This program is free software; you can redistribute it and/or modify
6+ * it under the terms of the GNU General Public License as published by
7+ * the Free Software Foundation; version 3.
8+ *
9+ * This program is distributed in the hope that it will be useful,
10+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
11+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+ * GNU General Public License for more details.
13+ *
14+ * You should have received a copy of the GNU General Public License
15+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
16+ */
17+
18+import QtQuick 2.4
19+import QtQuick.Window 2.2
20+import Ubuntu.Components 1.3
21+import Ubuntu.Telephony 0.1 as Telephony
22+import "../Components"
23+
24+FocusScope {
25+ id: root
26+
27+ property alias dragHandleLeftMargin: coverPage.dragHandleLeftMargin
28+ property alias launcherOffset: coverPage.launcherOffset
29+ property alias currentIndex: loginList.currentIndex
30+ property alias delayMinutes: delayedLockscreen.delayMinutes
31+ property real panelHeight
32+ property url background
33+ property real backgroundSourceSize
34+ property bool hasCustomBackground
35+ property bool locked
36+ property alias alphanumeric: loginList.alphanumeric
37+ property alias userModel: loginList.model
38+ property alias infographicModel: coverPage.infographicModel
39+ property string sessionToStart
40+ property bool waiting
41+ readonly property bool fullyShown: coverPage.showProgress === 1 || lockscreen.shown
42+ readonly property bool required: coverPage.required || lockscreen.required
43+ readonly property bool animating: coverPage.showAnimation.running || coverPage.hideAnimation.running
44+
45+ // so that it can be replaced in tests with a mock object
46+ property var inputMethod: Qt.inputMethod
47+
48+ signal selected(int index)
49+ signal responded(string response)
50+ signal tease()
51+ signal emergencyCall()
52+
53+ function hide() {
54+ lockscreen.hide();
55+ coverPage.hide();
56+ }
57+
58+ function showFakePassword() {
59+ loginList.showFakePassword();
60+ }
61+
62+ function notifyAuthenticationFailed() {
63+ loginList.showError();
64+ }
65+
66+ function showErrorMessage(msg) {
67+ coverPage.showErrorMessage(msg);
68+ }
69+
70+ function forceShow() {
71+ coverPage.show();
72+ }
73+
74+ function tryToUnlock(toTheRight) {
75+ var coverChanged = coverPage.shown;
76+ lockscreen.maybeShow();
77+ if (toTheRight) {
78+ coverPage.hideRight();
79+ } else {
80+ coverPage.hide();
81+ }
82+ return coverChanged;
83+ }
84+
85+ onLockedChanged: {
86+ if (locked) {
87+ lockscreen.maybeShow();
88+ } else {
89+ lockscreen.hide();
90+ }
91+ }
92+
93+ Showable {
94+ id: lockscreen
95+ objectName: "lockscreen"
96+ anchors.fill: parent
97+ shown: false
98+ opacity: 0
99+
100+ showAnimation: StandardAnimation { property: "opacity"; to: 1 }
101+ hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
102+
103+ Rectangle {
104+ // In case background fails to load or doesn't cover the whole screen
105+ id: backgroundBackup
106+ anchors.fill: parent
107+ color: "black"
108+ }
109+
110+ Wallpaper {
111+ id: lockscreenBackground
112+ objectName: "lockscreenBackground"
113+ anchors {
114+ fill: parent
115+ }
116+ source: root.background
117+ sourceSize: root.backgroundSourceSize
118+ }
119+
120+ // Darken background to match CoverPage
121+ Rectangle {
122+ objectName: "lockscreenShade"
123+ anchors.fill: parent
124+ color: "black"
125+ opacity: root.hasCustomBackground ? 0.4 : 0
126+ }
127+
128+ LoginList {
129+ id: loginList
130+ objectName: "loginList"
131+
132+ anchors {
133+ horizontalCenter: parent.horizontalCenter
134+ top: parent.top
135+ bottom: parent.bottom
136+ }
137+ width: units.gu(40)
138+ boxVerticalOffset: units.gu(14)
139+ enabled: !coverPage.shown && visible
140+ visible: !delayedLockscreen.visible
141+
142+ locked: root.locked
143+
144+ onSelected: if (enabled) root.selected(index)
145+ onResponded: root.responded(response)
146+ }
147+
148+ DelayedLockscreen {
149+ id: delayedLockscreen
150+ objectName: "delayedLockscreen"
151+ anchors.fill: parent
152+ visible: delayMinutes > 0
153+ alphaNumeric: loginList.alphanumeric
154+ }
155+
156+ function maybeShow() {
157+ if (root.locked && !shown) {
158+ showNow();
159+ }
160+ }
161+ }
162+
163+ Rectangle {
164+ anchors.fill: parent
165+ color: "black"
166+ opacity: coverPage.showProgress * 0.8
167+ }
168+
169+ CoverPage {
170+ id: coverPage
171+ objectName: "coverPage"
172+ height: parent.height
173+ width: parent.width
174+ background: root.background
175+ hasCustomBackground: root.hasCustomBackground
176+ panelHeight: root.panelHeight
177+ draggable: !root.waiting
178+ onTease: root.tease()
179+ onClicked: hide()
180+ backgroundSourceSize: root.backgroundSourceSize
181+
182+ onShowProgressChanged: {
183+ if (showProgress === 0) {
184+ if (lockscreen.shown) {
185+ loginList.tryToUnlock();
186+ } else {
187+ root.responded("");
188+ }
189+ }
190+ }
191+
192+ Clock {
193+ anchors {
194+ top: parent.top
195+ topMargin: units.gu(2) + panelHeight
196+ horizontalCenter: parent.horizontalCenter
197+ }
198+ }
199+ }
200+
201+ StyledItem {
202+ id: bottomBar
203+ visible: lockscreen.shown
204+ height: units.gu(4)
205+
206+ anchors.left: parent.left
207+ anchors.right: parent.right
208+ anchors.top: parent.bottom
209+ anchors.topMargin: - height * (1 - coverPage.showProgress)
210+ - (inputMethod && inputMethod.visible ?
211+ inputMethod.keyboardRectangle.height : 0)
212+
213+ Rectangle {
214+ color: UbuntuColors.porcelain // matches OSK background
215+ anchors.fill: parent
216+ }
217+
218+ Label {
219+ text: i18n.tr("Cancel")
220+ anchors.left: parent.left
221+ anchors.leftMargin: units.gu(2)
222+ anchors.top: parent.top
223+ anchors.bottom: parent.bottom
224+ verticalAlignment: Text.AlignVCenter
225+ font.weight: Font.Light
226+ fontSize: "small"
227+ color: UbuntuColors.slate
228+
229+ AbstractButton {
230+ anchors.fill: parent
231+ anchors.leftMargin: -units.gu(2)
232+ anchors.rightMargin: -units.gu(2)
233+ onClicked: coverPage.show()
234+ }
235+ }
236+
237+ Label {
238+ objectName: "emergencyCallLabel"
239+ text: callManager.hasCalls ? i18n.tr("Return to Call") : i18n.tr("Emergency")
240+ anchors.right: parent.right
241+ anchors.rightMargin: units.gu(2)
242+ anchors.top: parent.top
243+ anchors.bottom: parent.bottom
244+ verticalAlignment: Text.AlignVCenter
245+ font.weight: Font.Light
246+ fontSize: "small"
247+ color: UbuntuColors.slate
248+ // TODO: uncomment once bug 1616538 is fixed
249+ // visible: telepathyHelper.ready && telepathyHelper.emergencyCallsAvailable
250+ enabled: visible
251+
252+ AbstractButton {
253+ anchors.fill: parent
254+ anchors.leftMargin: -units.gu(2)
255+ anchors.rightMargin: -units.gu(2)
256+ onClicked: root.emergencyCall()
257+ }
258+ }
259+ }
260+
261+ // FIXME: It's difficult to keep something tied closely to the OSK (bug
262+ // 1616163). But as a hack to avoid the background peeking out,
263+ // we add an extra Rectangle that just serves to hide the background
264+ // during OSK animations.
265+ Rectangle {
266+ visible: bottomBar.visible
267+ height: inputMethod && inputMethod.visible ?
268+ inputMethod.keyboardRectangle.height : 0
269+ anchors.bottom: parent.bottom
270+ anchors.left: parent.left
271+ anchors.right: parent.right
272+ color: UbuntuColors.porcelain
273+ }
274+}
qml/Greeter/PinPrompt.qmldeleted−184
@@ -1,184 +0,0 @@
1−import QtQuick 2.4
2−import Ubuntu.Components 1.3
3−import "../Components"
4−
5−FocusScope {
6− id: root
7−
8− property string text
9− property bool isSecret
10− property bool interactive: true
11− property bool loginError: false
12− property bool hasKeyboard: false
13− property alias enteredText: passwordInput.text
14−
15− signal clicked()
16− signal canceled()
17− signal accepted(string response)
18−
19− StyledItem {
20− id: d
21−
22− readonly property color textColor: passwordInput.enabled ? theme.palette.normal.raisedText
23− : theme.palette.disabled.raisedText
24− readonly property color selectedColor: passwordInput.enabled ? theme.palette.normal.raised
25− : theme.palette.disabled.raised
26− readonly property color drawColor: passwordInput.enabled ? theme.palette.normal.raisedSecondaryText
27− : theme.palette.disabled.raisedSecondaryText
28− readonly property color errorColor: passwordInput.enabled ? theme.palette.normal.negative
29− : theme.palette.disabled.negative
30− }
31−
32− TextField {
33− id: passwordInput
34− objectName: "promptField"
35− anchors.left: extraIcons.left
36− focus: root.focus
37−
38− opacity: fakeLabel.visible ? 0 : 1
39− activeFocusOnTab: true
40−
41− onSelectedTextChanged: passwordInput.deselect()
42− onCursorPositionChanged: cursorPosition = length
43−
44− validator: RegExpValidator {
45− regExp: /^\d{4}$/
46− }
47−
48− inputMethodHints: Qt.ImhSensitiveData | Qt.ImhNoPredictiveText |
49− Qt.ImhMultiLine | // so OSK doesn't close on Enter
50− Qt.ImhDigitsOnly
51− echoMode: TextInput.Password
52− hasClearButton: false
53−
54− cursorDelegate: Item {}
55−
56− passwordCharacter: "●"
57− color: d.drawColor
58−
59− readonly property real letterSpacing: units.gu(1.75)
60− readonly property real frameSpacing: letterSpacing
61−
62− font.pixelSize: units.gu(3)
63− font.letterSpacing: letterSpacing
64−
65− style: StyledItem {
66− anchors.fill: parent
67− styleName: "FocusShape"
68−
69− // Properties needed by TextField
70− readonly property color color: d.textColor
71− readonly property color selectedTextColor: d.selectedColor
72− readonly property color selectionColor: d.textColor
73− readonly property color borderColor: "transparent"
74− readonly property color backgroundColor: "transparent"
75− readonly property color errorColor: d.errorColor
76− readonly property real frameSpacing: 0
77−
78− // Properties needed by FocusShape
79− readonly property bool enabled: styledItem.enabled
80− readonly property bool keyNavigationFocus: styledItem.keyNavigationFocus
81− property bool activeFocusOnTab
82− }
83−
84− onDisplayTextChanged: {
85− // We use onDisplayTextChanged instead of onTextChanged because
86− // displayText changes after text and if we did this before it
87− // updated, we would use the wrong displayText for fakeLabel.
88− root.loginError = false;
89− if (text.length >= 4) {
90− // hard limit of 4 for passcodes right now
91− respond();
92− }
93− }
94−
95− onAccepted: respond()
96−
97− function respond() {
98− if (root.interactive) {
99− root.accepted(passwordInput.text);
100− }
101− }
102−
103− Keys.onEscapePressed: {
104− root.canceled();
105− event.accepted = true;
106− }
107− }
108−
109− Row {
110− id: extraIcons
111− spacing: passwordInput.frameSpacing
112− anchors {
113− horizontalCenter: parent ? parent.horizontalCenter : undefined
114− horizontalCenterOffset: passwordInput.letterSpacing / 2
115− verticalCenter: passwordInput ? passwordInput.verticalCenter : undefined
116− }
117−
118− Label {
119− id: pinHint
120− objectName: "promptPinHint"
121−
122− text: "○○○○"
123− enabled: visible
124− color: d.drawColor
125− font {
126− pixelSize: units.gu(3)
127− letterSpacing: units.gu(1.75)
128− }
129− elide: Text.ElideRight
130− }
131− Icon {
132− name: "keyboard-caps-enabled"
133− height: units.gu(3)
134− width: height
135− color: d.drawColor
136− visible: false // TODO: detect when caps lock is on
137− anchors.verticalCenter: parent.verticalCenter
138− }
139− Icon {
140− objectName: "greeterPromptKeyboardButton"
141− name: "input-keyboard-symbolic"
142− height: units.gu(3)
143− width: height
144− color: d.drawColor
145− visible: !unity8Settings.alwaysShowOsk && root.hasKeyboard
146− anchors.verticalCenter: parent.verticalCenter
147− MouseArea {
148− anchors.fill: parent
149− onClicked: unity8Settings.alwaysShowOsk = true
150− }
151− }
152− Icon {
153− name: "dialog-warning-symbolic"
154− height: units.gu(3)
155− width: height
156− color: d.drawColor
157− visible: root.loginError
158− anchors.verticalCenter: parent.verticalCenter
159− }
160− }
161−
162− // Have a fake label that covers the text field after the user presses
163− // enter. What we *really* want is a disabled mode that doesn't lose OSK
164− // focus. Because our goal here is simply to keep the OSK up while
165− // we wait for PAM to get back to us, and while waiting, we don't want
166− // the user to be able to edit the field (simply because it would look
167− // weird if we allowed that). But until we have such a disabled mode,
168− // we'll fake it by covering the real text field with a label.
169− Label {
170− id: fakeLabel
171− anchors.verticalCenter: extraIcons ? extraIcons.verticalCenter : undefined
172− anchors.left: extraIcons ? extraIcons.left : undefined
173− anchors.right: parent ? parent.right : undefined
174− anchors.rightMargin: passwordInput.frameSpacing * 2 + extraIcons.width
175− color: d.drawColor
176− font {
177− pixelSize: pinHint.font.pixelSize
178− letterSpacing: pinHint.font.letterSpacing
179− }
180− text: passwordInput.displayText
181− visible: !root.interactive
182− enabled: visible
183− }
184−}
qml/Greeter/PromptList.qml+18−21
@@ -25,8 +25,6 @@ FocusScope {
2525
2626 property bool alphanumeric: true
2727 property bool interactive: true
28− property bool loginError: false
29− property bool hasKeyboard: false
3028
3129 signal responded(string text)
3230 signal clicked()
@@ -67,14 +65,10 @@ FocusScope {
6765
6866 readonly property bool isLabel: model.type == LightDMService.prompts.Message ||
6967 model.type == LightDMService.prompts.Error
70− readonly property var modelText: model.text
71− readonly property var modelType: model.type
72− readonly property var modelIndex: model.index
68+ readonly property var modelData: model
7369
7470 sourceComponent: isLabel ? infoLabel : greeterPrompt
7571
76− active: root.visible
77−
7872 onLoaded: {
7973 for (var i = 0; i < repeater.count; i++) {
8074 var item = repeater.itemAt(i);
@@ -85,6 +79,12 @@ FocusScope {
8579 }
8680 loader.item.opacity = 1;
8781 }
82+
83+ Binding {
84+ target: loader.item
85+ property: "model"
86+ value: loader.modelData
87+ }
8888 }
8989 }
9090 }
@@ -93,18 +93,17 @@ FocusScope {
9393 id: infoLabel
9494
9595 FadingLabel {
96− objectName: "infoLabel" + modelIndex
96+ objectName: "infoLabel" + model.index
9797 width: root.width
9898
99+ property var model
99100 readonly property bool isPrompt: false
100101
101− color: modelType === LightDMService.prompts.Message ? theme.palette.normal.raisedSecondaryText
102+ color: model.type === LightDMService.prompts.Message ? theme.palette.normal.raisedText
102103 : theme.palette.normal.negative
103104 fontSize: "small"
104105 textFormat: Text.PlainText
105− text: modelText
106−
107− visible: modelType === LightDMService.prompts.Message
106+ text: model.text
108107
109108 Behavior on opacity { UbuntuNumberAnimation {} }
110109 opacity: 0
@@ -115,23 +114,21 @@ FocusScope {
115114 id: greeterPrompt
116115
117116 GreeterPrompt {
118− objectName: "greeterPrompt" + modelIndex
117+ objectName: "greeterPrompt" + model.index
119118 width: root.width
120119
121− property bool isAlphanumeric: modelText !== "" || root.alphanumeric
120+ property var model
122121
123122 interactive: root.interactive
124− isPrompt: modelType !== LightDMService.prompts.Button
125− isSecret: modelType === LightDMService.prompts.Secret
126− isPinPrompt: isPrompt && !isAlphanumeric && isSecret
127− loginError: root.loginError
128− hasKeyboard: root.hasKeyboard
129− text: modelText ? modelText : (isAlphanumeric ? i18n.tr("Passphrase") : i18n.tr("Passcode"))
123+ isAlphanumeric: model.text !== "" || root.alphanumeric
124+ isPrompt: model.type !== LightDMService.prompts.Button
125+ isSecret: model.type === LightDMService.prompts.Secret
126+ text: model.text ? model.text : (isAlphanumeric ? i18n.tr("Passphrase") : i18n.tr("Passcode"))
130127
131128 onClicked: root.clicked()
132129 onAccepted: {
133130 // If there is another GreeterPrompt, focus it.
134− for (var i = modelIndex + 1; i < repeater.count; i++) {
131+ for (var i = model.index + 1; i < repeater.count; i++) {
135132 var item = repeater.itemAt(i).item;
136133 if (item.isPrompt) {
137134 item.forceActiveFocus();
qml/Greeter/SessionsList.qml+2−3
@@ -119,14 +119,13 @@ Item {
119119
120120 padding.leading: 0 // handled by parent's margins
121121
122− title.color: theme.palette.normal.raisedSecondaryText
122+ title.color: theme.palette.normal.raisedText
123123 title.font.pixelSize: units.gu(2.1)
124124 title.text: i18n.tr("Select desktop environment")
125125
126126 Icon {
127127 id: icon
128128 width: units.gu(3)
129− color: theme.palette.normal.raisedSecondaryText
130129 SlotsLayout.position: SlotsLayout.Leading
131130 name: "go-previous"
132131
@@ -188,7 +187,7 @@ Item {
188187 ListItemLayout {
189188 id: layout
190189
191− readonly property color itemColor: theme.palette.normal.raisedSecondaryText
190+ readonly property color itemColor: theme.palette.normal.raisedText
192191 SessionIcon {
193192 id: sessionIcon
194193 source: icon_url
qml/Greeter/TextPrompt.qmldeleted−198
@@ -1,198 +0,0 @@
1−import QtQuick 2.4
2−import Ubuntu.Components 1.3
3−import "../Components"
4−
5−FocusScope {
6− id: root
7−
8− property string text
9− property bool isSecret
10− property bool interactive: true
11− property bool loginError: false
12− property bool hasKeyboard: false
13− property alias enteredText: passwordInput.text
14−
15− signal clicked()
16− signal canceled()
17− signal accepted(string response)
18−
19− StyledItem {
20− id: d
21−
22− readonly property color textColor: passwordInput.enabled ? theme.palette.normal.raisedText
23− : theme.palette.disabled.raisedText
24− readonly property color selectedColor: passwordInput.enabled ? theme.palette.normal.raised
25− : theme.palette.disabled.raised
26− readonly property color drawColor: passwordInput.enabled ? theme.palette.normal.raisedSecondaryText
27− : theme.palette.disabled.raisedSecondaryText
28− readonly property color errorColor: passwordInput.enabled ? theme.palette.normal.negative
29− : theme.palette.disabled.negative
30− }
31−
32− Rectangle {
33− anchors.fill: parent
34− radius: units.gu(0.5)
35− color: "#7A111111"
36− Behavior on border.color {
37− ColorAnimation{}
38− }
39− border {
40− color: root.loginError ? d.errorColor : d.drawColor
41− width: root.loginError ? units.dp(2): units.dp(1)
42− }
43− }
44−
45− TextField {
46− id: passwordInput
47− objectName: "promptField"
48− anchors.fill: parent
49− focus: root.focus
50−
51− opacity: fakeLabel.visible ? 0 : 1
52− activeFocusOnTab: true
53−
54− onSelectedTextChanged: passwordInput.deselect()
55−
56− validator: RegExpValidator {
57− regExp: /^.*$/
58− }
59−
60− inputMethodHints: Qt.ImhSensitiveData | Qt.ImhNoPredictiveText |
61− Qt.ImhMultiLine // so OSK doesn't close on Enter
62− echoMode: root.isSecret ? TextInput.Password : TextInput.Normal
63− hasClearButton: false
64−
65− passwordCharacter: "●"
66− color: d.drawColor
67−
68− readonly property real frameSpacing: units.gu(1)
69−
70− style: StyledItem {
71− anchors.fill: parent
72− styleName: "FocusShape"
73−
74− // Properties needed by TextField
75− readonly property color color: d.textColor
76− readonly property color selectedTextColor: d.selectedColor
77− readonly property color selectionColor: d.textColor
78− readonly property color borderColor: "transparent"
79− readonly property color backgroundColor: "transparent"
80− readonly property color errorColor: d.errorColor
81− readonly property real frameSpacing: styledItem.frameSpacing
82−
83− // Properties needed by FocusShape
84− readonly property bool enabled: styledItem.enabled
85− readonly property bool keyNavigationFocus: styledItem.keyNavigationFocus
86− property bool activeFocusOnTab
87− }
88−
89− secondaryItem: [
90− Row {
91− id: extraIcons
92− spacing: passwordInput.frameSpacing
93− anchors.verticalCenter: parent.verticalCenter
94− Icon {
95− name: "keyboard-caps-enabled"
96− height: units.gu(3)
97− width: units.gu(3)
98− color: d.drawColor
99− visible: root.isSecret && false // TODO: detect when caps lock is on
100− anchors.verticalCenter: parent.verticalCenter
101− }
102− Icon {
103− objectName: "greeterPromptKeyboardButton"
104− name: "input-keyboard-symbolic"
105− height: units.gu(3)
106− width: units.gu(3)
107− color: d.drawColor
108− visible: !unity8Settings.alwaysShowOsk && root.hasKeyboard
109− anchors.verticalCenter: parent.verticalCenter
110− MouseArea {
111− anchors.fill: parent
112− onClicked: unity8Settings.alwaysShowOsk = true
113− }
114− }
115− Icon {
116− name: "dialog-warning-symbolic"
117− height: units.gu(3)
118− width: units.gu(3)
119− color: d.drawColor
120− visible: root.loginError
121− anchors.verticalCenter: parent.verticalCenter
122− }
123− Icon {
124− name: "toolkit_chevron-ltr_2gu"
125− height: units.gu(2.5)
126− width: units.gu(2.5)
127− color: d.drawColor
128− visible: !root.loginError
129− anchors.verticalCenter: parent.verticalCenter
130− MouseArea {
131− anchors.fill: parent
132− onClicked: root.accepted(passwordInput.text)
133− }
134− }
135− }
136− ]
137−
138− onDisplayTextChanged: {
139− // We use onDisplayTextChanged instead of onTextChanged because
140− // displayText changes after text and if we did this before it
141− // updated, we would use the wrong displayText for fakeLabel.
142− root.loginError = false;
143− }
144−
145− onAccepted: respond()
146−
147− function respond() {
148− if (root.interactive) {
149− root.accepted(passwordInput.text);
150− }
151− }
152−
153− Keys.onEscapePressed: {
154− root.canceled();
155− event.accepted = true;
156− }
157− }
158−
159− // We use our own custom placeholder label instead of the standard
160− // TextField one because the standard one hardcodes baseText as the
161− // palette color, whereas we want raisedSecondaryText.
162− Label {
163− id: passwordHint
164− objectName: "promptHint"
165− anchors {
166− left: passwordInput ? passwordInput.left : undefined
167− right: passwordInput ? passwordInput.right : undefined
168− verticalCenter: passwordInput ? passwordInput.verticalCenter : undefined
169− leftMargin: units.gu(2)
170− rightMargin: anchors.leftMargin + extraIcons.width
171− }
172− text: root.text
173− visible: passwordInput.text == "" && !passwordInput.inputMethodComposing
174− enabled: visible
175− color: d.drawColor
176− elide: Text.ElideRight
177− }
178−
179− // Have a fake label that covers the text field after the user presses
180− // enter. What we *really* want is a disabled mode that doesn't lose OSK
181− // focus. Because our goal here is simply to keep the OSK up while
182− // we wait for PAM to get back to us, and while waiting, we don't want
183− // the user to be able to edit the field (simply because it would look
184− // weird if we allowed that). But until we have such a disabled mode,
185− // we'll fake it by covering the real text field with a label.
186− FadingLabel {
187− id: fakeLabel
188− anchors.verticalCenter: parent ? parent.verticalCenter : undefined
189− anchors.left: parent ? parent.left : undefined
190− anchors.right: parent ? parent.right : undefined
191− anchors.leftMargin: passwordInput.frameSpacing * 2
192− anchors.rightMargin: passwordInput.frameSpacing * 2 + extraIcons.width
193− color: d.drawColor
194− text: passwordInput.displayText
195− visible: !root.interactive
196− enabled: visible
197− }
198−}
qml/Greeter/WideView.qmlnew file+210
@@ -0,0 +1,210 @@
1+/*
2+ * Copyright (C) 2015-2016 Canonical, Ltd.
3+ * Copyright (C) 2021 UBports Foundation
4+ *
5+ * This program is free software; you can redistribute it and/or modify
6+ * it under the terms of the GNU General Public License as published by
7+ * the Free Software Foundation; version 3.
8+ *
9+ * This program is distributed in the hope that it will be useful,
10+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
11+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+ * GNU General Public License for more details.
13+ *
14+ * You should have received a copy of the GNU General Public License
15+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
16+ */
17+
18+import QtQuick 2.4
19+import Ubuntu.Components 1.3
20+import "." 0.1
21+
22+FocusScope {
23+ id: root
24+ objectName: "WideView"
25+
26+ focus: true
27+
28+ property alias background: coverPage.background
29+ property alias backgroundSourceSize: coverPage.backgroundSourceSize
30+ property alias panelHeight: coverPage.panelHeight
31+ property alias hasCustomBackground: coverPage.hasCustomBackground
32+ property alias dragHandleLeftMargin: coverPage.dragHandleLeftMargin
33+ property alias infographicModel: coverPage.infographicModel
34+ property alias launcherOffset: coverPage.launcherOffset
35+ property alias currentIndex: loginList.currentIndex
36+ property int delayMinutes // TODO
37+ property alias alphanumeric: loginList.alphanumeric
38+ property alias locked: loginList.locked
39+ property alias waiting: loginList.waiting
40+ property var userModel // Set from outside
41+
42+ readonly property bool animating: coverPage.showAnimation.running || coverPage.hideAnimation.running
43+ readonly property bool fullyShown: coverPage.showProgress === 1
44+ readonly property bool required: coverPage.required
45+ readonly property alias sessionToStart: loginList.currentSession
46+
47+ // so that it can be replaced in tests with a mock object
48+ property var inputMethod: Qt.inputMethod
49+
50+ signal selected(int index)
51+ signal responded(string response)
52+ signal tease()
53+ signal emergencyCall() // unused
54+
55+ function notifyAuthenticationFailed() {
56+ loginList.showError();
57+ }
58+
59+ function forceShow() {
60+ // Nothing to do, we are always fully shown
61+ }
62+
63+ function tryToUnlock(toTheRight) {
64+ if (root.locked) {
65+ coverPage.show();
66+ loginList.tryToUnlock();
67+ return false;
68+ } else {
69+ var coverChanged = coverPage.shown;
70+ if (toTheRight) {
71+ coverPage.hideRight();
72+ } else {
73+ coverPage.hide();
74+ }
75+ return coverChanged;
76+ }
77+ }
78+
79+ function hide() {
80+ coverPage.hide();
81+ }
82+
83+ function showFakePassword() {
84+ loginList.showFakePassword();
85+ }
86+
87+ Rectangle {
88+ anchors.fill: parent
89+ color: "black"
90+ opacity: coverPage.showProgress * 0.8
91+ }
92+
93+ CoverPage {
94+ id: coverPage
95+ objectName: "coverPage"
96+ height: parent.height
97+ width: parent.width
98+ draggable: !root.locked && !root.waiting
99+ state: "LoginList"
100+
101+ infographics {
102+ anchors.topMargin: parent.height * 0.125
103+ anchors.bottomMargin: parent.height * 0.125
104+ anchors.leftMargin: loginList.x + loginList.width
105+ }
106+
107+ onTease: root.tease()
108+
109+ onShowProgressChanged: {
110+ if (showProgress === 0 && !root.locked) {
111+ root.responded("");
112+ }
113+ }
114+
115+ LoginList {
116+ id: loginList
117+ objectName: "loginList"
118+
119+ width: units.gu(40)
120+ anchors {
121+ left: parent.left
122+ leftMargin: Math.min(parent.width * 0.16, units.gu(20))
123+ top: parent.top
124+ bottom: parent.bottom
125+ }
126+
127+ boxVerticalOffset: (height - highlightedHeight -
128+ (inputMethod && inputMethod.visible ?
129+ inputMethod.keyboardRectangle.height : 0)) / 2
130+ Behavior on boxVerticalOffset { UbuntuNumberAnimation {} }
131+
132+ model: root.userModel
133+ onResponded: root.responded(response)
134+ onSelected: root.selected(index)
135+ onSessionChooserButtonClicked: parent.state = "SessionsList"
136+ onCurrentIndexChanged: setCurrentSession()
137+
138+ Keys.forwardTo: [sessionChooserLoader.item]
139+
140+ Component.onCompleted: setCurrentSession()
141+
142+ function setCurrentSession() {
143+ currentSession = LightDMService.users.data(currentIndex, LightDMService.userRoles.SessionRole);
144+ }
145+ }
146+
147+ Loader {
148+ id: sessionChooserLoader
149+
150+ height: loginList.height
151+ width: loginList.width
152+
153+ anchors {
154+ left: parent.left
155+ leftMargin: Math.min(parent.width * 0.16, units.gu(20))
156+ top: parent.top
157+ }
158+
159+ active: false
160+
161+ onLoaded: sessionChooserLoader.item.forceActiveFocus();
162+ onActiveChanged: {
163+ if (!active) return;
164+ item.updateHighlight(loginList.currentSession);
165+ }
166+
167+ Connections {
168+ target: sessionChooserLoader.item
169+ onSessionSelected: loginList.currentSession = sessionKey
170+ onShowLoginList: {
171+ coverPage.state = "LoginList"
172+ loginList.tryToUnlock();
173+ }
174+ ignoreUnknownSignals: true
175+ }
176+ }
177+
178+ states: [
179+ State {
180+ name: "SessionsList"
181+ PropertyChanges { target: loginList; opacity: 0 }
182+ PropertyChanges { target: sessionChooserLoader;
183+ active: true;
184+ opacity: 1
185+ source: "SessionsList.qml"
186+ }
187+ },
188+
189+ State {
190+ name: "LoginList"
191+ PropertyChanges { target: loginList; opacity: 1 }
192+ PropertyChanges { target: sessionChooserLoader;
193+ active: false;
194+ opacity: 0
195+ source: "";
196+ }
197+ }
198+ ]
199+
200+ transitions: [
201+ Transition {
202+ from: "*"
203+ to: "*"
204+ UbuntuNumberAnimation {
205+ property: "opacity";
206+ }
207+ }
208+ ]
209+ }
210+}
qml/Launcher/BackgroundBlur.qmldeleted−81
@@ -1,81 +0,0 @@
1−/*
2− * Copyright (C) 2016 Canonical, Ltd.
3− *
4− * This program is free software; you can redistribute it and/or modify
5− * it under the terms of the GNU General Public License as published by
6− * the Free Software Foundation; version 3.
7− *
8− * This program is distributed in the hope that it will be useful,
9− * but WITHOUT ANY WARRANTY; without even the implied warranty of
10− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11− * GNU General Public License for more details.
12− *
13− * You should have received a copy of the GNU General Public License
14− * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− */
16−
17−import QtQuick 2.4
18−import Ubuntu.Components 1.3
19−import QtGraphicalEffects 1.0
20−
21−Item {
22− id: root
23−
24− property int blurAmount: 32
25− property Item sourceItem
26− property rect blurRect: Qt.rect(0,0,0,0)
27− property alias cached: fastBlur.cached
28−
29− Rectangle {
30− id: blurMask
31− color: "yellow"
32− x: blurRect.x
33− y: blurRect.y
34− width: blurRect.width
35− height: blurRect.height
36− }
37−
38− ShaderEffect {
39− id: maskedBlurEffect
40− x: blurRect.x
41− y: blurRect.y
42− width: blurRect.width
43− height: blurRect.height
44−
45− property variant source: ShaderEffectSource {
46− id: shaderEffectSource
47− sourceItem: root.sourceItem
48− hideSource: false
49− sourceRect: root.blurRect
50− }
51−
52− property var mask: ShaderEffectSource {
53− sourceItem: blurMask
54− hideSource: true
55− }
56−
57− fragmentShader: "
58− varying highp vec2 qt_TexCoord0;
59− uniform sampler2D source;
60− uniform sampler2D mask;
61− void main(void)
62− {
63− highp vec4 sourceColor = texture2D(source, qt_TexCoord0);
64− highp vec4 maskColor = texture2D(mask, qt_TexCoord0);
65−
66− sourceColor *= maskColor.a;
67−
68− gl_FragColor = sourceColor;
69− }"
70− }
71−
72− FastBlur {
73− id: fastBlur
74− x: blurRect.x
75− y: blurRect.y
76− width: blurRect.width
77− height: blurRect.height
78− source: maskedBlurEffect
79− radius: Math.min(blurAmount, 128)
80− }
81− }
qml/Launcher/Drawer.qml+2−8
@@ -34,7 +34,6 @@ FocusScope {
3434 readonly property real delegateWidth: units.gu(10)
3535 property url background
3636 property alias backgroundSourceSize: background.sourceSize
37− property bool staticBlurEnabled : true
3837 visible: x > -width
3938 property var fullyOpen: x === 0
4039 property var fullyClosed: x === -width
@@ -156,21 +155,18 @@ FocusScope {
156155
157156 Rectangle {
158157 anchors.fill: parent
159− color: "#BF000000"
158+ color: "#111111"
159+ opacity: 0.99
160160
161161 Wallpaper {
162162 id: background
163163 objectName: "drawerBackground"
164− visible: staticBlurEnabled
165− enabled: staticBlurEnabled
166164 anchors.fill: parent
167165 source: root.background
168166 }
169167
170168 FastBlur {
171169 anchors.fill: background
172− visible: staticBlurEnabled
173− enabled: staticBlurEnabled
174170 source: background
175171 radius: 64
176172 cached: true
@@ -179,8 +175,6 @@ FocusScope {
179175 // Images with fastblur can't use opacity, so we'll put this on top
180176 Rectangle {
181177 anchors.fill: background
182− visible: staticBlurEnabled
183− enabled: staticBlurEnabled
184178 color: parent.color
185179 opacity: 0.67
186180 }
qml/Launcher/Launcher.qml+1−23
@@ -31,8 +31,6 @@ FocusScope {
3131 property bool lockedVisible: false
3232 property bool available: true // can be used to disable all interactions
3333 property alias inverted: panel.inverted
34− property Item blurSource: null
35− property bool interactiveBlur: false
3634 property int topPanelHeight: 0
3735 property bool drawerEnabled: true
3836 property alias privateMode: panel.privateMode
@@ -366,20 +364,6 @@ FocusScope {
366364 }
367365 }
368366
369− BackgroundBlur {
370− id: backgroundBlur
371− anchors.fill: parent
372− anchors.topMargin: root.inverted ? 0 : -root.topPanelHeight
373− visible: root.interactiveBlur && root.blurSource && drawer.x > -drawer.width
374− blurAmount: units.gu(6)
375− sourceItem: root.blurSource
376− blurRect: Qt.rect(panel.width,
377− root.topPanelHeight,
378− drawer.width + drawer.x - panel.width,
379− height - root.topPanelHeight)
380− cached: drawer.moving
381− }
382−
383367 Drawer {
384368 id: drawer
385369 objectName: "drawer"
@@ -393,7 +377,6 @@ FocusScope {
393377 width: Math.min(root.width, units.gu(81))
394378 panelWidth: panel.width
395379 allowSlidingAnimation: !dragArea.dragging && !launcherDragArea.drag.active && panel.animate
396− staticBlurEnabled: !root.interactiveBlur
397380
398381 onApplicationSelected: {
399382 root.launcherApplicationSelected(appId)
@@ -427,7 +410,7 @@ FocusScope {
427410 Connections {
428411 target: panel.dismissTimer
429412 onTriggered: {
430− if (root.state !== "drawer" && root.autohideEnabled && !root.lockedVisible) {
413+ if (root.autohideEnabled && !root.lockedVisible) {
431414 if (!edgeBarrier.containsMouse && !panel.preventHiding) {
432415 root.state = ""
433416 } else {
@@ -609,11 +592,6 @@ FocusScope {
609592 anchors.rightMargin: 0
610593 focus: false
611594 }
612− PropertyChanges {
613− target: root
614− restoreEntryValues: false
615− autohideEnabled: false
616− }
617595 },
618596 State {
619597 name: "drawer"
qml/OrientedShell.qml−1
@@ -273,7 +273,6 @@ Item {
273273 nativeWidth: root.width
274274 nativeHeight: root.height
275275 mode: applicationArguments.mode
276− interactiveBlur: applicationArguments.interactiveBlur
277276 hasMouse: pointerInputDevices > 0
278277 hasKeyboard: keyboardsModel.count > 0
279278 hasTouchscreen: touchScreensModel.count > 0
qml/Panel/Indicators/IndicatorItem.qml−6
@@ -117,12 +117,6 @@ IndicatorDelegate {
117117 source: modelData
118118 color: root.color
119119 Behavior on color { ColorAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing } }
120−
121− // Workaround indicators getting stretched/squished when (un)plugging external/virtual monitor
122− onHeightChanged: {
123− source = ""
124− source = modelData
125− }
126120 }
127121 }
128122 }
qml/Rotation/RotationStates.qml+3
@@ -139,6 +139,9 @@ StateGroup {
139139 } else if (Powerd.status === Powerd.Off) {
140140 // There's no point in animating if the user can't see it (display is off).
141141 d.animationType = d.noAnimation;
142+ } else if (root.shell.showingGreeter) {
143+ // A rotating greeter looks weird.
144+ d.animationType = d.noAnimation;
142145 } else {
143146 if (!root.shell.mainApp) {
144147 // shouldn't happen but, anyway
qml/Shell.qml+3−10
@@ -1,4 +1,4 @@
1−/*
1+/*
22 * Copyright (C) 2013-2016 Canonical, Ltd.
33 * Copyright (C) 2019-2021 UBports Foundation
44 *
@@ -59,7 +59,6 @@ StyledItem {
5959 property alias panelAreaShowProgress: panel.panelAreaShowProgress
6060 property string usageScenario: "phone" // supported values: "phone", "tablet" or "desktop"
6161 property string mode: "full-greeter"
62− property bool interactiveBlur: false
6362 property alias oskEnabled: inputMethod.enabled
6463 function updateFocusedAppOrientation() {
6564 stage.updateFocusedAppOrientation();
@@ -94,7 +93,7 @@ StyledItem {
9493
9594 readonly property bool orientationChangesEnabled: panel.indicators.fullyClosed
9695 && stage.orientationChangesEnabled
97− && (!greeter.animating)
96+ && (!greeter || !greeter.animating)
9897
9998 readonly property bool showingGreeter: greeter && greeter.shown
10099
@@ -105,7 +104,7 @@ StyledItem {
105104 if (startingUp) {
106105 // Ensure we don't rotate during start up
107106 return Qt.PrimaryOrientation;
108− } else if (notifications.topmostIsFullscreen) {
107+ } else if (showingGreeter || notifications.topmostIsFullscreen) {
109108 return Qt.PrimaryOrientation;
110109 } else {
111110 return shell.orientations ? shell.orientations.map(stage.supportedOrientations) : Qt.PrimaryOrientation;
@@ -437,14 +436,10 @@ StyledItem {
437436 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
438437 hides: [launcher, panel.indicators, panel.applicationMenus]
439438 tabletMode: shell.usageScenario != "phone"
440− usageMode: shell.usageScenario
441− orientation: shell.orientation
442439 forcedUnlock: wizard.active || shell.mode === "full-shell"
443440 background: wallpaperResolver.background
444441 backgroundSourceSize: shell.largestScreenDimension
445442 hasCustomBackground: wallpaperResolver.hasCustomBackground
446− inputMethodRect: inputMethod.visibleRect
447− hasKeyboard: shell.hasKeyboard
448443 allowFingerprint: !dialogs.hasActiveDialog &&
449444 !notifications.topmostIsFullscreen &&
450445 !panel.indicators.shown
@@ -605,8 +600,6 @@ StyledItem {
605600 superTabPressed: physicalKeysMapper.superTabPressed
606601 panelWidth: units.gu(settings.launcherWidth)
607602 lockedVisible: (lockedByUser || shell.atDesktop) && lockAllowed
608− blurSource: greeter.shown ? greeter : stages
609− interactiveBlur: shell.interactiveBlur
610603 topPanelHeight: panel.panelHeight
611604 drawerEnabled: !greeter.active && tutorial.launcherLongSwipeEnabled
612605 privateMode: greeter.active
qml/Stage/ApplicationWindow.qml+3−12
@@ -53,7 +53,6 @@ FocusScope {
5353 if (surface) {
5454 surfaceContainer.surface = surface;
5555 d.liveSurface = surface.live;
56− d.surfaceSeenLive = d.liveSurface;
5756 d.hadSurface = false;
5857 surfaceInitTimer.start();
5958 } else {
@@ -71,12 +70,7 @@ FocusScope {
7170 property bool liveSurface: false;
7271 property var con: Connections {
7372 target: root.surface
74− onLiveChanged: {
75− d.liveSurface = root.surface.live;
76− if (d.liveSurface) {
77− d.surfaceSeenLive = true;
78− }
79− }
73+ onLiveChanged: d.liveSurface = root.surface.live
8074 }
8175 // using liveSurface instead of root.surface.live because with the latter
8276 // this expression is not reevaluated when root.surface changes
@@ -107,7 +101,6 @@ FocusScope {
107101 // might show the UI accommodating due to surface resizes on startup.
108102 // Remove this when possible
109103 property bool surfaceInitialized: false
110− property bool surfaceSeenLive: false
111104
112105 readonly property bool supportsSurfaceResize:
113106 application &&
@@ -258,7 +251,6 @@ FocusScope {
258251 name: "splashScreen"
259252 when:
260253 !d.hadSurface && (!root.surface || !d.surfaceInitialized)
261− && (d.liveSurface || !d.surfaceSeenLive)
262254 &&
263255 screenshotImage.status !== Image.Ready
264256 },
@@ -291,9 +283,8 @@ FocusScope {
291283 when:
292284 // The surface died while the application is running. It must have been closed
293285 // by the shell or the application decided to destroy it by itself
294− root.surface && (d.surfaceInitialized || d.surfaceSeenLive) && !d.liveSurface
295− && (d.applicationState === ApplicationInfoInterface.Running
296− || d.applicationState === ApplicationInfoInterface.Starting)
286+ root.surface && d.surfaceInitialized && !d.liveSurface
287+ && d.applicationState === ApplicationInfoInterface.Running
297288 }
298289 ]
299290
src/ApplicationArguments.h−5
@@ -28,7 +28,6 @@ class ApplicationArguments : public QObject
2828 Q_OBJECT
2929 Q_PROPERTY(QString deviceName READ deviceName NOTIFY deviceNameChanged)
3030 Q_PROPERTY(QString mode READ mode CONSTANT)
31− Q_PROPERTY(bool interactiveBlur READ interactiveBlur CONSTANT)
3231public:
3332 ApplicationArguments(QObject *parent = nullptr);
3433
@@ -43,16 +42,12 @@ public:
4342 void setMode(const QString &mode) { m_mode = mode; }
4443 QString mode() const { return m_mode; }
4544
46− void setInteractiveBlur(const bool &value) { m_interactiveBlur = value; }
47− bool interactiveBlur() { return m_interactiveBlur; }
48−
4945Q_SIGNALS:
5046 void deviceNameChanged(const QString&);
5147
5248private:
5349 QString m_deviceName;
5450 QString m_mode;
55− bool m_interactiveBlur;
5651};
5752
5853#endif // APPLICATION_ARGUMENTS_H
src/ShellApplication.cpp−6
@@ -58,12 +58,6 @@ ShellApplication::ShellApplication(int & argc, char ** argv, bool isMirServer)
5858
5959 m_qmlArgs.setMode(parser.mode());
6060
61− {
62− char buffer[200];
63− property_get("ubuntu.unity8.interactive_blur", buffer, "false");
64− m_qmlArgs.setInteractiveBlur(QString(buffer) == QStringLiteral("true"));
65− }
66−
6761 // The testability driver is only loaded by QApplication but not by QGuiApplication.
6862 // However, QApplication depends on QWidget which would add some unneeded overhead => Let's load the testability driver on our own.
6963 if (parser.hasTestability() || getenv("QT_LOAD_TESTABILITY")) {
tests/qmltests/CMakeLists.txt+2−1
@@ -29,7 +29,8 @@ add_unity8_qmltest(Components ZoomableImage)
2929add_unity8_qmltest(Greeter Clock)
3030add_unity8_qmltest(Greeter Greeter)
3131add_unity8_qmltest(Greeter Infographics)
32−add_unity8_qmltest(Greeter GreeterView)
32+add_unity8_qmltest(Greeter NarrowView)
33+add_unity8_qmltest(Greeter WideView)
3334add_unity8_qmltest_data(Greeter TestView)
3435add_unity8_qmltest(Launcher Launcher)
3536add_unity8_qmltest(Launcher Drawer)
tests/qmltests/Greeter/TestView.qml−7
@@ -38,13 +38,6 @@ Item {
3838 readonly property bool required: _required
3939 readonly property bool animating: _animating
4040
41− property bool multiUser
42− property int orientation
43− property string usageMode
44− property bool hasKeyboard
45− property real backgroundSourceSize
46− property rect inputMethodRect
47−
4841 property bool _fullyShown: true
4942 property bool _required: true
5043 property bool _animating: true
tests/qmltests/Greeter/tst_GreeterView.qmldeleted−1366
@@ -1,1366 +0,0 @@
1−/*
2− * Copyright 2014-2016 Canonical Ltd.
3− * Copyright 2021 UBports Foundation
4− *
5− * This program is free software; you can redistribute it and/or modify
6− * it under the terms of the GNU General Public License as published by
7− * the Free Software Foundation; version 3.
8− *
9− * This program is distributed in the hope that it will be useful,
10− * but WITHOUT ANY WARRANTY; without even the implied warranty of
11− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12− * GNU General Public License for more details.
13− *
14− * You should have received a copy of the GNU General Public License
15− * along with this program. If not, see <http://www.gnu.org/licenses/>.
16− */
17−
18−import QtQuick 2.4
19−import QtQuick.Window 2.2
20−import QtTest 1.0
21−import ".."
22−import "../../../qml/Greeter"
23−import LightDMController 0.1
24−import LightDM.FullLightDM 0.1 as LightDM
25−import Ubuntu.Components 1.3
26−import Ubuntu.Components.ListItems 1.3 as ListItem
27−import Ubuntu.Telephony 0.1 as Telephony
28−import Unity.Test 0.1 as UT
29−
30−StyledItem {
31− id: root
32−
33− width: units.gu(160)
34− height: units.gu(80)
35− focus: true
36−
37− theme.name: "Ubuntu.Components.Themes.Ambiance"
38−
39− Component.onCompleted: {
40− // must set the mock mode before loading the Shell
41− LightDMController.userMode = "single";
42− }
43−
44− Row {
45− anchors.fill: parent
46− Rectangle {
47− width: root.width - controls.width
48− height: parent.height
49− color: "grey"
50−
51− Rectangle {
52− id: viewContainer
53− anchors.horizontalCenter: parent.horizontalCenter
54− anchors.verticalCenter: parent.verticalCenter
55− focus: true
56−
57− Loader {
58− id: loader
59− width: parent.width
60− height: parent.height
61− focus: true
62−
63− property bool itemDestroyed: false
64− sourceComponent: Component {
65− GreeterView {
66− id: view
67−
68− background: "/usr/share/backgrounds/warty-final-ubuntu.png"
69− backgroundSourceSize: width
70− userModel: LightDM.Users
71− infographicModel: LightDM.Infographic
72−
73− launcherOffset: parseFloat(launcherOffsetField.text)
74− currentIndex: parseInt(currentIndexField.text, 10)
75− delayMinutes: parseInt(delayMinutesField.text, 10)
76− panelHeight: parseFloat(panelHeightField.text)
77− locked: lockedCheckBox.checked
78− inputMethodRect: fakeKeyboard.childrenRect
79− property var testKeyboard: fakeKeyboard
80−
81− usageMode: usageScenarioSelector.model[usageScenarioSelector.selectedIndex]
82− multiUser: multiUserCheckBox.checked
83− orientation: orientationSelector.model[orientationSelector.selectedIndex] == "landscape" ? Qt.LandscapeOrientation : Qt.PortraitOrientation
84−
85− Component.onDestruction: {
86− loader.itemDestroyed = true
87− }
88−
89− onSelected: {
90− if (index >= 0)
91− currentIndexField.text = index;
92− }
93−
94− Rectangle {
95− id: fakeKeyboard
96− color: "green"
97− opacity: 0.7
98− anchors.bottom: view.bottom
99− width: view.width
100− height: visible ? view.height * 0.6 : 0
101− visible: keyboardVisibleCheckBox.checked
102− Text {
103− text: "Keyboard Rectangle"
104− color: "yellow"
105− font.bold: true
106− fontSizeMode: Text.Fit
107− minimumPixelSize: 10; font.pixelSize: 200
108− verticalAlignment: Text.AlignVCenter
109− x: (parent.width - width) / 2
110− y: (parent.height - height) / 2
111− width: parent.width
112− height: parent.height
113− }
114− }
115− }
116− }
117− }
118−
119− state: loader.item ? loader.item.usageMode + "-" + (loader.item.isLandscape ? "landscape" : "portrait") : "phone-portrait"
120−
121− states: [
122− State {
123− name: "phone-portrait"
124− PropertyChanges {
125− target: viewContainer
126− width: units.gu(40)
127− height: units.gu(71)
128− }
129− },
130− State {
131− name: "phone-landscape"
132− PropertyChanges {
133− target: viewContainer
134− width: units.gu(71)
135− height: units.gu(40)
136− }
137− },
138− State {
139− name: "tablet-landscape"
140− PropertyChanges {
141− target: viewContainer
142− width: units.gu(100)
143− height: units.gu(71)
144− }
145− },
146− State {
147− name: "tablet-portrait"
148− PropertyChanges {
149− target: viewContainer
150− width: units.gu(71)
151− height: units.gu(100)
152− }
153− },
154− State {
155− name: "desktop-portrait"
156− PropertyChanges {
157− target: viewContainer
158− width: parent.width
159− height: parent.height
160− }
161− },
162− State {
163− name: "desktop-landscape"
164− PropertyChanges {
165− target: viewContainer
166− width: parent.width
167− height: parent.height
168− }
169− }
170− ]
171− }
172− }
173−
174− Rectangle {
175− id: controls
176− color: theme.palette.normal.background
177− width: units.gu(40)
178− height: parent.height
179− Column {
180− anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
181− spacing: units.gu(1)
182− Row {
183− Button {
184− text: "Hide"
185− onClicked: loader.item.hide()
186− }
187− }
188− Row {
189− Button {
190− text: "Show Message"
191− onClicked: LightDMService.prompts.append(messageField.text, LightDMService.prompts.Message)
192− }
193− TextField {
194− id: messageField
195− width: units.gu(10)
196− text: ""
197− }
198− }
199− Row {
200− Button {
201− text: "Show Prompt"
202− onClicked: LightDMService.prompts.append(promptField.text, isSecretCheckBox.checked ? LightDMService.prompts.Secret : LightDMService.prompts.Question)
203− }
204− TextField {
205− id: promptField
206− width: units.gu(10)
207− text: ""
208− }
209− CheckBox {
210− id: isSecretCheckBox
211− }
212− Label {
213− text: "secret"
214− }
215− }
216− Row {
217− Button {
218− text: "Notify Auth Failure"
219− onClicked: {
220− loader.item.notifyAuthenticationFailed();
221− }
222− }
223− }
224− Row {
225− Button {
226− text: "Try To Unlock"
227− onClicked: loader.item.tryToUnlock(toTheRightCheckBox.checked)
228− }
229− CheckBox {
230− id: toTheRightCheckBox
231− }
232− Label {
233− text: "toTheRight"
234− }
235− }
236− Label {
237− text: "Usage scenario"
238− }
239−
240− ListItem.ItemSelector {
241− id: usageScenarioSelector
242− anchors { left: parent.left; right: parent.right }
243− activeFocusOnPress: false
244− model: ["phone", "tablet", "desktop"]
245− }
246− Label {
247− text: "Orientation"
248− }
249−
250− ListItem.ItemSelector {
251− id: orientationSelector
252− anchors { left: parent.left; right: parent.right }
253− activeFocusOnPress: false
254− model: ["portrait", "landscape"]
255− }
256− Row {
257− TextField {
258− id: launcherOffsetField
259− width: units.gu(10)
260− text: "0"
261− }
262− Label {
263− text: "launcherOffset"
264− }
265− }
266− Row {
267− TextField {
268− id: currentIndexField
269− width: units.gu(10)
270− text: "0"
271− }
272− Label {
273− text: "currentIndex"
274− }
275− }
276− Row {
277− TextField {
278− id: delayMinutesField
279− width: units.gu(10)
280− text: "0"
281− }
282− Label {
283− text: "delayMinutes"
284− }
285− }
286− Row {
287− TextField {
288− id: panelHeightField
289− width: units.gu(10)
290− text: "0"
291− }
292− Label {
293− text: "panelHeight"
294− }
295− }
296− Row {
297− CheckBox {
298− id: lockedCheckBox
299− }
300− Label {
301− text: "locked"
302− }
303− }
304− Row {
305− CheckBox {
306− id: multiUserCheckBox
307− onClicked: {
308− if (checked) {
309− LightDMController.userMode = "full";
310− } else {
311− LightDMController.userMode = "single";
312− }
313− }
314− Connections {
315− target: LightDMController
316− onSessionModeChanged: {
317− if (LightDMController.userMode === "full") {
318− multipleSessionsCheckbox.checked = true;
319− } else {
320− multipleSessionsCheckbox.checked = false;
321− }
322− }
323− }
324− }
325− Label {
326− text: "multi user"
327− }
328− }
329− Row {
330− CheckBox {
331− id: alphanumericCheckBox
332− checked: true
333− }
334− Label {
335− text: "alphanumeric"
336− }
337− }
338− Row {
339− Label {
340− text: "selected: " + selectedSpy.count
341− }
342− }
343− Row {
344− Label {
345− text: "responded: " + respondedSpy.count
346− }
347− }
348− Row {
349− Label {
350− text: "teased: " + teaseSpy.count
351− }
352− }
353− Row {
354− Label {
355− text: "emergency: " + emergencySpy.count
356− }
357− }
358− Row {
359− Label {
360− text: "infographics: " + infographicDataChangedSpy.count
361− }
362− }
363− Row {
364− id: multipleSessions
365− CheckBox {
366− id: multipleSessionsCheckbox
367− onClicked: {
368− if (checked) {
369− LightDMController.sessionMode = "full";
370− } else {
371− LightDMController.sessionMode = "single";
372− }
373− }
374− Connections {
375− target: LightDMController
376− onSessionModeChanged: {
377− if (LightDMController.sessionMode === "full") {
378− multipleSessionsCheckbox.checked = true;
379− } else {
380− multipleSessionsCheckbox.checked = false;
381− }
382− }
383− }
384− }
385− Label {
386− text: "Multiple Sessions"
387− }
388− }
389− Row {
390− Slider {
391− id: numSessionsSlider
392−
393− width: units.gu(10)
394− minimumValue: 0
395− maximumValue: LightDMController.numAvailableSessions
396− value: LightDMController.numSessions
397− visible: LightDMController.sessionMode === "full"
398− Binding {
399− target: LightDMController
400− property: "numSessions"
401− value: numSessionsSlider.value
402− }
403− }
404− Label {
405− text: "Available Sessions"
406− }
407− }
408− Row {
409− Button {
410− text: "Reload View"
411− onClicked: {
412− loader.active = false;
413− loader.active = true;
414− }
415− }
416− }
417− Row {
418− CheckBox {
419− id: keyboardVisibleCheckBox
420− }
421− Label {
422− text: "Keyboard Visible"
423− }
424− }
425− }
426− }
427− }
428−
429−/* FIXME: infographic user is hardcoded to "has-password",
430− * needs to be set to current user to test in multiUser mode
431− */
432− Binding {
433− target: LightDM.Infographic
434− property: "username"
435− value: "has-password"
436− }
437−
438− SignalSpy {
439− id: selectedSpy
440− target: loader.item
441− signalName: "selected"
442− }
443−
444− SignalSpy {
445− id: respondedSpy
446− target: loader.item
447− signalName: "responded"
448− }
449−
450− SignalSpy {
451− id: teaseSpy
452− target: loader.item
453− signalName: "tease"
454− }
455−
456− SignalSpy {
457− id: emergencySpy
458− target: loader.item
459− signalName: "emergencyCall"
460− }
461−
462− SignalSpy {
463− id: infographicDataChangedSpy
464− target: LightDM.Infographic
465− signalName: "dataChanged"
466− }
467−
468− UT.UnityTestCase {
469− name: "GreeterView"
470− when: windowShown
471−
472− property Item view: loader.status === Loader.Ready ? loader.item : null
473− property url testIconDirectory: Qt.resolvedUrl("../../../qml/Greeter/graphics/session_icons")
474−
475− function init() {
476− selectIndex(0); // break binding with text field
477− tryCompare(LightDMService.prompts, "count", 1);
478− selectedSpy.clear();
479− respondedSpy.clear();
480− teaseSpy.clear();
481− emergencySpy.clear();
482− infographicDataChangedSpy.clear();
483− LightDMController.reset();
484− LightDM.Sessions.iconSearchDirectories = [testIconDirectory];
485−
486− waitForRendering(view);
487− var userList = findChild(view, "userList");
488− tryCompare(userList, "movingInternally", false);
489− }
490−
491− function cleanup() {
492− keyboardVisibleCheckBox.checked = false;
493−
494− loader.itemDestroyed = false;
495− loader.active = false;
496− tryCompare(loader, "status", Loader.Null);
497− tryCompare(loader, "item", null);
498− tryCompare(loader, "itemDestroyed", true);
499− loader.active = true;
500− tryCompare(loader, "status", Loader.Ready);
501− removeTimeConstraintsFromSwipeAreas(loader.item);
502− LightDMController.sessionMode = "single";
503− }
504−
505− function setLightDMMockMode(mode) {
506− LightDMController.userMode = mode;
507− waitForRendering(root);
508− }
509−
510− function setUsageMode(usageMode) {
511− view.usageMode = usageMode;
512− if (usageMode == "desktop") {
513− view.multiUser = true;
514− setLightDMMockMode("full");
515− view.orientation = Qt.LandscapeOrientation;
516− } else if (usageMode == "phone") {
517− view.multiUser = false;
518− setLightDMMockMode("single");
519− view.orientation = Qt.PortraitOrientation;
520− telepathyHelper.ready = true;
521− telepathyHelper.emergencyCallsAvailable = true;
522− LightDM.Greeter.authenticate("no-password");
523− tryCompare(LightDMService.prompts, "count", 1);
524− }
525− }
526−
527− function swipeAwayCover(toTheRight) {
528− if (toTheRight === undefined) {
529− toTheRight = false;
530− }
531−
532− tryCompare(view, "fullyShown", true);
533− var touchY = view.height / 2;
534− if (toTheRight) {
535− touchFlick(view, 0, touchY, view.width, touchY);
536− } else {
537− touchFlick(view, view.width, touchY, 0, touchY);
538− }
539− var coverPage = findChild(view, "coverPage");
540− tryCompare(coverPage, "showProgress", 0);
541− waitForRendering(view);
542− }
543−
544− function getIndexOf(name) {
545− for (var i = 0; i < LightDM.Users.count; i++) {
546− if (name === LightDM.Users.data(i, LightDM.UserRoles.NameRole)) {
547− return i;
548− }
549− }
550− fail("Didn't find name")
551− return -1;
552− }
553−
554− function selectIndex(i) {
555− var user = LightDM.Users.data(i, LightDM.UserRoles.NameRole);
556− LightDM.Greeter.authenticate(user);
557−
558− var userList = findChild(view, "userList");
559− var promptList = findChild(view, "promptList");
560−
561− view.currentIndex = i;
562− tryCompare(promptList, "opacity", 1);
563− tryCompare(userList, "movingInternally", false);
564− }
565−
566− function selectUser(name) {
567− var i = getIndexOf(name);
568− selectIndex(i);
569− return i;
570− }
571−
572−/* Desktop view tests (multiuser) */
573− function test_sessionless_user_is_still_valid() {
574− setUsageMode("desktop");
575− var loginList = findChild(view, "loginList")
576−
577− /*
578− * If a user has never logged in before, or, for some reason
579− * has no sessionHint, ensure that the model returns the default
580− * session and that the view respects this
581− */
582− selectUser("no-session");
583− compare(LightDM.Users.data(loginList.currentIndex, LightDM.UserRoles.SessionRole), LightDM.Greeter.defaultSession);
584−
585− tryCompare(loginList, "currentSession", LightDM.Greeter.defaultSession);
586− }
587−
588− function test_changingSessionSticksToUser() {
589− setUsageMode("desktop");
590− var loginList = findChild(view, "loginList");
591−
592− selectUser("invalid-session");
593− tryCompare(loginList, "currentSession", "invalid");
594−
595− selectUser("has-password");
596− tryCompare(loginList, "currentSession", "ubuntu");
597−
598− selectUser("invalid-session")
599− tryCompare(loginList, "currentSession", "invalid");
600− }
601−
602− function test_sessionIconsAreValid() {
603− setUsageMode("desktop");
604− LightDMController.sessionMode = "full";
605− selectUser("has-password");
606−
607− // Test the login list icon is valid
608− var sessionChooserButton = findChild(view, "sessionChooserButton");
609− compare(sessionChooserButton.visible, true);
610−
611− var loginList = findChild(view, "loginList");
612− var session = String(loginList.currentSession).toLowerCase();
613− var icon = String(sessionChooserButton.icon);
614− compare(icon.indexOf(session) > -1, true);
615−
616− // Test the session list icons are valid
617− var lockscreen = findChild(view, "lockscreen");
618− lockscreen.state = "SessionsList"
619− var sessionsList = findChild(view, "sessionsList");
620− tryCompare(sessionsList, "visible", true);
621− }
622−
623− function test_choosingNewSessionChangesLoginListIcon() {
624− // Ensure the default session is selected (Ubuntu)
625− cleanup();
626− setUsageMode("desktop");
627− swipeAwayCover();
628−
629− LightDMController.sessionMode = "full";
630− selectUser("has-password");
631−
632− var sessionChooserButton = findChild(view, "sessionChooserButton");
633− var icon = String(sessionChooserButton.icon);
634− compare(icon.indexOf("ubuntu") > -1, true);
635−
636− tap(sessionChooserButton)
637− for(var i = 0; i < LightDM.Sessions.count; i++) {
638− var delegateName = "sessionDelegate" + String(i);
639− var currentDelegate = findChild(view, delegateName);
640− verify(currentDelegate);
641− var sessionKey = LightDM.Sessions.data(i,LightDM.SessionRoles.KeyRole);
642− if (sessionKey === "gnome-classic") {
643− waitForRendering(currentDelegate);
644− tap(currentDelegate);
645− waitForRendering(sessionChooserButton);
646− break;
647− }
648− }
649−
650− icon = String(sessionChooserButton.icon);
651− compare(icon.indexOf("gnome") > -1, true,
652− "Expected icon to contain gnome but it was " + icon);
653− }
654−
655− function test_noSessionsDoesntBreakView() {
656− setUsageMode("desktop");
657− LightDMController.sessionMode = "none";
658− compare(LightDM.Sessions.count, 0)
659− }
660−
661− function test_sessionIconNotShownWithOneSession() {
662− setUsageMode("desktop");
663− LightDMController.sessionMode = "single";
664− compare(LightDM.Sessions.count, 1);
665−
666− var sessionChooserButton = findChild(view, "sessionChooserButton");
667− tryCompare(sessionChooserButton, "visible", false);
668− }
669−
670− function test_sessionIconNotShownWithActiveUser() {
671− setUsageMode("desktop");
672− selectUser("active");
673−
674− var sessionChooserButton = findChild(view, "sessionChooserButton");
675− tryCompare(sessionChooserButton, "visible", false);
676− }
677−
678− function test_sessionIconShownWithMultipleSessions() {
679− setUsageMode("desktop");
680− LightDMController.sessionMode = "full";
681− selectUser("has-password");
682−
683− var sessionChooserButton = findChild(view, "sessionChooserButton");
684− tryCompare(sessionChooserButton, "visible", true);
685− }
686−
687− function test_selected() {
688− setUsageMode("desktop");
689− swipeAwayCover();
690− var delegate = findChild(view, "username2");
691− tap(delegate);
692− compare(selectedSpy.count, 1);
693− compare(selectedSpy.signalArguments[0][0], 2);
694− compare(view.currentIndex, 0); // confirm we didn't change
695− }
696−
697− function test_customBackground() {
698− setUsageMode("desktop");
699− var lockscreen = findChild(view, "lockscreen");
700− var backgroundShade = findChild(lockscreen, "backgroundShade");
701−
702− verify(!view.hasCustomBackground);
703− verify(!backgroundShade.visible);
704−
705− view.hasCustomBackground = true;
706− verify(backgroundShade.visible);
707− }
708−
709− function test_respondedWithPassword() {
710− setUsageMode("desktop");
711− swipeAwayCover();
712− selectUser("has-password");
713− var greeterPrompt = findChild(view, "greeterPrompt0");
714− verify(greeterPrompt.isSecret);
715− typeString("password");
716− keyClick(Qt.Key_Enter);
717− compare(respondedSpy.count, 1);
718− compare(respondedSpy.signalArguments[0][0], "password");
719− }
720−
721− function test_respondedWithNonSecret() {
722− setUsageMode("desktop");
723− swipeAwayCover();
724− selectUser("question-prompt");
725− var greeterPrompt = findChild(view, "greeterPrompt0");
726− verify(!greeterPrompt.isSecret);
727− typeString("foo");
728− keyClick(Qt.Key_Enter);
729− compare(respondedSpy.count, 1);
730− compare(respondedSpy.signalArguments[0][0], "foo");
731− }
732−
733− function test_fullyShown_data() {
734− return [
735− {tag: "multiuser", multiUser: true, usageMode: "desktop", results: [true, true, true, true]},
736− {tag: "singleuser", multiUser: false, usageMode: "desktop", results: [true, false, true, false]},
737− {tag: "multiuser", multiUser: true, usageMode: "phone", results: [true, true, true, true]},
738− {tag: "singleuser", multiUser: false, usageMode: "phone", results: [true, false, true, false]}
739− ]
740− }
741−
742− function test_fullyShown(data) {
743− setUsageMode(data.usageMode);
744− view.multiUser = data.multiUser;
745− tryCompare(view, "fullyShown", data.results[0]);
746− tryCompare(view, "required", data.results[0]);
747− swipeAwayCover();
748− tryCompare(view, "fullyShown", data.results[1]);
749− tryCompare(view, "required", data.results[1]);
750− view.locked = true;
751− tryCompare(view, "fullyShown", data.results[2]);
752− tryCompare(view, "required", data.results[2]);
753− view.locked = false;
754− tryCompare(view, "fullyShown", data.results[3]);
755− tryCompare(view, "required", data.results[3]);
756− }
757−
758− function test_infoPrompt() {
759− setUsageMode("desktop");
760− selectUser("info-prompt");
761−
762− var infoLabel = findChild(view, "infoLabel0");
763− compare(infoLabel.text, "Welcome to Unity Greeter");
764− compare(infoLabel.textFormat, Text.PlainText);
765−
766− verify(findChild(view, "greeterPrompt1") != null);
767− }
768−
769− function test_longInfoPrompt() {
770− setUsageMode("desktop");
771− selectUser("long-info-prompt");
772−
773− var infoLabel = findChild(view, "infoLabel0");
774− compare(infoLabel.text, "Welcome to Unity Greeter\n\nWe like to annoy you with super ridiculously long messages.\nLike this one\n\nThis is the last line of a multiple line message.");
775− verify(infoLabel.contentWidth > infoLabel.width);
776−
777− verify(findChild(view, "greeterPrompt1") != null);
778− }
779−
780− function test_multiInfoPrompt() {
781− setUsageMode("desktop");
782− selectUser("multi-info-prompt");
783−
784− var infoLabel0 = findChild(view, "infoLabel0");
785− compare(infoLabel0.text, "Welcome to Unity Greeter");
786−
787− var infoLabel1 = findChild(view, "infoLabel1");
788− compare(infoLabel1.text, "This is an error");
789− compare(infoLabel1.color, theme.palette.normal.negative);
790−
791− var infoLabel2 = findChild(view, "infoLabel2");
792− compare(infoLabel2.text, "You should have seen three messages");
793−
794− verify(findChild(view, "greeterPrompt3") != null);
795− }
796−
797− // Escape is used to reset the authentication, especially if PAM is unresponsive
798− function test_escape() {
799− setUsageMode("desktop");
800− swipeAwayCover();
801− var index = selectUser("has-password");
802− selectedSpy.clear();
803− view.locked = true;
804− var promptField = findChild(view, "promptField");
805− tap(promptField);
806− verify(promptField.activeFocus);
807− compare(promptField.opacity, 1);
808−
809− typeString("password");
810− keyClick(Qt.Key_Enter);
811− verify(promptField.activeFocus);
812− compare(promptField.opacity, 0); // hidden by fakeLabel
813−
814− compare(selectedSpy.count, 0);
815− keyClick(Qt.Key_Escape);
816− compare(selectedSpy.count, 1);
817− compare(selectedSpy.signalArguments[0][0], index);
818−
819− selectIndex(index);
820− var promptField = findChild(view, "promptField");
821− verify(promptField.activeFocus);
822− compare(promptField.opacity, 1);
823− }
824−
825− function test_unicode() {
826− setUsageMode("desktop");
827− var index = selectUser("unicode");
828− var label = findChild(view, "username" + index);
829− tryCompare(label, "text", "가나다라마");
830− }
831−
832− function test_authError() {
833− setUsageMode("desktop");
834− swipeAwayCover();
835− var index = selectUser("auth-error");
836− var greeterPrompt = findChild(view, "greeterPrompt1"); // after error message
837− view.locked = true;
838− compare(greeterPrompt.text, "Retry");
839− tap(greeterPrompt);
840− compare(respondedSpy.count, 0);
841− compare(selectedSpy.count, 1);
842− compare(selectedSpy.signalArguments[0][0], index);
843− }
844−
845− function test_noPassword() {
846− setUsageMode("desktop");
847− swipeAwayCover();
848− selectUser("no-password");
849− var greeterPrompt = findChild(view, "greeterPrompt0");
850− compare(greeterPrompt.text, "Log In");
851− tap(greeterPrompt);
852− compare(selectedSpy.count, 0);
853− compare(respondedSpy.count, 1);
854− compare(respondedSpy.signalArguments[0][0], "");
855− }
856−
857− function test_loginListNotCoveredByKeyboard() {
858− setUsageMode("desktop");
859− var loginList = findChild(view, "loginList");
860− compare(loginList.height, view.height);
861−
862− // when the vkb shows up, loginList is moved up to remain fully uncovered
863−
864− keyboardVisibleCheckBox.checked = true;
865−
866− var halfway = (view.height - loginList.highlightedHeight) / 2;
867− var halfwayWithOsk = halfway - view.inputMethodRect.height / 2;
868− tryCompare(loginList, "boxVerticalOffset", halfwayWithOsk);
869−
870− var highlightItem = findChild(loginList, "highlightItem");
871− tryCompareFunction( function() {
872− var highlightRect = highlightItem.mapToItem(view, 0, 0, highlightItem.width, highlightItem.height);
873− return highlightRect.y + highlightRect.height <= view.testKeyboard.y;
874− }, true);
875−
876− // once the vkb goes away, loginList goes back to its full height
877−
878− keyboardVisibleCheckBox.checked = false;
879−
880− tryCompare(loginList, "boxVerticalOffset", halfway);
881− }
882−
883− function test_passphrase() {
884− setUsageMode("desktop");
885− swipeAwayCover();
886− var index = selectUser("has-password");
887− var promptField = findChild(view, "promptField");
888−
889− verify(view.alphanumeric);
890− compare(promptField.inputMethodHints & Qt.ImhDigitsOnly, 0);
891−
892− keyClick(Qt.Key_D);
893− compare(promptField.text, "d");
894− }
895−
896− function test_passcode() {
897− setUsageMode("desktop");
898− swipeAwayCover();
899− var index = selectUser("has-pin");
900− view.alphanumeric = false;
901−
902− var promptField = findChild(view, "promptField");
903− compare(promptField.inputMethodHints & Qt.ImhDigitsOnly, Qt.ImhDigitsOnly);
904−
905− keyClick(Qt.Key_D);
906− compare(promptField.text, "");
907−
908− keyClick(Qt.Key_0);
909− keyClick(Qt.Key_0);
910− keyClick(Qt.Key_0);
911− keyClick(Qt.Key_0);
912− compare(promptField.text, "0000");
913−
914− compare(respondedSpy.count, 1);
915− compare(respondedSpy.signalArguments[0][0], "0000");
916−
917− compare(promptField.opacity, 0);
918− }
919−
920− function test_loginListMovement_data() {
921− return [
922− {tag: "up", key: Qt.Key_Up, result: 1},
923− {tag: "down", key: Qt.Key_Down, result: 3},
924− ]
925− }
926−
927− function test_loginListMovement(data) {
928− setUsageMode("desktop");
929− swipeAwayCover();
930− selectIndex(2);
931− selectedSpy.clear();
932−
933− keyClick(data.key);
934− compare(selectedSpy.count, 1);
935− compare(selectedSpy.signalArguments[0][0], data.result);
936− }
937−
938− function test_focusStaysActive() {
939− setUsageMode("desktop");
940− swipeAwayCover();
941− selectUser("no-password");
942− var greeterPrompt = findChild(view, "greeterPrompt0");
943− verify(greeterPrompt.activeFocus);
944− keyClick(Qt.Key_Enter);
945− compare(selectedSpy.count, 0);
946− compare(respondedSpy.count, 1);
947− compare(respondedSpy.signalArguments[0][0], "");
948− verify(greeterPrompt.activeFocus);
949− keyClick(Qt.Key_Enter);
950− compare(respondedSpy.count, 1);
951−
952− selectUser("has-password");
953− var greeterPrompt = findChild(view, "greeterPrompt0");
954− verify(greeterPrompt.activeFocus);
955− keyClick(Qt.Key_D);
956− keyClick(Qt.Key_Enter);
957− compare(selectedSpy.count, 0);
958− compare(respondedSpy.count, 2);
959− compare(respondedSpy.signalArguments[1][0], "d");
960− verify(greeterPrompt.activeFocus);
961− keyClick(Qt.Key_Enter);
962− compare(respondedSpy.count, 2);
963−
964− var index = selectUser("no-password");
965− var greeterPrompt = findChild(view, "greeterPrompt0");
966− view.locked = true;
967− verify(greeterPrompt.activeFocus);
968− keyClick(Qt.Key_Enter);
969− compare(respondedSpy.count, 2);
970− compare(selectedSpy.count, 1);
971− compare(selectedSpy.signalArguments[0][0], index);
972− verify(greeterPrompt.activeFocus);
973− keyClick(Qt.Key_Enter);
974− compare(selectedSpy.count, 1);
975−
976− selectUser("no-password");
977− var greeterPrompt = findChild(view, "greeterPrompt0");
978− verify(greeterPrompt.activeFocus);
979− }
980−
981− function test_scrollChangesIndex() {
982− /* Check if the index of the current user gets changed
983− * if we scroll through the list.
984− * Expected result: the list has moved at least by one
985− * user and the index of the user is reported using the
986− * selected signal in LoginList.qml
987− * https://github.com/ubports/unity8/issues/397
988− */
989− setUsageMode("desktop");
990− swipeAwayCover();
991− var loginList = findChild(view, "loginList");
992− // FIXME: Fix scrolling sensitivity (scrolling by -1 shouldn't move more than one user down)
993− mouseWheel( loginList, loginList.width / 2, loginList.height / 2, 0, -1, null );
994− selectedSpy.wait();
995− tryVerify(function(){ return selectedSpy.signalArguments[0][0] > 0 });
996− }
997−
998−// function test_dragChangesIndex() {
999−// /* Check if the index of the current user gets changed
1000−// * if we drag the list or swipe.on it.
1001−// * Expected result: the list has moved at least by one
1002−// * user and the index of the user is reported using the
1003−// * selected signal in LoginList.qml
1004−// * https://github.com/ubports/unity8/issues/397
1005−// */
1006−// setUsageMode("desktop");
1007−// swipeAwayCover();
1008−// var loginList = findChild(view, "loginList");
1009−// // FIXME: Fix scrolling sensitivity (the number of scrolled users is randomly changing)
1010−// touchFlick(loginList, loginList.width/2, loginList.height/3, loginList.width/2, loginList.height/3 -units.gu(2.1));
1011−// selectedSpy.wait();
1012−// tryVerify(function(){ return selectedSpy.signalArguments[0][0] > 0 });
1013−// }
1014−
1015−/* Mobile view tests */
1016− function test_tease_data() {
1017− return [
1018− {tag: "left", x: 0, offset: 0, count: 1},
1019− {tag: "leftWithOffsetPass", x: 10, offset: 10, count: 1},
1020− {tag: "leftWithOffsetFail", x: 9, offset: 10, count: 0},
1021− {tag: "right", x: view.width, offset: 0, count: 1},
1022− ]
1023− }
1024− function test_tease(data) {
1025− view.dragHandleLeftMargin = data.offset;
1026− tap(view, data.x, 0);
1027− tryCompare(teaseSpy, "count", data.count);
1028− }
1029−
1030− function test_respondedWithPin() {
1031− LightDM.Greeter.authenticate("has-pin");
1032− view.locked = true;
1033− view.alphanumeric = false;
1034− swipeAwayCover();
1035− typeString("1234");
1036− respondedSpy.wait();
1037− compare(respondedSpy.signalArguments[0][0], "1234");
1038− }
1039−
1040− function test_respondedWithPassphrase() {
1041− LightDM.Greeter.authenticate("has-password");
1042− view.locked = true;
1043− swipeAwayCover();
1044− typeString("test");
1045− keyClick(Qt.Key_Enter);
1046− compare(respondedSpy.count, 1);
1047− compare(respondedSpy.signalArguments[0][0], "test");
1048− }
1049−
1050− function test_respondedWithSwipe_data() {
1051− return [
1052− {tag: "left", toTheRight: false, hiddenX: -view.width},
1053− {tag: "right", toTheRight: true, hiddenX: view.width},
1054− ];
1055− }
1056− function test_respondedWithSwipe(data) {
1057− swipeAwayCover(data.toTheRight);
1058− var coverPage = findChild(view, "coverPage");
1059− compare(coverPage.x, data.hiddenX);
1060− compare(respondedSpy.count, 1);
1061− compare(respondedSpy.signalArguments[0][0], "");
1062− }
1063−
1064− function test_emergencyCall_data() {
1065− return [
1066− {tag: "phone", available: true},
1067− {tag: "desktop", available: false},
1068− ];
1069− }
1070− function test_emergencyCall(data) {
1071− telepathyHelper.emergencyCallsAvailable = data.available;
1072− view.locked = true;
1073− swipeAwayCover();
1074− var emergencyCallLabel = findChild(view, "emergencyCallLabel");
1075− tap(emergencyCallLabel);
1076− if (!data.available) {
1077− expectFail("", "Bug 1616538 prevents us supporting conditional emergency button support");
1078− }
1079− compare(emergencyCallLabel.visible, data.available ? true : false);
1080− compare(emergencySpy.count, data.available ? 1 : 0);
1081− }
1082−
1083− function test_emergencyCallAvailability() {
1084− view.locked = true;
1085− var emergencyCallLabel = findChild(view, "emergencyCallLabel");
1086− verify(emergencyCallLabel.visible);
1087−
1088− telepathyHelper.emergencyCallsAvailable = false;
1089− telepathyHelper.ready = true;
1090− expectFail("", "Bug 1616538 prevents us supporting conditional emergency button support");
1091− verify(!emergencyCallLabel.visible);
1092−
1093− telepathyHelper.emergencyCallsAvailable = true;
1094− telepathyHelper.ready = false;
1095− verify(!emergencyCallLabel.visible);
1096− }
1097−
1098− function test_tryToUnlock() {
1099− var coverPage = findChild(view, "coverPage");
1100− tryCompare(coverPage, "showProgress", 1);
1101− compare(view.tryToUnlock(false), true);
1102− tryCompare(coverPage, "showProgress", 0);
1103− compare(view.tryToUnlock(false), false);
1104− }
1105−
1106− /*
1107− Regression test for https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1388359
1108− "User metrics can no longer be changed by double tap"
1109− */
1110− function test_doubleTapSwitchesToNextInfographic() {
1111− setUsageMode("phone");
1112− infographicDataChangedSpy.clear();
1113− var coverPage = findChild(view, "coverPage");
1114− verify(coverPage);
1115−
1116− var infographicPrivate = findInvisibleChild(coverPage, "infographicPrivate");
1117− verify(infographicPrivate);
1118−
1119− // wait for the UI to settle down before double tapping it
1120− tryCompare(infographicPrivate, "animating", false);
1121−
1122− var dataCircle = findChild(coverPage, "dataCircle");
1123− verify(dataCircle);
1124−
1125− mouseDoubleClickSequence(dataCircle);
1126−
1127− tryCompare(infographicDataChangedSpy, "count", 1);
1128− }
1129−
1130− function test_movesBackIntoPlaceWhenNotDraggedFarEnough() {
1131− var coverPage = findChild(view, "coverPage");
1132−
1133− var dragEvaluator = findInvisibleChild(coverPage, "edgeDragEvaluator");
1134− verify(dragEvaluator);
1135−
1136− // Make it easier to get a rejection/rollback. Otherwise would have to inject
1137− // a fake timer into dragEvaluator.
1138− // Afterall, we are testing if the CoverPage indeed moves back on a
1139− // rollback decision, not the drag evaluation itself.
1140− dragEvaluator.minDragDistance = dragEvaluator.maxDragDistance / 2;
1141−
1142− // it starts as fully shown
1143− compare(coverPage.x, 0);
1144−
1145− // then we drag it a bit
1146− var startX = coverPage.width - 1;
1147− var touchY = coverPage.height / 2;
1148− var dragXDelta = -(dragEvaluator.minDragDistance * 0.3);
1149− touchFlick(coverPage,
1150− startX , touchY, // start pos
1151− startX + dragXDelta, touchY, // end pos
1152− true /* beginTouch */, false /* endTouch */);
1153−
1154− // which should make it move a bit
1155− tryCompareFunction(function() {return coverPage.x < 0;}, true);
1156−
1157− // then we release it
1158− touchRelease(coverPage, startX + dragXDelta, touchY);
1159−
1160− // which should make it move back into its original position as it didn't move
1161− // far enough to have it hidden
1162− tryCompare(coverPage, "x", 0);
1163− }
1164−
1165− function test_dragToHide_data() {
1166− return [
1167− {tag: "left", startX: view.width * 0.95, endX: view.width * 0.1, hiddenX: -view.width},
1168− {tag: "right", startX: view.width * 0.1, endX: view.width * 0.95, hiddenX: view.width},
1169− ];
1170− }
1171− function test_dragToHide(data) {
1172− var coverPage = findChild(view, "coverPage");
1173− compare(coverPage.x, 0);
1174− compare(coverPage.visible, true);
1175− compare(coverPage.shown, true);
1176− compare(coverPage.showProgress, 1);
1177− compare(view.fullyShown, true);
1178−
1179− touchFlick(view,
1180− data.startX, view.height / 2, // start pos
1181− data.endX, view.height / 2); // end pos
1182−
1183− tryCompare(coverPage, "x", data.hiddenX);
1184− tryCompare(coverPage, "visible", false);
1185− tryCompare(coverPage, "shown", false);
1186− tryCompare(coverPage, "showProgress", 0);
1187− compare(view.fullyShown, false);
1188− }
1189−
1190− function test_hiddenViewRemainsHiddenAfterResize_data() {
1191− return [
1192− {tag: "left", startX: view.width * 0.95, endX: view.width * 0.1},
1193− {tag: "right", startX: view.width * 0.1, endX: view.width * 0.95},
1194− ];
1195− }
1196− function test_hiddenViewRemainsHiddenAfterResize(data) {
1197− touchFlick(view,
1198− data.startX, view.height / 2, // start pos
1199− data.endX, view.height / 2); // end pos
1200−
1201− var coverPage = findChild(view, "coverPage");
1202− tryCompare(coverPage, "x", data.tag == "left" ? -view.width : view.width);
1203− tryCompare(coverPage, "visible", false);
1204− tryCompare(coverPage, "shown", false);
1205− tryCompare(coverPage, "showProgress", 0);
1206−
1207− // flip dimensions to simulate an orientation change
1208− view.width = loader.height;
1209− view.height = loader.width;
1210−
1211− // All properties should remain consistent
1212− tryCompare(coverPage, "x", data.tag == "left" ? -view.width : view.width);
1213− tryCompare(coverPage, "visible", false);
1214− tryCompare(coverPage, "shown", false);
1215− tryCompare(coverPage, "showProgress", 0);
1216− }
1217−
1218− // Make sure that if user has a mouse, they can still get rid of cover page
1219− function test_mouseClickHidesCoverPage() {
1220− var coverPage = findChild(view, "coverPage");
1221−
1222− verify(coverPage.shown);
1223− mouseClick(coverPage, coverPage.width/2, coverPage.height - units.gu(2));
1224− verify(!coverPage.shown);
1225− }
1226−
1227− function test_showErrorMessage() {
1228− var coverPage = findChild(view, "coverPage");
1229− var swipeHint = findChild(coverPage, "swipeHint");
1230− var errorMessageAnimation = findInvisibleChild(coverPage, "errorMessageAnimation");
1231−
1232− view.showErrorMessage("hello");
1233− compare(swipeHint.text, "《 hello 》");
1234− verify(errorMessageAnimation.running);
1235− verify(swipeHint.opacityAnimation.running);
1236−
1237− errorMessageAnimation.complete();
1238− swipeHint.opacityAnimation.complete();
1239− tryCompare(swipeHint, "text", "《 " + i18n.tr("Unlock") + " 》");
1240− }
1241−
1242− function test_keepPasswordOnRotation() {
1243− /* Check if the password is kept when rotating
1244− * the screen from portrait to landscape in mobile.
1245− */
1246− setUsageMode("phone");
1247− LightDM.Greeter.authenticate("has-password");
1248− view.locked = true;
1249− swipeAwayCover();
1250−
1251− let greeterPrompt = findChild(view, "greeterPrompt0");
1252− tryCompare(greeterPrompt, "enteredText", "");
1253− typeString("test");
1254− tryCompare(greeterPrompt, "enteredText", "test");
1255− view.orientation = Qt.LandscapeOrientation;
1256− tryCompare(greeterPrompt, "enteredText", "test");
1257− }
1258−
1259− function test_infographicsShownOnRotation_data() {
1260− return [
1261− {
1262− tag: "phone-portrait-singleuser",
1263− multiUser: false,
1264− usageMode: "phone",
1265− orientation: Qt.PortraitOrientation,
1266− coverPage: true,
1267− lockscreen: false
1268− },
1269− {
1270− tag: "phone-portrait-multiuser",
1271− multiUser: true,
1272− usageMode: "phone",
1273− orientation: Qt.PortraitOrientation,
1274− coverPage: false,
1275− lockscreen: false
1276− },
1277− {
1278− tag: "phone-landscape-singleuser",
1279− multiUser: false,
1280− usageMode: "phone",
1281− orientation: Qt.LandscapeOrientation,
1282− coverPage: true,
1283− lockscreen: false
1284− },
1285− {
1286− tag: "phone-landscape-multiuser",
1287− multiUser: true,
1288− usageMode: "phone",
1289− orientation: Qt.LandscapeOrientation,
1290− coverPage: false,
1291− lockscreen: false
1292− },
1293− {
1294− tag: "tablet-landscape-singleuser",
1295− multiUser: false,
1296− usageMode: "tablet",
1297− orientation: Qt.LandscapeOrientation,
1298− coverPage: true,
1299− lockscreen: false
1300− },
1301− {
1302− tag: "tablet-landscape-multiuser",
1303− multiUser: true,
1304− usageMode: "tablet",
1305− orientation: Qt.LandscapeOrientation,
1306− coverPage: false,
1307− lockscreen: true
1308− },
1309− {
1310− tag: "tablet-portrait-singleuser",
1311− multiUser: false,
1312− usageMode: "tablet",
1313− orientation: Qt.PortraitOrientation,
1314− coverPage: true,
1315− lockscreen: false
1316− },
1317− {
1318− tag: "tablet-portrait-multiuser",
1319− multiUser: true,
1320− usageMode: "tablet",
1321− orientation: Qt.PortraitOrientation,
1322− coverPage: false,
1323− lockscreen: false
1324− },
1325− {
1326− tag: "desktop-singleuser",
1327− multiUser: false,
1328− usageMode: "desktop",
1329− orientation: Qt.LandscapeOrientation,
1330− coverPage: false,
1331− lockscreen: true
1332− },
1333− {
1334− tag: "desktop-multiuser",
1335− multiUser: true,
1336− usageMode: "desktop",
1337− orientation: Qt.LandscapeOrientation,
1338− coverPage: false,
1339− lockscreen: true
1340− }
1341− ]
1342− }
1343−
1344− function test_infographicsShownOnRotation(data) {
1345− /* Check if infographics is shown in the correct
1346− * places. This test runs in all the combinations
1347− * of orientation and usage mode.
1348− */
1349− setUsageMode(data.usageMode);
1350− view.orientation = data.orientation;
1351− view.multiUser = data.multiUser;
1352− view.locked = true;
1353−
1354− let coverPage = findChild(view, "coverPage");
1355− let lockscreen = findChild(view, "lockscreen");
1356− let coverPageInfographics = findChild(coverPage, "infographicsLoader");
1357− let lockscreenInfographics = findChild(lockscreen, "infographicsLoader");
1358− verify(coverPageInfographics);
1359− verify(lockscreenInfographics);
1360−
1361− tryCompare(coverPageInfographics, "active", data.coverPage);
1362− swipeAwayCover();
1363− tryCompare(lockscreenInfographics, "active", data.lockscreen);
1364− }
1365− }
1366−}
tests/qmltests/Greeter/tst_NarrowView.qmlnew file+565
@@ -0,0 +1,565 @@
1+/*
2+ * Copyright 2014 Canonical Ltd.
3+ * Copyright 2021 UBports Foundation
4+ *
5+ * This program is free software; you can redistribute it and/or modify
6+ * it under the terms of the GNU General Public License as published by
7+ * the Free Software Foundation; version 3.
8+ *
9+ * This program is distributed in the hope that it will be useful,
10+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
11+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+ * GNU General Public License for more details.
13+ *
14+ * You should have received a copy of the GNU General Public License
15+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
16+ */
17+
18+import QtQuick 2.4
19+import QtTest 1.0
20+import ".."
21+import "../../../qml/Greeter"
22+import LightDMController 0.1
23+import LightDM.FullLightDM 0.1 as LightDM
24+import Ubuntu.Components 1.3
25+import Ubuntu.Telephony 0.1 as Telephony
26+import Unity.Test 0.1 as UT
27+
28+Item {
29+ id: root
30+ width: units.gu(90)
31+ height: units.gu(80)
32+
33+ Component.onCompleted: theme.name = "Ubuntu.Components.Themes.SuruDark" // use the same theme as the real shell
34+
35+ Binding {
36+ target: LightDMController
37+ property: "userMode"
38+ value: "single"
39+ }
40+
41+ Row {
42+ anchors.fill: parent
43+ Loader {
44+ id: loader
45+ width: root.width - controls.width
46+ height: parent.height
47+
48+ property bool itemDestroyed: false
49+ sourceComponent: Component {
50+ NarrowView {
51+ background: "/usr/share/backgrounds/warty-final-ubuntu.png"
52+ userModel: LightDM.Users
53+ infographicModel: LightDM.Infographic
54+
55+ launcherOffset: parseFloat(launcherOffsetField.text)
56+ currentIndex: parseInt(currentIndexField.text, 10)
57+ delayMinutes: parseInt(delayMinutesField.text, 10)
58+ panelHeight: parseFloat(panelHeightField.text)
59+ locked: lockedCheckBox.checked
60+ alphanumeric: alphanumericCheckBox.checked
61+
62+ Component.onDestruction: {
63+ loader.itemDestroyed = true
64+ }
65+
66+ onSelected: {
67+ currentIndexField.text = index;
68+ }
69+ }
70+ }
71+ }
72+
73+ Rectangle {
74+ id: controls
75+ color: theme.palette.normal.background
76+ width: units.gu(40)
77+ height: parent.height
78+
79+ Column {
80+ anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
81+ spacing: units.gu(1)
82+
83+ Row {
84+ Button {
85+ text: "Hide"
86+ onClicked: loader.item.hide()
87+ }
88+ }
89+ Row {
90+ Button {
91+ text: "Show Message"
92+ onClicked: LightDMService.prompts.append(messageField.text, LightDMService.prompts.Message)
93+ }
94+ TextField {
95+ id: messageField
96+ width: units.gu(10)
97+ text: ""
98+ }
99+ }
100+ Row {
101+ Button {
102+ text: "Show Prompt"
103+ onClicked: LightDMService.prompts.append(promptField.text, isSecretCheckBox.checked ? LightDMService.prompts.Secret : LightDMService.prompts.Question)
104+ }
105+ TextField {
106+ id: promptField
107+ width: units.gu(10)
108+ text: ""
109+ }
110+ CheckBox {
111+ id: isSecretCheckBox
112+ }
113+ Label {
114+ text: "secret"
115+ }
116+ }
117+ Row {
118+ Button {
119+ text: "Notify Auth Failure"
120+ onClicked: {
121+ loader.item.notifyAuthenticationFailed();
122+ }
123+ }
124+ }
125+ Row {
126+ Button {
127+ text: "Try To Unlock"
128+ onClicked: loader.item.tryToUnlock(toTheRightCheckBox.checked)
129+ }
130+ CheckBox {
131+ id: toTheRightCheckBox
132+ }
133+ Label {
134+ text: "toTheRight"
135+ }
136+ }
137+ Row {
138+ TextField {
139+ id: launcherOffsetField
140+ width: units.gu(10)
141+ text: "0"
142+ }
143+ Label {
144+ text: "launcherOffset"
145+ }
146+ }
147+ Row {
148+ TextField {
149+ id: currentIndexField
150+ width: units.gu(10)
151+ text: "0"
152+ }
153+ Label {
154+ text: "currentIndex"
155+ }
156+ }
157+ Row {
158+ TextField {
159+ id: delayMinutesField
160+ width: units.gu(10)
161+ text: "0"
162+ }
163+ Label {
164+ text: "delayMinutes"
165+ }
166+ }
167+ Row {
168+ TextField {
169+ id: panelHeightField
170+ width: units.gu(10)
171+ text: "0"
172+ }
173+ Label {
174+ text: "panelHeight"
175+ }
176+ }
177+ Row {
178+ CheckBox {
179+ id: lockedCheckBox
180+ }
181+ Label {
182+ text: "locked"
183+ }
184+ }
185+ Row {
186+ CheckBox {
187+ id: alphanumericCheckBox
188+ checked: true
189+ }
190+ Label {
191+ text: "alphanumeric"
192+ }
193+ }
194+ Row {
195+ Label {
196+ text: "selected: " + selectedSpy.count
197+ }
198+ }
199+ Row {
200+ Label {
201+ text: "responded: " + respondedSpy.count
202+ }
203+ }
204+ Row {
205+ Label {
206+ text: "teased: " + teaseSpy.count
207+ }
208+ }
209+ Row {
210+ Label {
211+ text: "emergency: " + emergencySpy.count
212+ }
213+ }
214+ Row {
215+ Button {
216+ text: "Reload View"
217+ onClicked: {
218+ loader.active = false;
219+ loader.active = true;
220+ }
221+ }
222+ }
223+ }
224+ }
225+ }
226+
227+ Binding {
228+ target: LightDM.Infographic
229+ property: "username"
230+ value: "has-password"
231+ }
232+
233+ SignalSpy {
234+ id: selectedSpy
235+ target: loader.item
236+ signalName: "selected"
237+ }
238+
239+ SignalSpy {
240+ id: respondedSpy
241+ target: loader.item
242+ signalName: "responded"
243+ }
244+
245+ SignalSpy {
246+ id: teaseSpy
247+ target: loader.item
248+ signalName: "tease"
249+ }
250+
251+ SignalSpy {
252+ id: emergencySpy
253+ target: loader.item
254+ signalName: "emergencyCall"
255+ }
256+
257+ SignalSpy {
258+ id: infographicDataChangedSpy
259+ target: LightDM.Infographic
260+ signalName: "dataChanged"
261+ }
262+
263+ UT.UnityTestCase {
264+ name: "NarrowView"
265+ when: windowShown
266+
267+ property Item view: loader.status === Loader.Ready ? loader.item : null
268+
269+ function init() {
270+ view.currentIndex = 0; // break binding with text field
271+
272+ LightDM.Greeter.authenticate("no-password");
273+ tryCompare(LightDMService.prompts, "count", 1);
274+
275+ telepathyHelper.ready = true;
276+ telepathyHelper.emergencyCallsAvailable = true;
277+ selectedSpy.clear();
278+ respondedSpy.clear();
279+ teaseSpy.clear();
280+ emergencySpy.clear();
281+ infographicDataChangedSpy.clear();
282+ }
283+
284+ function cleanup() {
285+ loader.itemDestroyed = false;
286+ loader.active = false;
287+ tryCompare(loader, "status", Loader.Null);
288+ tryCompare(loader, "item", null);
289+ tryCompare(loader, "itemDestroyed", true);
290+ loader.active = true;
291+ tryCompare(loader, "status", Loader.Ready);
292+ removeTimeConstraintsFromSwipeAreas(loader.item);
293+ }
294+
295+ function swipeAwayCover(toTheRight) {
296+ if (toTheRight === undefined) {
297+ toTheRight = false;
298+ }
299+
300+ tryCompare(view, "fullyShown", true);
301+ var touchY = view.height / 2;
302+ if (toTheRight) {
303+ touchFlick(view, 0, touchY, view.width, touchY);
304+ } else {
305+ touchFlick(view, view.width, touchY, 0, touchY);
306+ }
307+ var coverPage = findChild(view, "coverPage");
308+ tryCompare(coverPage, "showProgress", 0);
309+ waitForRendering(view);
310+ }
311+
312+ function test_tease_data() {
313+ return [
314+ {tag: "left", x: 0, offset: 0, count: 1},
315+ {tag: "leftWithOffsetPass", x: 10, offset: 10, count: 1},
316+ {tag: "leftWithOffsetFail", x: 9, offset: 10, count: 0},
317+ {tag: "right", x: view.width, offset: 0, count: 1},
318+ ]
319+ }
320+ function test_tease(data) {
321+ view.dragHandleLeftMargin = data.offset;
322+ tap(view, data.x, 0);
323+ tryCompare(teaseSpy, "count", data.count);
324+ }
325+
326+ function test_customBackground() {
327+ var lockscreenShade = findChild(view, "lockscreenShade");
328+ var backgroundShade = findChild(view, "backgroundShade");
329+
330+ compare(lockscreenShade.opacity, 0);
331+ verify(!backgroundShade.visible);
332+
333+ view.hasCustomBackground = true;
334+ compare(lockscreenShade.opacity, 0.4);
335+ verify(backgroundShade.visible);
336+ }
337+
338+ function test_respondedWithPin() {
339+ LightDM.Greeter.authenticate("has-pin");
340+ view.locked = true;
341+ view.alphanumeric = false;
342+ swipeAwayCover();
343+ typeString("1234");
344+ respondedSpy.wait();
345+ compare(respondedSpy.signalArguments[0][0], "1234");
346+ }
347+
348+ function test_respondedWithPassphrase() {
349+ LightDM.Greeter.authenticate("has-password");
350+ view.locked = true;
351+ swipeAwayCover();
352+ typeString("test");
353+ keyClick(Qt.Key_Enter);
354+ compare(respondedSpy.count, 1);
355+ compare(respondedSpy.signalArguments[0][0], "test");
356+ }
357+
358+ function test_respondedWithSwipe_data() {
359+ return [
360+ {tag: "left", toTheRight: false, hiddenX: -view.width},
361+ {tag: "right", toTheRight: true, hiddenX: view.width},
362+ ];
363+ }
364+ function test_respondedWithSwipe(data) {
365+ swipeAwayCover(data.toTheRight);
366+ var coverPage = findChild(view, "coverPage");
367+ compare(coverPage.x, data.hiddenX);
368+ compare(respondedSpy.count, 1);
369+ compare(respondedSpy.signalArguments[0][0], "");
370+ }
371+
372+ function test_emergencyCall_data() {
373+ return [
374+ {tag: "phone", available: true},
375+ {tag: "desktop", available: false},
376+ ];
377+ }
378+ function test_emergencyCall(data) {
379+ telepathyHelper.emergencyCallsAvailable = data.available;
380+ view.locked = true;
381+ swipeAwayCover();
382+ var emergencyCallLabel = findChild(view, "emergencyCallLabel");
383+ tap(emergencyCallLabel);
384+ if (!data.available) {
385+ expectFail("", "Bug 1616538 prevents us supporting conditional emergency button support");
386+ }
387+ compare(emergencyCallLabel.visible, data.available ? true : false);
388+ compare(emergencySpy.count, data.available ? 1 : 0);
389+ }
390+
391+ function test_emergencyCallAvailability() {
392+ view.locked = true;
393+ var emergencyCallLabel = findChild(view, "emergencyCallLabel");
394+ verify(emergencyCallLabel.visible);
395+
396+ telepathyHelper.emergencyCallsAvailable = false;
397+ telepathyHelper.ready = true;
398+ expectFail("", "Bug 1616538 prevents us supporting conditional emergency button support");
399+ verify(!emergencyCallLabel.visible);
400+
401+ telepathyHelper.emergencyCallsAvailable = true;
402+ telepathyHelper.ready = false;
403+ verify(!emergencyCallLabel.visible);
404+ }
405+
406+ function test_fullyShown() {
407+ tryCompare(view, "fullyShown", true);
408+ swipeAwayCover();
409+ tryCompare(view, "fullyShown", false);
410+ view.locked = true;
411+ tryCompare(view, "fullyShown", true);
412+ view.locked = false;
413+ tryCompare(view, "fullyShown", false);
414+ }
415+
416+ function test_required() {
417+ tryCompare(view, "required", true);
418+ swipeAwayCover();
419+ tryCompare(view, "required", false);
420+ view.locked = true;
421+ tryCompare(view, "required", true);
422+ view.locked = false;
423+ tryCompare(view, "required", false);
424+ }
425+
426+ function test_tryToUnlock() {
427+ var coverPage = findChild(view, "coverPage");
428+ tryCompare(coverPage, "showProgress", 1);
429+ compare(view.tryToUnlock(false), true);
430+ tryCompare(coverPage, "showProgress", 0);
431+ compare(view.tryToUnlock(false), false);
432+ }
433+
434+ /*
435+ Regression test for https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1388359
436+ "User metrics can no longer be changed by double tap"
437+ */
438+ function test_doubleTapSwitchesToNextInfographic() {
439+ var infographicPrivate = findInvisibleChild(view, "infographicPrivate");
440+ verify(infographicPrivate);
441+
442+ // wait for the UI to settle down before double tapping it
443+ tryCompare(infographicPrivate, "animating", false);
444+
445+ var dataCircle = findChild(view, "dataCircle");
446+ verify(dataCircle);
447+
448+ mouseDoubleClickSequence(dataCircle);
449+
450+ tryCompare(infographicDataChangedSpy, "count", 1);
451+ }
452+
453+ function test_movesBackIntoPlaceWhenNotDraggedFarEnough() {
454+ var coverPage = findChild(view, "coverPage");
455+
456+ var dragEvaluator = findInvisibleChild(coverPage, "edgeDragEvaluator");
457+ verify(dragEvaluator);
458+
459+ // Make it easier to get a rejection/rollback. Otherwise would have to inject
460+ // a fake timer into dragEvaluator.
461+ // Afterall, we are testing if the CoverPage indeed moves back on a
462+ // rollback decision, not the drag evaluation itself.
463+ dragEvaluator.minDragDistance = dragEvaluator.maxDragDistance / 2;
464+
465+ // it starts as fully shown
466+ compare(coverPage.x, 0);
467+
468+ // then we drag it a bit
469+ var startX = coverPage.width - 1;
470+ var touchY = coverPage.height / 2;
471+ var dragXDelta = -(dragEvaluator.minDragDistance * 0.3);
472+ touchFlick(coverPage,
473+ startX , touchY, // start pos
474+ startX + dragXDelta, touchY, // end pos
475+ true /* beginTouch */, false /* endTouch */);
476+
477+ // which should make it move a bit
478+ tryCompareFunction(function() {return coverPage.x < 0;}, true);
479+
480+ // then we release it
481+ touchRelease(coverPage, startX + dragXDelta, touchY);
482+
483+ // which should make it move back into its original position as it didn't move
484+ // far enough to have it hidden
485+ tryCompare(coverPage, "x", 0);
486+ }
487+
488+ function test_dragToHide_data() {
489+ return [
490+ {tag: "left", startX: view.width * 0.95, endX: view.width * 0.1, hiddenX: -view.width},
491+ {tag: "right", startX: view.width * 0.1, endX: view.width * 0.95, hiddenX: view.width},
492+ ];
493+ }
494+ function test_dragToHide(data) {
495+ var coverPage = findChild(view, "coverPage");
496+ compare(coverPage.x, 0);
497+ compare(coverPage.visible, true);
498+ compare(coverPage.shown, true);
499+ compare(coverPage.showProgress, 1);
500+ compare(view.fullyShown, true);
501+
502+ touchFlick(view,
503+ data.startX, view.height / 2, // start pos
504+ data.endX, view.height / 2); // end pos
505+
506+ tryCompare(coverPage, "x", data.hiddenX);
507+ tryCompare(coverPage, "visible", false);
508+ tryCompare(coverPage, "shown", false);
509+ tryCompare(coverPage, "showProgress", 0);
510+ compare(view.fullyShown, false);
511+ }
512+
513+ function test_hiddenViewRemainsHiddenAfterResize_data() {
514+ return [
515+ {tag: "left", startX: view.width * 0.95, endX: view.width * 0.1},
516+ {tag: "right", startX: view.width * 0.1, endX: view.width * 0.95},
517+ ];
518+ }
519+ function test_hiddenViewRemainsHiddenAfterResize(data) {
520+ touchFlick(view,
521+ data.startX, view.height / 2, // start pos
522+ data.endX, view.height / 2); // end pos
523+
524+ var coverPage = findChild(view, "coverPage");
525+ tryCompare(coverPage, "x", data.tag == "left" ? -view.width : view.width);
526+ tryCompare(coverPage, "visible", false);
527+ tryCompare(coverPage, "shown", false);
528+ tryCompare(coverPage, "showProgress", 0);
529+
530+ // flip dimensions to simulate an orientation change
531+ view.width = loader.height;
532+ view.height = loader.width;
533+
534+ // All properties should remain consistent
535+ tryCompare(coverPage, "x", data.tag == "left" ? -view.width : view.width);
536+ tryCompare(coverPage, "visible", false);
537+ tryCompare(coverPage, "shown", false);
538+ tryCompare(coverPage, "showProgress", 0);
539+ }
540+
541+ // Make sure that if user has a mouse, they can still get rid of cover page
542+ function test_mouseClickHidesCoverPage() {
543+ var coverPage = findChild(view, "coverPage");
544+
545+ verify(coverPage.shown);
546+ mouseClick(coverPage, coverPage.width/2, coverPage.height - units.gu(2));
547+ verify(!coverPage.shown);
548+ }
549+
550+ function test_showErrorMessage() {
551+ var coverPage = findChild(view, "coverPage");
552+ var swipeHint = findChild(coverPage, "swipeHint");
553+ var errorMessageAnimation = findInvisibleChild(coverPage, "errorMessageAnimation");
554+
555+ view.showErrorMessage("hello");
556+ compare(swipeHint.text, "《 hello 》");
557+ verify(errorMessageAnimation.running);
558+ verify(swipeHint.opacityAnimation.running);
559+
560+ errorMessageAnimation.complete();
561+ swipeHint.opacityAnimation.complete();
562+ tryCompare(swipeHint, "text", "《 " + i18n.tr("Unlock") + " 》");
563+ }
564+ }
565+}
tests/qmltests/Greeter/tst_WideView.qmlnew file+781
@@ -0,0 +1,781 @@
1+/*
2+ * Copyright 2014-2016 Canonical Ltd.
3+ * Copyright 2021 UBports Foundation
4+ *
5+ * This program is free software; you can redistribute it and/or modify
6+ * it under the terms of the GNU General Public License as published by
7+ * the Free Software Foundation; version 3.
8+ *
9+ * This program is distributed in the hope that it will be useful,
10+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
11+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+ * GNU General Public License for more details.
13+ *
14+ * You should have received a copy of the GNU General Public License
15+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
16+ */
17+
18+import QtQuick 2.4
19+import QtTest 1.0
20+import ".."
21+import "../../../qml/Greeter"
22+import LightDMController 0.1
23+import LightDM.FullLightDM 0.1 as LightDM
24+import Ubuntu.Components 1.3
25+import Unity.Test 0.1 as UT
26+
27+StyledItem {
28+ id: root
29+
30+ width: units.gu(120)
31+ height: units.gu(80)
32+ focus: true
33+
34+ theme.name: "Ubuntu.Components.Themes.Ambiance"
35+
36+ Row {
37+ anchors.fill: parent
38+ Loader {
39+ id: loader
40+ width: root.width - controls.width
41+ height: parent.height
42+ focus: true
43+
44+ property bool itemDestroyed: false
45+ sourceComponent: Component {
46+ WideView {
47+ id: view
48+
49+ background: "/usr/share/backgrounds/warty-final-ubuntu.png"
50+ userModel: LightDM.Users
51+ infographicModel: LightDM.Infographic
52+
53+ launcherOffset: parseFloat(launcherOffsetField.text)
54+ currentIndex: parseInt(currentIndexField.text, 10)
55+ delayMinutes: parseInt(delayMinutesField.text, 10)
56+ panelHeight: parseFloat(panelHeightField.text)
57+ locked: lockedCheckBox.checked
58+ inputMethod: fakeInputMethod
59+
60+ Component.onDestruction: {
61+ loader.itemDestroyed = true
62+ }
63+
64+ onSelected: {
65+ if (index >= 0)
66+ currentIndexField.text = index;
67+ }
68+
69+ QtObject {
70+ id: fakeInputMethod
71+ property bool visible: fakeKeyboard.visible
72+ property var keyboardRectangle: QtObject {
73+ property real x: fakeKeyboard.x
74+ property real y: fakeKeyboard.y
75+ property real width: fakeKeyboard.width
76+ property real height: fakeKeyboard.height
77+ }
78+ }
79+
80+ Rectangle {
81+ id: fakeKeyboard
82+ color: "green"
83+ opacity: 0.7
84+ anchors.bottom: view.bottom
85+ width: view.width
86+ height: view.height * 0.6
87+ visible: keyboardVisibleCheckBox.checked
88+ Text {
89+ text: "Keyboard Rectangle"
90+ color: "yellow"
91+ font.bold: true
92+ fontSizeMode: Text.Fit
93+ minimumPixelSize: 10; font.pixelSize: 200
94+ verticalAlignment: Text.AlignVCenter
95+ x: (parent.width - width) / 2
96+ y: (parent.height - height) / 2
97+ width: parent.width
98+ height: parent.height
99+ }
100+ }
101+
102+ }
103+ }
104+ }
105+
106+ Rectangle {
107+ id: controls
108+ color: theme.palette.normal.background
109+ width: units.gu(40)
110+ height: parent.height
111+ Column {
112+ anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
113+ spacing: units.gu(1)
114+ Row {
115+ Button {
116+ text: "Hide"
117+ onClicked: loader.item.hide()
118+ }
119+ }
120+ Row {
121+ Button {
122+ text: "Show Message"
123+ onClicked: LightDMService.prompts.append(messageField.text, LightDMService.prompts.Message)
124+ }
125+ TextField {
126+ id: messageField
127+ width: units.gu(10)
128+ text: ""
129+ }
130+ }
131+ Row {
132+ Button {
133+ text: "Show Prompt"
134+ onClicked: LightDMService.prompts.append(promptField.text, isSecretCheckBox.checked ? LightDMService.prompts.Secret : LightDMService.prompts.Question)
135+ }
136+ TextField {
137+ id: promptField
138+ width: units.gu(10)
139+ text: ""
140+ }
141+ CheckBox {
142+ id: isSecretCheckBox
143+ }
144+ Label {
145+ text: "secret"
146+ }
147+ }
148+ Row {
149+ Button {
150+ text: "Notify Auth Failure"
151+ onClicked: {
152+ loader.item.notifyAuthenticationFailed();
153+ }
154+ }
155+ }
156+ Row {
157+ Button {
158+ text: "Try To Unlock"
159+ onClicked: loader.item.tryToUnlock(toTheRightCheckBox.checked)
160+ }
161+ CheckBox {
162+ id: toTheRightCheckBox
163+ }
164+ Label {
165+ text: "toTheRight"
166+ }
167+ }
168+ Row {
169+ TextField {
170+ id: launcherOffsetField
171+ width: units.gu(10)
172+ text: "0"
173+ }
174+ Label {
175+ text: "launcherOffset"
176+ }
177+ }
178+ Row {
179+ TextField {
180+ id: currentIndexField
181+ width: units.gu(10)
182+ text: "0"
183+ }
184+ Label {
185+ text: "currentIndex"
186+ }
187+ }
188+ Row {
189+ TextField {
190+ id: delayMinutesField
191+ width: units.gu(10)
192+ text: "0"
193+ }
194+ Label {
195+ text: "delayMinutes"
196+ }
197+ }
198+ Row {
199+ TextField {
200+ id: panelHeightField
201+ width: units.gu(10)
202+ text: "0"
203+ }
204+ Label {
205+ text: "panelHeight"
206+ }
207+ }
208+ Row {
209+ CheckBox {
210+ id: lockedCheckBox
211+ }
212+ Label {
213+ text: "locked"
214+ }
215+ }
216+ Row {
217+ Label {
218+ text: "selected: " + selectedSpy.count
219+ }
220+ }
221+ Row {
222+ Label {
223+ text: "responded: " + respondedSpy.count
224+ }
225+ }
226+ Row {
227+ Label {
228+ text: "teased: " + teaseSpy.count
229+ }
230+ }
231+ Row {
232+ Label {
233+ text: "emergency: " + emergencySpy.count
234+ }
235+ }
236+ Row {
237+ id: multipleSessions
238+ CheckBox {
239+ id: multipleSessionsCheckbox
240+ onClicked: {
241+ if (checked) {
242+ LightDMController.sessionMode = "full";
243+ } else {
244+ LightDMController.sessionMode = "single";
245+ }
246+ }
247+ Connections {
248+ target: LightDMController
249+ onSessionModeChanged: {
250+ if (LightDMController.sessionMode === "full") {
251+ multipleSessionsCheckbox.checked = true;
252+ } else {
253+ multipleSessionsCheckbox.checked = false;
254+ }
255+ }
256+ }
257+ }
258+ Label {
259+ text: "Multiple Sessions"
260+ }
261+ }
262+ Row {
263+ Slider {
264+ id: numSessionsSlider
265+
266+ width: units.gu(10)
267+ minimumValue: 0
268+ maximumValue: LightDMController.numAvailableSessions
269+ value: LightDMController.numSessions
270+ visible: LightDMController.sessionMode === "full"
271+ Binding {
272+ target: LightDMController
273+ property: "numSessions"
274+ value: numSessionsSlider.value
275+ }
276+ }
277+ Label {
278+ text: "Available Sessions"
279+ }
280+ }
281+ Row {
282+ Button {
283+ text: "Reload View"
284+ onClicked: {
285+ loader.active = false;
286+ loader.active = true;
287+ }
288+ }
289+ }
290+ Row {
291+ CheckBox {
292+ id: keyboardVisibleCheckBox
293+ }
294+ Label {
295+ text: "Keyboard Visible"
296+ }
297+ }
298+ }
299+ }
300+ }
301+
302+ SignalSpy {
303+ id: selectedSpy
304+ target: loader.item
305+ signalName: "selected"
306+ }
307+
308+ SignalSpy {
309+ id: respondedSpy
310+ target: loader.item
311+ signalName: "responded"
312+ }
313+
314+ SignalSpy {
315+ id: teaseSpy
316+ target: loader.item
317+ signalName: "tease"
318+ }
319+
320+ SignalSpy {
321+ id: emergencySpy
322+ target: loader.item
323+ signalName: "emergencyCall"
324+ }
325+
326+ UT.UnityTestCase {
327+ name: "WideView"
328+ when: windowShown
329+
330+ property Item view: loader.status === Loader.Ready ? loader.item : null
331+ property url testIconDirectory: Qt.resolvedUrl("../../../qml/Greeter/graphics/session_icons")
332+
333+ function init() {
334+ selectIndex(0); // break binding with text field
335+ tryCompare(LightDMService.prompts, "count", 1);
336+ selectedSpy.clear();
337+ respondedSpy.clear();
338+ teaseSpy.clear();
339+ emergencySpy.clear();
340+ LightDMController.reset();
341+ LightDM.Sessions.iconSearchDirectories = [testIconDirectory];
342+
343+ waitForRendering(view);
344+ var userList = findChild(view, "userList");
345+ tryCompare(userList, "movingInternally", false);
346+ }
347+
348+ function cleanup() {
349+ keyboardVisibleCheckBox.checked = false;
350+
351+ loader.itemDestroyed = false;
352+ loader.active = false;
353+ tryCompare(loader, "status", Loader.Null);
354+ tryCompare(loader, "item", null);
355+ tryCompare(loader, "itemDestroyed", true);
356+ loader.active = true;
357+ tryCompare(loader, "status", Loader.Ready);
358+ removeTimeConstraintsFromSwipeAreas(loader.item);
359+ LightDMController.sessionMode = "single";
360+ }
361+
362+ function getIndexOf(name) {
363+ for (var i = 0; i < LightDM.Users.count; i++) {
364+ if (name === LightDM.Users.data(i, LightDM.UserRoles.NameRole)) {
365+ return i;
366+ }
367+ }
368+ fail("Didn't find name")
369+ return -1;
370+ }
371+
372+ function selectIndex(i) {
373+ var user = LightDM.Users.data(i, LightDM.UserRoles.NameRole);
374+ LightDM.Greeter.authenticate(user);
375+
376+ var userList = findChild(view, "userList");
377+ var promptList = findChild(view, "promptList");
378+
379+ view.currentIndex = i;
380+ tryCompare(promptList, "opacity", 1);
381+ tryCompare(userList, "movingInternally", false);
382+ }
383+
384+ function selectUser(name) {
385+ var i = getIndexOf(name);
386+ selectIndex(i);
387+ return i;
388+ }
389+
390+ function swipeAwayCover() {
391+ tryCompare(view, "fullyShown", true);
392+ var touchY = view.height / 2;
393+ touchFlick(view, view.width, touchY, 0, touchY);
394+ var coverPage = findChild(view, "coverPage");
395+ tryCompare(coverPage, "showProgress", 0);
396+ waitForRendering(view);
397+ }
398+
399+ function test_sessionless_user_is_still_valid() {
400+ var loginList = findChild(view, "loginList")
401+
402+ /*
403+ * If a user has never logged in before, or, for some reason
404+ * has no sessionHint, ensure that the model returns the default
405+ * session and that the view respects this
406+ */
407+ selectUser("no-session");
408+ compare(LightDM.Users.data(loginList.currentIndex, LightDM.UserRoles.SessionRole), LightDM.Greeter.defaultSession);
409+
410+ tryCompare(loginList, "currentSession", LightDM.Greeter.defaultSession);
411+ }
412+
413+ function test_changingSessionSticksToUser() {
414+ var loginList = findChild(view, "loginList");
415+
416+ selectUser("invalid-session");
417+ tryCompare(loginList, "currentSession", "invalid");
418+
419+ selectUser("has-password");
420+ tryCompare(loginList, "currentSession", "ubuntu");
421+
422+ selectUser("invalid-session")
423+ tryCompare(loginList, "currentSession", "invalid");
424+ }
425+
426+ function test_tease_data() {
427+ return [
428+ {tag: "locked", x: 0, offset: 0, count: 0, locked: true},
429+ {tag: "left", x: 0, offset: 0, count: 1, locked: false},
430+ {tag: "leftWithOffsetPass", x: 10, offset: 10, count: 1, locked: false},
431+ {tag: "leftWithOffsetFail", x: 9, offset: 10, count: 0, locked: false},
432+ {tag: "right", x: view.width, offset: 0, count: 1, locked: false},
433+ ]
434+ }
435+
436+ function test_sessionIconsAreValid() {
437+ LightDMController.sessionMode = "full";
438+ selectUser("has-password");
439+
440+ // Test the login list icon is valid
441+ var sessionChooserButton = findChild(view, "sessionChooserButton");
442+ compare(sessionChooserButton.visible, true);
443+
444+ var loginList = findChild(view, "loginList");
445+ var session = String(loginList.currentSession).toLowerCase();
446+ var icon = String(sessionChooserButton.icon);
447+ compare(icon.indexOf(session) > -1, true);
448+
449+ // Test the session list icons are valid
450+ var coverPage = findChild(view, "coverPage");
451+ coverPage.state = "SessionsList"
452+ var sessionsList = findChild(view, "sessionsList");
453+ tryCompare(sessionsList, "visible", true);
454+ }
455+
456+ function test_choosingNewSessionChangesLoginListIcon() {
457+ // Ensure the default session is selected (Ubuntu)
458+ cleanup();
459+
460+ LightDMController.sessionMode = "full";
461+ selectUser("has-password");
462+
463+ var sessionChooserButton = findChild(view, "sessionChooserButton");
464+ var icon = String(sessionChooserButton.icon);
465+ compare(icon.indexOf("ubuntu") > -1, true);
466+
467+ tap(sessionChooserButton)
468+ for(var i = 0; i < LightDM.Sessions.count; i++) {
469+ var delegateName = "sessionDelegate" + String(i);
470+ var currentDelegate = findChild(view, delegateName);
471+ var sessionKey = LightDM.Sessions.data(i,LightDM.SessionRoles.KeyRole);
472+ if (sessionKey === "gnome-classic") {
473+ waitForRendering(currentDelegate);
474+ tap(currentDelegate);
475+ waitForRendering(sessionChooserButton);
476+ break;
477+ }
478+ }
479+
480+ icon = String(sessionChooserButton.icon);
481+ compare(icon.indexOf("gnome") > -1, true,
482+ "Expected icon to contain gnome but it was " + icon);
483+ }
484+
485+ function test_noSessionsDoesntBreakView() {
486+ LightDMController.sessionMode = "none";
487+ compare(LightDM.Sessions.count, 0)
488+ }
489+
490+ function test_sessionIconNotShownWithOneSession() {
491+ LightDMController.sessionMode = "single";
492+ compare(LightDM.Sessions.count, 1);
493+
494+ var sessionChooserButton = findChild(view, "sessionChooserButton");
495+ tryCompare(sessionChooserButton, "visible", false);
496+ }
497+
498+ function test_sessionIconNotShownWithActiveUser() {
499+ selectUser("active");
500+
501+ var sessionChooserButton = findChild(view, "sessionChooserButton");
502+ tryCompare(sessionChooserButton, "visible", false);
503+ }
504+
505+ function test_sessionIconShownWithMultipleSessions() {
506+ LightDMController.sessionMode = "full";
507+ selectUser("has-password");
508+
509+ var sessionChooserButton = findChild(view, "sessionChooserButton");
510+ tryCompare(sessionChooserButton, "visible", true);
511+ }
512+
513+ function test_tease(data) {
514+ view.locked = data.locked;
515+ view.dragHandleLeftMargin = data.offset;
516+ tap(view, data.x, 0);
517+ compare(teaseSpy.count, data.count);
518+ }
519+
520+ function test_selected() {
521+ var delegate = findChild(view, "username2");
522+ tap(delegate);
523+ compare(selectedSpy.count, 1);
524+ compare(selectedSpy.signalArguments[0][0], 2);
525+ compare(view.currentIndex, 0); // confirm we didn't change
526+ }
527+
528+ function test_customBackground() {
529+ var coverPage = findChild(view, "coverPage");
530+ var backgroundShade = findChild(coverPage, "backgroundShade");
531+
532+ verify(!view.hasCustomBackground);
533+ verify(!backgroundShade.visible);
534+
535+ view.hasCustomBackground = true;
536+ verify(backgroundShade.visible);
537+ }
538+
539+ function test_respondedWithPassword() {
540+ selectUser("has-password");
541+ var greeterPrompt = findChild(view, "greeterPrompt0");
542+ verify(greeterPrompt.isSecret);
543+ typeString("password");
544+ keyClick(Qt.Key_Enter);
545+ compare(respondedSpy.count, 1);
546+ compare(respondedSpy.signalArguments[0][0], "password");
547+ }
548+
549+ function test_respondedWithNonSecret() {
550+ selectUser("question-prompt");
551+ var greeterPrompt = findChild(view, "greeterPrompt0");
552+ verify(!greeterPrompt.isSecret);
553+ typeString("foo");
554+ keyClick(Qt.Key_Enter);
555+ compare(respondedSpy.count, 1);
556+ compare(respondedSpy.signalArguments[0][0], "foo");
557+ }
558+
559+ function test_respondedWithSwipe() {
560+ swipeAwayCover();
561+ compare(respondedSpy.count, 1);
562+ compare(respondedSpy.signalArguments[0][0], "");
563+ }
564+
565+ function test_fullyShown() {
566+ tryCompare(view, "fullyShown", true);
567+ swipeAwayCover();
568+ tryCompare(view, "fullyShown", false);
569+ }
570+
571+ function test_required() {
572+ tryCompare(view, "required", true);
573+ swipeAwayCover();
574+ tryCompare(view, "required", false);
575+ }
576+
577+ function test_infoPrompt() {
578+ selectUser("info-prompt");
579+
580+ var infoLabel = findChild(view, "infoLabel0");
581+ compare(infoLabel.text, "Welcome to Unity Greeter");
582+ compare(infoLabel.textFormat, Text.PlainText);
583+
584+ verify(findChild(view, "greeterPrompt1") != null);
585+ }
586+
587+ function test_longInfoPrompt() {
588+ selectUser("long-info-prompt");
589+
590+ var infoLabel = findChild(view, "infoLabel0");
591+ compare(infoLabel.text, "Welcome to Unity Greeter\n\nWe like to annoy you with super ridiculously long messages.\nLike this one\n\nThis is the last line of a multiple line message.");
592+ verify(infoLabel.contentWidth > infoLabel.width);
593+
594+ verify(findChild(view, "greeterPrompt1") != null);
595+ }
596+
597+ function test_multiInfoPrompt() {
598+ selectUser("multi-info-prompt");
599+
600+ var infoLabel0 = findChild(view, "infoLabel0");
601+ compare(infoLabel0.text, "Welcome to Unity Greeter");
602+
603+ var infoLabel1 = findChild(view, "infoLabel1");
604+ compare(infoLabel1.text, "This is an error");
605+ compare(infoLabel1.color, theme.palette.normal.negative);
606+
607+ var infoLabel2 = findChild(view, "infoLabel2");
608+ compare(infoLabel2.text, "You should have seen three messages");
609+
610+ verify(findChild(view, "greeterPrompt3") != null);
611+ }
612+
613+ // Escape is used to reset the authentication, especially if PAM is unresponsive
614+ function test_escape() {
615+ var index = selectUser("has-password");
616+ selectedSpy.clear();
617+ view.locked = true;
618+ var promptField = findChild(view, "promptField");
619+ tap(promptField);
620+ verify(promptField.activeFocus);
621+ compare(promptField.opacity, 1);
622+
623+ typeString("password");
624+ keyClick(Qt.Key_Enter);
625+ verify(promptField.activeFocus);
626+ compare(promptField.opacity, 0); // hidden by fakeLabel
627+
628+ compare(selectedSpy.count, 0);
629+ keyClick(Qt.Key_Escape);
630+ compare(selectedSpy.count, 1);
631+ compare(selectedSpy.signalArguments[0][0], index);
632+
633+ selectIndex(index);
634+ var promptField = findChild(view, "promptField");
635+ verify(promptField.activeFocus);
636+ compare(promptField.opacity, 1);
637+ }
638+
639+ function test_unicode() {
640+ var index = selectUser("unicode");
641+ var label = findChild(view, "username" + index);
642+ tryCompare(label, "text", "가나다라마");
643+ }
644+
645+ function test_authError() {
646+ var index = selectUser("auth-error");
647+ var greeterPrompt = findChild(view, "greeterPrompt1"); // after error message
648+ view.locked = true;
649+ compare(greeterPrompt.text, "Retry");
650+ tap(greeterPrompt);
651+ compare(respondedSpy.count, 0);
652+ compare(selectedSpy.count, 1);
653+ compare(selectedSpy.signalArguments[0][0], index);
654+ }
655+
656+ function test_noPassword() {
657+ selectUser("no-password");
658+ var greeterPrompt = findChild(view, "greeterPrompt0");
659+ compare(greeterPrompt.text, "Log In");
660+ tap(greeterPrompt);
661+ compare(selectedSpy.count, 0);
662+ compare(respondedSpy.count, 1);
663+ compare(respondedSpy.signalArguments[0][0], "");
664+ }
665+
666+ function test_loginListNotCoveredByKeyboard() {
667+ var loginList = findChild(view, "loginList");
668+ compare(loginList.height, view.height);
669+
670+ // when the vkb shows up, loginList is moved up to remain fully uncovered
671+
672+ keyboardVisibleCheckBox.checked = true;
673+
674+ var halfway = (view.height - loginList.highlightedHeight) / 2;
675+ var halfwayWithOsk = halfway - view.inputMethod.keyboardRectangle.height / 2;
676+ tryCompare(loginList, "boxVerticalOffset", halfwayWithOsk);
677+
678+ var highlightItem = findChild(loginList, "highlightItem");
679+ tryCompareFunction( function() {
680+ var highlightRect = highlightItem.mapToItem(view, 0, 0, highlightItem.width, highlightItem.height);
681+ return highlightRect.y + highlightRect.height <= view.inputMethod.keyboardRectangle.y;
682+ }, true);
683+
684+ // once the vkb goes away, loginList goes back to its full height
685+
686+ keyboardVisibleCheckBox.checked = false;
687+
688+ tryCompare(loginList, "boxVerticalOffset", halfway);
689+ }
690+
691+ function test_passphrase() {
692+ var index = selectUser("has-password");
693+ var promptField = findChild(view, "promptField");
694+
695+ verify(view.alphanumeric);
696+ compare(promptField.inputMethodHints & Qt.ImhDigitsOnly, 0);
697+
698+ keyClick(Qt.Key_D);
699+ compare(promptField.text, "d");
700+ }
701+
702+ function test_passcode() {
703+ var index = selectUser("has-pin");
704+ var promptField = findChild(view, "promptField");
705+
706+ view.alphanumeric = false;
707+ compare(promptField.inputMethodHints & Qt.ImhDigitsOnly, Qt.ImhDigitsOnly);
708+
709+ keyClick(Qt.Key_D);
710+ compare(promptField.text, "");
711+
712+ keyClick(Qt.Key_0);
713+ keyClick(Qt.Key_0);
714+ keyClick(Qt.Key_0);
715+ keyClick(Qt.Key_0);
716+ compare(promptField.text, "0000");
717+
718+ compare(respondedSpy.count, 1);
719+ compare(respondedSpy.signalArguments[0][0], "0000");
720+
721+ compare(promptField.opacity, 0);
722+ }
723+
724+ function test_loginListMovement_data() {
725+ return [
726+ {tag: "up", key: Qt.Key_Up, result: 1},
727+ {tag: "down", key: Qt.Key_Down, result: 3},
728+ ]
729+ }
730+
731+ function test_loginListMovement(data) {
732+ selectIndex(2);
733+ selectedSpy.clear();
734+
735+ keyClick(data.key);
736+ compare(selectedSpy.count, 1);
737+ compare(selectedSpy.signalArguments[0][0], data.result);
738+ }
739+
740+ function test_focusStaysActive() {
741+ selectUser("no-password");
742+ var greeterPrompt = findChild(view, "greeterPrompt0");
743+ verify(greeterPrompt.activeFocus);
744+ keyClick(Qt.Key_Enter);
745+ compare(selectedSpy.count, 0);
746+ compare(respondedSpy.count, 1);
747+ compare(respondedSpy.signalArguments[0][0], "");
748+ verify(greeterPrompt.activeFocus);
749+ keyClick(Qt.Key_Enter);
750+ compare(respondedSpy.count, 1);
751+
752+ selectUser("has-password");
753+ var greeterPrompt = findChild(view, "greeterPrompt0");
754+ verify(greeterPrompt.activeFocus);
755+ keyClick(Qt.Key_D);
756+ keyClick(Qt.Key_Enter);
757+ compare(selectedSpy.count, 0);
758+ compare(respondedSpy.count, 2);
759+ compare(respondedSpy.signalArguments[1][0], "d");
760+ verify(greeterPrompt.activeFocus);
761+ keyClick(Qt.Key_Enter);
762+ compare(respondedSpy.count, 2);
763+
764+ var index = selectUser("no-password");
765+ var greeterPrompt = findChild(view, "greeterPrompt0");
766+ view.locked = true;
767+ verify(greeterPrompt.activeFocus);
768+ keyClick(Qt.Key_Enter);
769+ compare(respondedSpy.count, 2);
770+ compare(selectedSpy.count, 1);
771+ compare(selectedSpy.signalArguments[0][0], index);
772+ verify(greeterPrompt.activeFocus);
773+ keyClick(Qt.Key_Enter);
774+ compare(selectedSpy.count, 1);
775+
776+ selectUser("no-password");
777+ var greeterPrompt = findChild(view, "greeterPrompt0");
778+ verify(greeterPrompt.activeFocus);
779+ }
780+ }
781+}
tests/qmltests/tst_OrientedShell.qml+41−45
@@ -40,12 +40,11 @@ Rectangle {
4040 height: units.gu(100)
4141
4242 property var tryShell: null
43− property string ldmUserMode: "single"
4443
4544 Binding {
4645 target: LightDMController
4746 property: "userMode"
48− value: ldmUserMode
47+ value: "single"
4948 }
5049
5150 QtObject {
@@ -241,23 +240,6 @@ Rectangle {
241240 LightDM.Greeter.showGreeter();
242241 }
243242 }
244−
245−
246− Label {
247− text: "LightDM mock mode"
248− }
249−
250− ListItem.ItemSelector {
251− anchors { left: parent.left; right: parent.right }
252− activeFocusOnPress: false
253− model: ["single", "single-passphrase", "single-pin", "full"]
254− onSelectedIndexChanged: {
255− testCase.tearDown();
256− ldmUserMode = model[selectedIndex];
257− testCase.init();
258− }
259− }
260−
261243 Label {
262244 text: "Physical Orientation:"
263245 }
@@ -619,6 +601,43 @@ Rectangle {
619601 compare(shell.transformRotationAngle, root.primaryOrientationAngle + 180);
620602 }
621603
604+ function test_greeterRemainsInPrimaryOrientation_data() {
605+ return [
606+ {tag: "mako", deviceName: "mako"},
607+ {tag: "manta", deviceName: "manta"},
608+ {tag: "flo", deviceName: "flo"}
609+ ];
610+ }
611+ function test_greeterRemainsInPrimaryOrientation(data) {
612+ var orientedShell = loadShell(data.deviceName);
613+ var shell = findChild(orientedShell, "shell");
614+
615+ var gmailApp = ApplicationManager.startApplication("gmail-webapp");
616+ verify(gmailApp);
617+
618+ // ensure the mock gmail-webapp is as we expect
619+ compare(gmailApp.rotatesWindowContents, false);
620+ compare(gmailApp.supportedOrientations, Qt.PortraitOrientation | Qt.LandscapeOrientation
621+ | Qt.InvertedPortraitOrientation | Qt.InvertedLandscapeOrientation);
622+
623+ // wait until it's able to rotate
624+ tryCompare(shell, "orientationChangesEnabled", true);
625+
626+ compare(shell.transformRotationAngle, root.primaryOrientationAngle);
627+ rotateTo(90, orientedShell);
628+ tryCompare(shell, "transformRotationAngle", root.primaryOrientationAngle + 90);
629+
630+ showGreeter(orientedShell);
631+
632+ tryCompare(shell, "transformRotationAngle", root.primaryOrientationAngle);
633+ rotateTo(180, orientedShell);
634+ compare(shell.transformRotationAngle, root.primaryOrientationAngle);
635+ rotateTo(270, orientedShell);
636+ compare(shell.transformRotationAngle, root.primaryOrientationAngle);
637+ rotateTo(0, orientedShell);
638+ compare(shell.transformRotationAngle, root.primaryOrientationAngle);
639+ }
640+
622641 function test_appRotatesWindowContents_data() {
623642 return [
624643 {tag: "mako", deviceName: "mako", orientationAngleAfterRotation: 90},
@@ -1210,7 +1229,7 @@ Rectangle {
12101229
12111230 showGreeter(orientedShell);
12121231
1213− tryCompare(shell, "transformRotationAngle", root.primaryOrientationAngle + 90);
1232+ tryCompare(shell, "transformRotationAngle", root.primaryOrientationAngle);
12141233 }
12151234
12161235 /*
@@ -1463,8 +1482,7 @@ Rectangle {
14631482 touchFlick(shell, touchX, touchY, orientedShell.width * 0.1, touchY);
14641483
14651484 // wait until the animation has finished
1466− if (LightDMController.userMode == "single")
1467− tryCompare(greeter, "shown", false);
1485+ tryCompare(greeter, "shown", false);
14681486 waitForRendering(greeter);
14691487 }
14701488
@@ -1475,11 +1493,9 @@ Rectangle {
14751493 tryCompare(greeter, "fullyShown", true);
14761494 }
14771495
1478− function loadShell(deviceName, userMode = "single") {
1496+ function loadShell(deviceName) {
14791497 applicationArguments.deviceName = deviceName;
14801498
1481− ldmUserMode = userMode; // Set the mode for LightDM ( default is "single" )
1482−
14831499 // reload our test subject to get it in a fresh state once again
14841500 var orientedShell = createTemporaryObject(shellComponent, shellRect);
14851501
@@ -1681,25 +1697,5 @@ Rectangle {
16811697 MockInputDeviceBackend.removeDevice("/touchscreen");
16821698 tryCompare(tutorial, "paused", true);
16831699 }
1684−
1685− /* Check if the keyboard icon on the greeter screen
1686− * is only shown when an external keyboard is attached
1687− * and if it is hidden when no keyboard is attached.
1688− */
1689− function test_greeterKeyboardDetection() {
1690− var orientedShell = loadShell("mako", "single-passphrase");
1691− MockInputDeviceBackend.removeDevice("/indicator_kbd0");
1692−
1693− var greeterPrompt = findChild(orientedShell, "greeterPrompt0");
1694− verify(greeterPrompt);
1695−
1696− var promptKeyboard = findChild(greeterPrompt, "greeterPromptKeyboardButton");
1697−
1698− tryCompare(promptKeyboard, "visible", false);
1699−
1700− MockInputDeviceBackend.addMockDevice("/kbd0", InputInfo.Keyboard);
1701−
1702− tryCompare(promptKeyboard, "visible", true);
1703− }
17041700 }
17051701}
tests/qmltests/tst_Shell.qml+15−48
@@ -703,32 +703,6 @@ Rectangle {
703703 mockNotificationsModel.append(n)
704704 }
705705
706− function swipeAwayGreeterCover() {
707− var greeter = findChild(shell, "greeter");
708−
709− if (!greeter.shown) {
710− console.log("Greeter not shown. Not swiping.");
711− return;
712− }
713−
714− var greeterView = findChild(greeter, "GreeterView");
715− var lockscreen = findChild(greeter, "lockscreen");
716−
717− tryCompare(lockscreen, "shown", true);
718−
719− tryCompare(greeter, "fullyShown", true);
720− waitForGreeterToStabilize();
721− removeTimeConstraintsFromSwipeAreas(greeter);
722−
723− var touchX = shell.width - (shell.edgeSize / 2);
724− var touchY = shell.height / 2;
725− touchFlick(shell, touchX, touchY, shell.width * 0.1, touchY);
726−
727− var coverPage = findChild(greeterView, "coverPage");
728− tryCompare(coverPage, "showProgress", 0);
729− waitForRendering(greeterView);
730− }
731−
732706 function swipeAwayGreeter() {
733707 var greeter = findChild(shell, "greeter");
734708
@@ -789,16 +763,13 @@ Rectangle {
789763 var greeter = findChild(shell, "greeter")
790764 tryCompare(greeter, "fullyShown", true);
791765
792− if (isButton) {
793− var promptButton = findChild(greeter, "promptButton");
794− verify(promptButton);
795− tryCompare(promptButton, "visible", true);
796− mouseClick(promptButton);
797− } else {
798− var promptField = findChild(greeter, "promptField");
799− verify(promptField);
800− tryCompare(promptField, "visible", true);
801− }
766+ var promptButton = findChild(greeter, "promptButton");
767+ tryCompare(promptButton, "visible", isButton);
768+
769+ var promptField = findChild(greeter, "promptField");
770+ tryCompare(promptField, "visible", !isButton);
771+
772+ mouseClick(promptButton);
802773 }
803774
804775 function confirmLoggedIn(loggedIn) {
@@ -1022,13 +993,14 @@ Rectangle {
1022993 }
1023994
1024995 function test_greeterStartsCorrectSession() {
1025− setLightDMMockMode("full");
1026996 loadShell("desktop");
997+ setLightDMMockMode("full");
1027998
1028999 LightDMController.sessionMode = "full"
10291000 LightDMController.numSessions = LightDMController.numAvailableSessions;
10301001 var greeter = findChild(shell, "greeter");
1031− var view = findChild(greeter, "GreeterView");
1002+ var view = findChild(greeter, "WideView");
1003+ verify(view, "This test requires WideView to be loaded");
10321004
10331005 var loginList = findChild(view, "loginList");
10341006
@@ -1428,9 +1400,8 @@ Rectangle {
14281400 }
14291401
14301402 function test_tabletLogin(data) {
1431− setLightDMMockMode("full");
14321403 loadShell("tablet");
1433− swipeAwayGreeterCover();
1404+ setLightDMMockMode("full");
14341405
14351406 selectUser(data.user);
14361407
@@ -1453,10 +1424,8 @@ Rectangle {
14531424 }
14541425
14551426 function test_appLaunchDuringGreeter(data) {
1456− setLightDMMockMode("full");
14571427 loadShell("tablet");
1458−
1459− swipeAwayGreeterCover();
1428+ setLightDMMockMode("full");
14601429
14611430 selectUser(data.user)
14621431
@@ -1472,11 +1441,9 @@ Rectangle {
14721441 }
14731442
14741443 function test_manualLoginFlow() {
1475− setLightDMMockMode("full");
1476− LightDMController.showManualLoginHint = true;
14771444 loadShell("desktop");
1478−
1479− swipeAwayGreeterCover();
1445+ LightDMController.showManualLoginHint = true;
1446+ setLightDMMockMode("full");
14801447
14811448 var i = selectUser("*other");
14821449 var greeter = findChild(shell, "greeter");
@@ -2613,8 +2580,8 @@ Rectangle {
26132580
26142581 function test_fullShellModeHasNoInitialGreeter() {
26152582 shellRect.mode = "full-shell";
2616− setLightDMMockMode("single-pin");
26172583 loadShell("phone");
2584+ setLightDMMockMode("single-pin");
26182585 waitForRendering(shell);
26192586
26202587 var greeter = findChild(shell, "greeter");