on xenial help sign in
Greeter orientation (#402)
d1fffd2Capsia
qml/Greeter/CoverPage.qml+20−8
@@ -33,12 +33,17 @@ Showable {
33 property var infographicModel
33 property var infographicModel
34 property bool draggable: true
34 property bool draggable: true
35
35
36− property alias infographics: infographics
36+ property alias showInfographic: infographicsLoader.active
37+ property real infographicsLeftMargin: 0
38+ property real infographicsTopMargin: 0
39+ property real infographicsRightMargin: 0
40+ property real infographicsBottomMargin: 0
37
41
38 property alias blurAreaHeight: loginBoxEffects.height
42 property alias blurAreaHeight: loginBoxEffects.height
39 property alias blurAreaWidth: loginBoxEffects.width
43 property alias blurAreaWidth: loginBoxEffects.width
40 property alias blurAreaX: loginBoxEffects.x
44 property alias blurAreaX: loginBoxEffects.x
41 property alias blurAreaY: loginBoxEffects.y
45 property alias blurAreaY: loginBoxEffects.y
46+ property alias blurRadius: loginBoxBlur.radius
42
47
43 readonly property real showProgress: MathUtils.clamp((width - Math.abs(x + launcherOffset)) / width, 0, 1)
48 readonly property real showProgress: MathUtils.clamp((width - Math.abs(x + launcherOffset)) / width, 0, 1)
44
49
@@ -115,10 +120,10 @@ Showable {
115 }
120 }
116
121
117 FastBlur {
122 FastBlur {
123+ id: loginBoxBlur
118 visible: !draggable
124 visible: !draggable
119 anchors.fill: effectSource
125 anchors.fill: effectSource
120 source: effectSource
126 source: effectSource
121− radius: 64
122 transparentBorder: true
127 transparentBorder: true
123 }
128 }
124
129
@@ -132,14 +137,21 @@ Showable {
132 visible: false
137 visible: false
133 }
138 }
134
139
140+ Loader {
141+ id: infographicsLoader
142+ objectName: "infographicsLoader"
135− Infographics {
143+ sourceComponent:Infographics {
136− id: infographics
144+ id: infographics
137− objectName: "infographics"
145+ objectName: "infographics"
138− model: root.infographicModel
146+ model: root.infographicModel
139− clip: true // clip large data bubbles
147+ clip: true // clip large data bubbles
148+ }
140
149
141 anchors {
150 anchors {
151+ leftMargin: root.infographicsLeftMargin
142− topMargin: root.panelHeight
152+ topMargin: root.infographicsTopMargin ? root.infographicsTopMargin : root.panelHeight
153+ rightMargin: root.infographicsRightMargin
154+ bottomMargin: root.infographicsBottomMargin
143 top: parent.top
155 top: parent.top
144 bottom: parent.bottom
156 bottom: parent.bottom
145 left: parent.left
157 left: parent.left
qml/Greeter/Greeter.qml+21−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
@@ -69,6 +70,7 @@ Showable {
69 property rect inputMethodRect
70 property rect inputMethodRect
70
71
71 property bool hasKeyboard: false
72 property bool hasKeyboard: false
73+ property int orientation
72
74
73 signal tease()
75 signal tease()
74 signal sessionStarted()
76 signal sessionStarted()
@@ -356,8 +358,7 @@ Showable {
356 anchors.fill: parent
358 anchors.fill: parent
357
359
358 active: root.required
360 active: root.required
359− source: root.viewSource.toString() ? root.viewSource :
361+ source: root.viewSource.toString() ? root.viewSource : "GreeterView.qml"
360− (d.multiUser || root.tabletMode) ? "WideView.qml" : "NarrowView.qml"
361
362
362 onLoaded: {
363 onLoaded: {
363 root.lockedApp = "";
364 root.lockedApp = "";
@@ -476,6 +477,24 @@ Showable {
476 property: "hasKeyboard"
477 property: "hasKeyboard"
477 value: root.hasKeyboard
478 value: root.hasKeyboard
478 }
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+ }
479 }
498 }
480
499
481 Connections {
500 Connections {
qml/Greeter/GreeterView.qmlnew file+538
@@ -0,0 +1,538 @@
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+ blurAreaHeight: loginList.highlightedHeight + units.gu(4.5)
136+ blurAreaWidth: loginList.width + units.gu(3)
137+ blurAreaX: loginList.x - units.gu(1.5)
138+ blurAreaY: loginList.boxVerticalOffset + loginList.y - units.gu(3)
139+ blurRadius: root.usageMode != "phone" && root.usageMode != "tablet" ? 64 : 0
140+
141+ background: root.background
142+ backgroundSourceSize: root.backgroundSourceSize
143+ panelHeight: root.panelHeight
144+ hasCustomBackground: root.hasCustomBackground
145+
146+ showInfographic: root.usageMode != "phone" && isLandscape && !delayedLockscreen.visible
147+ infographicModel: root.infographicModel
148+
149+ shown: false
150+ opacity: 0
151+
152+ showAnimation: StandardAnimation { property: "opacity"; to: 1 }
153+ hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
154+
155+ infographicsTopMargin: parent.height * 0.125
156+ infographicsBottomMargin: parent.height * 0.125
157+ infographicsLeftMargin: loginList.x + loginList.width
158+
159+ onTease: root.tease()
160+
161+ onShowProgressChanged: {
162+ if (showProgress === 0 && !root.locked) {
163+ root.responded("");
164+ }
165+ }
166+
167+ LoginList {
168+ id: loginList
169+ objectName: "loginList"
170+
171+ width: units.gu(40)
172+ anchors {
173+ top: parent.top
174+ bottom: parent.bottom
175+ }
176+
177+ boxVerticalOffset: (height - highlightedHeight -
178+ inputMethodRect.height) / 2
179+ Behavior on boxVerticalOffset { UbuntuNumberAnimation {} }
180+
181+ enabled: !coverPage.shown && visible
182+ visible: !delayedLockscreen.visible
183+
184+ model: root.userModel
185+ onResponded: root.responded(response)
186+ onSelected: root.selected(index)
187+ onSessionChooserButtonClicked: parent.state = "SessionsList"
188+ onCurrentIndexChanged: setCurrentSession()
189+
190+ locked: root.locked
191+ waiting: root.waiting
192+
193+ Keys.forwardTo: [sessionChooserLoader.item]
194+
195+ Component.onCompleted: setCurrentSession()
196+
197+ function setCurrentSession() {
198+ currentSession = LightDMService.users.data(currentIndex, LightDMService.userRoles.SessionRole);
199+ }
200+ }
201+
202+ DelayedLockscreen {
203+ id: delayedLockscreen
204+ objectName: "delayedLockscreen"
205+ anchors.fill: parent
206+ visible: delayMinutes > 0
207+ alphaNumeric: loginList.alphanumeric
208+ }
209+
210+ function maybeShow() {
211+ if ((root.locked || root.multiUser) && !shown) {
212+ showNow();
213+ }
214+ }
215+
216+ Loader {
217+ id: sessionChooserLoader
218+
219+ height: loginList.height
220+ width: loginList.width
221+
222+ anchors {
223+ left: parent.left
224+ leftMargin: Math.min(parent.width * 0.16, units.gu(20))
225+ top: parent.top
226+ }
227+
228+ active: false
229+
230+ onLoaded: sessionChooserLoader.item.forceActiveFocus();
231+ onActiveChanged: {
232+ if (!active) return;
233+ item.updateHighlight(loginList.currentSession);
234+ }
235+
236+ Connections {
237+ target: sessionChooserLoader.item
238+ onSessionSelected: loginList.currentSession = sessionKey
239+ onShowLoginList: {
240+ lockscreen.state = "LoginList"
241+ loginList.tryToUnlock();
242+ }
243+ ignoreUnknownSignals: true
244+ }
245+ }
246+
247+ // Use an AbstractButton due to icon limitations with Button
248+ AbstractButton {
249+ id: sessionChooser
250+ objectName: "sessionChooserButton"
251+
252+ readonly property url icon: LightDMService.sessions.iconUrl(loginList.currentSession)
253+
254+ visible: LightDMService.sessions.count > 1 &&
255+ !LightDMService.users.data(loginList.currentUserIndex, LightDMService.userRoles.LoggedInRole)
256+
257+ height: units.gu(3.5)
258+ width: units.gu(3.5)
259+
260+ activeFocusOnTab: true
261+ anchors {
262+ right: parent.right
263+ rightMargin: units.gu(2)
264+
265+ bottom: parent.bottom
266+ bottomMargin: units.gu(1.5)
267+ }
268+
269+ Rectangle {
270+ id: badgeHighlight
271+
272+ anchors.fill: parent
273+ visible: parent.activeFocus
274+ color: "transparent"
275+ border.color: theme.palette.normal.focus
276+ border.width: units.dp(1)
277+ radius: 3
278+ }
279+
280+ Icon {
281+ id: badge
282+ anchors.fill: parent
283+ anchors.margins: units.dp(3)
284+ keyColor: "#ffffff" // icon providers give us white icons
285+ color: theme.palette.normal.raisedSecondaryText
286+ source: sessionChooser.icon
287+ }
288+
289+ Keys.onReturnPressed: {
290+ parent.state = "SessionsList";
291+ }
292+
293+ onClicked: {
294+ parent.state = "SessionsList";
295+ }
296+
297+ // Refresh the icon path if looking at different places at runtime
298+ // this is mainly for testing
299+ Connections {
300+ target: LightDMService.sessions
301+ onIconSearchDirectoriesChanged: {
302+ badge.source = LightDMService.sessions.iconUrl(root.currentSession)
303+ }
304+ }
305+ }
306+
307+ states: [
308+ State {
309+ name: "SessionsList"
310+ PropertyChanges { target: loginList; opacity: 0 }
311+ PropertyChanges { target: sessionChooserLoader;
312+ active: true;
313+ opacity: 1
314+ source: "SessionsList.qml"
315+ }
316+ },
317+
318+ State {
319+ name: "LoginList"
320+ PropertyChanges { target: loginList; opacity: 1 }
321+ PropertyChanges { target: sessionChooserLoader;
322+ active: false;
323+ opacity: 0
324+ source: "";
325+ }
326+ }
327+ ]
328+
329+ transitions: [
330+ Transition {
331+ from: "*"
332+ to: "*"
333+ UbuntuNumberAnimation {
334+ property: "opacity";
335+ }
336+ }
337+ ]
338+
339+ Component.onCompleted: if (root.multiUser) showNow()
340+ }
341+
342+ Rectangle {
343+ anchors.fill: parent
344+ color: "black"
345+ opacity: coverPage.showProgress * 0.8
346+ }
347+
348+ CoverPage {
349+ id: coverPage
350+ objectName: "coverPage"
351+ height: parent.height
352+ width: parent.width
353+ background: root.background
354+ hasCustomBackground: root.hasCustomBackground
355+ panelHeight: root.panelHeight
356+ draggable: !root.waiting
357+ onTease: root.tease()
358+ onClicked: hide()
359+ backgroundSourceSize: root.backgroundSourceSize
360+ infographicModel: root.infographicModel
361+
362+ showInfographic: !root.multiUser && (!isLandscape || root.usageMode == "phone")
363+
364+ onShowProgressChanged: {
365+ if (showProgress === 0) {
366+ if (lockscreen.shown) {
367+ loginList.tryToUnlock();
368+ } else {
369+ root.responded("");
370+ }
371+ }
372+ }
373+
374+ Clock {
375+ id: clock
376+ anchors.centerIn: parent
377+ }
378+
379+ states: [
380+ State {
381+ name: "landscape-with-infographics"
382+ when: isLandscape && coverPage.showInfographic
383+ AnchorChanges {
384+ target: clock
385+ anchors.top: undefined
386+ anchors.horizontalCenter: undefined
387+ anchors.verticalCenter: undefined
388+ }
389+ PropertyChanges {
390+ target: clock;
391+ anchors.topMargin: undefined
392+ anchors.centerIn: coverPage
393+ anchors.horizontalCenterOffset: - coverPage.width / 2 + clock.width / 2 + units.gu(8)
394+ }
395+ PropertyChanges {
396+ target: coverPage
397+ infographicsLeftMargin: clock.width + units.gu(8)
398+ }
399+ },
400+ State {
401+ name: "portrait"
402+ when: isPortrait && coverPage.showInfographic
403+ AnchorChanges {
404+ target: clock;
405+ anchors.top: coverPage.top
406+ anchors.horizontalCenter: coverPage.horizontalCenter
407+ anchors.verticalCenter: undefined
408+ }
409+ PropertyChanges {
410+ target: clock;
411+ anchors.topMargin: units.gu(2) + panelHeight
412+ anchors.centerIn: undefined
413+ anchors.horizontalCenterOffset: 0
414+ }
415+ PropertyChanges {
416+ target: coverPage
417+ infographicsLeftMargin: 0
418+ }
419+ },
420+ State {
421+ name: "without-infographics"
422+ when: !coverPage.showInfographic
423+ AnchorChanges {
424+ target: clock
425+ anchors.top: undefined
426+ anchors.horizontalCenter: coverPage.horizontalCenter
427+ anchors.verticalCenter: coverPage.verticalCenter
428+ }
429+ PropertyChanges {
430+ target: clock;
431+ anchors.topMargin: 0
432+ anchors.centerIn: undefined
433+ anchors.horizontalCenterOffset: 0
434+ }
435+ PropertyChanges {
436+ target: coverPage
437+ infographicsLeftMargin: 0
438+ }
439+ }
440+ ]
441+ }
442+
443+ StyledItem {
444+ id: bottomBar
445+ visible: usageMode == "phone" && lockscreen.shown
446+ height: units.gu(4)
447+
448+ anchors.left: parent.left
449+ anchors.right: parent.right
450+ anchors.top: parent.bottom
451+ anchors.topMargin: - height * (1 - coverPage.showProgress)
452+ - ( inputMethodRect.height )
453+
454+ Label {
455+ text: i18n.tr("Cancel")
456+ anchors.left: parent.left
457+ anchors.leftMargin: units.gu(2)
458+ anchors.top: parent.top
459+ anchors.bottom: parent.bottom
460+ verticalAlignment: Text.AlignVCenter
461+ font.weight: Font.Light
462+ fontSize: "small"
463+ color: theme.palette.normal.raisedSecondaryText
464+
465+ AbstractButton {
466+ anchors.fill: parent
467+ anchors.leftMargin: -units.gu(2)
468+ anchors.rightMargin: -units.gu(2)
469+ onClicked: coverPage.show()
470+ }
471+ }
472+
473+ Label {
474+ objectName: "emergencyCallLabel"
475+ text: callManager.hasCalls ? i18n.tr("Return to Call") : i18n.tr("Emergency")
476+ anchors.right: parent.right
477+ anchors.rightMargin: units.gu(2)
478+ anchors.top: parent.top
479+ anchors.bottom: parent.bottom
480+ verticalAlignment: Text.AlignVCenter
481+ font.weight: Font.Light
482+ fontSize: "small"
483+ color: theme.palette.normal.raisedSecondaryText
484+ // TODO: uncomment once bug 1616538 is fixed
485+ // visible: telepathyHelper.ready && telepathyHelper.emergencyCallsAvailable
486+ enabled: visible
487+
488+ AbstractButton {
489+ anchors.fill: parent
490+ anchors.leftMargin: -units.gu(2)
491+ anchors.rightMargin: -units.gu(2)
492+ onClicked: root.emergencyCall()
493+ }
494+ }
495+ }
496+
497+ states: [
498+ State {
499+ name: "phone"
500+ when: root.usageMode == "phone" || (root.usageMode == "tablet" && isPortrait)
501+ AnchorChanges {
502+ target: loginList;
503+ anchors.horizontalCenter: lockscreen.horizontalCenter;
504+ anchors.left: undefined;
505+ }
506+ PropertyChanges {
507+ target: loginList;
508+ anchors.leftMargin: 0;
509+ }
510+ },
511+ State {
512+ name: "tablet"
513+ when: root.usageMode == "tablet" && isLandscape
514+ AnchorChanges {
515+ target: loginList;
516+ anchors.horizontalCenter: undefined;
517+ anchors.left: lockscreen.left;
518+ }
519+ PropertyChanges {
520+ target: loginList;
521+ anchors.leftMargin: Math.min(lockscreen.width * 0.16, units.gu(8));
522+ }
523+ },
524+ State {
525+ name: "desktop"
526+ when: root.usageMode == "desktop"
527+ AnchorChanges {
528+ target: loginList;
529+ anchors.horizontalCenter: undefined;
530+ anchors.left: lockscreen.left;
531+ }
532+ PropertyChanges {
533+ target: loginList;
534+ anchors.leftMargin: Math.min(lockscreen.width * 0.16, units.gu(20));
535+ }
536+ }
537+ ]
538+}
qml/Greeter/LoginList.qml+4−2
@@ -122,6 +122,8 @@ StyledItem {
122 moveTimer.start();
122 moveTimer.start();
123 }
123 }
124
124
125+ onCountChanged: if (root.currentIndex >= count) root.selected(0)
126+
125 delegate: Item {
127 delegate: Item {
126 width: userList.width
128 width: userList.width
127 height: root.cellHeight
129 height: root.cellHeight
@@ -130,7 +132,7 @@ StyledItem {
130 readonly property bool aboveCurrent: (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)
131 readonly property int belowOffset: root.highlightedHeight - root.cellHeight
133 readonly property int belowOffset: root.highlightedHeight - root.cellHeight
132 readonly property string userSession: session
134 readonly property string userSession: session
133− readonly property string username: name
135+ readonly property string username: name ? name : ""
134
136
135 opacity: {
137 opacity: {
136 // 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
@@ -191,7 +193,7 @@ StyledItem {
191 text: userList.currentIndex === index
193 text: userList.currentIndex === index
192 && name === "*other"
194 && name === "*other"
193 && LightDMService.greeter.authenticationUser !== ""
195 && LightDMService.greeter.authenticationUser !== ""
194− ? LightDMService.greeter.authenticationUser : realName
196+ ? LightDMService.greeter.authenticationUser : realName ? realName : ""
195 color: userList.currentIndex !== index ? theme.palette.normal.raised
197 color: userList.currentIndex !== index ? theme.palette.normal.raised
196 : theme.palette.normal.raisedSecondaryText
198 : theme.palette.normal.raisedSecondaryText
197 font.weight: userList.currentIndex === index ? Font.Normal : Font.Light
199 font.weight: userList.currentIndex === index ? Font.Normal : Font.Light
qml/Greeter/NarrowView.qmldeleted−272
@@ -1,272 +0,0 @@
1−/*
2− * Copyright (C) 2015-2016 Canonical, Ltd.
3− * Copyright (C) 2021 UBports Foundation
4− *
5− * This program is free software; you can redistribute it and/or modify
6− * it under the terms of the GNU General Public License as published by
7− * the Free Software Foundation; version 3.
8− *
9− * This program is distributed in the hope that it will be useful,
10− * but WITHOUT ANY WARRANTY; without even the implied warranty of
11− * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12− * GNU General Public License for more details.
13− *
14− * You should have received a copy of the GNU General Public License
15− * along with this program. If not, see <http://www.gnu.org/licenses/>.
16− */
17−
18−import QtQuick 2.4
19−import QtQuick.Window 2.2
20−import QtGraphicalEffects 1.12
21−import Ubuntu.Components 1.3
22−import Ubuntu.Telephony 0.1 as Telephony
23−import "../Components"
24−
25−FocusScope {
26− id: root
27−
28− property alias dragHandleLeftMargin: coverPage.dragHandleLeftMargin
29− property alias launcherOffset: coverPage.launcherOffset
30− property alias currentIndex: loginList.currentIndex
31− property alias delayMinutes: delayedLockscreen.delayMinutes
32− property real panelHeight
33− property url background
34− property real backgroundSourceSize
35− property bool hasCustomBackground
36− property bool locked
37− property alias alphanumeric: loginList.alphanumeric
38− property alias hasKeyboard: loginList.hasKeyboard
39− property alias userModel: loginList.model
40− property alias infographicModel: coverPage.infographicModel
41− property string sessionToStart
42− property bool waiting
43− readonly property bool fullyShown: coverPage.showProgress === 1 || lockscreen.shown
44− readonly property bool required: coverPage.required || lockscreen.required
45− readonly property bool animating: coverPage.showAnimation.running || coverPage.hideAnimation.running
46−
47− property rect inputMethodRect
48−
49− signal selected(int index)
50− signal responded(string response)
51− signal tease()
52− signal emergencyCall()
53−
54− function hide() {
55− lockscreen.hide();
56− coverPage.hide();
57− }
58−
59− function showFakePassword() {
60− loginList.showFakePassword();
61− }
62−
63− function notifyAuthenticationFailed() {
64− loginList.showError();
65− }
66−
67− function showErrorMessage(msg) {
68− coverPage.showErrorMessage(msg);
69− }
70−
71− function forceShow() {
72− coverPage.show();
73− }
74−
75− function tryToUnlock(toTheRight) {
76− var coverChanged = coverPage.shown;
77− lockscreen.maybeShow();
78− if (toTheRight) {
79− coverPage.hideRight();
80− } else {
81− coverPage.hide();
82− }
83− return coverChanged;
84− }
85−
86− onLockedChanged: {
87− if (locked) {
88− lockscreen.maybeShow();
89− } else {
90− lockscreen.hide();
91− }
92− }
93−
94− Keys.onSpacePressed: coverPage.hide();
95− Keys.onReturnPressed: coverPage.hide();
96− Keys.onEnterPressed: coverPage.hide();
97−
98− Showable {
99− id: lockscreen
100− objectName: "lockscreen"
101− anchors.fill: parent
102− shown: false
103− opacity: 0
104−
105− showAnimation: StandardAnimation { property: "opacity"; to: 1 }
106− hideAnimation: StandardAnimation { property: "opacity"; to: 0 }
107−
108− Rectangle {
109− // In case background fails to load or doesn't cover the whole screen
110− id: backgroundBackup
111− anchors.fill: parent
112− color: "black"
113− }
114−
115− Wallpaper {
116− id: lockscreenBackground
117− objectName: "lockscreenBackground"
118− anchors {
119− fill: parent
120− }
121− source: root.background
122− sourceSize: root.backgroundSourceSize
123− }
124−
125− // Darken background to match CoverPage
126− Rectangle {
127− objectName: "lockscreenShade"
128− anchors.fill: parent
129− color: "black"
130− opacity: root.hasCustomBackground ? 0.4 : 0
131− }
132−
133− LoginList {
134− id: loginList
135− objectName: "loginList"
136−
137− anchors {
138− horizontalCenter: parent.horizontalCenter
139− top: parent.top
140− bottom: parent.bottom
141− }
142− width: units.gu(40)
143− boxVerticalOffset: units.gu(14)
144− enabled: !coverPage.shown && visible
145− visible: !delayedLockscreen.visible
146−
147− locked: root.locked
148−
149− onSelected: if (enabled) root.selected(index)
150− onResponded: root.responded(response)
151− }
152−
153− DelayedLockscreen {
154− id: delayedLockscreen
155− objectName: "delayedLockscreen"
156− anchors.fill: parent
157− visible: delayMinutes > 0
158− alphaNumeric: loginList.alphanumeric
159− }
160−
161− function maybeShow() {
162− if (root.locked && !shown) {
163− showNow();
164− }
165− }
166− }
167−
168− Rectangle {
169− anchors.fill: parent
170− color: "black"
171− opacity: coverPage.showProgress * 0.8
172− }
173−
174− CoverPage {
175− id: coverPage
176− objectName: "coverPage"
177− height: parent.height
178− width: parent.width
179− background: root.background
180− hasCustomBackground: root.hasCustomBackground
181− panelHeight: root.panelHeight
182− draggable: !root.waiting
183− onTease: root.tease()
184− onClicked: hide()
185− backgroundSourceSize: root.backgroundSourceSize
186−
187− onShowProgressChanged: {
188− if (showProgress === 0) {
189− if (lockscreen.shown) {
190− loginList.tryToUnlock();
191− } else {
192− root.responded("");
193− }
194− }
195− }
196−
197− Clock {
198− anchors {
199− top: parent.top
200− topMargin: units.gu(2) + panelHeight
201− horizontalCenter: parent.horizontalCenter
202− }
203− }
204− }
205−
206− StyledItem {
207− id: bottomBar
208− visible: lockscreen.shown
209− height: units.gu(4)
210−
211− anchors.left: parent.left
212− anchors.right: parent.right
213− anchors.top: parent.bottom
214− anchors.topMargin: - height * (1 - coverPage.showProgress)
215− - ( inputMethodRect.height )
216−
217− Label {
218− text: i18n.tr("Cancel")
219− anchors.left: parent.left
220− anchors.leftMargin: units.gu(2)
221− anchors.top: parent.top
222− anchors.bottom: parent.bottom
223− verticalAlignment: Text.AlignVCenter
224− font.weight: Font.Light
225− fontSize: "small"
226− color: theme.palette.normal.raisedSecondaryText
227−
228− AbstractButton {
229− anchors.fill: parent
230− anchors.leftMargin: -units.gu(2)
231− anchors.rightMargin: -units.gu(2)
232− onClicked: coverPage.show()
233− }
234− }
235−
236− Label {
237− objectName: "emergencyCallLabel"
238− text: callManager.hasCalls ? i18n.tr("Return to Call") : i18n.tr("Emergency")
239− anchors.right: parent.right
240− anchors.rightMargin: units.gu(2)
241− anchors.top: parent.top
242− anchors.bottom: parent.bottom
243− verticalAlignment: Text.AlignVCenter
244− font.weight: Font.Light
245− fontSize: "small"
246− color: theme.palette.normal.raisedSecondaryText
247− // TODO: uncomment once bug 1616538 is fixed
248− // visible: telepathyHelper.ready && telepathyHelper.emergencyCallsAvailable
249− enabled: visible
250−
251− AbstractButton {
252− anchors.fill: parent
253− anchors.leftMargin: -units.gu(2)
254− anchors.rightMargin: -units.gu(2)
255− onClicked: root.emergencyCall()
256− }
257− }
258− }
259−
260− // FIXME: It's difficult to keep something tied closely to the OSK (bug
261− // 1616163). But as a hack to avoid the background peeking out,
262− // we add an extra Rectangle that just serves to hide the background
263− // during OSK animations.
264− Rectangle {
265− visible: bottomBar.visible
266− height: inputMethodRect.height
267− anchors.bottom: parent.bottom
268− anchors.left: parent.left
269− anchors.right: parent.right
270− color: UbuntuColors.porcelain
271− }
272−}
qml/Greeter/WideView.qmldeleted−281
@@ -1,281 +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 hasKeyboard: loginList.hasKeyboard
39− property alias locked: loginList.locked
40− property alias waiting: loginList.waiting
41− property var userModel // Set from outside
42−
43− readonly property bool animating: coverPage.showAnimation.running || coverPage.hideAnimation.running
44− readonly property bool fullyShown: coverPage.showProgress === 1
45− readonly property bool required: coverPage.required
46− readonly property alias sessionToStart: loginList.currentSession
47−
48− property rect inputMethodRect
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− blurAreaHeight: loginList.highlightedHeight + units.gu(4.5)
101− blurAreaWidth: loginList.width + units.gu(3)
102− blurAreaX: loginList.x - units.gu(1.5)
103− blurAreaY: loginList.boxVerticalOffset + loginList.y - units.gu(3)
104−
105− // Darken background to match CoverPage
106− Rectangle {
107− objectName: "lockscreenShade"
108− anchors.fill: parent
109− color: "black"
110− opacity: root.hasCustomBackground ? 0.1 : 0
111− }
112−
113− infographics {
114− anchors.topMargin: parent.height * 0.125
115− anchors.bottomMargin: parent.height * 0.125
116− anchors.leftMargin: loginList.x + loginList.width
117− }
118−
119− onTease: root.tease()
120−
121− onShowProgressChanged: {
122− if (showProgress === 0 && !root.locked) {
123− root.responded("");
124− }
125− }
126−
127− LoginList {
128− id: loginList
129− objectName: "loginList"
130−
131− width: units.gu(40)
132− anchors {
133− left: parent.left
134− leftMargin: Math.min(parent.width * 0.16, units.gu(20))
135− top: parent.top
136− bottom: parent.bottom
137− }
138−
139− boxVerticalOffset: (height - highlightedHeight -
140− inputMethodRect.height) / 2
141− Behavior on boxVerticalOffset { UbuntuNumberAnimation {} }
142−
143− model: root.userModel
144− onResponded: root.responded(response)
145− onSelected: root.selected(index)
146− onSessionChooserButtonClicked: parent.state = "SessionsList"
147− onCurrentIndexChanged: setCurrentSession()
148−
149− Keys.forwardTo: [sessionChooserLoader.item]
150−
151− Component.onCompleted: setCurrentSession()
152−
153− function setCurrentSession() {
154− currentSession = LightDMService.users.data(currentIndex, LightDMService.userRoles.SessionRole);
155− }
156− }
157−
158− Loader {
159− id: sessionChooserLoader
160−
161− height: loginList.height
162− width: loginList.width
163−
164− anchors {
165− left: parent.left
166− leftMargin: Math.min(parent.width * 0.16, units.gu(20))
167− top: parent.top
168− }
169−
170− active: false
171−
172− onLoaded: sessionChooserLoader.item.forceActiveFocus();
173− onActiveChanged: {
174− if (!active) return;
175− item.updateHighlight(loginList.currentSession);
176− }
177−
178− Connections {
179− target: sessionChooserLoader.item
180− onSessionSelected: loginList.currentSession = sessionKey
181− onShowLoginList: {
182− coverPage.state = "LoginList"
183− loginList.tryToUnlock();
184− }
185− ignoreUnknownSignals: true
186− }
187− }
188−
189− // Use an AbstractButton due to icon limitations with Button
190− AbstractButton {
191− id: sessionChooser
192− objectName: "sessionChooserButton"
193−
194− readonly property url icon: LightDMService.sessions.iconUrl(loginList.currentSession)
195−
196− visible: LightDMService.sessions.count > 1 &&
197− !LightDMService.users.data(loginList.currentUserIndex, LightDMService.userRoles.LoggedInRole)
198−
199− height: units.gu(3.5)
200− width: units.gu(3.5)
201−
202− activeFocusOnTab: true
203− anchors {
204− right: parent.right
205− rightMargin: units.gu(2)
206−
207− bottom: parent.bottom
208− bottomMargin: units.gu(1.5)
209− }
210−
211− Rectangle {
212− id: badgeHighlight
213−
214− anchors.fill: parent
215− visible: parent.activeFocus
216− color: "transparent"
217− border.color: theme.palette.normal.focus
218− border.width: units.dp(1)
219− radius: 3
220− }
221−
222− Icon {
223− id: badge
224− anchors.fill: parent
225− anchors.margins: units.dp(3)
226− keyColor: "#ffffff" // icon providers give us white icons
227− color: theme.palette.normal.raisedSecondaryText
228− source: sessionChooser.icon
229− }
230−
231− Keys.onReturnPressed: {
232− parent.state = "SessionsList";
233− }
234−
235− onClicked: {
236− parent.state = "SessionsList";
237− }
238−
239− // Refresh the icon path if looking at different places at runtime
240− // this is mainly for testing
241− Connections {
242− target: LightDMService.sessions
243− onIconSearchDirectoriesChanged: {
244− badge.source = LightDMService.sessions.iconUrl(root.currentSession)
245− }
246− }
247− }
248−
249− states: [
250− State {
251− name: "SessionsList"
252− PropertyChanges { target: loginList; opacity: 0 }
253− PropertyChanges { target: sessionChooserLoader;
254− active: true;
255− opacity: 1
256− source: "SessionsList.qml"
257− }
258− },
259−
260− State {
261− name: "LoginList"
262− PropertyChanges { target: loginList; opacity: 1 }
263− PropertyChanges { target: sessionChooserLoader;
264− active: false;
265− opacity: 0
266− source: "";
267− }
268− }
269− ]
270−
271− transitions: [
272− Transition {
273− from: "*"
274− to: "*"
275− UbuntuNumberAnimation {
276− property: "opacity";
277− }
278− }
279− ]
280− }
281−}
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+4−2
@@ -93,7 +93,7 @@ StyledItem {
93
93
94 readonly property bool orientationChangesEnabled: panel.indicators.fullyClosed
94 readonly property bool orientationChangesEnabled: panel.indicators.fullyClosed
95 && stage.orientationChangesEnabled
95 && stage.orientationChangesEnabled
96− && (!greeter || !greeter.animating)
96+ && (!greeter.animating)
97
97
98 readonly property bool showingGreeter: greeter && greeter.shown
98 readonly property bool showingGreeter: greeter && greeter.shown
99
99
@@ -104,7 +104,7 @@ StyledItem {
104 if (startingUp) {
104 if (startingUp) {
105 // Ensure we don't rotate during start up
105 // Ensure we don't rotate during start up
106 return Qt.PrimaryOrientation;
106 return Qt.PrimaryOrientation;
107− } else if (showingGreeter || notifications.topmostIsFullscreen) {
107+ } else if (notifications.topmostIsFullscreen) {
108 return Qt.PrimaryOrientation;
108 return Qt.PrimaryOrientation;
109 } else {
109 } else {
110 return shell.orientations ? shell.orientations.map(stage.supportedOrientations) : Qt.PrimaryOrientation;
110 return shell.orientations ? shell.orientations.map(stage.supportedOrientations) : Qt.PrimaryOrientation;
@@ -436,6 +436,8 @@ StyledItem {
436 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
436 enabled: panel.indicators.fullyClosed // hides OSK when panel is open
437 hides: [launcher, panel.indicators, panel.applicationMenus]
437 hides: [launcher, panel.indicators, panel.applicationMenus]
438 tabletMode: shell.usageScenario != "phone"
438 tabletMode: shell.usageScenario != "phone"
439+ usageMode: shell.usageScenario
440+ orientation: shell.orientation
439 forcedUnlock: wizard.active || shell.mode === "full-shell"
441 forcedUnlock: wizard.active || shell.mode === "full-shell"
440 background: wallpaperResolver.background
442 background: wallpaperResolver.background
441 backgroundSourceSize: shell.largestScreenDimension
443 backgroundSourceSize: shell.largestScreenDimension
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: false,
1299+ lockscreen: true
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−801
@@ -1,801 +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− inputMethodRect: fakeKeyboard.childrenRect
59− property var testKeyboard: fakeKeyboard
60−
61− Component.onDestruction: {
62− loader.itemDestroyed = true
63− }
64−
65− onSelected: {
66− if (index >= 0)
67− currentIndexField.text = index;
68− }
69−
70− Rectangle {
71− id: fakeKeyboard
72− color: "green"
73− opacity: 0.7
74− anchors.bottom: view.bottom
75− width: view.width
76− height: visible ? view.height * 0.6 : 0
77− visible: keyboardVisibleCheckBox.checked
78− Text {
79− text: "Keyboard Rectangle"
80− color: "yellow"
81− font.bold: true
82− fontSizeMode: Text.Fit
83− minimumPixelSize: 10; font.pixelSize: 200
84− verticalAlignment: Text.AlignVCenter
85− x: (parent.width - width) / 2
86− y: (parent.height - height) / 2
87− width: parent.width
88− height: parent.height
89− }
90− }
91−
92− }
93− }
94− }
95−
96− Rectangle {
97− id: controls
98− color: theme.palette.normal.background
99− width: units.gu(40)
100− height: parent.height
101− Column {
102− anchors { left: parent.left; right: parent.right; top: parent.top; margins: units.gu(1) }
103− spacing: units.gu(1)
104− Row {
105− Button {
106− text: "Hide"
107− onClicked: loader.item.hide()
108− }
109− }
110− Row {
111− Button {
112− text: "Show Message"
113− onClicked: LightDMService.prompts.append(messageField.text, LightDMService.prompts.Message)
114− }
115− TextField {
116− id: messageField
117− width: units.gu(10)
118− text: ""
119− }
120− }
121− Row {
122− Button {
123− text: "Show Prompt"
124− onClicked: LightDMService.prompts.append(promptField.text, isSecretCheckBox.checked ? LightDMService.prompts.Secret : LightDMService.prompts.Question)
125− }
126− TextField {
127− id: promptField
128− width: units.gu(10)
129− text: ""
130− }
131− CheckBox {
132− id: isSecretCheckBox
133− }
134− Label {
135− text: "secret"
136− }
137− }
138− Row {
139− Button {
140− text: "Notify Auth Failure"
141− onClicked: {
142− loader.item.notifyAuthenticationFailed();
143− }
144− }
145− }
146− Row {
147− Button {
148− text: "Try To Unlock"
149− onClicked: loader.item.tryToUnlock(toTheRightCheckBox.checked)
150− }
151− CheckBox {
152− id: toTheRightCheckBox
153− }
154− Label {
155− text: "toTheRight"
156− }
157− }
158− Row {
159− TextField {
160− id: launcherOffsetField
161− width: units.gu(10)
162− text: "0"
163− }
164− Label {
165− text: "launcherOffset"
166− }
167− }
168− Row {
169− TextField {
170− id: currentIndexField
171− width: units.gu(10)
172− text: "0"
173− }
174− Label {
175− text: "currentIndex"
176− }
177− }
178− Row {
179− TextField {
180− id: delayMinutesField
181− width: units.gu(10)
182− text: "0"
183− }
184− Label {
185− text: "delayMinutes"
186− }
187− }
188− Row {
189− TextField {
190− id: panelHeightField
191− width: units.gu(10)
192− text: "0"
193− }
194− Label {
195− text: "panelHeight"
196− }
197− }
198− Row {
199− CheckBox {
200− id: lockedCheckBox
201− }
202− Label {
203− text: "locked"
204− }
205− }
206− Row {
207− Label {
208− text: "selected: " + selectedSpy.count
209− }
210− }
211− Row {
212− Label {
213− text: "responded: " + respondedSpy.count
214− }
215− }
216− Row {
217− Label {
218− text: "teased: " + teaseSpy.count
219− }
220− }
221− Row {
222− Label {
223− text: "emergency: " + emergencySpy.count
224− }
225− }
226− Row {
227− id: multipleSessions
228− CheckBox {
229− id: multipleSessionsCheckbox
230− onClicked: {
231− if (checked) {
232− LightDMController.sessionMode = "full";
233− } else {
234− LightDMController.sessionMode = "single";
235− }
236− }
237− Connections {
238− target: LightDMController
239− onSessionModeChanged: {
240− if (LightDMController.sessionMode === "full") {
241− multipleSessionsCheckbox.checked = true;
242− } else {
243− multipleSessionsCheckbox.checked = false;
244− }
245− }
246− }
247− }
248− Label {
249− text: "Multiple Sessions"
250− }
251− }
252− Row {
253− Slider {
254− id: numSessionsSlider
255−
256− width: units.gu(10)
257− minimumValue: 0
258− maximumValue: LightDMController.numAvailableSessions
259− value: LightDMController.numSessions
260− visible: LightDMController.sessionMode === "full"
261− Binding {
262− target: LightDMController
263− property: "numSessions"
264− value: numSessionsSlider.value
265− }
266− }
267− Label {
268− text: "Available Sessions"
269− }
270− }
271− Row {
272− Button {
273− text: "Reload View"
274− onClicked: {
275− loader.active = false;
276− loader.active = true;
277− }
278− }
279− }
280− Row {
281− CheckBox {
282− id: keyboardVisibleCheckBox
283− }
284− Label {
285− text: "Keyboard Visible"
286− }
287− }
288− }
289− }
290− }
291−
292− SignalSpy {
293− id: selectedSpy
294− target: loader.item
295− signalName: "selected"
296− }
297−
298− SignalSpy {
299− id: respondedSpy
300− target: loader.item
301− signalName: "responded"
302− }
303−
304− SignalSpy {
305− id: teaseSpy
306− target: loader.item
307− signalName: "tease"
308− }
309−
310− SignalSpy {
311− id: emergencySpy
312− target: loader.item
313− signalName: "emergencyCall"
314− }
315−
316− UT.UnityTestCase {
317− name: "WideView"
318− when: windowShown
319−
320− property Item view: loader.status === Loader.Ready ? loader.item : null
321− property url testIconDirectory: Qt.resolvedUrl("../../../qml/Greeter/graphics/session_icons")
322−
323− function init() {
324− selectIndex(0); // break binding with text field
325− tryCompare(LightDMService.prompts, "count", 1);
326− selectedSpy.clear();
327− respondedSpy.clear();
328− teaseSpy.clear();
329− emergencySpy.clear();
330− LightDMController.reset();
331− LightDM.Sessions.iconSearchDirectories = [testIconDirectory];
332−
333− waitForRendering(view);
334− var userList = findChild(view, "userList");
335− tryCompare(userList, "movingInternally", false);
336− }
337−
338− function cleanup() {
339− keyboardVisibleCheckBox.checked = false;
340−
341− loader.itemDestroyed = false;
342− loader.active = false;
343− tryCompare(loader, "status", Loader.Null);
344− tryCompare(loader, "item", null);
345− tryCompare(loader, "itemDestroyed", true);
346− loader.active = true;
347− tryCompare(loader, "status", Loader.Ready);
348− removeTimeConstraintsFromSwipeAreas(loader.item);
349− LightDMController.sessionMode = "single";
350− }
351−
352− function getIndexOf(name) {
353− for (var i = 0; i < LightDM.Users.count; i++) {
354− if (name === LightDM.Users.data(i, LightDM.UserRoles.NameRole)) {
355− return i;
356− }
357− }
358− fail("Didn't find name")
359− return -1;
360− }
361−
362− function selectIndex(i) {
363− var user = LightDM.Users.data(i, LightDM.UserRoles.NameRole);
364− LightDM.Greeter.authenticate(user);
365−
366− var userList = findChild(view, "userList");
367− var promptList = findChild(view, "promptList");
368−
369− view.currentIndex = i;
370− tryCompare(promptList, "opacity", 1);
371− tryCompare(userList, "movingInternally", false);
372− }
373−
374− function selectUser(name) {
375− var i = getIndexOf(name);
376− selectIndex(i);
377− return i;
378− }
379−
380− function swipeAwayCover() {
381− tryCompare(view, "fullyShown", true);
382− var touchY = view.height / 2;
383− touchFlick(view, view.width, touchY, 0, touchY);
384− var coverPage = findChild(view, "coverPage");
385− tryCompare(coverPage, "showProgress", 0);
386− waitForRendering(view);
387− }
388−
389− function test_sessionless_user_is_still_valid() {
390− var loginList = findChild(view, "loginList")
391−
392− /*
393− * If a user has never logged in before, or, for some reason
394− * has no sessionHint, ensure that the model returns the default
395− * session and that the view respects this
396− */
397− selectUser("no-session");
398− compare(LightDM.Users.data(loginList.currentIndex, LightDM.UserRoles.SessionRole), LightDM.Greeter.defaultSession);
399−
400− tryCompare(loginList, "currentSession", LightDM.Greeter.defaultSession);
401− }
402−
403− function test_changingSessionSticksToUser() {
404− var loginList = findChild(view, "loginList");
405−
406− selectUser("invalid-session");
407− tryCompare(loginList, "currentSession", "invalid");
408−
409− selectUser("has-password");
410− tryCompare(loginList, "currentSession", "ubuntu");
411−
412− selectUser("invalid-session")
413− tryCompare(loginList, "currentSession", "invalid");
414− }
415−
416− function test_tease_data() {
417− return [
418− {tag: "locked", x: 0, offset: 0, count: 0, locked: true},
419− {tag: "left", x: 0, offset: 0, count: 1, locked: false},
420− {tag: "leftWithOffsetPass", x: 10, offset: 10, count: 1, locked: false},
421− {tag: "leftWithOffsetFail", x: 9, offset: 10, count: 0, locked: false},
422− {tag: "right", x: view.width, offset: 0, count: 1, locked: false},
423− ]
424− }
425−
426− function test_sessionIconsAreValid() {
427− LightDMController.sessionMode = "full";
428− selectUser("has-password");
429−
430− // Test the login list icon is valid
431− var sessionChooserButton = findChild(view, "sessionChooserButton");
432− compare(sessionChooserButton.visible, true);
433−
434− var loginList = findChild(view, "loginList");
435− var session = String(loginList.currentSession).toLowerCase();
436− var icon = String(sessionChooserButton.icon);
437− compare(icon.indexOf(session) > -1, true);
438−
439− // Test the session list icons are valid
440− var coverPage = findChild(view, "coverPage");
441− coverPage.state = "SessionsList"
442− var sessionsList = findChild(view, "sessionsList");
443− tryCompare(sessionsList, "visible", true);
444− }
445−
446− function test_choosingNewSessionChangesLoginListIcon() {
447− // Ensure the default session is selected (Ubuntu)
448− cleanup();
449−
450− LightDMController.sessionMode = "full";
451− selectUser("has-password");
452−
453− var sessionChooserButton = findChild(view, "sessionChooserButton");
454− var icon = String(sessionChooserButton.icon);
455− compare(icon.indexOf("ubuntu") > -1, true);
456−
457− tap(sessionChooserButton)
458− for(var i = 0; i < LightDM.Sessions.count; i++) {
459− var delegateName = "sessionDelegate" + String(i);
460− var currentDelegate = findChild(view, delegateName);
461− var sessionKey = LightDM.Sessions.data(i,LightDM.SessionRoles.KeyRole);
462− if (sessionKey === "gnome-classic") {
463− waitForRendering(currentDelegate);
464− tap(currentDelegate);
465− waitForRendering(sessionChooserButton);
466− break;
467− }
468− }
469−
470− icon = String(sessionChooserButton.icon);
471− compare(icon.indexOf("gnome") > -1, true,
472− "Expected icon to contain gnome but it was " + icon);
473− }
474−
475− function test_noSessionsDoesntBreakView() {
476− LightDMController.sessionMode = "none";
477− compare(LightDM.Sessions.count, 0)
478− }
479−
480− function test_sessionIconNotShownWithOneSession() {
481− LightDMController.sessionMode = "single";
482− compare(LightDM.Sessions.count, 1);
483−
484− var sessionChooserButton = findChild(view, "sessionChooserButton");
485− tryCompare(sessionChooserButton, "visible", false);
486− }
487−
488− function test_sessionIconNotShownWithActiveUser() {
489− selectUser("active");
490−
491− var sessionChooserButton = findChild(view, "sessionChooserButton");
492− tryCompare(sessionChooserButton, "visible", false);
493− }
494−
495− function test_sessionIconShownWithMultipleSessions() {
496− LightDMController.sessionMode = "full";
497− selectUser("has-password");
498−
499− var sessionChooserButton = findChild(view, "sessionChooserButton");
500− tryCompare(sessionChooserButton, "visible", true);
501− }
502−
503− function test_tease(data) {
504− view.locked = data.locked;
505− view.dragHandleLeftMargin = data.offset;
506− tap(view, data.x, 0);
507− compare(teaseSpy.count, data.count);
508− }
509−
510− function test_selected() {
511− var delegate = findChild(view, "username2");
512− tap(delegate);
513− compare(selectedSpy.count, 1);
514− compare(selectedSpy.signalArguments[0][0], 2);
515− compare(view.currentIndex, 0); // confirm we didn't change
516− }
517−
518− function test_customBackground() {
519− var coverPage = findChild(view, "coverPage");
520− var backgroundShade = findChild(coverPage, "backgroundShade");
521−
522− verify(!view.hasCustomBackground);
523− verify(!backgroundShade.visible);
524−
525− view.hasCustomBackground = true;
526− verify(backgroundShade.visible);
527− }
528−
529− function test_respondedWithPassword() {
530− selectUser("has-password");
531− var greeterPrompt = findChild(view, "greeterPrompt0");
532− verify(greeterPrompt.isSecret);
533− typeString("password");
534− keyClick(Qt.Key_Enter);
535− compare(respondedSpy.count, 1);
536− compare(respondedSpy.signalArguments[0][0], "password");
537− }
538−
539− function test_respondedWithNonSecret() {
540− selectUser("question-prompt");
541− var greeterPrompt = findChild(view, "greeterPrompt0");
542− verify(!greeterPrompt.isSecret);
543− typeString("foo");
544− keyClick(Qt.Key_Enter);
545− compare(respondedSpy.count, 1);
546− compare(respondedSpy.signalArguments[0][0], "foo");
547− }
548−
549− function test_respondedWithSwipe() {
550− swipeAwayCover();
551− compare(respondedSpy.count, 1);
552− compare(respondedSpy.signalArguments[0][0], "");
553− }
554−
555− function test_fullyShown() {
556− tryCompare(view, "fullyShown", true);
557− swipeAwayCover();
558− tryCompare(view, "fullyShown", false);
559− }
560−
561− function test_required() {
562− tryCompare(view, "required", true);
563− swipeAwayCover();
564− tryCompare(view, "required", false);
565− }
566−
567− function test_infoPrompt() {
568− selectUser("info-prompt");
569−
570− var infoLabel = findChild(view, "infoLabel0");
571− compare(infoLabel.text, "Welcome to Unity Greeter");
572− compare(infoLabel.textFormat, Text.PlainText);
573−
574− verify(findChild(view, "greeterPrompt1") != null);
575− }
576−
577− function test_longInfoPrompt() {
578− selectUser("long-info-prompt");
579−
580− var infoLabel = findChild(view, "infoLabel0");
581− 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.");
582− verify(infoLabel.contentWidth > infoLabel.width);
583−
584− verify(findChild(view, "greeterPrompt1") != null);
585− }
586−
587− function test_multiInfoPrompt() {
588− selectUser("multi-info-prompt");
589−
590− var infoLabel0 = findChild(view, "infoLabel0");
591− compare(infoLabel0.text, "Welcome to Unity Greeter");
592−
593− var infoLabel1 = findChild(view, "infoLabel1");
594− compare(infoLabel1.text, "This is an error");
595− compare(infoLabel1.color, theme.palette.normal.negative);
596−
597− var infoLabel2 = findChild(view, "infoLabel2");
598− compare(infoLabel2.text, "You should have seen three messages");
599−
600− verify(findChild(view, "greeterPrompt3") != null);
601− }
602−
603− // Escape is used to reset the authentication, especially if PAM is unresponsive
604− function test_escape() {
605− var index = selectUser("has-password");
606− selectedSpy.clear();
607− view.locked = true;
608− var promptField = findChild(view, "promptField");
609− tap(promptField);
610− verify(promptField.activeFocus);
611− compare(promptField.opacity, 1);
612−
613− typeString("password");
614− keyClick(Qt.Key_Enter);
615− verify(promptField.activeFocus);
616− compare(promptField.opacity, 0); // hidden by fakeLabel
617−
618− compare(selectedSpy.count, 0);
619− keyClick(Qt.Key_Escape);
620− compare(selectedSpy.count, 1);
621− compare(selectedSpy.signalArguments[0][0], index);
622−
623− selectIndex(index);
624− var promptField = findChild(view, "promptField");
625− verify(promptField.activeFocus);
626− compare(promptField.opacity, 1);
627− }
628−
629− function test_unicode() {
630− var index = selectUser("unicode");
631− var label = findChild(view, "username" + index);
632− tryCompare(label, "text", "가나다라마");
633− }
634−
635− function test_authError() {
636− var index = selectUser("auth-error");
637− var greeterPrompt = findChild(view, "greeterPrompt1"); // after error message
638− view.locked = true;
639− compare(greeterPrompt.text, "Retry");
640− tap(greeterPrompt);
641− compare(respondedSpy.count, 0);
642− compare(selectedSpy.count, 1);
643− compare(selectedSpy.signalArguments[0][0], index);
644− }
645−
646− function test_noPassword() {
647− selectUser("no-password");
648− var greeterPrompt = findChild(view, "greeterPrompt0");
649− compare(greeterPrompt.text, "Log In");
650− tap(greeterPrompt);
651− compare(selectedSpy.count, 0);
652− compare(respondedSpy.count, 1);
653− compare(respondedSpy.signalArguments[0][0], "");
654− }
655−
656− function test_loginListNotCoveredByKeyboard() {
657− var loginList = findChild(view, "loginList");
658− compare(loginList.height, view.height);
659−
660− // when the vkb shows up, loginList is moved up to remain fully uncovered
661−
662− keyboardVisibleCheckBox.checked = true;
663−
664− var halfway = (view.height - loginList.highlightedHeight) / 2;
665− var halfwayWithOsk = halfway - view.inputMethodRect.height / 2;
666− tryCompare(loginList, "boxVerticalOffset", halfwayWithOsk);
667−
668− var highlightItem = findChild(loginList, "highlightItem");
669− tryCompareFunction( function() {
670− var highlightRect = highlightItem.mapToItem(view, 0, 0, highlightItem.width, highlightItem.height);
671− return highlightRect.y + highlightRect.height <= view.testKeyboard.y;
672− }, true);
673−
674− // once the vkb goes away, loginList goes back to its full height
675−
676− keyboardVisibleCheckBox.checked = false;
677−
678− tryCompare(loginList, "boxVerticalOffset", halfway);
679− }
680−
681− function test_passphrase() {
682− var index = selectUser("has-password");
683− var promptField = findChild(view, "promptField");
684−
685− verify(view.alphanumeric);
686− compare(promptField.inputMethodHints & Qt.ImhDigitsOnly, 0);
687−
688− keyClick(Qt.Key_D);
689− compare(promptField.text, "d");
690− }
691−
692− function test_passcode() {
693− var index = selectUser("has-pin");
694− view.alphanumeric = false;
695−
696− var promptField = findChild(view, "promptField");
697− compare(promptField.inputMethodHints & Qt.ImhDigitsOnly, Qt.ImhDigitsOnly);
698−
699− keyClick(Qt.Key_D);
700− compare(promptField.text, "");
701−
702− keyClick(Qt.Key_0);
703− keyClick(Qt.Key_0);
704− keyClick(Qt.Key_0);
705− keyClick(Qt.Key_0);
706− compare(promptField.text, "0000");
707−
708− compare(respondedSpy.count, 1);
709− compare(respondedSpy.signalArguments[0][0], "0000");
710−
711− compare(promptField.opacity, 0);
712− }
713−
714− function test_loginListMovement_data() {
715− return [
716− {tag: "up", key: Qt.Key_Up, result: 1},
717− {tag: "down", key: Qt.Key_Down, result: 3},
718− ]
719− }
720−
721− function test_loginListMovement(data) {
722− selectIndex(2);
723− selectedSpy.clear();
724−
725− keyClick(data.key);
726− compare(selectedSpy.count, 1);
727− compare(selectedSpy.signalArguments[0][0], data.result);
728− }
729−
730− function test_focusStaysActive() {
731− selectUser("no-password");
732− var greeterPrompt = findChild(view, "greeterPrompt0");
733− verify(greeterPrompt.activeFocus);
734− keyClick(Qt.Key_Enter);
735− compare(selectedSpy.count, 0);
736− compare(respondedSpy.count, 1);
737− compare(respondedSpy.signalArguments[0][0], "");
738− verify(greeterPrompt.activeFocus);
739− keyClick(Qt.Key_Enter);
740− compare(respondedSpy.count, 1);
741−
742− selectUser("has-password");
743− var greeterPrompt = findChild(view, "greeterPrompt0");
744− verify(greeterPrompt.activeFocus);
745− keyClick(Qt.Key_D);
746− keyClick(Qt.Key_Enter);
747− compare(selectedSpy.count, 0);
748− compare(respondedSpy.count, 2);
749− compare(respondedSpy.signalArguments[1][0], "d");
750− verify(greeterPrompt.activeFocus);
751− keyClick(Qt.Key_Enter);
752− compare(respondedSpy.count, 2);
753−
754− var index = selectUser("no-password");
755− var greeterPrompt = findChild(view, "greeterPrompt0");
756− view.locked = true;
757− verify(greeterPrompt.activeFocus);
758− keyClick(Qt.Key_Enter);
759− compare(respondedSpy.count, 2);
760− compare(selectedSpy.count, 1);
761− compare(selectedSpy.signalArguments[0][0], index);
762− verify(greeterPrompt.activeFocus);
763− keyClick(Qt.Key_Enter);
764− compare(selectedSpy.count, 1);
765−
766− selectUser("no-password");
767− var greeterPrompt = findChild(view, "greeterPrompt0");
768− verify(greeterPrompt.activeFocus);
769− }
770−
771− function test_scrollChangesIndex() {
772− /* Check if the index of the current user gets changed
773− * if we scroll through the list.
774− * Expected result: the list has moved at least by one
775− * user and the index of the user is reported using the
776− * selected signal in LoginList.qml
777− * https://github.com/ubports/unity8/issues/397
778− */
779− var loginList = findChild(view, "loginList");
780− // FIXME: Fix scrolling sensitivity (scrolling by -1 shouldn't move more than one user down)
781− mouseWheel( loginList, loginList.width / 2, loginList.height / 2, 0, -1, null );
782− selectedSpy.wait();
783− tryVerify(function(){ return selectedSpy.signalArguments[0][0] > 0 });
784− }
785−
786− function test_dragChangesIndex() {
787− /* Check if the index of the current user gets changed
788− * if we drag the list or swipe.on it.
789− * Expected result: the list has moved at least by one
790− * user and the index of the user is reported using the
791− * selected signal in LoginList.qml
792− * https://github.com/ubports/unity8/issues/397
793− */
794− var loginList = findChild(view, "loginList");
795− // FIXME: Fix scrolling sensitivity (the number of scrolled users is randomly changing)
796− touchFlick(loginList, loginList.width/2, loginList.height/3, loginList.width/2, loginList.height/3 -units.gu(2.1));
797− selectedSpy.wait();
798− tryVerify(function(){ return selectedSpy.signalArguments[0][0] > 0 });
799− }
800− }
801−}
tests/qmltests/tst_OrientedShell.qml+1−38
@@ -619,43 +619,6 @@ Rectangle {
619 compare(shell.transformRotationAngle, root.primaryOrientationAngle + 180);
619 compare(shell.transformRotationAngle, root.primaryOrientationAngle + 180);
620 }
620 }
621
621
622− function test_greeterRemainsInPrimaryOrientation_data() {
623− return [
624− {tag: "mako", deviceName: "mako"},
625− {tag: "manta", deviceName: "manta"},
626− {tag: "flo", deviceName: "flo"}
627− ];
628− }
629− function test_greeterRemainsInPrimaryOrientation(data) {
630− var orientedShell = loadShell(data.deviceName);
631− var shell = findChild(orientedShell, "shell");
632−
633− var gmailApp = ApplicationManager.startApplication("gmail-webapp");
634− verify(gmailApp);
635−
636− // ensure the mock gmail-webapp is as we expect
637− compare(gmailApp.rotatesWindowContents, false);
638− compare(gmailApp.supportedOrientations, Qt.PortraitOrientation | Qt.LandscapeOrientation
639− | Qt.InvertedPortraitOrientation | Qt.InvertedLandscapeOrientation);
640−
641− // wait until it's able to rotate
642− tryCompare(shell, "orientationChangesEnabled", true);
643−
644− compare(shell.transformRotationAngle, root.primaryOrientationAngle);
645− rotateTo(90, orientedShell);
646− tryCompare(shell, "transformRotationAngle", root.primaryOrientationAngle + 90);
647−
648− showGreeter(orientedShell);
649−
650− tryCompare(shell, "transformRotationAngle", root.primaryOrientationAngle);
651− rotateTo(180, orientedShell);
652− compare(shell.transformRotationAngle, root.primaryOrientationAngle);
653− rotateTo(270, orientedShell);
654− compare(shell.transformRotationAngle, root.primaryOrientationAngle);
655− rotateTo(0, orientedShell);
656− compare(shell.transformRotationAngle, root.primaryOrientationAngle);
657− }
658−
659 function test_appRotatesWindowContents_data() {
622 function test_appRotatesWindowContents_data() {
660 return [
623 return [
661 {tag: "mako", deviceName: "mako", orientationAngleAfterRotation: 90},
624 {tag: "mako", deviceName: "mako", orientationAngleAfterRotation: 90},
@@ -1247,7 +1210,7 @@ Rectangle {
1247
1210
1248 showGreeter(orientedShell);
1211 showGreeter(orientedShell);
1249
1212
1250− tryCompare(shell, "transformRotationAngle", root.primaryOrientationAngle);
1213+ tryCompare(shell, "transformRotationAngle", root.primaryOrientationAngle + 90);
1251 }
1214 }
1252
1215
1253 /*
1216 /*
tests/qmltests/tst_Shell.qml+38−8
@@ -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
@@ -996,14 +1022,13 @@ Rectangle {
996 }
1022 }
997
1023
998 function test_greeterStartsCorrectSession() {
1024 function test_greeterStartsCorrectSession() {
999− loadShell("desktop");
1000 setLightDMMockMode("full");
1025 setLightDMMockMode("full");
1026+ loadShell("desktop");
1001
1027
1002 LightDMController.sessionMode = "full"
1028 LightDMController.sessionMode = "full"
1003 LightDMController.numSessions = LightDMController.numAvailableSessions;
1029 LightDMController.numSessions = LightDMController.numAvailableSessions;
1004 var greeter = findChild(shell, "greeter");
1030 var greeter = findChild(shell, "greeter");
1005− var view = findChild(greeter, "WideView");
1031+ var view = findChild(greeter, "GreeterView");
1006− verify(view, "This test requires WideView to be loaded");
1007
1032
1008 var loginList = findChild(view, "loginList");
1033 var loginList = findChild(view, "loginList");
1009
1034
@@ -1403,8 +1428,9 @@ Rectangle {
1403 }
1428 }
1404
1429
1405 function test_tabletLogin(data) {
1430 function test_tabletLogin(data) {
1406− loadShell("tablet");
1407 setLightDMMockMode("full");
1431 setLightDMMockMode("full");
1432+ loadShell("tablet");
1433+ swipeAwayGreeterCover();
1408
1434
1409 selectUser(data.user);
1435 selectUser(data.user);
1410
1436
@@ -1427,8 +1453,10 @@ Rectangle {
1427 }
1453 }
1428
1454
1429 function test_appLaunchDuringGreeter(data) {
1455 function test_appLaunchDuringGreeter(data) {
1430− loadShell("tablet");
1431 setLightDMMockMode("full");
1456 setLightDMMockMode("full");
1457+ loadShell("tablet");
1458+
1459+ swipeAwayGreeterCover();
1432
1460
1433 selectUser(data.user)
1461 selectUser(data.user)
1434
1462
@@ -1444,9 +1472,11 @@ Rectangle {
1444 }
1472 }
1445
1473
1446 function test_manualLoginFlow() {
1474 function test_manualLoginFlow() {
1447− loadShell("desktop");
1448− LightDMController.showManualLoginHint = true;
1449 setLightDMMockMode("full");
1475 setLightDMMockMode("full");
1476+ LightDMController.showManualLoginHint = true;
1477+ loadShell("desktop");
1478+
1479+ swipeAwayGreeterCover();
1450
1480
1451 var i = selectUser("*other");
1481 var i = selectUser("*other");
1452 var greeter = findChild(shell, "greeter");
1482 var greeter = findChild(shell, "greeter");
@@ -2583,8 +2613,8 @@ Rectangle {
2583
2613
2584 function test_fullShellModeHasNoInitialGreeter() {
2614 function test_fullShellModeHasNoInitialGreeter() {
2585 shellRect.mode = "full-shell";
2615 shellRect.mode = "full-shell";
2586− loadShell("phone");
2587 setLightDMMockMode("single-pin");
2616 setLightDMMockMode("single-pin");
2617+ loadShell("phone");
2588 waitForRendering(shell);
2618 waitForRendering(shell);
2589
2619
2590 var greeter = findChild(shell, "greeter");
2620 var greeter = findChild(shell, "greeter");