Lomiri
CoverPage.qml
1 /*
2  * Copyright (C) 2013-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 QtGraphicalEffects 1.12
20 import Lomiri.Components 1.3
21 import Lomiri.Gestures 0.1
22 import "../Components"
23 
24 Showable {
25  id: root
26 
27  property real dragHandleLeftMargin
28  property real launcherOffset
29  property alias background: greeterBackground.source
30  property alias backgroundSourceSize: greeterBackground.sourceSize
31  property alias hasCustomBackground: backgroundShade.visible
32  property real panelHeight
33  property var infographicModel
34  property bool draggable: true
35 
36  property alias infographics: infographics
37 
38  property alias blurAreaHeight: loginBoxEffects.height
39  property alias blurAreaWidth: loginBoxEffects.width
40  property alias blurAreaX: loginBoxEffects.x
41  property alias blurAreaY: loginBoxEffects.y
42 
43  readonly property real showProgress: MathUtils.clamp((width - Math.abs(x + launcherOffset)) / width, 0, 1)
44 
45  signal tease()
46  signal clicked()
47 
48  function hideRight() {
49  d.forceRightOnNextHideAnimation = true;
50  hide();
51  }
52 
53  function showErrorMessage(msg) {
54  d.errorMessage = msg;
55  showLabelAnimation.start();
56  errorMessageAnimation.start();
57  }
58 
59  QtObject {
60  id: d
61  property bool forceRightOnNextHideAnimation: false
62  property string errorMessage
63  }
64 
65  prepareToHide: function () {
66  hideTranslation.from = root.x + translation.x
67  hideTranslation.to = root.x > 0 || d.forceRightOnNextHideAnimation ? root.width : -root.width;
68  d.forceRightOnNextHideAnimation = false;
69  }
70 
71  // We don't directly bind "x" because that's owned by the DragHandle. So
72  // instead, we can get a little extra horizontal push by using transforms.
73  transform: Translate { id: translation; x: root.draggable ? launcherOffset : 0 }
74 
75  // Eat events elsewhere on the coverpage, except mouse clicks which we pass
76  // up (they are used in the NarrowView to hide the cover page)
77  MouseArea {
78  anchors.fill: parent
79  onClicked: root.clicked()
80 
81  MultiPointTouchArea {
82  anchors.fill: parent
83  mouseEnabled: false
84  }
85  }
86 
87  Rectangle {
88  // In case background fails to load
89  id: backgroundBackup
90  anchors.fill: parent
91  color: "black"
92  }
93 
94  Wallpaper {
95  id: greeterBackground
96  objectName: "greeterBackground"
97  anchors {
98  fill: parent
99  }
100  }
101 
102  Rectangle {
103  id: loginBoxEffects
104  color: "transparent"
105  }
106 
107  ShaderEffectSource {
108  id: effectSource
109 
110  sourceItem: greeterBackground
111  anchors.centerIn: loginBoxEffects
112  width: loginBoxEffects.width
113  height: loginBoxEffects.height
114  sourceRect: Qt.rect(x,y, width, height)
115  }
116 
117  FastBlur {
118  visible: !draggable
119  anchors.fill: effectSource
120  source: effectSource
121  radius: 64
122  transparentBorder: true
123  }
124 
125  // Darkens wallpaper so that we can read text on it and see infographic
126  Rectangle {
127  id: backgroundShade
128  objectName: "backgroundShade"
129  anchors.fill: parent
130  color: "black"
131  opacity: 0.4
132  visible: false
133  }
134 
135  Infographics {
136  id: infographics
137  objectName: "infographics"
138  model: root.infographicModel
139  clip: true // clip large data bubbles
140 
141  anchors {
142  topMargin: root.panelHeight
143  top: parent.top
144  bottom: parent.bottom
145  left: parent.left
146  right: parent.right
147  }
148  }
149 
150  Label {
151  id: swipeHint
152  objectName: "swipeHint"
153  property real baseOpacity: 0.5
154  opacity: 0.0
155  anchors.horizontalCenter: parent.horizontalCenter
156  anchors.bottom: parent.bottom
157  anchors.bottomMargin: units.gu(5)
158  text: "《 " + (d.errorMessage ? d.errorMessage : i18n.tr("Unlock")) + " 》"
159  color: "white"
160  font.weight: Font.Light
161 
162  readonly property var opacityAnimation: showLabelAnimation // for testing
163 
164  SequentialAnimation on opacity {
165  id: showLabelAnimation
166  running: false
167  loops: 2
168 
169  StandardAnimation {
170  from: 0.0
171  to: swipeHint.baseOpacity
172  duration: LomiriAnimation.SleepyDuration
173  }
174  PauseAnimation { duration: LomiriAnimation.BriskDuration }
175  StandardAnimation {
176  from: swipeHint.baseOpacity
177  to: 0.0
178  duration: LomiriAnimation.SleepyDuration
179  }
180 
181  onRunningChanged: {
182  if (!running)
183  d.errorMessage = "";
184  }
185  }
186  }
187 
188  WrongPasswordAnimation {
189  id: errorMessageAnimation
190  objectName: "errorMessageAnimation"
191  target: swipeHint
192  }
193 
194  DragHandle {
195  id: dragHandle
196  objectName: "coverPageDragHandle"
197  anchors.fill: parent
198  anchors.leftMargin: root.dragHandleLeftMargin
199  enabled: root.draggable
200  direction: Direction.Horizontal
201 
202  onPressedChanged: {
203  if (pressed) {
204  root.tease();
205  showLabelAnimation.start();
206  }
207  }
208  }
209 
210  // right side shadow
211  Image {
212  anchors.left: parent.right
213  anchors.top: parent.top
214  anchors.bottom: parent.bottom
215  fillMode: Image.Tile
216  source: "../graphics/dropshadow_right.png"
217  }
218 
219  // left side shadow
220  Image {
221  anchors.right: parent.left
222  anchors.top: parent.top
223  anchors.bottom: parent.bottom
224  fillMode: Image.Tile
225  source: "../graphics/dropshadow_left.png"
226  }
227 
228  Binding {
229  id: positionLock
230 
231  property bool enabled: false
232  onEnabledChanged: {
233  if (enabled === __enabled) {
234  return;
235  }
236 
237  if (enabled) {
238  if (root.x > 0) {
239  value = Qt.binding(function() { return root.width; })
240  } else {
241  value = Qt.binding(function() { return -root.width; })
242  }
243  }
244 
245  __enabled = enabled;
246  }
247 
248  property bool __enabled: false
249 
250  target: root
251  when: __enabled
252  property: "x"
253  }
254 
255  hideAnimation: SequentialAnimation {
256  id: hideAnimation
257  objectName: "hideAnimation"
258  property var target // unused, here to silence Showable warning
259  StandardAnimation {
260  id: hideTranslation
261  property: "x"
262  target: root
263  }
264  PropertyAction { target: root; property: "visible"; value: false }
265  PropertyAction { target: positionLock; property: "enabled"; value: true }
266  }
267 
268  showAnimation: SequentialAnimation {
269  id: showAnimation
270  objectName: "showAnimation"
271  property var target // unused, here to silence Showable warning
272  PropertyAction { target: root; property: "visible"; value: true }
273  PropertyAction { target: positionLock; property: "enabled"; value: false }
274  StandardAnimation {
275  property: "x"
276  target: root
277  to: 0
278  duration: LomiriAnimation.FastDuration
279  }
280  }
281 }