on cc0661c help sign in
60 files changed+2583−3461
since splitsnapshots
unifiedsplit
.gitignore−1 README.md+38−18 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/ja.po+18−15 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/Components/ItemGrabber.qml+1−1 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
.gitignore−1
@@ -13,4 +13,3 @@
1313/.kdev4/
1414/build/
1515/*.kdev4
16−.clickable/
README.md+38−18
@@ -1,31 +1,51 @@
1−# Lomiri
1+# Unity8
22
3−Lomiri is the operating environment for everywhere. It is able to span the gaps between touch, mouse, and keyboard; between phones, tablets, and workstations; and look good doing it.
3+A convergent desktop environment.
44
5−If you want to contribute to Lomiri on Ubuntu Touch, you've come to the right place. If you want to package Lomiri for other operating systems, you'll be better served by the main branch on the repository that we're migrating to: https://gitlab.com/ubports/core/lomiri
65
7−Lomiri has been renamed from Unity8. Most of the software on-device in Ubuntu Touch based on Ubuntu 16.04 is still called `unity8`.
6+[![Unity 8](http://ubuntufun.de/wp-content/uploads/2016/05/screenshot20160518_232322980.png)](https://unity8.io)
87
9−## Where to report issues
108
11−Issues related to the version of Lomiri that currently runs on Ubuntu Touch devices may still be reported on this repository. We will move these reports to the GitLab repository as part of our final migration.
9+### How to install
1210
13−## Developing Lomiri on Ubuntu Touch
11+The latest development preview of Unity8 for desktop can be installed on Ubuntu 16.04 (xenial) and 18.04 (bionic) as follows:
1412
15−This repository contains the code for Lomiri currently running on Ubuntu Touch. If you would like to modify this version of Lomiri, follow the steps below. If you would like to modify Lomiri in another operating system, check out our new repository: https://gitlab.com/ubports/core/lomiri
13+1. Run the install script
14+```
15+bash <(wget -qO- https://raw.githubusercontent.com/ubports/unity8-desktop-install-tools/master/install.sh)
16+```
17+2. Reboot, select "unity8" at the login prompt, and enjoy :)
1618
17−Lomiri can be built and its test suite run using [`clickable`](https://clickable-ut.dev). This is a convenient method to try out most graphical changes to Lomiri without an annoying redeployment process. If you wish to test your changes on your device or make changes that are more difficult to test without real hardware, check out [Making changes and testing locally on the UBports documentation](https://docs.ubports.com/en/latest/systemdev/testing-locally.html). If not, read on.
1819
19−Before you start, [install Clickable](https://clickable-ut.dev/en/latest/install.html).
20+**NOTE:** The above installation instructions currently only work on Ubuntu 16.04 (xenial) and 18.04 (bionic). Please be aware that the script will install the latest *development preview* of Unity8 for desktop. ***Expect bugs!*** Only install this preview if you understand the risks and are willing/able to fix your system if the preview breaks anything. You are advised *not* to install this preview onto a production system.
2021
21−Now, clone this repository to your computer: `git clone https://github.com/ubports/unity8.git`
22+See [CODING](CODING) for build instructions.
2223
23−Move 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+### Where to report issues
2626
27−* `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...
30−* `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.
31−* `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.
27+Issues related to the version of Unity8 for desktop (the latest development preview) installed using the above instructions should be reported on the [Unity8 issue tracker](https://github.com/ubports/unity8/issues) in *this* repository.
28+
29+Note that issues related to the version of Unity8 that currently runs on Ubuntu Touch devices should *NOT* be reported in this repository, but instead should be reported on the [Ubuntu Touch issue tracker](https://github.com/ubports/ubuntu-touch/issues). This will change once Ubuntu Touch moves to the latest version of Unity8.
30+
31+
32+### What works / what does not work
33+
34+*This list is incomplete, please help improve it*
35+
36+What works:
37+- Most Wayland apps work
38+- Many report being able to log in using LightDM, but may require a few attempts
39+
40+Does not work:
41+- Most Xapps don't (yet) work
42+- Proprietary drivers don't (yet) work
43+- Many report being unable to log in using GDM
44+
45+
46+### Get involved
47+
48+* [Kanban-Board](https://github.com/ubports/unity8/projects/1)
49+* If you are interesting in helping us in any way you can join [Unity8 Forum](https://forums.ubports.com/category/36/unity8).
50+* For reporting bugs or filing feature-requests, please use the project’s [issues page](https://github.com/ubports/unity8/issues).
51+* For IM you can join our Telegram group [for Developer users](https://t.me/UBports_Unity8).
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/ja.po+18−15
@@ -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-08-10 18:11+0000\n"
12−"Last-Translator: Oyamada Jun <oyamada.junjun@outlook.jp>\n"
11+"PO-Revision-Date: 2021-05-09 19:51+0000\n"
12+"Last-Translator: M.Sugahara <equaaqua@hotmail.com>\n"
1313"Language-Team: Japanese <https://translate.ubports.com/projects/ubports/"
1414"lomiri/ja/>\n"
1515"Language: ja\n"
@@ -278,11 +278,11 @@ msgstr "Ctrl + Super + Down"
278278
279279#: qml/Components/KeyboardShortcutsOverlay.qml:323
280280msgid "Minimizes or restores the current window."
281−msgstr "現在のウィンドウを最小化したり、復元したりします。"
281+msgstr ""
282282
283283#: qml/Components/KeyboardShortcutsOverlay.qml:331
284284msgid "Ctrl + Super + Left or Right"
285−msgstr "Ctrl + Super + Left or Right"
285+msgstr ""
286286
287287#: qml/Components/KeyboardShortcutsOverlay.qml:336
288288msgid "Semi-maximizes the current window."
@@ -342,7 +342,7 @@ msgstr ""
342342
343343#: qml/Components/ModeSwitchWarningDialog.qml:94
344344msgid "Close all"
345−msgstr "すべて閉じる"
345+msgstr ""
346346
347347#: qml/Components/SharingPicker.qml:54
348348msgid "Preview Share Item"
@@ -412,7 +412,7 @@ msgstr "パスコードを入力"
412412
413413#: qml/Greeter/NarrowView.qml:235
414414msgid "Emergency"
415−msgstr "緊急"
415+msgstr ""
416416
417417#: qml/Greeter/PromptList.qml:126
418418msgid "Passphrase"
@@ -420,11 +420,11 @@ msgstr ""
420420
421421#: qml/Greeter/PromptList.qml:126
422422msgid "Passcode"
423−msgstr "パスコード"
423+msgstr ""
424424
425425#: qml/Greeter/SessionsList.qml:124
426426msgid "Select desktop environment"
427−msgstr "デスクトップ環境を選択"
427+msgstr ""
428428
429429#: qml/Launcher/Drawer.qml:250
430430#, fuzzy
@@ -460,29 +460,32 @@ msgstr "再生するものがありません"
460460#, qt-format
461461msgid "%1 hour"
462462msgid_plural "%1 hours"
463−msgstr[0] "%1 時間"
463+msgstr[0] ""
464+msgstr[1] ""
464465
465466#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1001
466467#, qt-format
467468msgid "%1 minute"
468469msgid_plural "%1 minutes"
469−msgstr[0] "%1 分"
470+msgstr[0] ""
471+msgstr[1] ""
470472
471473#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1006
472474#, qt-format
473475msgid "%1 second"
474476msgid_plural "%1 seconds"
475−msgstr[0] "%1 秒"
477+msgstr[0] ""
478+msgstr[1] ""
476479
477480#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1009
478481msgid "0 seconds"
479−msgstr "0 秒"
482+msgstr ""
480483
481484#. Translators: String like "1 hour, 2 minutes, 3 seconds remaining"
482485#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1011
483486#, qt-format
484487msgid "%1 remaining"
485−msgstr "残り %1"
488+msgstr ""
486489
487490#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1017
488491msgid "In queue…"
@@ -512,12 +515,12 @@ msgstr "失敗しました、再試行するにはタップしてください"
512515#: qml/Panel/Indicators/MessageMenuItemFactory.qml:210
513516msgctxt "Button: Send a reply message"
514517msgid "Send"
515−msgstr "送信"
518+msgstr ""
516519
517520#: qml/Panel/Indicators/MessageMenuItemFactory.qml:152
518521msgctxt "Label: Hint in message indicator line edit"
519522msgid "Reply"
520−msgstr "返信"
523+msgstr ""
521524
522525#: qml/Panel/Indicators/MessageMenuItemFactory.qml:209
523526msgctxt "Button: Call back on phone"
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/Components/ItemGrabber.qml+1−1
@@ -35,7 +35,7 @@ Rectangle {
3535
3636 NotificationAudio {
3737 id: shutterSound
38− source: "/usr/share/sounds/ubports/camera/click/camera_click.ogg"
38+ source: "/system/media/audio/ui/camera_click.ogg"
3939 }
4040
4141 function capture(item) {
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");