on xenial help sign in
.gitignore+1
since splitsnapshots
unifiedsplit
@@ -13,3 +13,4 @@
13/.kdev4/
13/.kdev4/
14/build/
14/build/
15/*.kdev4
15/*.kdev4
16+.clickable/
README.md+18−38
@@ -1,51 +1,31 @@
1−# Unity8
1+# Lomiri
2
2
3−A convergent desktop environment.
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.
4
4
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
5
6
6−[![Unity 8](http://ubuntufun.de/wp-content/uploads/2016/05/screenshot20160518_232322980.png)](https://unity8.io)
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`.
7
8
9+## Where to report issues
8
10
9−### How to install
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.
10
12
11−The latest development preview of Unity8 for desktop can be installed on Ubuntu 16.04 (xenial) and 18.04 (bionic) as follows:
13+## Developing Lomiri on Ubuntu Touch
12
14
13−1. Run the install script
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
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 :)
18
16
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.
19
18
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.
19+Before you start, [install Clickable](https://clickable-ut.dev/en/latest/install.html).
21
20
22−See [CODING](CODING) for build instructions.
21+Now, clone this repository to your computer: `git clone https://github.com/ubports/unity8.git`
23
22
23+Move into this directory: `cd unity8`
24
24
25−### Where to report issues
25+Now you can use the full suite of tools provided by this repository's [clickable.yaml](clickable.yaml). For example:
26
26
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.
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−
28+* `clickable build --libs --verbose` will build Lomiri in the same environment as it would receive in Ubuntu Touch.
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.
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−
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−
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.
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.yamlnew file+153
@@ -0,0 +1,153 @@
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+5−1
@@ -1,5 +1,6 @@
1pkg_check_modules(ANDROID_HEADERS REQUIRED android-headers)
1pkg_check_modules(ANDROID_HEADERS REQUIRED android-headers)
2pkg_check_modules(ANDROID_HARDWARE REQUIRED libhardware)
2pkg_check_modules(ANDROID_HARDWARE REQUIRED libhardware)
3+pkg_check_modules(ANDROID_PROPERTIES REQUIRED libandroid-properties)
3
4
4include_directories(
5include_directories(
5 SYSTEM
6 SYSTEM
@@ -9,11 +10,14 @@ include_directories(
9add_library(Lights-qml MODULE
10add_library(Lights-qml MODULE
10 plugin.cpp
11 plugin.cpp
11 Lights.cpp
12 Lights.cpp
13+ LegacyLights.cpp
14+ HfdLights.cpp
12 )
15 )
13
16
14target_link_libraries(Lights-qml
17target_link_libraries(Lights-qml
15− Qt5::Qml Qt5::Gui
18+ Qt5::Qml Qt5::Gui Qt5::DBus
16 ${ANDROID_HARDWARE_LIBRARIES}
19 ${ANDROID_HARDWARE_LIBRARIES}
20+ ${ANDROID_PROPERTIES_LIBRARIES}
17 )
21 )
18
22
19add_unity8_plugin(Lights 0.1 Lights TARGETS Lights-qml)
23add_unity8_plugin(Lights 0.1 Lights TARGETS Lights-qml)
plugins/Lights/HfdLights.cppnew file+62
@@ -0,0 +1,62 @@
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.hnew file+42
@@ -0,0 +1,42 @@
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.cppnew file+91
@@ -0,0 +1,91 @@
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.hnew file+45
@@ -0,0 +1,45 @@
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,20 +26,12 @@ extern "C" {
26
26
27Lights::Lights(QObject* parent)
27Lights::Lights(QObject* parent)
28 : QObject(parent),
28 : QObject(parent),
29− m_lightDevice(0),
30 m_color("blue"),
29 m_color("blue"),
31 m_state(Lights::Off),
30 m_state(Lights::Off),
32 m_onMs(1000),
31 m_onMs(1000),
33 m_offMs(3000)
32 m_offMs(3000)
34{
33{
35−}
36
34
37−Lights::~Lights()
38−{
39− if (m_lightDevice) {
40− hw_device_t* device = (hw_device_t*) m_lightDevice;
41− device->close(device);
42− }
43}
35}
44
36
45void Lights::setState(Lights::State newState)
37void Lights::setState(Lights::State newState)
@@ -107,60 +99,3 @@ void Lights::setOffMillisec(int offMs)
107 // FIXME: update the property if the light is already on
99 // FIXME: update the property if the light is already on
108 }
100 }
109}
101}
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+4−8
@@ -22,8 +22,6 @@
22#include <QtCore/QObject>
22#include <QtCore/QObject>
23#include <QtGui/QColor>
23#include <QtGui/QColor>
24
24
25−struct light_device_t;
26−
27class Lights: public QObject
25class Lights: public QObject
28{
26{
29 Q_OBJECT
27 Q_OBJECT
@@ -40,7 +38,6 @@ public:
40 Q_ENUM(State)
38 Q_ENUM(State)
41
39
42 explicit Lights(QObject *parent = 0);
40 explicit Lights(QObject *parent = 0);
43− ~Lights();
44
41
45 void setState(State newState);
42 void setState(State newState);
46 State state() const;
43 State state() const;
@@ -60,16 +57,15 @@ Q_SIGNALS:
60 void onMillisecChanged(int onMs);
57 void onMillisecChanged(int onMs);
61 void offMillisecChanged(int offMs);
58 void offMillisecChanged(int offMs);
62
59
63−private:
60+protected:
64− light_device_t* m_lightDevice;
65 QColor m_color;
61 QColor m_color;
66 State m_state;
62 State m_state;
67 int m_onMs;
63 int m_onMs;
68 int m_offMs;
64 int m_offMs;
69
65
70− bool init();
66+ virtual bool init() = 0;
71− void turnOff();
67+ virtual void turnOff() = 0;
72− void turnOn();
68+ virtual void turnOn() = 0;
73};
69};
74
70
75#endif
71#endif
plugins/Lights/plugin.cpp+20−1
@@ -18,6 +18,11 @@
18
18
19#include "plugin.h"
19#include "plugin.h"
20#include "Lights.h"
20#include "Lights.h"
21+#include "HfdLights.h"
22+#include "LegacyLights.h"
23+
24+// libandroid-properties
25+#include <hybris/properties/properties.h>
21
26
22#include <QtQml/qqml.h>
27#include <QtQml/qqml.h>
23
28
@@ -25,7 +30,21 @@ static QObject *lights_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
25{
30{
26 Q_UNUSED(engine)
31 Q_UNUSED(engine)
27 Q_UNUSED(scriptEngine)
32 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) {
28− return new Lights();
44+ return new HfdLights();
45+ } else {
46+ return new LegacyLights();
47+ }
29}
48}
30
49
31void LightsPlugin::registerTypes(const char *uri)
50void LightsPlugin::registerTypes(const char *uri)
plugins/WindowManager/TopLevelWindowModel.cpp+4−3
@@ -317,10 +317,11 @@ void TopLevelWindowModel::onSurfaceDied(unityapi::MirSurfaceInterface *surface)
317
317
318 auto application = m_windowModel[i].application;
318 auto application = m_windowModel[i].application;
319
319
320− // can't be starting if it already has a surface
320+ DEBUG_MSG << " application->name()=" << application->name()
321− Q_ASSERT(application->state() != unityapi::ApplicationInfoInterface::Starting);
321+ << " application->state()=" << application->state();
322
322
323− if (application->state() == unityapi::ApplicationInfoInterface::Running) {
323+ if (application->state() == unityapi::ApplicationInfoInterface::Running
324+ || application->state() == unityapi::ApplicationInfoInterface::Starting) {
324 m_windowModel[i].removeOnceSurfaceDestroyed = true;
325 m_windowModel[i].removeOnceSurfaceDestroyed = true;
325 } else {
326 } else {
326 // assume it got killed by the out-of-memory daemon.
327 // assume it got killed by the out-of-memory daemon.
po/am.po+4−6
@@ -8,10 +8,10 @@ msgstr ""
8"Project-Id-Version: unity8\n"
8"Project-Id-Version: unity8\n"
9"Report-Msgid-Bugs-To: \n"
9"Report-Msgid-Bugs-To: \n"
10"POT-Creation-Date: 2021-01-29 11:42-0600\n"
10"POT-Creation-Date: 2021-01-29 11:42-0600\n"
11−"PO-Revision-Date: 2021-01-31 01:03+0000\n"
11+"PO-Revision-Date: 2021-08-29 20:18+0000\n"
12"Last-Translator: Samson <sambelet@yahoo.com>\n"
12"Last-Translator: Samson <sambelet@yahoo.com>\n"
13−"Language-Team: Amharic <https://translate.ubports.com/projects/unity8/unity8/"
13+"Language-Team: Amharic <https://translate.ubports.com/projects/ubports/"
14−"am/>\n"
14+"lomiri/am/>\n"
15"Language: am\n"
15"Language: am\n"
16"MIME-Version: 1.0\n"
16"MIME-Version: 1.0\n"
17"Content-Type: text/plain; charset=UTF-8\n"
17"Content-Type: text/plain; charset=UTF-8\n"
@@ -131,8 +131,6 @@ msgid "Restart"
131msgstr "እንደገና ማስጀመሪያ"
131msgstr "እንደገና ማስጀመሪያ"
132
132
133#: qml/Components/Dialogs.qml:276
133#: qml/Components/Dialogs.qml:276
134−#, fuzzy
135−#| msgid "Takes a screenshot."
136msgid "Screenshot"
134msgid "Screenshot"
137msgstr "መመልከቻ ፎቶ ማንሻ"
135msgstr "መመልከቻ ፎቶ ማንሻ"
138
136
@@ -142,7 +140,7 @@ msgstr "የ ፊደል ገበታ አቋራጮች"
142
140
143#: qml/Components/KeyboardShortcutsOverlay.qml:55
141#: qml/Components/KeyboardShortcutsOverlay.qml:55
144msgid "Lomiri"
142msgid "Lomiri"
145−msgstr ""
143+msgstr "Lomiri"
146
144
147#: qml/Components/KeyboardShortcutsOverlay.qml:62
145#: qml/Components/KeyboardShortcutsOverlay.qml:62
148msgid "PrtScr"
146msgid "PrtScr"
po/ar.po+20−25
@@ -8,9 +8,9 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: \n"
9"Report-Msgid-Bugs-To: \n"
10"POT-Creation-Date: 2021-01-29 11:42-0600\n"
10"POT-Creation-Date: 2021-01-29 11:42-0600\n"
11−"PO-Revision-Date: 2020-12-07 19:09+0000\n"
11+"PO-Revision-Date: 2021-10-12 07:49+0000\n"
12−"Last-Translator: abidin <abidin24@tutanota.com>\n"
12+"Last-Translator: HEROBRINE7GAMER <tapermeerkat77@gmail.com>\n"
13−"Language-Team: Arabic <https://translate.ubports.com/projects/unity8/unity8/"
13+"Language-Team: Arabic <https://translate.ubports.com/projects/ubports/lomiri/"
14"ar/>\n"
14"ar/>\n"
15"Language: ar\n"
15"Language: ar\n"
16"MIME-Version: 1.0\n"
16"MIME-Version: 1.0\n"
@@ -39,7 +39,7 @@ msgstr "كلمة المرور غير صحيحة، الرجاء المحاولة
39
39
40#: plugins/LightDM/Greeter.cpp:270
40#: plugins/LightDM/Greeter.cpp:270
41msgid "Log In"
41msgid "Log In"
42−msgstr "لِج"
42+msgstr "سجل الدخول"
43
43
44#: plugins/LightDM/Greeter.cpp:270
44#: plugins/LightDM/Greeter.cpp:270
45msgid "Retry"
45msgid "Retry"
@@ -47,7 +47,7 @@ msgstr "حاول مجدداً"
47
47
48#: plugins/LightDM/UsersModel.cpp:157
48#: plugins/LightDM/UsersModel.cpp:157
49msgid "Login"
49msgid "Login"
50−msgstr "لِج"
50+msgstr "سجل الدخول"
51
51
52#: plugins/LightDM/UsersModel.cpp:165
52#: plugins/LightDM/UsersModel.cpp:165
53msgid "Guest Session"
53msgid "Guest Session"
@@ -73,7 +73,7 @@ msgstr "اخرج"
73
73
74#: qml/Components/Dialogs.qml:177
74#: qml/Components/Dialogs.qml:177
75msgid "Are you sure you want to log out?"
75msgid "Are you sure you want to log out?"
76−msgstr "أمتأكد أنك تريد الخروج ؟"
76+msgstr "أمتأكد أنك تريد تسجيل الخروج ؟"
77
77
78#: qml/Components/Dialogs.qml:180
78#: qml/Components/Dialogs.qml:180
79msgctxt "Button: Lock the system"
79msgctxt "Button: Lock the system"
@@ -132,10 +132,8 @@ msgid "Restart"
132msgstr "أعد التشغيل"
132msgstr "أعد التشغيل"
133
133
134#: qml/Components/Dialogs.qml:276
134#: qml/Components/Dialogs.qml:276
135−#, fuzzy
136−#| msgid "Takes a screenshot."
137msgid "Screenshot"
135msgid "Screenshot"
138−msgstr "خُذ لقطة للشاشة."
136+msgstr "لقطة شاشة"
139
137
140#: qml/Components/KeyboardShortcutsOverlay.qml:41
138#: qml/Components/KeyboardShortcutsOverlay.qml:41
141msgid "Keyboard Shortcuts"
139msgid "Keyboard Shortcuts"
@@ -143,7 +141,7 @@ msgstr "اختصارات لوحة المفاتيح"
143
141
144#: qml/Components/KeyboardShortcutsOverlay.qml:55
142#: qml/Components/KeyboardShortcutsOverlay.qml:55
145msgid "Lomiri"
143msgid "Lomiri"
146−msgstr ""
144+msgstr "لوميري"
147
145
148#: qml/Components/KeyboardShortcutsOverlay.qml:62
146#: qml/Components/KeyboardShortcutsOverlay.qml:62
149msgid "PrtScr"
147msgid "PrtScr"
@@ -187,7 +185,7 @@ msgstr "شغِل الطرفية."
187
185
188#: qml/Components/KeyboardShortcutsOverlay.qml:131
186#: qml/Components/KeyboardShortcutsOverlay.qml:131
189msgid "Launcher"
187msgid "Launcher"
190−msgstr "مُطْلِق"
188+msgstr "مطلق"
191
189
192#: qml/Components/KeyboardShortcutsOverlay.qml:138
190#: qml/Components/KeyboardShortcutsOverlay.qml:138
193msgid "Super (Hold)"
191msgid "Super (Hold)"
@@ -202,9 +200,8 @@ msgid "Alt + F1"
202msgstr "Alt + F1"
200msgstr "Alt + F1"
203
201
204#: qml/Components/KeyboardShortcutsOverlay.qml:156
202#: qml/Components/KeyboardShortcutsOverlay.qml:156
205−#, fuzzy
206msgid "Opens launcher keyboard navigation mode."
203msgid "Opens launcher keyboard navigation mode."
207−msgstr "افتح وضع التصفح باستخدام لوحة مفاتيح المطلق."
204+msgstr "فتح وضع التنقل بلوحة المفاتيح المشغلة."
208
205
209#: qml/Components/KeyboardShortcutsOverlay.qml:164
206#: qml/Components/KeyboardShortcutsOverlay.qml:164
210msgid "Super + Tab"
207msgid "Super + Tab"
@@ -351,7 +348,6 @@ msgid "Close all"
351msgstr "أغلق الكل"
348msgstr "أغلق الكل"
352
349
353#: qml/Components/SharingPicker.qml:54
350#: qml/Components/SharingPicker.qml:54
354−#, fuzzy
355msgid "Preview Share Item"
351msgid "Preview Share Item"
356msgstr "معاينة الملف المشارك"
352msgstr "معاينة الملف المشارك"
357
353
@@ -424,7 +420,7 @@ msgstr "طوارئ"
424
420
425#: qml/Greeter/PromptList.qml:126
421#: qml/Greeter/PromptList.qml:126
426msgid "Passphrase"
422msgid "Passphrase"
427−msgstr "العبارة السرية"
423+msgstr "كلمة السر"
428
424
429#: qml/Greeter/PromptList.qml:126
425#: qml/Greeter/PromptList.qml:126
430msgid "Passcode"
426msgid "Passcode"
@@ -440,11 +436,11 @@ msgstr "ابحث…"
440
436
441#: qml/Launcher/PullToRefreshScopeStyle.qml:56
437#: qml/Launcher/PullToRefreshScopeStyle.qml:56
442msgid "Pull to refresh…"
438msgid "Pull to refresh…"
443−msgstr "اسحب للتحديث..."
439+msgstr "اسحب للتحديث…"
444
440
445#: qml/Launcher/PullToRefreshScopeStyle.qml:61
441#: qml/Launcher/PullToRefreshScopeStyle.qml:61
446msgid "Release to refresh…"
442msgid "Release to refresh…"
447−msgstr "حرّر للتحديث..."
443+msgstr "حرّر للتحديث…"
448
444
449#: qml/Notifications/NotificationMenuItemFactory.qml:124
445#: qml/Notifications/NotificationMenuItemFactory.qml:124
450msgid "Show password"
446msgid "Show password"
@@ -477,9 +473,9 @@ msgstr[5] "%1 ساعة"
477#, qt-format
473#, qt-format
478msgid "%1 minute"
474msgid "%1 minute"
479msgid_plural "%1 minutes"
475msgid_plural "%1 minutes"
480−msgstr[0] "أقل من دقيقة (%1)"
476+msgstr[0] "%1 دقيقة"
481−msgstr[1] "دقيقة واحدة (%1)"
477+msgstr[1] "دقيقة واحدة"
482−msgstr[2] "دقيقتان (%1)"
478+msgstr[2] "دقيقتان"
483msgstr[3] "%1 دقائق"
479msgstr[3] "%1 دقائق"
484msgstr[4] "%1 دقيقة"
480msgstr[4] "%1 دقيقة"
485msgstr[5] "%1 دقيقة"
481msgstr[5] "%1 دقيقة"
@@ -541,7 +537,6 @@ msgid "Reply"
541msgstr "رد"
537msgstr "رد"
542
538
543#: qml/Panel/Indicators/MessageMenuItemFactory.qml:209
539#: qml/Panel/Indicators/MessageMenuItemFactory.qml:209
544−#, fuzzy
545msgctxt "Button: Call back on phone"
540msgctxt "Button: Call back on phone"
546msgid "Call back"
541msgid "Call back"
547msgstr "عاود الاتصال"
542msgstr "عاود الاتصال"
@@ -630,11 +625,11 @@ msgstr "تخطيط لوحة المفاتيح"
630#: qml/Wizard/Pages/60-account.qml:72 qml/Wizard/Pages/79-system-update.qml:144
625#: qml/Wizard/Pages/60-account.qml:72 qml/Wizard/Pages/79-system-update.qml:144
631#: qml/Wizard/Pages/sim.qml:101
626#: qml/Wizard/Pages/sim.qml:101
632msgid "Skip"
627msgid "Skip"
633−msgstr "تخطى"
628+msgstr "تخط"
634
629
635#: qml/Wizard/Pages/30-wifi.qml:31
630#: qml/Wizard/Pages/30-wifi.qml:31
636msgid "Connect to Wi‑Fi"
631msgid "Connect to Wi‑Fi"
637−msgstr "اتصل بالواي فاي"
632+msgstr "اتصل بالإنترنت"
638
633
639#: qml/Wizard/Pages/30-wifi.qml:139
634#: qml/Wizard/Pages/30-wifi.qml:139
640msgid "Connected"
635msgid "Connected"
@@ -735,7 +730,7 @@ msgstr "ابدأ"
735#: qml/Wizard/Pages/passcode-confirm.qml:43
730#: qml/Wizard/Pages/passcode-confirm.qml:43
736#: qml/Wizard/Pages/passcode-desktop.qml:104
731#: qml/Wizard/Pages/passcode-desktop.qml:104
737msgid "Confirm passcode"
732msgid "Confirm passcode"
738−msgstr "تأكيد الرمز السري"
733+msgstr "تأكيد كلمة السر"
739
734
740#: qml/Wizard/Pages/passcode-confirm.qml:45
735#: qml/Wizard/Pages/passcode-confirm.qml:45
741msgid "Incorrect passcode."
736msgid "Incorrect passcode."
@@ -757,7 +752,7 @@ msgstr "أدخل 4 أرقام لإعداد الرمز السري"
757#: qml/Wizard/Pages/passcode-desktop.qml:84
752#: qml/Wizard/Pages/passcode-desktop.qml:84
758#: qml/Wizard/Pages/passcode-set.qml:54
753#: qml/Wizard/Pages/passcode-set.qml:54
759msgid "Choose passcode"
754msgid "Choose passcode"
760−msgstr "اختر الرمز السري"
755+msgstr "اختر كلمة السر"
761
756
762#: qml/Wizard/Pages/passcode-desktop.qml:139
757#: qml/Wizard/Pages/passcode-desktop.qml:139
763msgid "Passcode too short"
758msgid "Passcode too short"
po/cs.po+12−12
@@ -8,7 +8,7 @@ msgstr ""
8"Project-Id-Version: unity8\n"
8"Project-Id-Version: unity8\n"
9"Report-Msgid-Bugs-To: \n"
9"Report-Msgid-Bugs-To: \n"
10"POT-Creation-Date: 2021-01-29 11:42-0600\n"
10"POT-Creation-Date: 2021-01-29 11:42-0600\n"
11−"PO-Revision-Date: 2021-04-30 20:44+0000\n"
11+"PO-Revision-Date: 2021-12-05 18:44+0000\n"
12"Last-Translator: Milan Korecký <milan.korecky@gmail.com>\n"
12"Last-Translator: Milan Korecký <milan.korecky@gmail.com>\n"
13"Language-Team: Czech <https://translate.ubports.com/projects/ubports/lomiri/"
13"Language-Team: Czech <https://translate.ubports.com/projects/ubports/lomiri/"
14"cs/>\n"
14"cs/>\n"
@@ -30,11 +30,11 @@ msgstr "Uživatelské jméno"
30
30
31#: plugins/LightDM/Greeter.cpp:258
31#: plugins/LightDM/Greeter.cpp:258
32msgid "Failed to authenticate"
32msgid "Failed to authenticate"
33−msgstr "Chyba při ověřování se"
33+msgstr "Chyba při ověřování"
34
34
35#: plugins/LightDM/Greeter.cpp:260
35#: plugins/LightDM/Greeter.cpp:260
36msgid "Invalid password, please try again"
36msgid "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"
38
38
39#: plugins/LightDM/Greeter.cpp:270
39#: plugins/LightDM/Greeter.cpp:270
40msgid "Log In"
40msgid "Log In"
@@ -42,11 +42,11 @@ msgstr "Přihlásit se"
42
42
43#: plugins/LightDM/Greeter.cpp:270
43#: plugins/LightDM/Greeter.cpp:270
44msgid "Retry"
44msgid "Retry"
45−msgstr "Znovu"
45+msgstr "Zkusit znovu"
46
46
47#: plugins/LightDM/UsersModel.cpp:157
47#: plugins/LightDM/UsersModel.cpp:157
48msgid "Login"
48msgid "Login"
49−msgstr "Zahájit relaci"
49+msgstr "Přihlášení"
50
50
51#: plugins/LightDM/UsersModel.cpp:165
51#: plugins/LightDM/UsersModel.cpp:165
52msgid "Guest Session"
52msgid "Guest Session"
@@ -55,7 +55,7 @@ msgstr "Sezení hosta"
55#: plugins/Unity/Launcher/launcheritem.cpp:48
55#: plugins/Unity/Launcher/launcheritem.cpp:48
56#: plugins/Unity/Launcher/launcheritem.cpp:122
56#: plugins/Unity/Launcher/launcheritem.cpp:122
57msgid "Pin shortcut"
57msgid "Pin shortcut"
58−msgstr "Připnout zástupce"
58+msgstr "Připnout odkaz"
59
59
60#: plugins/Unity/Launcher/launcheritem.cpp:54
60#: plugins/Unity/Launcher/launcheritem.cpp:54
61msgid "Quit"
61msgid "Quit"
@@ -63,7 +63,7 @@ msgstr "Ukončit"
63
63
64#: plugins/Unity/Launcher/launcheritem.cpp:122
64#: plugins/Unity/Launcher/launcheritem.cpp:122
65msgid "Unpin shortcut"
65msgid "Unpin shortcut"
66−msgstr "Zrušit připnutí zástupce"
66+msgstr "Zrušit připnutý odkaz"
67
67
68#: qml/Components/Dialogs.qml:176
68#: qml/Components/Dialogs.qml:176
69msgctxt "Title: Lock/Log out dialog"
69msgctxt "Title: Lock/Log out dialog"
@@ -308,7 +308,7 @@ msgstr "Tísňové volání"
308
308
309#: qml/Components/Lockscreen.qml:245
309#: qml/Components/Lockscreen.qml:245
310msgid "OK"
310msgid "OK"
311−msgstr "Přijmout"
311+msgstr "OK"
312
312
313#: qml/Components/MediaServices/VideoPlayerControls.qml:44
313#: qml/Components/MediaServices/VideoPlayerControls.qml:44
314#, qt-format
314#, qt-format
@@ -481,8 +481,8 @@ msgstr[2] "%1 minuty"
481msgid "%1 second"
481msgid "%1 second"
482msgid_plural "%1 seconds"
482msgid_plural "%1 seconds"
483msgstr[0] "%1 sekunda"
483msgstr[0] "%1 sekunda"
484−msgstr[1] "%1 sekundy"
484+msgstr[1] "%1 sekund"
485−msgstr[2] "%1 sekund"
485+msgstr[2] ""
486
486
487#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1009
487#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1009
488msgid "0 seconds"
488msgid "0 seconds"
@@ -512,7 +512,7 @@ msgstr "Zrušeno"
512
512
513#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1027
513#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1027
514msgid "Finished"
514msgid "Finished"
515−msgstr "Dokončeno"
515+msgstr "Hotovo"
516
516
517#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1029
517#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1029
518msgid "Failed, tap to retry"
518msgid "Failed, tap to retry"
@@ -585,7 +585,7 @@ msgstr "Jazyk"
585#: qml/Wizard/Pages/passcode-desktop.qml:154
585#: qml/Wizard/Pages/passcode-desktop.qml:154
586#: qml/Wizard/Pages/password-set.qml:153
586#: qml/Wizard/Pages/password-set.qml:153
587msgid "Next"
587msgid "Next"
588−msgstr "Následující"
588+msgstr "Další"
589
589
590#: qml/Wizard/Pages/10-welcome-update.qml:91
590#: qml/Wizard/Pages/10-welcome-update.qml:91
591msgid "Welcome to "
591msgid "Welcome to "
po/de.po+3−3
@@ -8,8 +8,8 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: \n"
9"Report-Msgid-Bugs-To: \n"
10"POT-Creation-Date: 2021-01-29 11:42-0600\n"
10"POT-Creation-Date: 2021-01-29 11:42-0600\n"
11−"PO-Revision-Date: 2021-04-22 05:43+0000\n"
11+"PO-Revision-Date: 2021-07-24 19:07+0000\n"
12−"Last-Translator: Daniel Frost <one@frostinfo.de>\n"
12+"Last-Translator: Stefan Kalb <stefan.kalb@gmx.ch>\n"
13"Language-Team: German <https://translate.ubports.com/projects/ubports/lomiri/"
13"Language-Team: German <https://translate.ubports.com/projects/ubports/lomiri/"
14"de/>\n"
14"de/>\n"
15"Language: de\n"
15"Language: de\n"
@@ -655,7 +655,7 @@ msgstr "Bevorzugter Name"
655
655
656#: qml/Wizard/Pages/70-passwd-type.qml:40
656#: qml/Wizard/Pages/70-passwd-type.qml:40
657msgid "Lock Screen"
657msgid "Lock Screen"
658−msgstr "Sperrbildschirm"
658+msgstr "Bildschirm sperren"
659
659
660#: qml/Wizard/Pages/70-passwd-type.qml:103
660#: qml/Wizard/Pages/70-passwd-type.qml:103
661msgctxt "Label: Type of security method"
661msgctxt "Label: Type of security method"
po/el.po+4−6
@@ -8,9 +8,9 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: \n"
9"Report-Msgid-Bugs-To: \n"
10"POT-Creation-Date: 2021-01-29 11:42-0600\n"
10"POT-Creation-Date: 2021-01-29 11:42-0600\n"
11−"PO-Revision-Date: 2020-08-19 00:03+0000\n"
11+"PO-Revision-Date: 2021-09-20 23:03+0000\n"
12"Last-Translator: Efstathios Iosifidis <iefstathios@gmail.com>\n"
12"Last-Translator: Efstathios Iosifidis <iefstathios@gmail.com>\n"
13−"Language-Team: Greek <https://translate.ubports.com/projects/unity8/unity8/"
13+"Language-Team: Greek <https://translate.ubports.com/projects/ubports/lomiri/"
14"el/>\n"
14"el/>\n"
15"Language: el\n"
15"Language: el\n"
16"MIME-Version: 1.0\n"
16"MIME-Version: 1.0\n"
@@ -131,10 +131,8 @@ msgid "Restart"
131msgstr "Επανεκκίνηση"
131msgstr "Επανεκκίνηση"
132
132
133#: qml/Components/Dialogs.qml:276
133#: qml/Components/Dialogs.qml:276
134−#, fuzzy
135−#| msgid "Takes a screenshot."
136msgid "Screenshot"
134msgid "Screenshot"
137−msgstr "Λαμβάνει ένα στιγμιότυπο οθόνης."
135+msgstr "Στιγμιότυπο οθόνης"
138
136
139#: qml/Components/KeyboardShortcutsOverlay.qml:41
137#: qml/Components/KeyboardShortcutsOverlay.qml:41
140msgid "Keyboard Shortcuts"
138msgid "Keyboard Shortcuts"
@@ -142,7 +140,7 @@ msgstr "Συντομεύσεις Πληκτρολογίου"
142
140
143#: qml/Components/KeyboardShortcutsOverlay.qml:55
141#: qml/Components/KeyboardShortcutsOverlay.qml:55
144msgid "Lomiri"
142msgid "Lomiri"
145−msgstr ""
143+msgstr "Lomiri"
146
144
147#: qml/Components/KeyboardShortcutsOverlay.qml:62
145#: qml/Components/KeyboardShortcutsOverlay.qml:62
148msgid "PrtScr"
146msgid "PrtScr"
po/es.po+22−22
@@ -8,8 +8,8 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: \n"
9"Report-Msgid-Bugs-To: \n"
10"POT-Creation-Date: 2021-01-29 11:42-0600\n"
10"POT-Creation-Date: 2021-01-29 11:42-0600\n"
11−"PO-Revision-Date: 2021-03-31 05:24+0000\n"
11+"PO-Revision-Date: 2021-10-04 12:20+0000\n"
12−"Last-Translator: Josué <j2g2rp@gmail.com>\n"
12+"Last-Translator: Adolfo Jayme Barrientos <fitojb@ubuntu.com>\n"
13"Language-Team: Spanish <https://translate.ubports.com/projects/ubports/"
13"Language-Team: Spanish <https://translate.ubports.com/projects/ubports/"
14"lomiri/es/>\n"
14"lomiri/es/>\n"
15"Language: es\n"
15"Language: es\n"
@@ -34,11 +34,11 @@ msgstr "Fallo al autenticar"
34
34
35#: plugins/LightDM/Greeter.cpp:260
35#: plugins/LightDM/Greeter.cpp:260
36msgid "Invalid password, please try again"
36msgid "Invalid password, please try again"
37−msgstr "Contraseña incorrecta, por favor, inténtelo de nuevo"
37+msgstr "Contraseña incorrecta; inténtelo de nuevo"
38
38
39#: plugins/LightDM/Greeter.cpp:270
39#: plugins/LightDM/Greeter.cpp:270
40msgid "Log In"
40msgid "Log In"
41−msgstr "Iniciar sesión"
41+msgstr "Acceder"
42
42
43#: plugins/LightDM/Greeter.cpp:270
43#: plugins/LightDM/Greeter.cpp:270
44msgid "Retry"
44msgid "Retry"
@@ -46,7 +46,7 @@ msgstr "Reintentar"
46
46
47#: plugins/LightDM/UsersModel.cpp:157
47#: plugins/LightDM/UsersModel.cpp:157
48msgid "Login"
48msgid "Login"
49−msgstr "Iniciar sesión"
49+msgstr "Acceder"
50
50
51#: plugins/LightDM/UsersModel.cpp:165
51#: plugins/LightDM/UsersModel.cpp:165
52msgid "Guest Session"
52msgid "Guest Session"
@@ -68,7 +68,7 @@ msgstr "Desanclar atajo"
68#: qml/Components/Dialogs.qml:176
68#: qml/Components/Dialogs.qml:176
69msgctxt "Title: Lock/Log out dialog"
69msgctxt "Title: Lock/Log out dialog"
70msgid "Log out"
70msgid "Log out"
71−msgstr "Cerrar la sesión"
71+msgstr "Cerrar sesión"
72
72
73#: qml/Components/Dialogs.qml:177
73#: qml/Components/Dialogs.qml:177
74msgid "Are you sure you want to log out?"
74msgid "Are you sure you want to log out?"
@@ -82,7 +82,7 @@ msgstr "Bloquear"
82#: qml/Components/Dialogs.qml:191
82#: qml/Components/Dialogs.qml:191
83msgctxt "Button: Log out from the system"
83msgctxt "Button: Log out from the system"
84msgid "Log Out"
84msgid "Log Out"
85−msgstr "Cerrar la sesión"
85+msgstr "Cerrar sesión"
86
86
87#: qml/Components/Dialogs.qml:199 qml/Components/Dialogs.qml:284
87#: qml/Components/Dialogs.qml:199 qml/Components/Dialogs.qml:284
88#: qml/Greeter/NarrowView.qml:215 qml/Wizard/Pages/passcode-confirm.qml:32
88#: qml/Greeter/NarrowView.qml:215 qml/Wizard/Pages/passcode-confirm.qml:32
@@ -164,7 +164,7 @@ msgstr "Super + Espacio"
164
164
165#: qml/Components/KeyboardShortcutsOverlay.qml:93
165#: qml/Components/KeyboardShortcutsOverlay.qml:93
166msgid "Switches to next keyboard layout."
166msgid "Switches to next keyboard layout."
167−msgstr "Cambia a la siguiente disposición de teclado."
167+msgstr "Cambia a la disposición de teclado siguiente."
168
168
169#: qml/Components/KeyboardShortcutsOverlay.qml:101
169#: qml/Components/KeyboardShortcutsOverlay.qml:101
170msgid "Super + Shift + Space"
170msgid "Super + Shift + Space"
@@ -172,7 +172,7 @@ msgstr "Super + Mayús + Espacio"
172
172
173#: qml/Components/KeyboardShortcutsOverlay.qml:106
173#: qml/Components/KeyboardShortcutsOverlay.qml:106
174msgid "Switches to previous keyboard layout."
174msgid "Switches to previous keyboard layout."
175−msgstr "Cambia a la anterior disposición de teclado."
175+msgstr "Cambia a la disposición de teclado anterior."
176
176
177#: qml/Components/KeyboardShortcutsOverlay.qml:114
177#: qml/Components/KeyboardShortcutsOverlay.qml:114
178msgid "Ctrl + Alt + T"
178msgid "Ctrl + Alt + T"
@@ -184,15 +184,15 @@ msgstr "Abre la aplicación Terminal."
184
184
185#: qml/Components/KeyboardShortcutsOverlay.qml:131
185#: qml/Components/KeyboardShortcutsOverlay.qml:131
186msgid "Launcher"
186msgid "Launcher"
187−msgstr "Lanzador"
187+msgstr "Iniciador"
188
188
189#: qml/Components/KeyboardShortcutsOverlay.qml:138
189#: qml/Components/KeyboardShortcutsOverlay.qml:138
190msgid "Super (Hold)"
190msgid "Super (Hold)"
191−msgstr "Super (Mantener)"
191+msgstr "Súper (mantener)"
192
192
193#: qml/Components/KeyboardShortcutsOverlay.qml:143
193#: qml/Components/KeyboardShortcutsOverlay.qml:143
194msgid "Opens the launcher, displays shortcuts."
194msgid "Opens the launcher, displays shortcuts."
195−msgstr "Abre el lanzador y muestra los atajos."
195+msgstr "Abre el iniciador y muestra los atajos."
196
196
197#: qml/Components/KeyboardShortcutsOverlay.qml:151
197#: qml/Components/KeyboardShortcutsOverlay.qml:151
198msgid "Alt + F1"
198msgid "Alt + F1"
@@ -200,7 +200,7 @@ msgstr "Alt + F1"
200
200
201#: qml/Components/KeyboardShortcutsOverlay.qml:156
201#: qml/Components/KeyboardShortcutsOverlay.qml:156
202msgid "Opens launcher keyboard navigation mode."
202msgid "Opens launcher keyboard navigation mode."
203−msgstr "Abre el Lanzador en modo de navegación de teclado."
203+msgstr "Abre el iniciador en modo de navegación de teclado."
204
204
205#: qml/Components/KeyboardShortcutsOverlay.qml:164
205#: qml/Components/KeyboardShortcutsOverlay.qml:164
206msgid "Super + Tab"
206msgid "Super + Tab"
@@ -208,7 +208,7 @@ msgstr "Super + Tab"
208
208
209#: qml/Components/KeyboardShortcutsOverlay.qml:169
209#: qml/Components/KeyboardShortcutsOverlay.qml:169
210msgid "Switches applications via the launcher."
210msgid "Switches applications via the launcher."
211−msgstr "Recorre las aplicaciones del Lanzador."
211+msgstr "Cambia de aplicación mediante el iniciador."
212
212
213#: qml/Components/KeyboardShortcutsOverlay.qml:177
213#: qml/Components/KeyboardShortcutsOverlay.qml:177
214msgid "Super + 1 to 0"
214msgid "Super + 1 to 0"
@@ -216,7 +216,7 @@ msgstr "Súper + 1 a 0"
216
216
217#: qml/Components/KeyboardShortcutsOverlay.qml:182
217#: qml/Components/KeyboardShortcutsOverlay.qml:182
218msgid "Same as clicking on a launcher icon."
218msgid "Same as clicking on a launcher icon."
219−msgstr "Lo mismo que hacer clic en un icono del Lanzador."
219+msgstr "Lo mismo que pulsar en un icono del iniciador."
220
220
221#: qml/Components/KeyboardShortcutsOverlay.qml:190
221#: qml/Components/KeyboardShortcutsOverlay.qml:190
222msgid "Super + A"
222msgid "Super + A"
@@ -288,7 +288,7 @@ msgstr "Ctrl + Super + Arriba o Derecha"
288
288
289#: qml/Components/KeyboardShortcutsOverlay.qml:336
289#: qml/Components/KeyboardShortcutsOverlay.qml:336
290msgid "Semi-maximizes the current window."
290msgid "Semi-maximizes the current window."
291−msgstr "Semi-maximiza la ventana actual."
291+msgstr "Semimaximiza la ventana actual."
292
292
293#: qml/Components/KeyboardShortcutsOverlay.qml:344
293#: qml/Components/KeyboardShortcutsOverlay.qml:344
294msgid "Alt + F4"
294msgid "Alt + F4"
@@ -373,7 +373,7 @@ msgstr "Deslice con dos dedos para desplazarse."
373
373
374#: qml/Components/VirtualTouchPad.qml:396
374#: qml/Components/VirtualTouchPad.qml:396
375msgid "Find more settings in the system settings."
375msgid "Find more settings in the system settings."
376−msgstr "Encuentre mas ajustes en la configuración del sistema."
376+msgstr "Encuentre más ajustes en Configuración del sistema."
377
377
378#: qml/Greeter/CoverPage.qml:127
378#: qml/Greeter/CoverPage.qml:127
379msgid "Unlock"
379msgid "Unlock"
@@ -549,21 +549,21 @@ msgstr ""
549
549
550#: qml/Tutorial/TutorialLeft.qml:47
550#: qml/Tutorial/TutorialLeft.qml:47
551msgid "Short swipe from the left edge to open the launcher"
551msgid "Short swipe from the left edge to open the launcher"
552−msgstr "Deslizar un poco desde el borde izquierdo para abrir el lanzador"
552+msgstr "Deslice un poco desde el borde izquierdo para abrir el iniciador"
553
553
554#: qml/Tutorial/TutorialRight.qml:54
554#: qml/Tutorial/TutorialRight.qml:54
555msgid "Push your mouse against the right edge to view your open apps"
555msgid "Push your mouse against the right edge to view your open apps"
556msgstr ""
556msgstr ""
557−"Para ver las aplicaciones abiertas empuja el ratón contra el borde derecho"
557+"Para ver las aplicaciones abiertas empuje el ratón contra el borde derecho"
558
558
559#: qml/Tutorial/TutorialRight.qml:55
559#: qml/Tutorial/TutorialRight.qml:55
560msgid "Swipe from the right edge to view your open apps"
560msgid "Swipe from the right edge to view your open apps"
561−msgstr "Deslizar desde el borde derecho para ver las aplis abiertas"
561+msgstr "Deslice desde el borde derecho para ver las aplicaciones abiertas"
562
562
563#: qml/Tutorial/TutorialTop.qml:53
563#: qml/Tutorial/TutorialTop.qml:53
564msgid "Swipe from the top edge to access notifications and quick settings"
564msgid "Swipe from the top edge to access notifications and quick settings"
565msgstr ""
565msgstr ""
566−"Deslizar desde el borde superior para acceder a las notificaciones y "
566+"Deslice desde el borde superior para acceder a las notificaciones y "
567"configuración"
567"configuración"
568
568
569#: qml/Wizard/Page.qml:59
569#: qml/Wizard/Page.qml:59
@@ -583,7 +583,7 @@ msgstr "Idioma"
583#: qml/Wizard/Pages/passcode-desktop.qml:154
583#: qml/Wizard/Pages/passcode-desktop.qml:154
584#: qml/Wizard/Pages/password-set.qml:153
584#: qml/Wizard/Pages/password-set.qml:153
585msgid "Next"
585msgid "Next"
586−msgstr "Siguiente"
586+msgstr "Sig."
587
587
588#: qml/Wizard/Pages/10-welcome-update.qml:91
588#: qml/Wizard/Pages/10-welcome-update.qml:91
589msgid "Welcome to "
589msgid "Welcome to "
po/he.po+9−11
@@ -8,9 +8,9 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: \n"
9"Report-Msgid-Bugs-To: \n"
10"POT-Creation-Date: 2021-01-29 11:42-0600\n"
10"POT-Creation-Date: 2021-01-29 11:42-0600\n"
11−"PO-Revision-Date: 2020-09-05 22:10+0000\n"
11+"PO-Revision-Date: 2022-02-07 20:15+0000\n"
12−"Last-Translator: tamer dab <dabsantamer@yahoo.com>\n"
12+"Last-Translator: anonymous <noreply@weblate.org>\n"
13−"Language-Team: Hebrew <https://translate.ubports.com/projects/unity8/unity8/"
13+"Language-Team: Hebrew <https://translate.ubports.com/projects/ubports/lomiri/"
14"he/>\n"
14"he/>\n"
15"Language: he\n"
15"Language: he\n"
16"MIME-Version: 1.0\n"
16"MIME-Version: 1.0\n"
@@ -129,10 +129,8 @@ msgid "Restart"
129msgstr "הפעלה מחדש"
129msgstr "הפעלה מחדש"
130
130
131#: qml/Components/Dialogs.qml:276
131#: qml/Components/Dialogs.qml:276
132−#, fuzzy
133−#| msgid "Takes a screenshot."
134msgid "Screenshot"
132msgid "Screenshot"
135−msgstr "מצלם מסך."
133+msgstr "צילום מסך"
136
134
137#: qml/Components/KeyboardShortcutsOverlay.qml:41
135#: qml/Components/KeyboardShortcutsOverlay.qml:41
138msgid "Keyboard Shortcuts"
136msgid "Keyboard Shortcuts"
@@ -140,7 +138,7 @@ msgstr "קיצורי דרך במקלדת"
140
138
141#: qml/Components/KeyboardShortcutsOverlay.qml:55
139#: qml/Components/KeyboardShortcutsOverlay.qml:55
142msgid "Lomiri"
140msgid "Lomiri"
143−msgstr ""
141+msgstr "Lomiri"
144
142
145#: qml/Components/KeyboardShortcutsOverlay.qml:62
143#: qml/Components/KeyboardShortcutsOverlay.qml:62
146msgid "PrtScr"
144msgid "PrtScr"
@@ -458,13 +456,13 @@ msgstr "שום דבר לא מתנגן"
458msgid "%1 hour"
456msgid "%1 hour"
459msgid_plural "%1 hours"
457msgid_plural "%1 hours"
460msgstr[0] "%1 שעה"
458msgstr[0] "%1 שעה"
461−msgstr[1] "%1 שעות"
459+msgstr[1] "%1 שעת"
462
460
463#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1001
461#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1001
464#, qt-format
462#, qt-format
465msgid "%1 minute"
463msgid "%1 minute"
466msgid_plural "%1 minutes"
464msgid_plural "%1 minutes"
467−msgstr[0] "דקה %1"
465+msgstr[0] "%1 דקה"
468msgstr[1] "%1 דקות"
466msgstr[1] "%1 דקות"
469
467
470#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1006
468#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1006
@@ -526,7 +524,7 @@ msgstr "התקשר חזרה"
526
524
527#: qml/Panel/PanelMenuPage.qml:92
525#: qml/Panel/PanelMenuPage.qml:92
528msgid "Back"
526msgid "Back"
529−msgstr "חזור"
527+msgstr "חזרה"
530
528
531#: qml/Stage/SideStage.qml:76
529#: qml/Stage/SideStage.qml:76
532msgid "Drag using 3 fingers any application from one window to the other"
530msgid "Drag using 3 fingers any application from one window to the other"
@@ -559,7 +557,7 @@ msgstr "החלק מהקצה העליון כדי לגשת להתראות ולהג
559#: qml/Wizard/Page.qml:59
557#: qml/Wizard/Page.qml:59
560msgctxt "Button: Go back one page in the Wizard"
558msgctxt "Button: Go back one page in the Wizard"
561msgid "Back"
559msgid "Back"
562−msgstr "חזור"
560+msgstr "חזרה"
563
561
564#: qml/Wizard/Pages/10-welcome.qml:29
562#: qml/Wizard/Pages/10-welcome.qml:29
565msgid "Language"
563msgid "Language"
po/hu.po+9−11
@@ -8,8 +8,8 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: \n"
9"Report-Msgid-Bugs-To: \n"
10"POT-Creation-Date: 2021-01-29 11:42-0600\n"
10"POT-Creation-Date: 2021-01-29 11:42-0600\n"
11−"PO-Revision-Date: 2021-03-17 10:43+0000\n"
11+"PO-Revision-Date: 2022-02-06 00:18+0000\n"
12−"Last-Translator: David Farkas <dvdfrks@gmail.com>\n"
12+"Last-Translator: Lundrin <berenyi.vilmos@tutanota.com>\n"
13"Language-Team: Hungarian <https://translate.ubports.com/projects/ubports/"
13"Language-Team: Hungarian <https://translate.ubports.com/projects/ubports/"
14"lomiri/hu/>\n"
14"lomiri/hu/>\n"
15"Language: hu\n"
15"Language: hu\n"
@@ -88,7 +88,7 @@ msgstr "Kijelentkezés"
88#: qml/Greeter/NarrowView.qml:215 qml/Wizard/Pages/passcode-confirm.qml:32
88#: qml/Greeter/NarrowView.qml:215 qml/Wizard/Pages/passcode-confirm.qml:32
89#: qml/Wizard/Pages/passcode-set.qml:32
89#: qml/Wizard/Pages/passcode-set.qml:32
90msgid "Cancel"
90msgid "Cancel"
91−msgstr "Mégsem"
91+msgstr "Mégse"
92
92
93#: qml/Components/Dialogs.qml:211
93#: qml/Components/Dialogs.qml:211
94msgctxt "Title: Reboot dialog"
94msgctxt "Title: Reboot dialog"
@@ -131,10 +131,8 @@ msgid "Restart"
131msgstr "Újraindítás"
131msgstr "Újraindítás"
132
132
133#: qml/Components/Dialogs.qml:276
133#: qml/Components/Dialogs.qml:276
134−#, fuzzy
135−#| msgid "Takes a screenshot."
136msgid "Screenshot"
134msgid "Screenshot"
137−msgstr "Képernyőkép készítése."
135+msgstr "Képernyőmentés"
138
136
139#: qml/Components/KeyboardShortcutsOverlay.qml:41
137#: qml/Components/KeyboardShortcutsOverlay.qml:41
140msgid "Keyboard Shortcuts"
138msgid "Keyboard Shortcuts"
@@ -142,7 +140,7 @@ msgstr "Gyorsbillentyűk"
142
140
143#: qml/Components/KeyboardShortcutsOverlay.qml:55
141#: qml/Components/KeyboardShortcutsOverlay.qml:55
144msgid "Lomiri"
142msgid "Lomiri"
145−msgstr ""
143+msgstr "Lomiri"
146
144
147#: qml/Components/KeyboardShortcutsOverlay.qml:62
145#: qml/Components/KeyboardShortcutsOverlay.qml:62
148msgid "PrtScr"
146msgid "PrtScr"
@@ -506,7 +504,7 @@ msgstr "Megszakítva"
506
504
507#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1027
505#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1027
508msgid "Finished"
506msgid "Finished"
509−msgstr "Befejeződött"
507+msgstr "Kész"
510
508
511#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1029
509#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1029
512msgid "Failed, tap to retry"
510msgid "Failed, tap to retry"
@@ -578,7 +576,7 @@ msgstr "Nyelv"
578#: qml/Wizard/Pages/passcode-desktop.qml:154
576#: qml/Wizard/Pages/passcode-desktop.qml:154
579#: qml/Wizard/Pages/password-set.qml:153
577#: qml/Wizard/Pages/password-set.qml:153
580msgid "Next"
578msgid "Next"
581−msgstr "Következő"
579+msgstr "Köv."
582
580
583#: qml/Wizard/Pages/10-welcome-update.qml:91
581#: qml/Wizard/Pages/10-welcome-update.qml:91
584msgid "Welcome to "
582msgid "Welcome to "
@@ -620,7 +618,7 @@ msgstr "Csatlakozás Wi-Fi hálózathoz"
620
618
621#: qml/Wizard/Pages/30-wifi.qml:139
619#: qml/Wizard/Pages/30-wifi.qml:139
622msgid "Connected"
620msgid "Connected"
623−msgstr "Kapcsolódva"
621+msgstr "Csatlakozva"
624
622
625#: qml/Wizard/Pages/30-wifi.qml:172
623#: qml/Wizard/Pages/30-wifi.qml:172
626msgid "Available Wi-Fi networks"
624msgid "Available Wi-Fi networks"
@@ -741,7 +739,7 @@ msgstr "Adjon meg egy 4 számjegyű jelkódot"
741#: qml/Wizard/Pages/passcode-desktop.qml:84
739#: qml/Wizard/Pages/passcode-desktop.qml:84
742#: qml/Wizard/Pages/passcode-set.qml:54
740#: qml/Wizard/Pages/passcode-set.qml:54
743msgid "Choose passcode"
741msgid "Choose passcode"
744−msgstr "Jelkód megadása"
742+msgstr "Válasszon jelkódot"
745
743
746#: qml/Wizard/Pages/passcode-desktop.qml:139
744#: qml/Wizard/Pages/passcode-desktop.qml:139
747msgid "Passcode too short"
745msgid "Passcode too short"
po/it.po+7−7
@@ -8,8 +8,8 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: \n"
9"Report-Msgid-Bugs-To: \n"
10"POT-Creation-Date: 2021-01-29 11:42-0600\n"
10"POT-Creation-Date: 2021-01-29 11:42-0600\n"
11−"PO-Revision-Date: 2021-04-23 15:39+0000\n"
11+"PO-Revision-Date: 2022-01-22 21:58+0000\n"
12−"Last-Translator: Davide Pruccoli <davide.pruccoli@protonmail.com>\n"
12+"Last-Translator: Matteo <mattbel10@hotmail.com>\n"
13"Language-Team: Italian <https://translate.ubports.com/projects/ubports/"
13"Language-Team: Italian <https://translate.ubports.com/projects/ubports/"
14"lomiri/it/>\n"
14"lomiri/it/>\n"
15"Language: it\n"
15"Language: it\n"
@@ -132,7 +132,7 @@ msgstr "Riavvia"
132
132
133#: qml/Components/Dialogs.qml:276
133#: qml/Components/Dialogs.qml:276
134msgid "Screenshot"
134msgid "Screenshot"
135−msgstr "Cattura schermata."
135+msgstr "Cattura schermata"
136
136
137#: qml/Components/KeyboardShortcutsOverlay.qml:41
137#: qml/Components/KeyboardShortcutsOverlay.qml:41
138msgid "Keyboard Shortcuts"
138msgid "Keyboard Shortcuts"
@@ -409,7 +409,7 @@ msgstr "Riprova"
409
409
410#: qml/Greeter/Greeter.qml:551
410#: qml/Greeter/Greeter.qml:551
411msgid "Enter passphrase to unlock"
411msgid "Enter passphrase to unlock"
412−msgstr "Inserire passphrase per sbloccare"
412+msgstr "Inserire parola chiave per sbloccare"
413
413
414#: qml/Greeter/Greeter.qml:552
414#: qml/Greeter/Greeter.qml:552
415msgid "Enter passcode to unlock"
415msgid "Enter passcode to unlock"
@@ -421,7 +421,7 @@ msgstr "Emergenza"
421
421
422#: qml/Greeter/PromptList.qml:126
422#: qml/Greeter/PromptList.qml:126
423msgid "Passphrase"
423msgid "Passphrase"
424−msgstr "Passphrase"
424+msgstr "Parola chiave"
425
425
426#: qml/Greeter/PromptList.qml:126
426#: qml/Greeter/PromptList.qml:126
427msgid "Passcode"
427msgid "Passcode"
@@ -540,12 +540,12 @@ msgstr "Trascinare con 3 dita qualsiasi applicazione da una finestra all'altra"
540
540
541#: qml/Stage/Stage.qml:671
541#: qml/Stage/Stage.qml:671
542msgid "No running apps"
542msgid "No running apps"
543−msgstr "Nessun applicazione in esecuzione"
543+msgstr "Nessuna applicazione in esecuzione"
544
544
545#: qml/Tutorial/TutorialLeftLong.qml:49
545#: qml/Tutorial/TutorialLeftLong.qml:49
546msgid "Long swipe from the left edge to open the Application Drawer"
546msgid "Long swipe from the left edge to open the Application Drawer"
547msgstr ""
547msgstr ""
548−"Scorrere lentamente dal bordo sinistro per aprire il Drawer delle "
548+"Scorrere lentamente dal bordo sinistro per aprire il Cassetto delle "
549"Applicazioni"
549"Applicazioni"
550
550
551#: qml/Tutorial/TutorialLeft.qml:47
551#: qml/Tutorial/TutorialLeft.qml:47
po/ja.po+15−18
@@ -8,8 +8,8 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: \n"
9"Report-Msgid-Bugs-To: \n"
10"POT-Creation-Date: 2021-01-29 11:42-0600\n"
10"POT-Creation-Date: 2021-01-29 11:42-0600\n"
11−"PO-Revision-Date: 2021-05-09 19:51+0000\n"
11+"PO-Revision-Date: 2021-08-10 18:11+0000\n"
12−"Last-Translator: M.Sugahara <equaaqua@hotmail.com>\n"
12+"Last-Translator: Oyamada Jun <oyamada.junjun@outlook.jp>\n"
13"Language-Team: Japanese <https://translate.ubports.com/projects/ubports/"
13"Language-Team: Japanese <https://translate.ubports.com/projects/ubports/"
14"lomiri/ja/>\n"
14"lomiri/ja/>\n"
15"Language: ja\n"
15"Language: ja\n"
@@ -278,11 +278,11 @@ msgstr "Ctrl + Super + Down"
278
278
279#: qml/Components/KeyboardShortcutsOverlay.qml:323
279#: qml/Components/KeyboardShortcutsOverlay.qml:323
280msgid "Minimizes or restores the current window."
280msgid "Minimizes or restores the current window."
281−msgstr ""
281+msgstr "現在のウィンドウを最小化したり、復元したりします。"
282
282
283#: qml/Components/KeyboardShortcutsOverlay.qml:331
283#: qml/Components/KeyboardShortcutsOverlay.qml:331
284msgid "Ctrl + Super + Left or Right"
284msgid "Ctrl + Super + Left or Right"
285−msgstr ""
285+msgstr "Ctrl + Super + Left or Right"
286
286
287#: qml/Components/KeyboardShortcutsOverlay.qml:336
287#: qml/Components/KeyboardShortcutsOverlay.qml:336
288msgid "Semi-maximizes the current window."
288msgid "Semi-maximizes the current window."
@@ -342,7 +342,7 @@ msgstr ""
342
342
343#: qml/Components/ModeSwitchWarningDialog.qml:94
343#: qml/Components/ModeSwitchWarningDialog.qml:94
344msgid "Close all"
344msgid "Close all"
345−msgstr ""
345+msgstr "すべて閉じる"
346
346
347#: qml/Components/SharingPicker.qml:54
347#: qml/Components/SharingPicker.qml:54
348msgid "Preview Share Item"
348msgid "Preview Share Item"
@@ -412,7 +412,7 @@ msgstr "パスコードを入力"
412
412
413#: qml/Greeter/NarrowView.qml:235
413#: qml/Greeter/NarrowView.qml:235
414msgid "Emergency"
414msgid "Emergency"
415−msgstr ""
415+msgstr "緊急"
416
416
417#: qml/Greeter/PromptList.qml:126
417#: qml/Greeter/PromptList.qml:126
418msgid "Passphrase"
418msgid "Passphrase"
@@ -420,11 +420,11 @@ msgstr ""
420
420
421#: qml/Greeter/PromptList.qml:126
421#: qml/Greeter/PromptList.qml:126
422msgid "Passcode"
422msgid "Passcode"
423−msgstr ""
423+msgstr "パスコード"
424
424
425#: qml/Greeter/SessionsList.qml:124
425#: qml/Greeter/SessionsList.qml:124
426msgid "Select desktop environment"
426msgid "Select desktop environment"
427−msgstr ""
427+msgstr "デスクトップ環境を選択"
428
428
429#: qml/Launcher/Drawer.qml:250
429#: qml/Launcher/Drawer.qml:250
430#, fuzzy
430#, fuzzy
@@ -460,32 +460,29 @@ msgstr "再生するものがありません"
460#, qt-format
460#, qt-format
461msgid "%1 hour"
461msgid "%1 hour"
462msgid_plural "%1 hours"
462msgid_plural "%1 hours"
463−msgstr[0] ""
463+msgstr[0] "%1 時間"
464−msgstr[1] ""
465
464
466#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1001
465#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1001
467#, qt-format
466#, qt-format
468msgid "%1 minute"
467msgid "%1 minute"
469msgid_plural "%1 minutes"
468msgid_plural "%1 minutes"
470−msgstr[0] ""
469+msgstr[0] "%1 分"
471−msgstr[1] ""
472
470
473#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1006
471#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1006
474#, qt-format
472#, qt-format
475msgid "%1 second"
473msgid "%1 second"
476msgid_plural "%1 seconds"
474msgid_plural "%1 seconds"
477−msgstr[0] ""
475+msgstr[0] "%1 秒"
478−msgstr[1] ""
479
476
480#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1009
477#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1009
481msgid "0 seconds"
478msgid "0 seconds"
482−msgstr ""
479+msgstr "0 秒"
483
480
484#. Translators: String like "1 hour, 2 minutes, 3 seconds remaining"
481#. Translators: String like "1 hour, 2 minutes, 3 seconds remaining"
485#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1011
482#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1011
486#, qt-format
483#, qt-format
487msgid "%1 remaining"
484msgid "%1 remaining"
488−msgstr ""
485+msgstr "残り %1"
489
486
490#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1017
487#: qml/Panel/Indicators/IndicatorMenuItemFactory.qml:1017
491msgid "In queue…"
488msgid "In queue…"
@@ -515,12 +512,12 @@ msgstr "失敗しました、再試行するにはタップしてください"
515#: qml/Panel/Indicators/MessageMenuItemFactory.qml:210
512#: qml/Panel/Indicators/MessageMenuItemFactory.qml:210
516msgctxt "Button: Send a reply message"
513msgctxt "Button: Send a reply message"
517msgid "Send"
514msgid "Send"
518−msgstr ""
515+msgstr "送信"
519
516
520#: qml/Panel/Indicators/MessageMenuItemFactory.qml:152
517#: qml/Panel/Indicators/MessageMenuItemFactory.qml:152
521msgctxt "Label: Hint in message indicator line edit"
518msgctxt "Label: Hint in message indicator line edit"
522msgid "Reply"
519msgid "Reply"
523−msgstr ""
520+msgstr "返信"
524
521
525#: qml/Panel/Indicators/MessageMenuItemFactory.qml:209
522#: qml/Panel/Indicators/MessageMenuItemFactory.qml:209
526msgctxt "Button: Call back on phone"
523msgctxt "Button: Call back on phone"
po/km.po+11−14
@@ -8,16 +8,17 @@ msgstr ""
8"Project-Id-Version: unity8\n"
8"Project-Id-Version: unity8\n"
9"Report-Msgid-Bugs-To: \n"
9"Report-Msgid-Bugs-To: \n"
10"POT-Creation-Date: 2021-01-29 11:42-0600\n"
10"POT-Creation-Date: 2021-01-29 11:42-0600\n"
11−"PO-Revision-Date: 2014-08-20 02:48+0000\n"
11+"PO-Revision-Date: 2022-03-14 13:38+0000\n"
12−"Last-Translator: Sophea Sok <sksophea@gmail.com>\n"
12+"Last-Translator: ប៉ុកណូ រ៉ូយ៉ាល់ <poknoroyal@outlook.com>\n"
13−"Language-Team: Khmer <km@li.org>\n"
13+"Language-Team: Central Khmer <https://translate.ubports.com/projects/ubports/"
14+"lomiri/km/>\n"
14−"Language: \n"
15+"Language: km\n"
15"MIME-Version: 1.0\n"
16"MIME-Version: 1.0\n"
16"Content-Type: text/plain; charset=UTF-8\n"
17"Content-Type: text/plain; charset=UTF-8\n"
17"Content-Transfer-Encoding: 8bit\n"
18"Content-Transfer-Encoding: 8bit\n"
18"Plural-Forms: nplurals=1; plural=0;\n"
19"Plural-Forms: nplurals=1; plural=0;\n"
20+"X-Generator: Weblate 3.11.3\n"
19"X-Launchpad-Export-Date: 2016-11-23 06:35+0000\n"
21"X-Launchpad-Export-Date: 2016-11-23 06:35+0000\n"
20−"X-Generator: Launchpad (build 18269)\n"
21
22
22#: plugins/LightDM/Greeter.cpp:199
23#: plugins/LightDM/Greeter.cpp:199
23msgid "Password: "
24msgid "Password: "
@@ -25,7 +26,7 @@ msgstr "ពាក្យសម្ងាត់៖ "
25
26
26#: plugins/LightDM/Greeter.cpp:214
27#: plugins/LightDM/Greeter.cpp:214
27msgid "Username"
28msgid "Username"
28−msgstr ""
29+msgstr "អត្ថនាម"
29
30
30#: plugins/LightDM/Greeter.cpp:258
31#: plugins/LightDM/Greeter.cpp:258
31msgid "Failed to authenticate"
32msgid "Failed to authenticate"
@@ -375,7 +376,7 @@ msgstr ""
375
376
376#: qml/Greeter/CoverPage.qml:127
377#: qml/Greeter/CoverPage.qml:127
377msgid "Unlock"
378msgid "Unlock"
378−msgstr "ដោះ​សោ"
379+msgstr "ដោះសោ"
379
380
380#: qml/Greeter/DelayedLockscreen.qml:43
381#: qml/Greeter/DelayedLockscreen.qml:43
381msgid "Device Locked"
382msgid "Device Locked"
@@ -523,11 +524,8 @@ msgid "Call back"
523msgstr "ហៅ​មកវិញ"
524msgstr "ហៅ​មកវិញ"
524
525
525#: qml/Panel/PanelMenuPage.qml:92
526#: qml/Panel/PanelMenuPage.qml:92
526−#, fuzzy
527−#| msgctxt "Button: Go back one page in the Wizard"
528−#| msgid "Back"
529msgid "Back"
527msgid "Back"
530−msgstr "ថយក្រោយ"
528+msgstr "បកក្រោយ"
531
529
532#: qml/Stage/SideStage.qml:76
530#: qml/Stage/SideStage.qml:76
533msgid "Drag using 3 fingers any application from one window to the other"
531msgid "Drag using 3 fingers any application from one window to the other"
@@ -592,9 +590,8 @@ msgid "What's new"
592msgstr ""
590msgstr ""
593
591
594#: qml/Wizard/Pages/11-changelog.qml:74
592#: qml/Wizard/Pages/11-changelog.qml:74
595−#, fuzzy
596msgid "Loading..."
593msgid "Loading..."
597−msgstr "កំពុង​និយាយ..."
594+msgstr "កំពុងផ្ទុក…"
598
595
599#: qml/Wizard/Pages/20-keyboard.qml:31
596#: qml/Wizard/Pages/20-keyboard.qml:31
600msgid "Select Keyboard"
597msgid "Select Keyboard"
@@ -606,7 +603,7 @@ msgstr ""
606
603
607#: qml/Wizard/Pages/20-keyboard.qml:93
604#: qml/Wizard/Pages/20-keyboard.qml:93
608msgid "Keyboard layout"
605msgid "Keyboard layout"
609−msgstr ""
606+msgstr "ប្លង់ក្ដារចុច"
610
607
611#: qml/Wizard/Pages/20-keyboard.qml:152 qml/Wizard/Pages/30-wifi.qml:214
608#: qml/Wizard/Pages/20-keyboard.qml:152 qml/Wizard/Pages/30-wifi.qml:214
612#: qml/Wizard/Pages/60-account.qml:72 qml/Wizard/Pages/79-system-update.qml:144
609#: qml/Wizard/Pages/60-account.qml:72 qml/Wizard/Pages/79-system-update.qml:144
po/nb.po+4−6
@@ -8,8 +8,8 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: \n"
9"Report-Msgid-Bugs-To: \n"
10"POT-Creation-Date: 2021-01-29 11:42-0600\n"
10"POT-Creation-Date: 2021-01-29 11:42-0600\n"
11−"PO-Revision-Date: 2021-03-12 02:36+0000\n"
11+"PO-Revision-Date: 2021-10-15 10:11+0000\n"
12−"Last-Translator: Jan Christian Sherdahl <jan@sherdahl.no>\n"
12+"Last-Translator: Allan Nordhøy <epost@anotheragency.no>\n"
13"Language-Team: Norwegian Bokmål <https://translate.ubports.com/projects/"
13"Language-Team: Norwegian Bokmål <https://translate.ubports.com/projects/"
14"ubports/lomiri/nb/>\n"
14"ubports/lomiri/nb/>\n"
15"Language: nb\n"
15"Language: nb\n"
@@ -131,10 +131,8 @@ msgid "Restart"
131msgstr "Start på nytt"
131msgstr "Start på nytt"
132
132
133#: qml/Components/Dialogs.qml:276
133#: qml/Components/Dialogs.qml:276
134−#, fuzzy
135−#| msgid "Takes a screenshot."
136msgid "Screenshot"
134msgid "Screenshot"
137−msgstr "Tar bilde av skjermen."
135+msgstr "Skjermavbildning"
138
136
139#: qml/Components/KeyboardShortcutsOverlay.qml:41
137#: qml/Components/KeyboardShortcutsOverlay.qml:41
140msgid "Keyboard Shortcuts"
138msgid "Keyboard Shortcuts"
@@ -142,7 +140,7 @@ msgstr "Tastatursnarveier"
142
140
143#: qml/Components/KeyboardShortcutsOverlay.qml:55
141#: qml/Components/KeyboardShortcutsOverlay.qml:55
144msgid "Lomiri"
142msgid "Lomiri"
145−msgstr ""
143+msgstr "Lomiri"
146
144
147#: qml/Components/KeyboardShortcutsOverlay.qml:62
145#: qml/Components/KeyboardShortcutsOverlay.qml:62
148msgid "PrtScr"
146msgid "PrtScr"
po/nl.po+3−3
@@ -8,7 +8,7 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: \n"
9"Report-Msgid-Bugs-To: \n"
10"POT-Creation-Date: 2021-01-29 11:42-0600\n"
10"POT-Creation-Date: 2021-01-29 11:42-0600\n"
11−"PO-Revision-Date: 2021-05-29 18:40+0000\n"
11+"PO-Revision-Date: 2021-09-21 19:30+0000\n"
12"Last-Translator: Heimen Stoffels <vistausss@fastmail.com>\n"
12"Last-Translator: Heimen Stoffels <vistausss@fastmail.com>\n"
13"Language-Team: Dutch <https://translate.ubports.com/projects/ubports/lomiri/"
13"Language-Team: Dutch <https://translate.ubports.com/projects/ubports/lomiri/"
14"nl/>\n"
14"nl/>\n"
@@ -594,7 +594,7 @@ msgstr "Wijzigingslog"
594
594
595#: qml/Wizard/Pages/11-changelog.qml:74
595#: qml/Wizard/Pages/11-changelog.qml:74
596msgid "Loading..."
596msgid "Loading..."
597−msgstr "Bezig met laden..."
597+msgstr "Bezig met laden…"
598
598
599#: qml/Wizard/Pages/20-keyboard.qml:31
599#: qml/Wizard/Pages/20-keyboard.qml:31
600msgid "Select Keyboard"
600msgid "Select Keyboard"
@@ -797,7 +797,7 @@ msgstr "Zonder simkaart kun je niet bellen of sms'en."
797
797
798#: qml/Wizard/Pages/sim.qml:90
798#: qml/Wizard/Pages/sim.qml:90
799msgid "To proceed with no SIM tap Skip."
799msgid "To proceed with no SIM tap Skip."
800−msgstr "Druk op 'Overslaan' om door te gaan zonder simkaart."
800+msgstr "Druk op ‘Overslaan’ om door te gaan zonder simkaart."
801
801
802#: qml/Wizard/PasswordMeter.qml:87
802#: qml/Wizard/PasswordMeter.qml:87
803msgid "Password too short"
803msgid "Password too short"
po/pt.po+4−4
@@ -8,8 +8,8 @@ msgstr ""
8"Project-Id-Version: unity8\n"
8"Project-Id-Version: unity8\n"
9"Report-Msgid-Bugs-To: \n"
9"Report-Msgid-Bugs-To: \n"
10"POT-Creation-Date: 2021-01-29 11:42-0600\n"
10"POT-Creation-Date: 2021-01-29 11:42-0600\n"
11−"PO-Revision-Date: 2021-06-01 14:51+0000\n"
11+"PO-Revision-Date: 2021-11-19 22:39+0000\n"
12−"Last-Translator: Ivo Xavier <ivofernandes12@gmail.com>\n"
12+"Last-Translator: Hugo Carvalho <hugokarvalho@hotmail.com>\n"
13"Language-Team: Portuguese <https://translate.ubports.com/projects/ubports/"
13"Language-Team: Portuguese <https://translate.ubports.com/projects/ubports/"
14"lomiri/pt/>\n"
14"lomiri/pt/>\n"
15"Language: pt\n"
15"Language: pt\n"
@@ -531,7 +531,7 @@ msgstr "Ligar de volta"
531
531
532#: qml/Panel/PanelMenuPage.qml:92
532#: qml/Panel/PanelMenuPage.qml:92
533msgid "Back"
533msgid "Back"
534−msgstr "Retroceder"
534+msgstr "Voltar"
535
535
536#: qml/Stage/SideStage.qml:76
536#: qml/Stage/SideStage.qml:76
537msgid "Drag using 3 fingers any application from one window to the other"
537msgid "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"
712
712
713#: qml/Wizard/Pages/80-finished.qml:126
713#: qml/Wizard/Pages/80-finished.qml:126
714msgid "Continue"
714msgid "Continue"
715−msgstr "Avançar"
715+msgstr "Continuar"
716
716
717#: qml/Wizard/Pages/80-finished.qml:126
717#: qml/Wizard/Pages/80-finished.qml:126
718msgid "Get Started"
718msgid "Get Started"
po/ru.po+4−4
@@ -8,8 +8,8 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: \n"
9"Report-Msgid-Bugs-To: \n"
10"POT-Creation-Date: 2021-01-29 11:42-0600\n"
10"POT-Creation-Date: 2021-01-29 11:42-0600\n"
11−"PO-Revision-Date: 2021-03-26 09:58+0000\n"
11+"PO-Revision-Date: 2021-07-01 18:37+0000\n"
12−"Last-Translator: vokaliz <vokaliz@protonmail.com>\n"
12+"Last-Translator: Герман Семенов <GermanAizek@yandex.ru>\n"
13"Language-Team: Russian <https://translate.ubports.com/projects/ubports/"
13"Language-Team: Russian <https://translate.ubports.com/projects/ubports/"
14"lomiri/ru/>\n"
14"lomiri/ru/>\n"
15"Language: ru\n"
15"Language: ru\n"
@@ -131,7 +131,7 @@ msgstr "Перезагрузить"
131
131
132#: qml/Components/Dialogs.qml:276
132#: qml/Components/Dialogs.qml:276
133msgid "Screenshot"
133msgid "Screenshot"
134−msgstr "Снимок экрана."
134+msgstr "Снимок экрана"
135
135
136#: qml/Components/KeyboardShortcutsOverlay.qml:41
136#: qml/Components/KeyboardShortcutsOverlay.qml:41
137msgid "Keyboard Shortcuts"
137msgid "Keyboard Shortcuts"
@@ -307,7 +307,7 @@ msgstr "Экстренный вызов"
307
307
308#: qml/Components/Lockscreen.qml:245
308#: qml/Components/Lockscreen.qml:245
309msgid "OK"
309msgid "OK"
310−msgstr "Да"
310+msgstr "Хорошо"
311
311
312#: qml/Components/MediaServices/VideoPlayerControls.qml:44
312#: qml/Components/MediaServices/VideoPlayerControls.qml:44
313#, qt-format
313#, qt-format
po/sc.po+6−6
@@ -8,7 +8,7 @@ msgstr ""
8"Project-Id-Version: unity8\n"
8"Project-Id-Version: unity8\n"
9"Report-Msgid-Bugs-To: \n"
9"Report-Msgid-Bugs-To: \n"
10"POT-Creation-Date: 2021-01-29 11:42-0600\n"
10"POT-Creation-Date: 2021-01-29 11:42-0600\n"
11−"PO-Revision-Date: 2021-04-22 10:44+0000\n"
11+"PO-Revision-Date: 2021-11-21 15:09+0000\n"
12"Last-Translator: Adria <adriamartinmor@gmail.com>\n"
12"Last-Translator: Adria <adriamartinmor@gmail.com>\n"
13"Language-Team: Sardinian <https://translate.ubports.com/projects/ubports/"
13"Language-Team: Sardinian <https://translate.ubports.com/projects/ubports/"
14"lomiri/sc/>\n"
14"lomiri/sc/>\n"
@@ -539,7 +539,7 @@ msgstr "Traga cun 3 ditos is aplicatziones dae una ventana a s'àtera"
539
539
540#: qml/Stage/Stage.qml:671
540#: qml/Stage/Stage.qml:671
541msgid "No running apps"
541msgid "No running apps"
542−msgstr "Peruna aplicatzione ativa"
542+msgstr "Nissuna aplicatzione ativa"
543
543
544#: qml/Tutorial/TutorialLeftLong.qml:49
544#: qml/Tutorial/TutorialLeftLong.qml:49
545msgid "Long swipe from the left edge to open the Application Drawer"
545msgid "Long swipe from the left edge to open the Application Drawer"
@@ -634,7 +634,7 @@ msgstr "Retes Wi-Fi a disponimentu"
634
634
635#: qml/Wizard/Pages/30-wifi.qml:173
635#: qml/Wizard/Pages/30-wifi.qml:173
636msgid "No available Wi-Fi networks"
636msgid "No available Wi-Fi networks"
637−msgstr "Peruna rete Wi-Fi a disponimentu"
637+msgstr "Nissuna rete Wi-Fi a disponimentu"
638
638
639#: qml/Wizard/Pages/50-timezone.qml:30
639#: qml/Wizard/Pages/50-timezone.qml:30
640msgid "Time Zone"
640msgid "Time Zone"
@@ -669,7 +669,7 @@ msgstr "Crea unu còdighe de atzessu (nùmeros isceti)"
669#: qml/Wizard/Pages/70-passwd-type.qml:107
669#: qml/Wizard/Pages/70-passwd-type.qml:107
670msgctxt "Label: Type of security method"
670msgctxt "Label: Type of security method"
671msgid "No lock code"
671msgid "No lock code"
672−msgstr "Perunu còdighe de blocu"
672+msgstr "Nissunu còdighe de blocu"
673
673
674#: qml/Wizard/Pages/79-system-update.qml:28
674#: qml/Wizard/Pages/79-system-update.qml:28
675msgid "Update Device"
675msgid "Update Device"
@@ -696,7 +696,7 @@ msgstr "Versione %1"
696
696
697#: qml/Wizard/Pages/79-system-update.qml:95
697#: qml/Wizard/Pages/79-system-update.qml:95
698msgid "This could take a few minutes..."
698msgid "This could take a few minutes..."
699−msgstr "B'at a pònnere carchi minutu..."
699+msgstr "Podet trigare calicunu minutu..."
700
700
701#: qml/Wizard/Pages/79-system-update.qml:109
701#: qml/Wizard/Pages/79-system-update.qml:109
702msgid "Install and restart now"
702msgid "Install and restart now"
@@ -783,7 +783,7 @@ msgstr "Cunfirma sa crae"
783
783
784#: qml/Wizard/Pages/sim.qml:27
784#: qml/Wizard/Pages/sim.qml:27
785msgid "No SIM card installed"
785msgid "No SIM card installed"
786−msgstr "Peruna SIM installada"
786+msgstr "Nissuna SIM installada"
787
787
788#: qml/Wizard/Pages/sim.qml:54
788#: qml/Wizard/Pages/sim.qml:54
789msgid "SIM card added"
789msgid "SIM card added"
po/sv.po+30−60
@@ -8,16 +8,16 @@ msgstr ""
8"Project-Id-Version: unity\n"
8"Project-Id-Version: unity\n"
9"Report-Msgid-Bugs-To: \n"
9"Report-Msgid-Bugs-To: \n"
10"POT-Creation-Date: 2021-01-29 11:42-0600\n"
10"POT-Creation-Date: 2021-01-29 11:42-0600\n"
11−"PO-Revision-Date: 2020-02-13 13:06+0000\n"
11+"PO-Revision-Date: 2021-10-19 20:55+0000\n"
12−"Last-Translator: Rondy Andersson <rondarius@vivaldi.net>\n"
12+"Last-Translator: Jakob Fridesjö <jakob.fridesjo@tuta.io>\n"
13−"Language-Team: Swedish <https://translate.ubports.com/projects/unity8/unity8/"
13+"Language-Team: Swedish <https://translate.ubports.com/projects/ubports/"
14−"sv/>\n"
14+"lomiri/sv/>\n"
15"Language: sv\n"
15"Language: sv\n"
16"MIME-Version: 1.0\n"
16"MIME-Version: 1.0\n"
17"Content-Type: text/plain; charset=UTF-8\n"
17"Content-Type: text/plain; charset=UTF-8\n"
18"Content-Transfer-Encoding: 8bit\n"
18"Content-Transfer-Encoding: 8bit\n"
19"Plural-Forms: nplurals=2; plural=n != 1;\n"
19"Plural-Forms: nplurals=2; plural=n != 1;\n"
20−"X-Generator: Weblate 3.8\n"
20+"X-Generator: Weblate 3.11.3\n"
21"X-Launchpad-Export-Date: 2016-11-23 06:36+0000\n"
21"X-Launchpad-Export-Date: 2016-11-23 06:36+0000\n"
22
22
23#: plugins/LightDM/Greeter.cpp:199
23#: plugins/LightDM/Greeter.cpp:199
@@ -26,15 +26,15 @@ msgstr "Lösenord: "
26
26
27#: plugins/LightDM/Greeter.cpp:214
27#: plugins/LightDM/Greeter.cpp:214
28msgid "Username"
28msgid "Username"
29−msgstr ""
29+msgstr "Användarnamn"
30
30
31#: plugins/LightDM/Greeter.cpp:258
31#: plugins/LightDM/Greeter.cpp:258
32msgid "Failed to authenticate"
32msgid "Failed to authenticate"
33−msgstr ""
33+msgstr "Misslyckades att autentisera"
34
34
35#: plugins/LightDM/Greeter.cpp:260
35#: plugins/LightDM/Greeter.cpp:260
36msgid "Invalid password, please try again"
36msgid "Invalid password, please try again"
37−msgstr ""
37+msgstr "Felaktigt lösenord, vänligen försök igen"
38
38
39#: plugins/LightDM/Greeter.cpp:270
39#: plugins/LightDM/Greeter.cpp:270
40msgid "Log In"
40msgid "Log In"
@@ -46,11 +46,11 @@ msgstr "Försök igen"
46
46
47#: plugins/LightDM/UsersModel.cpp:157
47#: plugins/LightDM/UsersModel.cpp:157
48msgid "Login"
48msgid "Login"
49−msgstr ""
49+msgstr "Inloggning"
50
50
51#: plugins/LightDM/UsersModel.cpp:165
51#: plugins/LightDM/UsersModel.cpp:165
52msgid "Guest Session"
52msgid "Guest Session"
53−msgstr ""
53+msgstr "Gästsession"
54
54
55#: plugins/Unity/Launcher/launcheritem.cpp:48
55#: plugins/Unity/Launcher/launcheritem.cpp:48
56#: plugins/Unity/Launcher/launcheritem.cpp:122
56#: plugins/Unity/Launcher/launcheritem.cpp:122
@@ -131,10 +131,8 @@ msgid "Restart"
131msgstr "Starta om"
131msgstr "Starta om"
132
132
133#: qml/Components/Dialogs.qml:276
133#: qml/Components/Dialogs.qml:276
134−#, fuzzy
135−#| msgid "Takes a screenshot."
136msgid "Screenshot"
134msgid "Screenshot"
137−msgstr "Tar en skärmbild."
135+msgstr "Skärmdump"
138
136
139#: qml/Components/KeyboardShortcutsOverlay.qml:41
137#: qml/Components/KeyboardShortcutsOverlay.qml:41
140msgid "Keyboard Shortcuts"
138msgid "Keyboard Shortcuts"
@@ -142,7 +140,7 @@ msgstr "Tangentbordsgenvägar"
142
140
143#: qml/Components/KeyboardShortcutsOverlay.qml:55
141#: qml/Components/KeyboardShortcutsOverlay.qml:55
144msgid "Lomiri"
142msgid "Lomiri"
145−msgstr ""
143+msgstr "Lomiri"
146
144
147#: qml/Components/KeyboardShortcutsOverlay.qml:62
145#: qml/Components/KeyboardShortcutsOverlay.qml:62
148msgid "PrtScr"
146msgid "PrtScr"
@@ -157,10 +155,8 @@ msgid "Alt + PrtScr"
157msgstr "Alt + PrtScr"
155msgstr "Alt + PrtScr"
158
156
159#: qml/Components/KeyboardShortcutsOverlay.qml:80
157#: qml/Components/KeyboardShortcutsOverlay.qml:80
160−#, fuzzy
161−#| msgid "Takes a screenshot of a window."
162msgid "Takes a screenshot of the current window."
158msgid "Takes a screenshot of the current window."
163−msgstr "Tar en skärmbild av ett fönster."
159+msgstr "Tar en skärmdump av det nuvarande fönstret."
164
160
165#: qml/Components/KeyboardShortcutsOverlay.qml:88
161#: qml/Components/KeyboardShortcutsOverlay.qml:88
166msgid "Super + Space"
162msgid "Super + Space"
@@ -179,16 +175,12 @@ msgid "Switches to previous keyboard layout."
179msgstr "Byter till tidigare tangentbordslayout."
175msgstr "Byter till tidigare tangentbordslayout."
180
176
181#: qml/Components/KeyboardShortcutsOverlay.qml:114
177#: qml/Components/KeyboardShortcutsOverlay.qml:114
182−#, fuzzy
183−#| msgid "Ctrl + Super + D"
184msgid "Ctrl + Alt + T"
178msgid "Ctrl + Alt + T"
185−msgstr "Ctrl + Super + D"
179+msgstr "Ctrl + Alt + T"
186
180
187#: qml/Components/KeyboardShortcutsOverlay.qml:119
181#: qml/Components/KeyboardShortcutsOverlay.qml:119
188−#, fuzzy
189−#| msgid "Switches between applications."
190msgid "Starts the Terminal application."
182msgid "Starts the Terminal application."
191−msgstr "Växlar mellan program."
183+msgstr "Startar terminalen."
192
184
193#: qml/Components/KeyboardShortcutsOverlay.qml:131
185#: qml/Components/KeyboardShortcutsOverlay.qml:131
194msgid "Launcher"
186msgid "Launcher"
@@ -219,26 +211,20 @@ msgid "Switches applications via the launcher."
219msgstr "Byter program via Programstartaren."
211msgstr "Byter program via Programstartaren."
220
212
221#: qml/Components/KeyboardShortcutsOverlay.qml:177
213#: qml/Components/KeyboardShortcutsOverlay.qml:177
222−#, fuzzy
223−#| msgid "Super + 0 to 9"
224msgid "Super + 1 to 0"
214msgid "Super + 1 to 0"
225−msgstr "Super + 0 till 9"
215+msgstr "Super + 1 till 0"
226
216
227#: qml/Components/KeyboardShortcutsOverlay.qml:182
217#: qml/Components/KeyboardShortcutsOverlay.qml:182
228msgid "Same as clicking on a launcher icon."
218msgid "Same as clicking on a launcher icon."
229msgstr "Samma som att klicka på en ikon i Programstartaren."
219msgstr "Samma som att klicka på en ikon i Programstartaren."
230
220
231#: qml/Components/KeyboardShortcutsOverlay.qml:190
221#: qml/Components/KeyboardShortcutsOverlay.qml:190
232−#, fuzzy
233−#| msgid "Super + W"
234msgid "Super + A"
222msgid "Super + A"
235−msgstr "Super + W"
223+msgstr "Super + A"
236
224
237#: qml/Components/KeyboardShortcutsOverlay.qml:195
225#: qml/Components/KeyboardShortcutsOverlay.qml:195
238−#, fuzzy
239−#| msgid "Open the launcher"
240msgid "Opens the Application Drawer."
226msgid "Opens the Application Drawer."
241−msgstr "Öppna programstartaren"
227+msgstr "Öppnar programstartaren."
242
228
243#: qml/Components/KeyboardShortcutsOverlay.qml:235
229#: qml/Components/KeyboardShortcutsOverlay.qml:235
244msgid "Switching"
230msgid "Switching"
@@ -374,22 +360,20 @@ msgstr ""
374"styrplatta för att interagera med pekaren."
360"styrplatta för att interagera med pekaren."
375
361
376#: qml/Components/VirtualTouchPad.qml:327
362#: qml/Components/VirtualTouchPad.qml:327
377−#, fuzzy
378−#| msgid "Tap to return to call..."
379msgid "Tap left button to click."
363msgid "Tap left button to click."
380−msgstr "Tryck för att återgå till samtal…"
364+msgstr "Tryck på vänster knapp för att klicka."
381
365
382#: qml/Components/VirtualTouchPad.qml:339
366#: qml/Components/VirtualTouchPad.qml:339
383msgid "Tap right button to right click."
367msgid "Tap right button to right click."
384−msgstr ""
368+msgstr "Tryck på höger knapp för att högerklicka."
385
369
386#: qml/Components/VirtualTouchPad.qml:351
370#: qml/Components/VirtualTouchPad.qml:351
387msgid "Swipe with two fingers to scroll."
371msgid "Swipe with two fingers to scroll."
388−msgstr ""
372+msgstr "Svep med två fingrar för att scrolla."
389
373
390#: qml/Components/VirtualTouchPad.qml:396
374#: qml/Components/VirtualTouchPad.qml:396
391msgid "Find more settings in the system settings."
375msgid "Find more settings in the system settings."
392−msgstr ""
376+msgstr "Hitta fler inställningar i systeminställningarna."
393
377
394#: qml/Greeter/CoverPage.qml:127
378#: qml/Greeter/CoverPage.qml:127
395msgid "Unlock"
379msgid "Unlock"
@@ -419,16 +403,12 @@ msgid "Try again"
419msgstr "Försök igen"
403msgstr "Försök igen"
420
404
421#: qml/Greeter/Greeter.qml:551
405#: qml/Greeter/Greeter.qml:551
422−#, fuzzy
423−#| msgid "Enter passphrase"
424msgid "Enter passphrase to unlock"
406msgid "Enter passphrase to unlock"
425−msgstr "Ange lösenfras"
407+msgstr "Ange lösefras för att låsa upp"
426
408
427#: qml/Greeter/Greeter.qml:552
409#: qml/Greeter/Greeter.qml:552
428−#, fuzzy
429−#| msgid "Enter passcode"
430msgid "Enter passcode to unlock"
410msgid "Enter passcode to unlock"
431−msgstr "Ange lösenkod"
411+msgstr "Ange lösenkod för att låsa upp"
432
412
433#: qml/Greeter/NarrowView.qml:235
413#: qml/Greeter/NarrowView.qml:235
434msgid "Emergency"
414msgid "Emergency"
@@ -447,10 +427,8 @@ msgid "Select desktop environment"
447msgstr "Välj skrivbordsmiljö"
427msgstr "Välj skrivbordsmiljö"
448
428
449#: qml/Launcher/Drawer.qml:250
429#: qml/Launcher/Drawer.qml:250
450−#, fuzzy
451−#| msgid "Search"
452msgid "Search…"
430msgid "Search…"
453−msgstr "Sök"
431+msgstr "Sök…"
454
432
455#: qml/Launcher/PullToRefreshScopeStyle.qml:56
433#: qml/Launcher/PullToRefreshScopeStyle.qml:56
456msgid "Pull to refresh…"
434msgid "Pull to refresh…"
@@ -548,11 +526,8 @@ msgid "Call back"
548msgstr "Ring upp"
526msgstr "Ring upp"
549
527
550#: qml/Panel/PanelMenuPage.qml:92
528#: qml/Panel/PanelMenuPage.qml:92
551−#, fuzzy
552−#| msgctxt "Button: Go back one page in the Wizard"
553−#| msgid "Back"
554msgid "Back"
529msgid "Back"
555−msgstr "Bakåt"
530+msgstr "Backa"
556
531
557#: qml/Stage/SideStage.qml:76
532#: qml/Stage/SideStage.qml:76
558msgid "Drag using 3 fingers any application from one window to the other"
533msgid "Drag using 3 fingers any application from one window to the other"
@@ -560,13 +535,11 @@ msgstr "Dra ett program från ett fönster till det andra med tre fingrar"
560
535
561#: qml/Stage/Stage.qml:671
536#: qml/Stage/Stage.qml:671
562msgid "No running apps"
537msgid "No running apps"
563−msgstr ""
538+msgstr "Inga appar körs"
564
539
565#: qml/Tutorial/TutorialLeftLong.qml:49
540#: qml/Tutorial/TutorialLeftLong.qml:49
566−#, fuzzy
567−#| msgid "Long swipe from the left edge to open the Today scope"
568msgid "Long swipe from the left edge to open the Application Drawer"
541msgid "Long swipe from the left edge to open the Application Drawer"
569−msgstr "Gör ett lång svep från vänstra kanten för öppna vyn Idag"
542+msgstr "Gör ett långt svep från vänstra kanten för öppna applikationsstartaren"
570
543
571#: qml/Tutorial/TutorialLeft.qml:47
544#: qml/Tutorial/TutorialLeft.qml:47
572msgid "Short swipe from the left edge to open the launcher"
545msgid "Short swipe from the left edge to open the launcher"
@@ -618,7 +591,7 @@ msgstr "Vad är nytt"
618
591
619#: qml/Wizard/Pages/11-changelog.qml:74
592#: qml/Wizard/Pages/11-changelog.qml:74
620msgid "Loading..."
593msgid "Loading..."
621−msgstr "Laddar…"
594+msgstr "Läser in…"
622
595
623#: qml/Wizard/Pages/20-keyboard.qml:31
596#: qml/Wizard/Pages/20-keyboard.qml:31
624msgid "Select Keyboard"
597msgid "Select Keyboard"
@@ -702,12 +675,9 @@ msgstr ""
702"kommer enheten att startas om automatiskt."
675"kommer enheten att startas om automatiskt."
703
676
704#: qml/Wizard/Pages/79-system-update.qml:76
677#: qml/Wizard/Pages/79-system-update.qml:76
705−#, fuzzy
706−#| msgctxt "string identifying name of the update"
707−#| msgid "Ubuntu system"
708msgctxt "string identifying name of the update"
678msgctxt "string identifying name of the update"
709msgid "Ubuntu Touch system"
679msgid "Ubuntu Touch system"
710−msgstr "Ubuntusystem"
680+msgstr "Ubuntu Touch system"
711
681
712#: qml/Wizard/Pages/79-system-update.qml:83
682#: qml/Wizard/Pages/79-system-update.qml:83
713#, qt-format
683#, qt-format
po/th.po+20−20
@@ -8,7 +8,7 @@ msgstr ""
8"Project-Id-Version: unity8\n"
8"Project-Id-Version: unity8\n"
9"Report-Msgid-Bugs-To: \n"
9"Report-Msgid-Bugs-To: \n"
10"POT-Creation-Date: 2021-01-29 11:42-0600\n"
10"POT-Creation-Date: 2021-01-29 11:42-0600\n"
11−"PO-Revision-Date: 2021-05-23 05:10+0000\n"
11+"PO-Revision-Date: 2021-10-14 16:02+0000\n"
12"Last-Translator: Wannaphong Phatthiyaphaibun <wannaphong@yahoo.com>\n"
12"Last-Translator: Wannaphong Phatthiyaphaibun <wannaphong@yahoo.com>\n"
13"Language-Team: Thai <https://translate.ubports.com/projects/ubports/lomiri/"
13"Language-Team: Thai <https://translate.ubports.com/projects/ubports/lomiri/"
14"th/>\n"
14"th/>\n"
@@ -22,31 +22,31 @@ msgstr ""
22
22
23#: plugins/LightDM/Greeter.cpp:199
23#: plugins/LightDM/Greeter.cpp:199
24msgid "Password: "
24msgid "Password: "
25−msgstr ""
25+msgstr "รหัสผ่าน: "
26
26
27#: plugins/LightDM/Greeter.cpp:214
27#: plugins/LightDM/Greeter.cpp:214
28msgid "Username"
28msgid "Username"
29−msgstr ""
29+msgstr "ชื่อผู้ใช้"
30
30
31#: plugins/LightDM/Greeter.cpp:258
31#: plugins/LightDM/Greeter.cpp:258
32msgid "Failed to authenticate"
32msgid "Failed to authenticate"
33−msgstr ""
33+msgstr "ไม่สามารถตรวจสอบสิทธิ์ได้"
34
34
35#: plugins/LightDM/Greeter.cpp:260
35#: plugins/LightDM/Greeter.cpp:260
36msgid "Invalid password, please try again"
36msgid "Invalid password, please try again"
37−msgstr ""
37+msgstr "รหัสผ่านไม่ถูกต้อง กรุณาลองอีกครั้ง"
38
38
39#: plugins/LightDM/Greeter.cpp:270
39#: plugins/LightDM/Greeter.cpp:270
40msgid "Log In"
40msgid "Log In"
41−msgstr ""
41+msgstr "เข้าสู่ระบบ"
42
42
43#: plugins/LightDM/Greeter.cpp:270
43#: plugins/LightDM/Greeter.cpp:270
44msgid "Retry"
44msgid "Retry"
45−msgstr ""
45+msgstr "ลองอีกครั้ง"
46
46
47#: plugins/LightDM/UsersModel.cpp:157
47#: plugins/LightDM/UsersModel.cpp:157
48msgid "Login"
48msgid "Login"
49−msgstr ""
49+msgstr "เข้าสู่ระบบ"
50
50
51#: plugins/LightDM/UsersModel.cpp:165
51#: plugins/LightDM/UsersModel.cpp:165
52msgid "Guest Session"
52msgid "Guest Session"
@@ -68,7 +68,7 @@ msgstr ""
68#: qml/Components/Dialogs.qml:176
68#: qml/Components/Dialogs.qml:176
69msgctxt "Title: Lock/Log out dialog"
69msgctxt "Title: Lock/Log out dialog"
70msgid "Log out"
70msgid "Log out"
71−msgstr ""
71+msgstr "ลงชื่อออก"
72
72
73#: qml/Components/Dialogs.qml:177
73#: qml/Components/Dialogs.qml:177
74msgid "Are you sure you want to log out?"
74msgid "Are you sure you want to log out?"
@@ -77,7 +77,7 @@ msgstr ""
77#: qml/Components/Dialogs.qml:180
77#: qml/Components/Dialogs.qml:180
78msgctxt "Button: Lock the system"
78msgctxt "Button: Lock the system"
79msgid "Lock"
79msgid "Lock"
80−msgstr ""
80+msgstr "ล็อก"
81
81
82#: qml/Components/Dialogs.qml:191
82#: qml/Components/Dialogs.qml:191
83msgctxt "Button: Log out from the system"
83msgctxt "Button: Log out from the system"
@@ -101,16 +101,16 @@ msgstr ""
101
101
102#: qml/Components/Dialogs.qml:215
102#: qml/Components/Dialogs.qml:215
103msgid "No"
103msgid "No"
104−msgstr ""
104+msgstr "ไม่"
105
105
106#: qml/Components/Dialogs.qml:223
106#: qml/Components/Dialogs.qml:223
107msgid "Yes"
107msgid "Yes"
108−msgstr ""
108+msgstr "ใช่"
109
109
110#: qml/Components/Dialogs.qml:243
110#: qml/Components/Dialogs.qml:243
111msgctxt "Title: Power off/Restart dialog"
111msgctxt "Title: Power off/Restart dialog"
112msgid "Power"
112msgid "Power"
113−msgstr ""
113+msgstr "พลังงาน"
114
114
115#: qml/Components/Dialogs.qml:244
115#: qml/Components/Dialogs.qml:244
116msgid ""
116msgid ""
@@ -121,7 +121,7 @@ msgstr ""
121#: qml/Components/Dialogs.qml:248
121#: qml/Components/Dialogs.qml:248
122msgctxt "Button: Power off the system"
122msgctxt "Button: Power off the system"
123msgid "Power off"
123msgid "Power off"
124−msgstr ""
124+msgstr "ปิดเครื่อง"
125
125
126#: qml/Components/Dialogs.qml:263
126#: qml/Components/Dialogs.qml:263
127msgctxt "Button: Restart the system"
127msgctxt "Button: Restart the system"
@@ -138,7 +138,7 @@ msgstr ""
138
138
139#: qml/Components/KeyboardShortcutsOverlay.qml:55
139#: qml/Components/KeyboardShortcutsOverlay.qml:55
140msgid "Lomiri"
140msgid "Lomiri"
141−msgstr ""
141+msgstr "Lomiri"
142
142
143#: qml/Components/KeyboardShortcutsOverlay.qml:62
143#: qml/Components/KeyboardShortcutsOverlay.qml:62
144msgid "PrtScr"
144msgid "PrtScr"
@@ -395,7 +395,7 @@ msgstr[1] ""
395
395
396#: qml/Greeter/Greeter.qml:550
396#: qml/Greeter/Greeter.qml:550
397msgid "Try again"
397msgid "Try again"
398−msgstr ""
398+msgstr "ลองอีกครั้ง"
399
399
400#: qml/Greeter/Greeter.qml:551
400#: qml/Greeter/Greeter.qml:551
401msgid "Enter passphrase to unlock"
401msgid "Enter passphrase to unlock"
@@ -407,7 +407,7 @@ msgstr ""
407
407
408#: qml/Greeter/NarrowView.qml:235
408#: qml/Greeter/NarrowView.qml:235
409msgid "Emergency"
409msgid "Emergency"
410−msgstr ""
410+msgstr "เหตุฉุกเฉิน"
411
411
412#: qml/Greeter/PromptList.qml:126
412#: qml/Greeter/PromptList.qml:126
413msgid "Passphrase"
413msgid "Passphrase"
@@ -423,7 +423,7 @@ msgstr ""
423
423
424#: qml/Launcher/Drawer.qml:250
424#: qml/Launcher/Drawer.qml:250
425msgid "Search…"
425msgid "Search…"
426−msgstr ""
426+msgstr "ค้นหา…"
427
427
428#: qml/Launcher/PullToRefreshScopeStyle.qml:56
428#: qml/Launcher/PullToRefreshScopeStyle.qml:56
429msgid "Pull to refresh…"
429msgid "Pull to refresh…"
@@ -435,7 +435,7 @@ msgstr ""
435
435
436#: qml/Notifications/NotificationMenuItemFactory.qml:124
436#: qml/Notifications/NotificationMenuItemFactory.qml:124
437msgid "Show password"
437msgid "Show password"
438−msgstr ""
438+msgstr "แสดงรหัสผ่าน"
439
439
440#: qml/Panel/ActiveCallHint.qml:79
440#: qml/Panel/ActiveCallHint.qml:79
441msgid "Tap to return to call..."
441msgid "Tap to return to call..."
@@ -699,7 +699,7 @@ msgstr ""
699
699
700#: qml/Wizard/Pages/80-finished.qml:126
700#: qml/Wizard/Pages/80-finished.qml:126
701msgid "Continue"
701msgid "Continue"
702−msgstr ""
702+msgstr "ดำเนินการต่อ"
703
703
704#: qml/Wizard/Pages/80-finished.qml:126
704#: qml/Wizard/Pages/80-finished.qml:126
705msgid "Get Started"
705msgid "Get Started"
po/ur.po+31−28
@@ -8,99 +8,100 @@ msgstr ""
8"Project-Id-Version: unity8\n"
8"Project-Id-Version: unity8\n"
9"Report-Msgid-Bugs-To: \n"
9"Report-Msgid-Bugs-To: \n"
10"POT-Creation-Date: 2021-01-29 11:42-0600\n"
10"POT-Creation-Date: 2021-01-29 11:42-0600\n"
11−"PO-Revision-Date: 2015-05-05 03:12+0000\n"
11+"PO-Revision-Date: 2022-02-11 14:14+0000\n"
12−"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
12+"Last-Translator: Muhammad <alimuhammad42620@gmail.com>\n"
13−"Language-Team: Urdu <ur@li.org>\n"
13+"Language-Team: Urdu <https://translate.ubports.com/projects/ubports/lomiri/"
14+"ur/>\n"
14"Language: ur\n"
15"Language: ur\n"
15"MIME-Version: 1.0\n"
16"MIME-Version: 1.0\n"
16"Content-Type: text/plain; charset=UTF-8\n"
17"Content-Type: text/plain; charset=UTF-8\n"
17"Content-Transfer-Encoding: 8bit\n"
18"Content-Transfer-Encoding: 8bit\n"
18"Plural-Forms: nplurals=2; plural=n != 1;\n"
19"Plural-Forms: nplurals=2; plural=n != 1;\n"
20+"X-Generator: Weblate 3.11.3\n"
19"X-Launchpad-Export-Date: 2016-11-23 06:36+0000\n"
21"X-Launchpad-Export-Date: 2016-11-23 06:36+0000\n"
20−"X-Generator: Launchpad (build 18269)\n"
21
22
22#: plugins/LightDM/Greeter.cpp:199
23#: plugins/LightDM/Greeter.cpp:199
23msgid "Password: "
24msgid "Password: "
24−msgstr ""
25+msgstr "پاسورڈ "
25
26
26#: plugins/LightDM/Greeter.cpp:214
27#: plugins/LightDM/Greeter.cpp:214
27msgid "Username"
28msgid "Username"
28−msgstr ""
29+msgstr "یوزر نیم"
29
30
30#: plugins/LightDM/Greeter.cpp:258
31#: plugins/LightDM/Greeter.cpp:258
31msgid "Failed to authenticate"
32msgid "Failed to authenticate"
32−msgstr ""
33+msgstr "تصدیق میں ناکامی"
33
34
34#: plugins/LightDM/Greeter.cpp:260
35#: plugins/LightDM/Greeter.cpp:260
35msgid "Invalid password, please try again"
36msgid "Invalid password, please try again"
36−msgstr ""
37+msgstr "پاسورڈ غلط ھے، دوبارا کوشش کریں"
37
38
38#: plugins/LightDM/Greeter.cpp:270
39#: plugins/LightDM/Greeter.cpp:270
39msgid "Log In"
40msgid "Log In"
40−msgstr ""
41+msgstr "لاگ ان کریں"
41
42
42#: plugins/LightDM/Greeter.cpp:270
43#: plugins/LightDM/Greeter.cpp:270
43msgid "Retry"
44msgid "Retry"
44−msgstr ""
45+msgstr "دوبارا کوشش کریں"
45
46
46#: plugins/LightDM/UsersModel.cpp:157
47#: plugins/LightDM/UsersModel.cpp:157
47msgid "Login"
48msgid "Login"
48−msgstr ""
49+msgstr "لاگ ان کریں"
49
50
50#: plugins/LightDM/UsersModel.cpp:165
51#: plugins/LightDM/UsersModel.cpp:165
51msgid "Guest Session"
52msgid "Guest Session"
52−msgstr ""
53+msgstr "مہمان سیشن"
53
54
54#: plugins/Unity/Launcher/launcheritem.cpp:48
55#: plugins/Unity/Launcher/launcheritem.cpp:48
55#: plugins/Unity/Launcher/launcheritem.cpp:122
56#: plugins/Unity/Launcher/launcheritem.cpp:122
56msgid "Pin shortcut"
57msgid "Pin shortcut"
57−msgstr ""
58+msgstr "شورت کٹ پن کیجیے"
58
59
59#: plugins/Unity/Launcher/launcheritem.cpp:54
60#: plugins/Unity/Launcher/launcheritem.cpp:54
60msgid "Quit"
61msgid "Quit"
61−msgstr ""
62+msgstr "چھوڑیں"
62
63
63#: plugins/Unity/Launcher/launcheritem.cpp:122
64#: plugins/Unity/Launcher/launcheritem.cpp:122
64msgid "Unpin shortcut"
65msgid "Unpin shortcut"
65−msgstr ""
66+msgstr "شورت کٹ انپن کیجیے"
66
67
67#: qml/Components/Dialogs.qml:176
68#: qml/Components/Dialogs.qml:176
68msgctxt "Title: Lock/Log out dialog"
69msgctxt "Title: Lock/Log out dialog"
69msgid "Log out"
70msgid "Log out"
70−msgstr ""
71+msgstr "لاگ آؤٹ کیجیے"
71
72
72#: qml/Components/Dialogs.qml:177
73#: qml/Components/Dialogs.qml:177
73msgid "Are you sure you want to log out?"
74msgid "Are you sure you want to log out?"
74−msgstr ""
75+msgstr "کیا آپ لاگ آؤٹ کرنا چاہتے ہیں؟"
75
76
76#: qml/Components/Dialogs.qml:180
77#: qml/Components/Dialogs.qml:180
77msgctxt "Button: Lock the system"
78msgctxt "Button: Lock the system"
78msgid "Lock"
79msgid "Lock"
79−msgstr ""
80+msgstr "بند"
80
81
81#: qml/Components/Dialogs.qml:191
82#: qml/Components/Dialogs.qml:191
82msgctxt "Button: Log out from the system"
83msgctxt "Button: Log out from the system"
83msgid "Log Out"
84msgid "Log Out"
84−msgstr ""
85+msgstr "لاگ آؤٹ"
85
86
86#: qml/Components/Dialogs.qml:199 qml/Components/Dialogs.qml:284
87#: qml/Components/Dialogs.qml:199 qml/Components/Dialogs.qml:284
87#: qml/Greeter/NarrowView.qml:215 qml/Wizard/Pages/passcode-confirm.qml:32
88#: qml/Greeter/NarrowView.qml:215 qml/Wizard/Pages/passcode-confirm.qml:32
88#: qml/Wizard/Pages/passcode-set.qml:32
89#: qml/Wizard/Pages/passcode-set.qml:32
89msgid "Cancel"
90msgid "Cancel"
90−msgstr ""
91+msgstr "نہیں"
91
92
92#: qml/Components/Dialogs.qml:211
93#: qml/Components/Dialogs.qml:211
93msgctxt "Title: Reboot dialog"
94msgctxt "Title: Reboot dialog"
94msgid "Reboot"
95msgid "Reboot"
95−msgstr ""
96+msgstr "ریبوٹ"
96
97
97#: qml/Components/Dialogs.qml:212
98#: qml/Components/Dialogs.qml:212
98msgid "Are you sure you want to reboot?"
99msgid "Are you sure you want to reboot?"
99−msgstr ""
100+msgstr "کیا آپ ریبوٹ کرنا چاہتے ہیں؟"
100
101
101#: qml/Components/Dialogs.qml:215
102#: qml/Components/Dialogs.qml:215
102msgid "No"
103msgid "No"
103−msgstr ""
104+msgstr "نہیں"
104
105
105#: qml/Components/Dialogs.qml:223
106#: qml/Components/Dialogs.qml:223
106msgid "Yes"
107msgid "Yes"
@@ -116,20 +117,22 @@ msgid ""
116"Are you sure you would like\n"
117"Are you sure you would like\n"
117"to power off?"
118"to power off?"
118msgstr ""
119msgstr ""
120+"کیا آپ موبائل کو\n"
121+" بند کرنا چاہتے ہیں؟"
119
122
120#: qml/Components/Dialogs.qml:248
123#: qml/Components/Dialogs.qml:248
121msgctxt "Button: Power off the system"
124msgctxt "Button: Power off the system"
122msgid "Power off"
125msgid "Power off"
123−msgstr ""
126+msgstr "بند"
124
127
125#: qml/Components/Dialogs.qml:263
128#: qml/Components/Dialogs.qml:263
126msgctxt "Button: Restart the system"
129msgctxt "Button: Restart the system"
127msgid "Restart"
130msgid "Restart"
128−msgstr ""
131+msgstr "ریسارٹ"
129
132
130#: qml/Components/Dialogs.qml:276
133#: qml/Components/Dialogs.qml:276
131msgid "Screenshot"
134msgid "Screenshot"
132−msgstr ""
135+msgstr "سکرین کی تصویر"
133
136
134#: qml/Components/KeyboardShortcutsOverlay.qml:41
137#: qml/Components/KeyboardShortcutsOverlay.qml:41
135msgid "Keyboard Shortcuts"
138msgid "Keyboard Shortcuts"
@@ -521,7 +524,7 @@ msgstr ""
521
524
522#: qml/Panel/PanelMenuPage.qml:92
525#: qml/Panel/PanelMenuPage.qml:92
523msgid "Back"
526msgid "Back"
524−msgstr ""
527+msgstr "پیچھے"
525
528
526#: qml/Stage/SideStage.qml:76
529#: qml/Stage/SideStage.qml:76
527msgid "Drag using 3 fingers any application from one window to the other"
530msgid "Drag using 3 fingers any application from one window to the other"
@@ -777,7 +780,7 @@ msgstr ""
777
780
778#: qml/Wizard/Pages/sim.qml:59
781#: qml/Wizard/Pages/sim.qml:59
779msgid "Restart"
782msgid "Restart"
780−msgstr ""
783+msgstr "دوباره شروع"
781
784
782#: qml/Wizard/Pages/sim.qml:78
785#: qml/Wizard/Pages/sim.qml:78
783msgid "You won’t be able to make calls or use text messaging without a SIM."
786msgid "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 {
35
35
36 NotificationAudio {
36 NotificationAudio {
37 id: shutterSound
37 id: shutterSound
38− source: "/system/media/audio/ui/camera_click.ogg"
38+ source: "/usr/share/sounds/ubports/camera/click/camera_click.ogg"
39 }
39 }
40
40
41 function capture(item) {
41 function capture(item) {
qml/Greeter/ButtonPrompt.qmlnew file+51
@@ -0,0 +1,51 @@
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+27−8
@@ -16,6 +16,7 @@
16 */
16 */
17
17
18import QtQuick 2.4
18import QtQuick 2.4
19+import QtGraphicalEffects 1.12
19import Ubuntu.Components 1.3
20import Ubuntu.Components 1.3
20import Ubuntu.Gestures 0.1
21import Ubuntu.Gestures 0.1
21import "../Components"
22import "../Components"
@@ -28,11 +29,16 @@ Showable {
28 property alias background: greeterBackground.source
29 property alias background: greeterBackground.source
29 property alias backgroundSourceSize: greeterBackground.sourceSize
30 property alias backgroundSourceSize: greeterBackground.sourceSize
30 property alias hasCustomBackground: backgroundShade.visible
31 property alias hasCustomBackground: backgroundShade.visible
32+ property alias backgroundShadeOpacity: backgroundShade.opacity
31 property real panelHeight
33 property real panelHeight
32 property var infographicModel
34 property var infographicModel
33 property bool draggable: true
35 property bool draggable: true
34
36
35− property alias infographics: infographics
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
36
42
37 readonly property real showProgress: MathUtils.clamp((width - Math.abs(x + launcherOffset)) / width, 0, 1)
43 readonly property real showProgress: MathUtils.clamp((width - Math.abs(x + launcherOffset)) / width, 0, 1)
38
44
@@ -99,18 +105,17 @@ Showable {
99 objectName: "backgroundShade"
105 objectName: "backgroundShade"
100 anchors.fill: parent
106 anchors.fill: parent
101 color: "black"
107 color: "black"
102− opacity: 0.4
103 visible: false
108 visible: false
104 }
109 }
105
110
106− Infographics {
111+ Item {
107− id: infographics
112+ id: infographicsArea
108− objectName: "infographics"
109− model: root.infographicModel
110− clip: true // clip large data bubbles
111
113
112 anchors {
114 anchors {
115+ leftMargin: root.infographicsLeftMargin
113− topMargin: root.panelHeight
116+ topMargin: root.infographicsTopMargin ? root.infographicsTopMargin : root.panelHeight
117+ rightMargin: root.infographicsRightMargin
118+ bottomMargin: root.infographicsBottomMargin
114 top: parent.top
119 top: parent.top
115 bottom: parent.bottom
120 bottom: parent.bottom
116 left: parent.left
121 left: parent.left
@@ -118,6 +123,20 @@ Showable {
118 }
123 }
119 }
124 }
120
125
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+
121 Label {
140 Label {
122 id: swipeHint
141 id: swipeHint
123 objectName: "swipeHint"
142 objectName: "swipeHint"
qml/Greeter/Greeter.qml+37−2
@@ -58,6 +58,7 @@ Showable {
58 readonly property bool locked: LightDMService.greeter.active && !LightDMService.greeter.authenticated && !forcedUnlock
58 readonly property bool locked: LightDMService.greeter.active && !LightDMService.greeter.authenticated && !forcedUnlock
59
59
60 property bool tabletMode
60 property bool tabletMode
61+ property string usageMode
61 property url viewSource // only used for testing
62 property url viewSource // only used for testing
62
63
63 property int failedLoginsDelayAttempts: 7 // number of failed logins
64 property int failedLoginsDelayAttempts: 7 // number of failed logins
@@ -66,6 +67,11 @@ Showable {
66
67
67 readonly property bool animating: loader.item ? loader.item.animating : false
68 readonly property bool animating: loader.item ? loader.item.animating : false
68
69
70+ property rect inputMethodRect
71+
72+ property bool hasKeyboard: false
73+ property int orientation
74+
69 signal tease()
75 signal tease()
70 signal sessionStarted()
76 signal sessionStarted()
71 signal emergencyCall()
77 signal emergencyCall()
@@ -352,8 +358,7 @@ Showable {
352 anchors.fill: parent
358 anchors.fill: parent
353
359
354 active: root.required
360 active: root.required
355− source: root.viewSource.toString() ? root.viewSource :
361+ source: root.viewSource.toString() ? root.viewSource : "GreeterView.qml"
356− (d.multiUser || root.tabletMode) ? "WideView.qml" : "NarrowView.qml"
357
362
358 onLoaded: {
363 onLoaded: {
359 root.lockedApp = "";
364 root.lockedApp = "";
@@ -460,6 +465,36 @@ Showable {
460 property: "infographicModel"
465 property: "infographicModel"
461 value: LightDMService.infographic
466 value: LightDMService.infographic
462 }
467 }
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+ }
463 }
498 }
464
499
465 Connections {
500 Connections {
qml/Greeter/GreeterPrompt.qml+64−187
@@ -25,226 +25,103 @@ FocusScope {
25 focus: true
25 focus: true
26
26
27 property bool isPrompt
27 property bool isPrompt
28− property bool isAlphanumeric
28+ property bool isPinPrompt
29 property string text
29 property string text
30 property bool isSecret
30 property bool isSecret
31 property bool interactive: true
31 property bool interactive: true
32+ property bool loginError: false
32− readonly property alias enteredText: passwordInput.text
33+ readonly property string enteredText: loader.item.enteredText
34+ property bool hasKeyboard: false
35+ property bool waitingToAccept: false
33
36
34 signal clicked()
37 signal clicked()
35 signal canceled()
38 signal canceled()
36 signal accepted()
39 signal accepted()
37
40
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−
47 GSettings {
41 GSettings {
48 id: unity8Settings
42 id: unity8Settings
49 schema.id: "com.canonical.Unity8"
43 schema.id: "com.canonical.Unity8"
50 }
44 }
51
45
52− StyledItem {
46+ onEnteredTextChanged: if (waitingToAccept) root.accepted()
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− }
64
47
65− Rectangle {
48+ function showFakePassword() {
66− anchors.fill: parent
49+ // Just a silly hack for looking like 4 pin numbers got entered, if
67− border.width: units.dp(1)
50+ // a fingerprint was used and we happen to be using a pin. This was
68− border.color: d.drawColor
51+ // a request from Design.
69− radius: units.gu(0.5)
52+ if (isSecret) {
70− color: "transparent"
53+ loader.item.enteredText = "...."; // actual text doesn't matter
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;
80 }
54 }
81 }
55 }
82
56
83− StyledItem {
57+ Loader {
84− id: promptButton
58+ id: loader
85− objectName: "promptButton"
59+ objectName: "promptLoader"
86− anchors.fill: parent
87− visible: !root.isPrompt
88− activeFocusOnTab: true
89
60
90− styleName: "FocusShape"
61+ focus: true
91
62
92− function triggered() {
63+ anchors.fill: parent
93− if (root.interactive) {
94− root.clicked();
95− }
96− }
97
64
98− Rectangle {
65+ Connections {
99− height: parent.height;
66+ target: loader.item
100− width: parent.width
67+ onClicked: root.clicked()
101− color: "transparent"
68+ onCanceled: root.canceled()
102− border {
69+ onAccepted: {
103− color: d.textColor
70+ if (response == enteredText)
104− width: units.dp(1)
71+ root.accepted();
72+ else
73+ waitingToAccept = true;
105 }
74 }
106 }
75 }
107
76
108− Keys.onSpacePressed: triggered();
77+ Binding {
109− Keys.onReturnPressed: triggered();
78+ target: loader.item
110− Keys.onEnterPressed: triggered();
79+ property: "text"
111− MouseArea {
80+ value: root.text
112− anchors.fill: parent
113− onClicked: parent.triggered();
114 }
81 }
115
82
116− Label {
83+ Binding {
117− anchors.centerIn: parent
84+ target: loader.item
118− color: d.textColor
85+ property: "isSecret"
119− text: root.text
86+ value: root.isSecret
120 }
87 }
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
130
88
131− validator: RegExpValidator {
89+ Binding {
132− regExp: root.isAlphanumeric ? /^.*$/ : /^\d{4}$/
90+ target: loader.item
91+ property: "interactive"
92+ value: root.interactive
133 }
93 }
134
94
135− inputMethodHints: Qt.ImhSensitiveData | Qt.ImhNoPredictiveText |
95+ Binding {
136− Qt.ImhMultiLine | // so OSK doesn't close on Enter
96+ target: loader.item
137− (root.isAlphanumeric ? Qt.ImhNone : Qt.ImhDigitsOnly)
97+ property: "loginError"
138− echoMode: root.isSecret ? TextInput.Password : TextInput.Normal
98+ value: root.loginError
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
160 }
99 }
161
100
162− secondaryItem: [
101+ Binding {
163− Row {
102+ target: loader.item
164− id: extraIcons
103+ property: "hasKeyboard"
165− spacing: passwordInput.frameSpacing
104+ value: root.hasKeyboard
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− }
195 }
105 }
196
106
197− onAccepted: respond()
107+ onLoaded: loader.item.focus = true
198−
199− function respond() {
200− if (root.interactive) {
201− root.accepted();
202− }
203− }
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 }
108 }
230
109
231− // Have a fake label that covers the text field after the user presses
110+ states: [
232− // enter. What we *really* want is a disabled mode that doesn't lose OSK
111+ State {
233− // focus. Because our goal here is simply to keep the OSK up while
112+ name: "ButtonPrompt"
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
113+ when: !root.isPrompt
248− enabled: visible
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" }
249− }
125+ }
126+ ]
250}
127}
qml/Greeter/GreeterView.qmlnew file+534
@@ -0,0 +1,534 @@
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 {
21 BorderImage {
21 BorderImage {
22 anchors {
22 anchors {
23 fill: parent
23 fill: parent
24− topMargin: -units.gu(1)
24+ topMargin: -units.gu(3)
25 leftMargin: -units.gu(1.5)
25 leftMargin: -units.gu(1.5)
26 rightMargin: -units.gu(1.5)
26 rightMargin: -units.gu(1.5)
27 bottomMargin: -units.gu(1.5)
27 bottomMargin: -units.gu(1.5)
28 }
28 }
29 source: "../graphics/dropshadow2gu.sci"
29 source: "../graphics/dropshadow2gu.sci"
30− opacity: 0.35
30+ opacity: 0
31 }
31 }
32
32
33 UbuntuShape {
33 UbuntuShape {
34 anchors.fill: parent
34 anchors.fill: parent
35 aspect: UbuntuShape.Flat
35 aspect: UbuntuShape.Flat
36− backgroundColor: theme.palette.normal.raised
36+ backgroundColor: "transparent"
37 }
37 }
38}
38}
qml/Greeter/LoginList.qml+71−98
@@ -26,6 +26,7 @@ StyledItem {
26
26
27 property alias model: userList.model
27 property alias model: userList.model
28 property alias alphanumeric: promptList.alphanumeric
28 property alias alphanumeric: promptList.alphanumeric
29+ property alias hasKeyboard: promptList.hasKeyboard
29 property int currentIndex
30 property int currentIndex
30 property bool locked
31 property bool locked
31 property bool waiting
32 property bool waiting
@@ -39,6 +40,8 @@ StyledItem {
39 property string currentSession // Initially set by LightDM
40 property string currentSession // Initially set by LightDM
40 readonly property string currentUser: userList.currentItem.username
41 readonly property string currentUser: userList.currentItem.username
41
42
43+ readonly property alias currentUserIndex: userList.currentIndex
44+
42 signal responded(string response)
45 signal responded(string response)
43 signal selected(int index)
46 signal selected(int index)
44 signal sessionChooserButtonClicked()
47 signal sessionChooserButtonClicked()
@@ -48,6 +51,7 @@ StyledItem {
48 }
51 }
49
52
50 function showError() {
53 function showError() {
54+ promptList.loginError = true;
51 wrongPasswordAnimation.start();
55 wrongPasswordAnimation.start();
52 }
56 }
53
57
@@ -79,6 +83,7 @@ StyledItem {
79
83
80 onCurrentIndexChanged: {
84 onCurrentIndexChanged: {
81 userList.currentIndex = currentIndex;
85 userList.currentIndex = currentIndex;
86+ promptList.loginError = false;
82 }
87 }
83
88
84 LoginAreaContainer {
89 LoginAreaContainer {
@@ -93,18 +98,6 @@ StyledItem {
93
98
94 height: Math.max(units.gu(15), promptList.height + units.gu(8))
99 height: Math.max(units.gu(15), promptList.height + units.gu(8))
95 Behavior on height { NumberAnimation { duration: root.moveDuration; easing.type: Easing.InOutQuad; } }
100 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− }
108 }
101 }
109
102
110 ListView {
103 ListView {
@@ -115,26 +108,31 @@ StyledItem {
115 anchors.leftMargin: units.gu(2)
108 anchors.leftMargin: units.gu(2)
116 anchors.rightMargin: units.gu(2)
109 anchors.rightMargin: units.gu(2)
117
110
118− preferredHighlightBegin: highlightItem.y
111+ preferredHighlightBegin: highlightItem.y + units.gu(1.5)
119− preferredHighlightEnd: highlightItem.y
112+ preferredHighlightEnd: highlightItem.y + units.gu(1.5)
120 highlightRangeMode: ListView.StrictlyEnforceRange
113 highlightRangeMode: ListView.StrictlyEnforceRange
121 highlightMoveDuration: root.moveDuration
114 highlightMoveDuration: root.moveDuration
122 interactive: count > 1
115 interactive: count > 1
123
116
124 readonly property bool movingInternally: moveTimer.running || userList.moving
117 readonly property bool movingInternally: moveTimer.running || userList.moving
125
118
119+ onMovingChanged: if (!moving) root.selected(currentIndex)
120+
126 onCurrentIndexChanged: {
121 onCurrentIndexChanged: {
127 moveTimer.start();
122 moveTimer.start();
128 }
123 }
129
124
125+ onCountChanged: if (root.currentIndex >= count) root.selected(0)
126+
130 delegate: Item {
127 delegate: Item {
131 width: userList.width
128 width: userList.width
132 height: root.cellHeight
129 height: root.cellHeight
133
130
134 readonly property bool belowHighlight: (userList.currentIndex < 0 && index > 0) || (userList.currentIndex >= 0 && index > userList.currentIndex)
131 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)
135 readonly property int belowOffset: root.highlightedHeight - root.cellHeight
133 readonly property int belowOffset: root.highlightedHeight - root.cellHeight
136 readonly property string userSession: session
134 readonly property string userSession: session
137− readonly property string username: name
135+ readonly property string username: name ? name : ""
138
136
139 opacity: {
137 opacity: {
140 // The goal here is to make names less and less opaque as they
138 // The goal here is to make names less and less opaque as they
@@ -154,40 +152,74 @@ StyledItem {
154 return 1 - Math.min(1, (Math.abs(highlightDist) + root.cellHeight) / ((root.numAboveBelow + 1) * root.cellHeight))
152 return 1 - Math.min(1, (Math.abs(highlightDist) + root.cellHeight) / ((root.numAboveBelow + 1) * root.cellHeight))
155 }
153 }
156
154
157− FadingLabel {
155+ Row {
158− objectName: "username" + index
156+ spacing: units.gu(1)
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
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
160
158
161 anchors {
159 anchors {
162− left: parent.left
163 leftMargin: units.gu(2)
160 leftMargin: units.gu(2)
164− right: parent.right
165 rightMargin: units.gu(2)
161 rightMargin: units.gu(2)
162+ horizontalCenter: parent.horizontalCenter
166 bottom: parent.top
163 bottom: parent.top
167 // Add an offset to bottomMargin for any items below the highlight
164 // Add an offset to bottomMargin for any items below the highlight
168− bottomMargin: -(units.gu(4) + (parent.belowHighlight ? parent.belowOffset : 0))
165+ bottomMargin: -(units.gu(4) + (parent.belowHighlight ? parent.belowOffset : parent.aboveCurrent ? -units.gu(5) : 0))
169 }
166 }
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; } }
180
167
181 Rectangle {
168 Rectangle {
182 id: activeIndicator
169 id: activeIndicator
183− anchors.horizontalCenter: parent.left
184− anchors.horizontalCenterOffset: -units.gu(1)
185 anchors.verticalCenter: parent.verticalCenter
170 anchors.verticalCenter: parent.verticalCenter
186− color: userList.currentIndex !== index ? theme.palette.normal.raised
171+ color: theme.palette.normal.raised
187− : theme.palette.normal.focus
188 visible: userList.count > 1 && loggedIn
172 visible: userList.count > 1 && loggedIn
189− height: units.gu(0.5)
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)
190 width: height
181 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+ }
191 }
223 }
192 }
224 }
193
225
@@ -197,7 +229,7 @@ StyledItem {
197 right: parent.right
229 right: parent.right
198 top: parent.top
230 top: parent.top
199 // Add an offset to topMargin for any items below the highlight
231 // Add an offset to topMargin for any items below the highlight
200− topMargin: parent.belowHighlight ? parent.belowOffset : 0
232+ topMargin: parent.belowHighlight ? parent.belowOffset : parent.aboveCurrent ? -units.gu(5) : 0
201 }
233 }
202 height: parent.height
234 height: parent.height
203 enabled: userList.currentIndex !== index && parent.opacity > 0
235 enabled: userList.currentIndex !== index && parent.opacity > 0
@@ -217,67 +249,6 @@ StyledItem {
217 }
249 }
218 }
250 }
219
251
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−
281 PromptList {
252 PromptList {
282 id: promptList
253 id: promptList
283 objectName: "promptList"
254 objectName: "promptList"
@@ -288,6 +259,8 @@ StyledItem {
288 }
259 }
289 width: highlightItem.width - anchors.margins * 2
260 width: highlightItem.width - anchors.margins * 2
290
261
262+ focus: true
263+
291 onClicked: {
264 onClicked: {
292 interactive = false;
265 interactive = false;
293 if (root.locked) {
266 if (root.locked) {
qml/Greeter/NarrowView.qmldeleted−274
@@ -1,274 +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 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.qmlnew file+184
@@ -0,0 +1,184 @@
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+21−18
@@ -25,6 +25,8 @@ FocusScope {
25
25
26 property bool alphanumeric: true
26 property bool alphanumeric: true
27 property bool interactive: true
27 property bool interactive: true
28+ property bool loginError: false
29+ property bool hasKeyboard: false
28
30
29 signal responded(string text)
31 signal responded(string text)
30 signal clicked()
32 signal clicked()
@@ -65,10 +67,14 @@ FocusScope {
65
67
66 readonly property bool isLabel: model.type == LightDMService.prompts.Message ||
68 readonly property bool isLabel: model.type == LightDMService.prompts.Message ||
67 model.type == LightDMService.prompts.Error
69 model.type == LightDMService.prompts.Error
68− readonly property var modelData: model
70+ readonly property var modelText: model.text
71+ readonly property var modelType: model.type
72+ readonly property var modelIndex: model.index
69
73
70 sourceComponent: isLabel ? infoLabel : greeterPrompt
74 sourceComponent: isLabel ? infoLabel : greeterPrompt
71
75
76+ active: root.visible
77+
72 onLoaded: {
78 onLoaded: {
73 for (var i = 0; i < repeater.count; i++) {
79 for (var i = 0; i < repeater.count; i++) {
74 var item = repeater.itemAt(i);
80 var item = repeater.itemAt(i);
@@ -79,12 +85,6 @@ FocusScope {
79 }
85 }
80 loader.item.opacity = 1;
86 loader.item.opacity = 1;
81 }
87 }
82−
83− Binding {
84− target: loader.item
85− property: "model"
86− value: loader.modelData
87− }
88 }
88 }
89 }
89 }
90 }
90 }
@@ -93,17 +93,18 @@ FocusScope {
93 id: infoLabel
93 id: infoLabel
94
94
95 FadingLabel {
95 FadingLabel {
96− objectName: "infoLabel" + model.index
96+ objectName: "infoLabel" + modelIndex
97 width: root.width
97 width: root.width
98
98
99− property var model
100 readonly property bool isPrompt: false
99 readonly property bool isPrompt: false
101
100
102− color: model.type === LightDMService.prompts.Message ? theme.palette.normal.raisedText
101+ color: modelType === LightDMService.prompts.Message ? theme.palette.normal.raisedSecondaryText
103 : theme.palette.normal.negative
102 : theme.palette.normal.negative
104 fontSize: "small"
103 fontSize: "small"
105 textFormat: Text.PlainText
104 textFormat: Text.PlainText
106− text: model.text
105+ text: modelText
106+
107+ visible: modelType === LightDMService.prompts.Message
107
108
108 Behavior on opacity { UbuntuNumberAnimation {} }
109 Behavior on opacity { UbuntuNumberAnimation {} }
109 opacity: 0
110 opacity: 0
@@ -114,21 +115,23 @@ FocusScope {
114 id: greeterPrompt
115 id: greeterPrompt
115
116
116 GreeterPrompt {
117 GreeterPrompt {
117− objectName: "greeterPrompt" + model.index
118+ objectName: "greeterPrompt" + modelIndex
118 width: root.width
119 width: root.width
119
120
120− property var model
121+ property bool isAlphanumeric: modelText !== "" || root.alphanumeric
121
122
122 interactive: root.interactive
123 interactive: root.interactive
123− isAlphanumeric: model.text !== "" || root.alphanumeric
124− isPrompt: model.type !== LightDMService.prompts.Button
124+ isPrompt: modelType !== LightDMService.prompts.Button
125− isSecret: model.type === LightDMService.prompts.Secret
125+ isSecret: modelType === LightDMService.prompts.Secret
126+ isPinPrompt: isPrompt && !isAlphanumeric && isSecret
127+ loginError: root.loginError
128+ hasKeyboard: root.hasKeyboard
126− text: model.text ? model.text : (isAlphanumeric ? i18n.tr("Passphrase") : i18n.tr("Passcode"))
129+ text: modelText ? modelText : (isAlphanumeric ? i18n.tr("Passphrase") : i18n.tr("Passcode"))
127
130
128 onClicked: root.clicked()
131 onClicked: root.clicked()
129 onAccepted: {
132 onAccepted: {
130 // If there is another GreeterPrompt, focus it.
133 // If there is another GreeterPrompt, focus it.
131− for (var i = model.index + 1; i < repeater.count; i++) {
134+ for (var i = modelIndex + 1; i < repeater.count; i++) {
132 var item = repeater.itemAt(i).item;
135 var item = repeater.itemAt(i).item;
133 if (item.isPrompt) {
136 if (item.isPrompt) {
134 item.forceActiveFocus();
137 item.forceActiveFocus();
qml/Greeter/SessionsList.qml+3−2
@@ -119,13 +119,14 @@ Item {
119
119
120 padding.leading: 0 // handled by parent's margins
120 padding.leading: 0 // handled by parent's margins
121
121
122− title.color: theme.palette.normal.raisedText
122+ title.color: theme.palette.normal.raisedSecondaryText
123 title.font.pixelSize: units.gu(2.1)
123 title.font.pixelSize: units.gu(2.1)
124 title.text: i18n.tr("Select desktop environment")
124 title.text: i18n.tr("Select desktop environment")
125
125
126 Icon {
126 Icon {
127 id: icon
127 id: icon
128 width: units.gu(3)
128 width: units.gu(3)
129+ color: theme.palette.normal.raisedSecondaryText
129 SlotsLayout.position: SlotsLayout.Leading
130 SlotsLayout.position: SlotsLayout.Leading
130 name: "go-previous"
131 name: "go-previous"
131
132
@@ -187,7 +188,7 @@ Item {
187 ListItemLayout {
188 ListItemLayout {
188 id: layout
189 id: layout
189
190
190− readonly property color itemColor: theme.palette.normal.raisedText
191+ readonly property color itemColor: theme.palette.normal.raisedSecondaryText
191 SessionIcon {
192 SessionIcon {
192 id: sessionIcon
193 id: sessionIcon
193 source: icon_url
194 source: icon_url
qml/Greeter/TextPrompt.qmlnew file+198
@@ -0,0 +1,198 @@
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.qmldeleted−210
@@ -1,210 +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 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.qmlnew file+81
@@ -0,0 +1,81 @@
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+8−2
@@ -34,6 +34,7 @@ FocusScope {
34 readonly property real delegateWidth: units.gu(10)
34 readonly property real delegateWidth: units.gu(10)
35 property url background
35 property url background
36 property alias backgroundSourceSize: background.sourceSize
36 property alias backgroundSourceSize: background.sourceSize
37+ property bool staticBlurEnabled : true
37 visible: x > -width
38 visible: x > -width
38 property var fullyOpen: x === 0
39 property var fullyOpen: x === 0
39 property var fullyClosed: x === -width
40 property var fullyClosed: x === -width
@@ -155,18 +156,21 @@ FocusScope {
155
156
156 Rectangle {
157 Rectangle {
157 anchors.fill: parent
158 anchors.fill: parent
158− color: "#111111"
159+ color: "#BF000000"
159− opacity: 0.99
160
160
161 Wallpaper {
161 Wallpaper {
162 id: background
162 id: background
163 objectName: "drawerBackground"
163 objectName: "drawerBackground"
164+ visible: staticBlurEnabled
165+ enabled: staticBlurEnabled
164 anchors.fill: parent
166 anchors.fill: parent
165 source: root.background
167 source: root.background
166 }
168 }
167
169
168 FastBlur {
170 FastBlur {
169 anchors.fill: background
171 anchors.fill: background
172+ visible: staticBlurEnabled
173+ enabled: staticBlurEnabled
170 source: background
174 source: background
171 radius: 64
175 radius: 64
172 cached: true
176 cached: true
@@ -175,6 +179,8 @@ FocusScope {
175 // Images with fastblur can't use opacity, so we'll put this on top
179 // Images with fastblur can't use opacity, so we'll put this on top
176 Rectangle {
180 Rectangle {
177 anchors.fill: background
181 anchors.fill: background
182+ visible: staticBlurEnabled
183+ enabled: staticBlurEnabled
178 color: parent.color
184 color: parent.color
179 opacity: 0.67
185 opacity: 0.67
180 }
186 }
qml/Launcher/Launcher.qml+23−1
@@ -31,6 +31,8 @@ FocusScope {
31 property bool lockedVisible: false
31 property bool lockedVisible: false
32 property bool available: true // can be used to disable all interactions
32 property bool available: true // can be used to disable all interactions
33 property alias inverted: panel.inverted
33 property alias inverted: panel.inverted
34+ property Item blurSource: null
35+ property bool interactiveBlur: false
34 property int topPanelHeight: 0
36 property int topPanelHeight: 0
35 property bool drawerEnabled: true
37 property bool drawerEnabled: true
36 property alias privateMode: panel.privateMode
38 property alias privateMode: panel.privateMode
@@ -364,6 +366,20 @@ FocusScope {
364 }
366 }
365 }
367 }
366
368
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+
367 Drawer {
383 Drawer {
368 id: drawer
384 id: drawer
369 objectName: "drawer"
385 objectName: "drawer"
@@ -377,6 +393,7 @@ FocusScope {
377 width: Math.min(root.width, units.gu(81))
393 width: Math.min(root.width, units.gu(81))
378 panelWidth: panel.width
394 panelWidth: panel.width
379 allowSlidingAnimation: !dragArea.dragging && !launcherDragArea.drag.active && panel.animate
395 allowSlidingAnimation: !dragArea.dragging && !launcherDragArea.drag.active && panel.animate
396+ staticBlurEnabled: !root.interactiveBlur
380
397
381 onApplicationSelected: {
398 onApplicationSelected: {
382 root.launcherApplicationSelected(appId)
399 root.launcherApplicationSelected(appId)
@@ -410,7 +427,7 @@ FocusScope {
410 Connections {
427 Connections {
411 target: panel.dismissTimer
428 target: panel.dismissTimer
412 onTriggered: {
429 onTriggered: {
413− if (root.autohideEnabled && !root.lockedVisible) {
430+ if (root.state !== "drawer" && root.autohideEnabled && !root.lockedVisible) {
414 if (!edgeBarrier.containsMouse && !panel.preventHiding) {
431 if (!edgeBarrier.containsMouse && !panel.preventHiding) {
415 root.state = ""
432 root.state = ""
416 } else {
433 } else {
@@ -592,6 +609,11 @@ FocusScope {
592 anchors.rightMargin: 0
609 anchors.rightMargin: 0
593 focus: false
610 focus: false
594 }
611 }
612+ PropertyChanges {
613+ target: root
614+ restoreEntryValues: false
615+ autohideEnabled: false
616+ }
595 },
617 },
596 State {
618 State {
597 name: "drawer"
619 name: "drawer"
qml/OrientedShell.qml+1
@@ -273,6 +273,7 @@ Item {
273 nativeWidth: root.width
273 nativeWidth: root.width
274 nativeHeight: root.height
274 nativeHeight: root.height
275 mode: applicationArguments.mode
275 mode: applicationArguments.mode
276+ interactiveBlur: applicationArguments.interactiveBlur
276 hasMouse: pointerInputDevices > 0
277 hasMouse: pointerInputDevices > 0
277 hasKeyboard: keyboardsModel.count > 0
278 hasKeyboard: keyboardsModel.count > 0
278 hasTouchscreen: touchScreensModel.count > 0
279 hasTouchscreen: touchScreensModel.count > 0
qml/Panel/Indicators/IndicatorItem.qml+6
@@ -117,6 +117,12 @@ IndicatorDelegate {
117 source: modelData
117 source: modelData
118 color: root.color
118 color: root.color
119 Behavior on color { ColorAnimation { duration: UbuntuAnimation.FastDuration; easing: UbuntuAnimation.StandardEasing } }
119 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+ }
120 }
126 }
121 }
127 }
122 }
128 }
qml/Rotation/RotationStates.qml−3
@@ -139,9 +139,6 @@ StateGroup {
139 } else if (Powerd.status === Powerd.Off) {
139 } else if (Powerd.status === Powerd.Off) {
140 // There's no point in animating if the user can't see it (display is off).
140 // There's no point in animating if the user can't see it (display is off).
141 d.animationType = d.noAnimation;
141 d.animationType = d.noAnimation;
142− } else if (root.shell.showingGreeter) {
143− // A rotating greeter looks weird.
144− d.animationType = d.noAnimation;
145 } else {
142 } else {
146 if (!root.shell.mainApp) {
143 if (!root.shell.mainApp) {
147 // shouldn't happen but, anyway
144 // shouldn't happen but, anyway
qml/Shell.qml+10−3
@@ -1,4 +1,4 @@
1−/*
1+/*
2 * Copyright (C) 2013-2016 Canonical, Ltd.
2 * Copyright (C) 2013-2016 Canonical, Ltd.
3 * Copyright (C) 2019-2021 UBports Foundation
3 * Copyright (C) 2019-2021 UBports Foundation
4 *
4 *
@@ -59,6 +59,7 @@ StyledItem {
59 property alias panelAreaShowProgress: panel.panelAreaShowProgress
59 property alias panelAreaShowProgress: panel.panelAreaShowProgress
60 property string usageScenario: "phone" // supported values: "phone", "tablet" or "desktop"
60 property string usageScenario: "phone" // supported values: "phone", "tablet" or "desktop"
61 property string mode: "full-greeter"
61 property string mode: "full-greeter"
62+ property bool interactiveBlur: false
62 property alias oskEnabled: inputMethod.enabled
63 property alias oskEnabled: inputMethod.enabled
63 function updateFocusedAppOrientation() {
64 function updateFocusedAppOrientation() {
64 stage.updateFocusedAppOrientation();
65 stage.updateFocusedAppOrientation();
@@ -93,7 +94,7 @@ StyledItem {
93
94
94 readonly property bool orientationChangesEnabled: panel.indicators.fullyClosed
95 readonly property bool orientationChangesEnabled: panel.indicators.fullyClosed
95 && stage.orientationChangesEnabled
96 && stage.orientationChangesEnabled
96− && (!greeter || !greeter.animating)
97+ && (!greeter.animating)
97
98
98 readonly property bool showingGreeter: greeter && greeter.shown
99 readonly property bool showingGreeter: greeter && greeter.shown
99
100
@@ -104,7 +105,7 @@ StyledItem {
104 if (startingUp) {
105 if (startingUp) {
105 // Ensure we don't rotate during start up
106 // Ensure we don't rotate during start up
106 return Qt.PrimaryOrientation;
107 return Qt.PrimaryOrientation;
107− } else if (showingGreeter || notifications.topmostIsFullscreen) {
108+ } else if (notifications.topmostIsFullscreen) {
108 return Qt.PrimaryOrientation;
109 return Qt.PrimaryOrientation;
109 } else {
110 } else {
110 return shell.orientations ? shell.orientations.map(stage.supportedOrientations) : Qt.PrimaryOrientation;
111 return shell.orientations ? shell.orientations.map(stage.supportedOrientations) : Qt.PrimaryOrientation;
@@ -436,10 +437,14 @@ StyledItem {
436 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
437 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
437 hides: [launcher, panel.indicators, panel.applicationMenus]
438 hides: [launcher, panel.indicators, panel.applicationMenus]
438 tabletMode: shell.usageScenario != "phone"
439 tabletMode: shell.usageScenario != "phone"
440+ usageMode: shell.usageScenario
441+ orientation: shell.orientation
439 forcedUnlock: wizard.active || shell.mode === "full-shell"
442 forcedUnlock: wizard.active || shell.mode === "full-shell"
440 background: wallpaperResolver.background
443 background: wallpaperResolver.background
441 backgroundSourceSize: shell.largestScreenDimension
444 backgroundSourceSize: shell.largestScreenDimension
442 hasCustomBackground: wallpaperResolver.hasCustomBackground
445 hasCustomBackground: wallpaperResolver.hasCustomBackground
446+ inputMethodRect: inputMethod.visibleRect
447+ hasKeyboard: shell.hasKeyboard
443 allowFingerprint: !dialogs.hasActiveDialog &&
448 allowFingerprint: !dialogs.hasActiveDialog &&
444 !notifications.topmostIsFullscreen &&
449 !notifications.topmostIsFullscreen &&
445 !panel.indicators.shown
450 !panel.indicators.shown
@@ -600,6 +605,8 @@ StyledItem {
600 superTabPressed: physicalKeysMapper.superTabPressed
605 superTabPressed: physicalKeysMapper.superTabPressed
601 panelWidth: units.gu(settings.launcherWidth)
606 panelWidth: units.gu(settings.launcherWidth)
602 lockedVisible: (lockedByUser || shell.atDesktop) && lockAllowed
607 lockedVisible: (lockedByUser || shell.atDesktop) && lockAllowed
608+ blurSource: greeter.shown ? greeter : stages
609+ interactiveBlur: shell.interactiveBlur
603 topPanelHeight: panel.panelHeight
610 topPanelHeight: panel.panelHeight
604 drawerEnabled: !greeter.active && tutorial.launcherLongSwipeEnabled
611 drawerEnabled: !greeter.active && tutorial.launcherLongSwipeEnabled
605 privateMode: greeter.active
612 privateMode: greeter.active
qml/Stage/ApplicationWindow.qml+12−3
@@ -53,6 +53,7 @@ FocusScope {
53 if (surface) {
53 if (surface) {
54 surfaceContainer.surface = surface;
54 surfaceContainer.surface = surface;
55 d.liveSurface = surface.live;
55 d.liveSurface = surface.live;
56+ d.surfaceSeenLive = d.liveSurface;
56 d.hadSurface = false;
57 d.hadSurface = false;
57 surfaceInitTimer.start();
58 surfaceInitTimer.start();
58 } else {
59 } else {
@@ -70,7 +71,12 @@ FocusScope {
70 property bool liveSurface: false;
71 property bool liveSurface: false;
71 property var con: Connections {
72 property var con: Connections {
72 target: root.surface
73 target: root.surface
74+ onLiveChanged: {
73− onLiveChanged: d.liveSurface = root.surface.live
75+ d.liveSurface = root.surface.live;
76+ if (d.liveSurface) {
77+ d.surfaceSeenLive = true;
78+ }
79+ }
74 }
80 }
75 // using liveSurface instead of root.surface.live because with the latter
81 // using liveSurface instead of root.surface.live because with the latter
76 // this expression is not reevaluated when root.surface changes
82 // this expression is not reevaluated when root.surface changes
@@ -101,6 +107,7 @@ FocusScope {
101 // might show the UI accommodating due to surface resizes on startup.
107 // might show the UI accommodating due to surface resizes on startup.
102 // Remove this when possible
108 // Remove this when possible
103 property bool surfaceInitialized: false
109 property bool surfaceInitialized: false
110+ property bool surfaceSeenLive: false
104
111
105 readonly property bool supportsSurfaceResize:
112 readonly property bool supportsSurfaceResize:
106 application &&
113 application &&
@@ -251,6 +258,7 @@ FocusScope {
251 name: "splashScreen"
258 name: "splashScreen"
252 when:
259 when:
253 !d.hadSurface && (!root.surface || !d.surfaceInitialized)
260 !d.hadSurface && (!root.surface || !d.surfaceInitialized)
261+ && (d.liveSurface || !d.surfaceSeenLive)
254 &&
262 &&
255 screenshotImage.status !== Image.Ready
263 screenshotImage.status !== Image.Ready
256 },
264 },
@@ -283,8 +291,9 @@ FocusScope {
283 when:
291 when:
284 // The surface died while the application is running. It must have been closed
292 // The surface died while the application is running. It must have been closed
285 // by the shell or the application decided to destroy it by itself
293 // by the shell or the application decided to destroy it by itself
286− root.surface && d.surfaceInitialized && !d.liveSurface
294+ root.surface && (d.surfaceInitialized || d.surfaceSeenLive) && !d.liveSurface
287− && d.applicationState === ApplicationInfoInterface.Running
295+ && (d.applicationState === ApplicationInfoInterface.Running
296+ || d.applicationState === ApplicationInfoInterface.Starting)
288 }
297 }
289 ]
298 ]
290
299
src/ApplicationArguments.h+5
@@ -28,6 +28,7 @@ class ApplicationArguments : public QObject
28 Q_OBJECT
28 Q_OBJECT
29 Q_PROPERTY(QString deviceName READ deviceName NOTIFY deviceNameChanged)
29 Q_PROPERTY(QString deviceName READ deviceName NOTIFY deviceNameChanged)
30 Q_PROPERTY(QString mode READ mode CONSTANT)
30 Q_PROPERTY(QString mode READ mode CONSTANT)
31+ Q_PROPERTY(bool interactiveBlur READ interactiveBlur CONSTANT)
31public:
32public:
32 ApplicationArguments(QObject *parent = nullptr);
33 ApplicationArguments(QObject *parent = nullptr);
33
34
@@ -42,12 +43,16 @@ public:
42 void setMode(const QString &mode) { m_mode = mode; }
43 void setMode(const QString &mode) { m_mode = mode; }
43 QString mode() const { return m_mode; }
44 QString mode() const { return m_mode; }
44
45
46+ void setInteractiveBlur(const bool &value) { m_interactiveBlur = value; }
47+ bool interactiveBlur() { return m_interactiveBlur; }
48+
45Q_SIGNALS:
49Q_SIGNALS:
46 void deviceNameChanged(const QString&);
50 void deviceNameChanged(const QString&);
47
51
48private:
52private:
49 QString m_deviceName;
53 QString m_deviceName;
50 QString m_mode;
54 QString m_mode;
55+ bool m_interactiveBlur;
51};
56};
52
57
53#endif // APPLICATION_ARGUMENTS_H
58#endif // APPLICATION_ARGUMENTS_H
src/ShellApplication.cpp+6
@@ -58,6 +58,12 @@ ShellApplication::ShellApplication(int & argc, char ** argv, bool isMirServer)
58
58
59 m_qmlArgs.setMode(parser.mode());
59 m_qmlArgs.setMode(parser.mode());
60
60
61+ {
62+ char buffer[200];
63+ property_get("ubuntu.unity8.interactive_blur", buffer, "false");
64+ m_qmlArgs.setInteractiveBlur(QString(buffer) == QStringLiteral("true"));
65+ }
66+
61 // The testability driver is only loaded by QApplication but not by QGuiApplication.
67 // The testability driver is only loaded by QApplication but not by QGuiApplication.
62 // However, QApplication depends on QWidget which would add some unneeded overhead => Let's load the testability driver on our own.
68 // However, QApplication depends on QWidget which would add some unneeded overhead => Let's load the testability driver on our own.
63 if (parser.hasTestability() || getenv("QT_LOAD_TESTABILITY")) {
69 if (parser.hasTestability() || getenv("QT_LOAD_TESTABILITY")) {
tests/qmltests/CMakeLists.txt+1−2
@@ -29,8 +29,7 @@ add_unity8_qmltest(Components ZoomableImage)
29add_unity8_qmltest(Greeter Clock)
29add_unity8_qmltest(Greeter Clock)
30add_unity8_qmltest(Greeter Greeter)
30add_unity8_qmltest(Greeter Greeter)
31add_unity8_qmltest(Greeter Infographics)
31add_unity8_qmltest(Greeter Infographics)
32−add_unity8_qmltest(Greeter NarrowView)
32+add_unity8_qmltest(Greeter GreeterView)
33−add_unity8_qmltest(Greeter WideView)
34add_unity8_qmltest_data(Greeter TestView)
33add_unity8_qmltest_data(Greeter TestView)
35add_unity8_qmltest(Launcher Launcher)
34add_unity8_qmltest(Launcher Launcher)
36add_unity8_qmltest(Launcher Drawer)
35add_unity8_qmltest(Launcher Drawer)
tests/qmltests/Greeter/TestView.qml+7
@@ -38,6 +38,13 @@ Item {
38 readonly property bool required: _required
38 readonly property bool required: _required
39 readonly property bool animating: _animating
39 readonly property bool animating: _animating
40
40
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+
41 property bool _fullyShown: true
48 property bool _fullyShown: true
42 property bool _required: true
49 property bool _required: true
43 property bool _animating: true
50 property bool _animating: true
tests/qmltests/Greeter/tst_GreeterView.qmlnew file+1366
@@ -0,0 +1,1366 @@
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.qmldeleted−565
@@ -1,565 +0,0 @@
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.qmldeleted−781
@@ -1,781 +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 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+45−41
@@ -40,11 +40,12 @@ Rectangle {
40 height: units.gu(100)
40 height: units.gu(100)
41
41
42 property var tryShell: null
42 property var tryShell: null
43+ property string ldmUserMode: "single"
43
44
44 Binding {
45 Binding {
45 target: LightDMController
46 target: LightDMController
46 property: "userMode"
47 property: "userMode"
47− value: "single"
48+ value: ldmUserMode
48 }
49 }
49
50
50 QtObject {
51 QtObject {
@@ -240,6 +241,23 @@ Rectangle {
240 LightDM.Greeter.showGreeter();
241 LightDM.Greeter.showGreeter();
241 }
242 }
242 }
243 }
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+
243 Label {
261 Label {
244 text: "Physical Orientation:"
262 text: "Physical Orientation:"
245 }
263 }
@@ -601,43 +619,6 @@ Rectangle {
601 compare(shell.transformRotationAngle, root.primaryOrientationAngle + 180);
619 compare(shell.transformRotationAngle, root.primaryOrientationAngle + 180);
602 }
620 }
603
621
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−
641 function test_appRotatesWindowContents_data() {
622 function test_appRotatesWindowContents_data() {
642 return [
623 return [
643 {tag: "mako", deviceName: "mako", orientationAngleAfterRotation: 90},
624 {tag: "mako", deviceName: "mako", orientationAngleAfterRotation: 90},
@@ -1229,7 +1210,7 @@ Rectangle {
1229
1210
1230 showGreeter(orientedShell);
1211 showGreeter(orientedShell);
1231
1212
1232− tryCompare(shell, "transformRotationAngle", root.primaryOrientationAngle);
1213+ tryCompare(shell, "transformRotationAngle", root.primaryOrientationAngle + 90);
1233 }
1214 }
1234
1215
1235 /*
1216 /*
@@ -1482,7 +1463,8 @@ Rectangle {
1482 touchFlick(shell, touchX, touchY, orientedShell.width * 0.1, touchY);
1463 touchFlick(shell, touchX, touchY, orientedShell.width * 0.1, touchY);
1483
1464
1484 // wait until the animation has finished
1465 // wait until the animation has finished
1466+ if (LightDMController.userMode == "single")
1485− tryCompare(greeter, "shown", false);
1467+ tryCompare(greeter, "shown", false);
1486 waitForRendering(greeter);
1468 waitForRendering(greeter);
1487 }
1469 }
1488
1470
@@ -1493,9 +1475,11 @@ Rectangle {
1493 tryCompare(greeter, "fullyShown", true);
1475 tryCompare(greeter, "fullyShown", true);
1494 }
1476 }
1495
1477
1496− function loadShell(deviceName) {
1478+ function loadShell(deviceName, userMode = "single") {
1497 applicationArguments.deviceName = deviceName;
1479 applicationArguments.deviceName = deviceName;
1498
1480
1481+ ldmUserMode = userMode; // Set the mode for LightDM ( default is "single" )
1482+
1499 // reload our test subject to get it in a fresh state once again
1483 // reload our test subject to get it in a fresh state once again
1500 var orientedShell = createTemporaryObject(shellComponent, shellRect);
1484 var orientedShell = createTemporaryObject(shellComponent, shellRect);
1501
1485
@@ -1697,5 +1681,25 @@ Rectangle {
1697 MockInputDeviceBackend.removeDevice("/touchscreen");
1681 MockInputDeviceBackend.removeDevice("/touchscreen");
1698 tryCompare(tutorial, "paused", true);
1682 tryCompare(tutorial, "paused", true);
1699 }
1683 }
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+ }
1700 }
1704 }
1701}
1705}
tests/qmltests/tst_Shell.qml+48−15
@@ -703,6 +703,32 @@ Rectangle {
703 mockNotificationsModel.append(n)
703 mockNotificationsModel.append(n)
704 }
704 }
705
705
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+
706 function swipeAwayGreeter() {
732 function swipeAwayGreeter() {
707 var greeter = findChild(shell, "greeter");
733 var greeter = findChild(shell, "greeter");
708
734
@@ -763,13 +789,16 @@ Rectangle {
763 var greeter = findChild(shell, "greeter")
789 var greeter = findChild(shell, "greeter")
764 tryCompare(greeter, "fullyShown", true);
790 tryCompare(greeter, "fullyShown", true);
765
791
792+ if (isButton) {
766− var promptButton = findChild(greeter, "promptButton");
793+ var promptButton = findChild(greeter, "promptButton");
767− tryCompare(promptButton, "visible", isButton);
794+ verify(promptButton);
768−
769− var promptField = findChild(greeter, "promptField");
770− tryCompare(promptField, "visible", !isButton);
795+ tryCompare(promptButton, "visible", true);
771−
772− mouseClick(promptButton);
796+ mouseClick(promptButton);
797+ } else {
798+ var promptField = findChild(greeter, "promptField");
799+ verify(promptField);
800+ tryCompare(promptField, "visible", true);
801+ }
773 }
802 }
774
803
775 function confirmLoggedIn(loggedIn) {
804 function confirmLoggedIn(loggedIn) {
@@ -993,14 +1022,13 @@ Rectangle {
993 }
1022 }
994
1023
995 function test_greeterStartsCorrectSession() {
1024 function test_greeterStartsCorrectSession() {
996− loadShell("desktop");
997 setLightDMMockMode("full");
1025 setLightDMMockMode("full");
1026+ loadShell("desktop");
998
1027
999 LightDMController.sessionMode = "full"
1028 LightDMController.sessionMode = "full"
1000 LightDMController.numSessions = LightDMController.numAvailableSessions;
1029 LightDMController.numSessions = LightDMController.numAvailableSessions;
1001 var greeter = findChild(shell, "greeter");
1030 var greeter = findChild(shell, "greeter");
1002− var view = findChild(greeter, "WideView");
1031+ var view = findChild(greeter, "GreeterView");
1003− verify(view, "This test requires WideView to be loaded");
1004
1032
1005 var loginList = findChild(view, "loginList");
1033 var loginList = findChild(view, "loginList");
1006
1034
@@ -1400,8 +1428,9 @@ Rectangle {
1400 }
1428 }
1401
1429
1402 function test_tabletLogin(data) {
1430 function test_tabletLogin(data) {
1403− loadShell("tablet");
1404 setLightDMMockMode("full");
1431 setLightDMMockMode("full");
1432+ loadShell("tablet");
1433+ swipeAwayGreeterCover();
1405
1434
1406 selectUser(data.user);
1435 selectUser(data.user);
1407
1436
@@ -1424,8 +1453,10 @@ Rectangle {
1424 }
1453 }
1425
1454
1426 function test_appLaunchDuringGreeter(data) {
1455 function test_appLaunchDuringGreeter(data) {
1427− loadShell("tablet");
1428 setLightDMMockMode("full");
1456 setLightDMMockMode("full");
1457+ loadShell("tablet");
1458+
1459+ swipeAwayGreeterCover();
1429
1460
1430 selectUser(data.user)
1461 selectUser(data.user)
1431
1462
@@ -1441,9 +1472,11 @@ Rectangle {
1441 }
1472 }
1442
1473
1443 function test_manualLoginFlow() {
1474 function test_manualLoginFlow() {
1444− loadShell("desktop");
1445− LightDMController.showManualLoginHint = true;
1446 setLightDMMockMode("full");
1475 setLightDMMockMode("full");
1476+ LightDMController.showManualLoginHint = true;
1477+ loadShell("desktop");
1478+
1479+ swipeAwayGreeterCover();
1447
1480
1448 var i = selectUser("*other");
1481 var i = selectUser("*other");
1449 var greeter = findChild(shell, "greeter");
1482 var greeter = findChild(shell, "greeter");
@@ -2580,8 +2613,8 @@ Rectangle {
2580
2613
2581 function test_fullShellModeHasNoInitialGreeter() {
2614 function test_fullShellModeHasNoInitialGreeter() {
2582 shellRect.mode = "full-shell";
2615 shellRect.mode = "full-shell";
2583− loadShell("phone");
2584 setLightDMMockMode("single-pin");
2616 setLightDMMockMode("single-pin");
2617+ loadShell("phone");
2585 waitForRendering(shell);
2618 waitForRendering(shell);
2586
2619
2587 var greeter = findChild(shell, "greeter");
2620 var greeter = findChild(shell, "greeter");