mirror of
https://invent.kde.org/marcoa/shift-shell.git
synced 2026-07-26 06:14:45 +00:00
lockscreen: Refactor and lazy load notifications
This commit is contained in:
parent
c4472ca39a
commit
2a779900f3
5 changed files with 310 additions and 294 deletions
|
|
@ -12,80 +12,75 @@ import org.kde.plasma.private.mobileshell 1.0 as MobileShell
|
|||
|
||||
import org.kde.notificationmanager 1.0 as NotificationManager
|
||||
|
||||
Loader {
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property real openFactor
|
||||
|
||||
readonly property real statusBarHeight: PlasmaCore.Units.gridUnit * 1.25
|
||||
required property real statusBarHeight
|
||||
|
||||
property var notificationsModel: []
|
||||
|
||||
signal passwordRequested()
|
||||
|
||||
asynchronous: true
|
||||
// top status bar
|
||||
MobileShell.StatusBar {
|
||||
id: statusBar
|
||||
|
||||
sourceComponent: Item {
|
||||
// top status bar
|
||||
MobileShell.StatusBar {
|
||||
id: statusBar
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: root.statusBarHeight
|
||||
|
||||
height: root.statusBarHeight
|
||||
colorGroup: PlasmaCore.Theme.ComplementaryColorGroup
|
||||
backgroundColor: "transparent"
|
||||
|
||||
colorGroup: PlasmaCore.Theme.ComplementaryColorGroup
|
||||
backgroundColor: "transparent"
|
||||
showSecondRow: false
|
||||
showDropShadow: true
|
||||
showTime: false
|
||||
disableSystemTray: true // prevent SIGABRT, since loading the system tray on the lockscreen leads to bad... things
|
||||
}
|
||||
|
||||
showSecondRow: false
|
||||
showDropShadow: true
|
||||
showTime: false
|
||||
disableSystemTray: true // prevent SIGABRT, since loading the system tray on the lockscreen leads to bad... things
|
||||
// drag down gesture to open action drawer
|
||||
MobileShell.ActionDrawerOpenSurface {
|
||||
id: swipeArea
|
||||
actionDrawer: drawer
|
||||
anchors.fill: statusBar
|
||||
}
|
||||
|
||||
// action drawer component
|
||||
MobileShell.ActionDrawer {
|
||||
id: drawer
|
||||
anchors.fill: parent
|
||||
|
||||
visible: offset !== 0
|
||||
restrictedPermissions: true
|
||||
|
||||
notificationSettings: NotificationManager.Settings {}
|
||||
notificationModel: root.notificationsModel
|
||||
notificationModelType: MobileShell.NotificationsModelType.WatchedNotificationsModel
|
||||
|
||||
property bool requestNotificationAction: false
|
||||
|
||||
// notification button clicked, requesting auth
|
||||
onPermissionsRequested: {
|
||||
requestNotificationAction = true;
|
||||
drawer.close();
|
||||
root.passwordRequested();
|
||||
}
|
||||
}
|
||||
|
||||
// drag down gesture to open action drawer
|
||||
MobileShell.ActionDrawerOpenSurface {
|
||||
id: swipeArea
|
||||
actionDrawer: drawer
|
||||
anchors.fill: statusBar
|
||||
}
|
||||
|
||||
// action drawer component
|
||||
MobileShell.ActionDrawer {
|
||||
id: drawer
|
||||
anchors.fill: parent
|
||||
|
||||
restrictedPermissions: true
|
||||
|
||||
notificationSettings: NotificationManager.Settings {}
|
||||
notificationModel: root.notificationsModel
|
||||
notificationModelType: MobileShell.NotificationsModelType.WatchedNotificationsModel
|
||||
|
||||
property bool requestNotificationAction: false
|
||||
|
||||
// notification button clicked, requesting auth
|
||||
onPermissionsRequested: {
|
||||
requestNotificationAction = true;
|
||||
drawer.close();
|
||||
root.passwordRequested();
|
||||
}
|
||||
}
|
||||
|
||||
// listen to authentication events
|
||||
Connections {
|
||||
target: authenticator
|
||||
function onSucceeded() {
|
||||
// run pending action if successfully unlocked
|
||||
if (drawer.requestNotificationAction) {
|
||||
drawer.runPendingAction();
|
||||
drawer.requestNotificationAction = false;
|
||||
}
|
||||
}
|
||||
function onFailed() {
|
||||
// listen to authentication events
|
||||
Connections {
|
||||
target: authenticator
|
||||
function onSucceeded() {
|
||||
// run pending action if successfully unlocked
|
||||
if (drawer.requestNotificationAction) {
|
||||
drawer.runPendingAction();
|
||||
drawer.requestNotificationAction = false;
|
||||
}
|
||||
}
|
||||
function onFailed() {
|
||||
drawer.requestNotificationAction = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import org.kde.kirigami 2.12 as Kirigami
|
|||
*
|
||||
* Special attention must be paid to ensuring the GUI loads as fast as possible.
|
||||
*/
|
||||
PlasmaCore.ColorScope {
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property var lockScreenState: LockScreenState {}
|
||||
|
|
@ -33,9 +33,6 @@ PlasmaCore.ColorScope {
|
|||
readonly property bool drawerOpen: flickable.openFactor >= 1
|
||||
property var passwordBar: keypadLoader.item.passwordBar
|
||||
|
||||
colorGroup: PlasmaCore.Theme.ComplementaryColorGroup
|
||||
anchors.fill: parent
|
||||
|
||||
// listen for keyboard events, and focus on input area
|
||||
Component.onCompleted: forceActiveFocus();
|
||||
Keys.onPressed: {
|
||||
|
|
@ -66,17 +63,6 @@ PlasmaCore.ColorScope {
|
|||
}
|
||||
}
|
||||
|
||||
// header bar and action drawer
|
||||
HeaderComponent {
|
||||
id: headerBar
|
||||
z: 1 // on top of flick area
|
||||
anchors.fill: parent
|
||||
|
||||
openFactor: flickable.openFactor
|
||||
notificationsModel: root.notifModel
|
||||
onPasswordRequested: root.askPassword()
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root.lockScreenState
|
||||
|
||||
|
|
@ -86,160 +72,182 @@ PlasmaCore.ColorScope {
|
|||
}
|
||||
}
|
||||
|
||||
FlickContainer {
|
||||
id: flickable
|
||||
PlasmaCore.ColorScope {
|
||||
anchors.fill: parent
|
||||
colorGroup: PlasmaCore.Theme.ComplementaryColorGroup
|
||||
|
||||
property real openFactor: position / keypadHeight
|
||||
// header bar and action drawer
|
||||
Loader {
|
||||
id: headerBarLoader
|
||||
z: 1 // on top of flick area
|
||||
readonly property real statusBarHeight: PlasmaCore.Units.gridUnit * 1.25
|
||||
|
||||
onOpened: {
|
||||
if (root.lockScreenState.passwordless) {
|
||||
// try unlocking if flicked to the top, and we have passwordless login
|
||||
root.lockScreenState.tryPassword();
|
||||
}
|
||||
}
|
||||
anchors.fill: parent
|
||||
asynchronous: true
|
||||
|
||||
keypadHeight: PlasmaCore.Units.gridUnit * 20
|
||||
|
||||
// go to closed position when loaded
|
||||
Component.onCompleted: {
|
||||
flickable.position = 0;
|
||||
flickable.goToClosePosition();
|
||||
}
|
||||
|
||||
// update position, and cap it at the keypad height
|
||||
onPositionChanged: {
|
||||
if (position > keypadHeight) {
|
||||
position = keypadHeight;
|
||||
} else if (position < 0) {
|
||||
position = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
width: flickable.width
|
||||
height: flickable.height
|
||||
y: flickable.contentY // effectively anchored to the screen
|
||||
|
||||
LockScreenNarrowContent {
|
||||
id: phoneComponent
|
||||
|
||||
visible: !isWidescreen
|
||||
active: visible
|
||||
opacity: 1 - flickable.openFactor
|
||||
|
||||
fullHeight: root.height
|
||||
|
||||
lockScreenState: root.lockScreenState
|
||||
sourceComponent: HeaderComponent {
|
||||
statusBarHeight: headerBarLoader.statusBarHeight
|
||||
openFactor: flickable.openFactor
|
||||
notificationsModel: root.notifModel
|
||||
onNotificationsShownChanged: root.notificationsShown = notificationsShown
|
||||
|
||||
onPasswordRequested: flickable.goToOpenPosition()
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: scrollUpIconLoader.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
// move while swiping up
|
||||
transform: Translate { y: Math.round((1 - phoneComponent.opacity) * (-root.height / 6)) }
|
||||
onPasswordRequested: root.askPassword()
|
||||
}
|
||||
}
|
||||
|
||||
LockScreenWideScreenContent {
|
||||
id: tabletComponent
|
||||
FlickContainer {
|
||||
id: flickable
|
||||
anchors.fill: parent
|
||||
|
||||
visible: isWidescreen
|
||||
active: visible
|
||||
opacity: 1 - flickable.openFactor
|
||||
property real openFactor: position / keypadHeight
|
||||
|
||||
lockScreenState: root.lockScreenState
|
||||
notificationsModel: root.notifModel
|
||||
onNotificationsShownChanged: root.notificationsShown = notificationsShown
|
||||
|
||||
onPasswordRequested: flickable.goToOpenPosition()
|
||||
|
||||
anchors.topMargin: headerBar.statusBarHeight
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: scrollUpIconLoader.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
// move while swiping up
|
||||
transform: Translate { y: Math.round((1 - phoneComponent.opacity) * (-root.height / 6)) }
|
||||
}
|
||||
|
||||
// scroll up icon
|
||||
Loader {
|
||||
id: scrollUpIconLoader
|
||||
asynchronous: true
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: PlasmaCore.Units.gridUnit + flickable.position * 0.5
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
sourceComponent: PlasmaCore.IconItem {
|
||||
id: scrollUpIcon
|
||||
implicitWidth: PlasmaCore.Units.iconSizes.smallMedium
|
||||
implicitHeight: PlasmaCore.Units.iconSizes.smallMedium
|
||||
opacity: 1 - flickable.openFactor
|
||||
|
||||
colorGroup: PlasmaCore.Theme.ComplementaryColorGroup
|
||||
source: "arrow-up"
|
||||
onOpened: {
|
||||
if (root.lockScreenState.passwordless) {
|
||||
// try unlocking if flicked to the top, and we have passwordless login
|
||||
root.lockScreenState.tryPassword();
|
||||
}
|
||||
}
|
||||
|
||||
// password keypad
|
||||
Loader {
|
||||
id: keypadLoader
|
||||
width: parent.width
|
||||
asynchronous: true
|
||||
active: !root.lockScreenState.passwordless // only load keypad if not passwordless
|
||||
keypadHeight: PlasmaCore.Units.gridUnit * 20
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
// go to closed position when loaded
|
||||
Component.onCompleted: {
|
||||
flickable.position = 0;
|
||||
flickable.goToClosePosition();
|
||||
}
|
||||
|
||||
sourceComponent: ColumnLayout {
|
||||
property alias passwordBar: keypad.passwordBar
|
||||
// update position, and cap it at the keypad height
|
||||
onPositionChanged: {
|
||||
if (position > keypadHeight) {
|
||||
position = keypadHeight;
|
||||
} else if (position < 0) {
|
||||
position = 0;
|
||||
}
|
||||
}
|
||||
|
||||
transform: Translate { y: flickable.keypadHeight - flickable.position }
|
||||
spacing: 0
|
||||
Item {
|
||||
width: flickable.width
|
||||
height: flickable.height
|
||||
y: flickable.contentY // effectively anchored to the screen
|
||||
|
||||
// info notification text
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||
Layout.leftMargin: Kirigami.Units.largeSpacing
|
||||
Layout.bottomMargin: PlasmaCore.Units.smallSpacing * 2
|
||||
font.pointSize: 9
|
||||
LockScreenNarrowContent {
|
||||
id: phoneComponent
|
||||
|
||||
elide: Text.ElideRight
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: root.lockScreenState.info
|
||||
opacity: (root.lockScreenState.info.length === 0 || flickable.openFactor < 1) ? 0 : 1
|
||||
color: 'white'
|
||||
visible: !isWidescreen
|
||||
active: visible
|
||||
opacity: 1 - flickable.openFactor
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation { duration: 200 }
|
||||
}
|
||||
}
|
||||
fullHeight: root.height
|
||||
|
||||
// scroll down icon
|
||||
PlasmaCore.IconItem {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.bottomMargin: PlasmaCore.Units.gridUnit
|
||||
lockScreenState: root.lockScreenState
|
||||
notificationsModel: root.notifModel
|
||||
onNotificationsShownChanged: root.notificationsShown = notificationsShown
|
||||
|
||||
onPasswordRequested: flickable.goToOpenPosition()
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: scrollUpIconLoader.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
// move while swiping up
|
||||
transform: Translate { y: Math.round((1 - phoneComponent.opacity) * (-root.height / 6)) }
|
||||
}
|
||||
|
||||
LockScreenWideScreenContent {
|
||||
id: tabletComponent
|
||||
|
||||
visible: isWidescreen
|
||||
active: visible
|
||||
opacity: 1 - flickable.openFactor
|
||||
|
||||
lockScreenState: root.lockScreenState
|
||||
notificationsModel: root.notifModel
|
||||
onNotificationsShownChanged: root.notificationsShown = notificationsShown
|
||||
|
||||
onPasswordRequested: flickable.goToOpenPosition()
|
||||
|
||||
anchors.topMargin: headerBarLoader.statusBarHeight
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: scrollUpIconLoader.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
// move while swiping up
|
||||
transform: Translate { y: Math.round((1 - phoneComponent.opacity) * (-root.height / 6)) }
|
||||
}
|
||||
|
||||
// scroll up icon
|
||||
Loader {
|
||||
id: scrollUpIconLoader
|
||||
asynchronous: true
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: PlasmaCore.Units.gridUnit + flickable.position * 0.5
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
sourceComponent: PlasmaCore.IconItem {
|
||||
id: scrollUpIcon
|
||||
implicitWidth: PlasmaCore.Units.iconSizes.smallMedium
|
||||
implicitHeight: PlasmaCore.Units.iconSizes.smallMedium
|
||||
opacity: 1 - flickable.openFactor
|
||||
|
||||
colorGroup: PlasmaCore.Theme.ComplementaryColorGroup
|
||||
source: "arrow-down"
|
||||
opacity: Math.sin((Math.PI / 2) * flickable.openFactor + 1.5 * Math.PI) + 1
|
||||
source: "arrow-up"
|
||||
}
|
||||
}
|
||||
|
||||
Keypad {
|
||||
id: keypad
|
||||
Layout.fillWidth: true
|
||||
focus: true
|
||||
// password keypad
|
||||
Loader {
|
||||
id: keypadLoader
|
||||
width: parent.width
|
||||
asynchronous: true
|
||||
active: !root.lockScreenState.passwordless // only load keypad if not passwordless
|
||||
|
||||
lockScreenState: root.lockScreenState
|
||||
swipeProgress: flickable.openFactor
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
sourceComponent: ColumnLayout {
|
||||
property alias passwordBar: keypad.passwordBar
|
||||
|
||||
transform: Translate { y: flickable.keypadHeight - flickable.position }
|
||||
spacing: 0
|
||||
|
||||
// info notification text
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||
Layout.leftMargin: Kirigami.Units.largeSpacing
|
||||
Layout.bottomMargin: PlasmaCore.Units.smallSpacing * 2
|
||||
font.pointSize: 9
|
||||
|
||||
elide: Text.ElideRight
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: root.lockScreenState.info
|
||||
opacity: (root.lockScreenState.info.length === 0 || flickable.openFactor < 1) ? 0 : 1
|
||||
color: 'white'
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation { duration: 200 }
|
||||
}
|
||||
}
|
||||
|
||||
// scroll down icon
|
||||
PlasmaCore.IconItem {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.bottomMargin: PlasmaCore.Units.gridUnit
|
||||
implicitWidth: PlasmaCore.Units.iconSizes.smallMedium
|
||||
implicitHeight: PlasmaCore.Units.iconSizes.smallMedium
|
||||
colorGroup: PlasmaCore.Theme.ComplementaryColorGroup
|
||||
source: "arrow-down"
|
||||
opacity: Math.sin((Math.PI / 2) * flickable.openFactor + 1.5 * Math.PI) + 1
|
||||
}
|
||||
|
||||
Keypad {
|
||||
id: keypad
|
||||
Layout.fillWidth: true
|
||||
focus: true
|
||||
|
||||
lockScreenState: root.lockScreenState
|
||||
swipeProgress: flickable.openFactor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ Loader {
|
|||
id: root
|
||||
|
||||
required property var lockScreenState
|
||||
property var notificationsModel: []
|
||||
|
||||
property var notificationsModel: []
|
||||
property bool notificationsShown: false
|
||||
|
||||
property real fullHeight
|
||||
|
|
@ -27,11 +27,11 @@ Loader {
|
|||
onLoaded: loadTimer.restart();
|
||||
Timer {
|
||||
id: loadTimer
|
||||
interval: PlasmaCore.Units.longDuration
|
||||
interval: 200
|
||||
}
|
||||
|
||||
// move while swiping up
|
||||
transform: Translate { y: Math.round((1 - phoneComponent.opacity) * (-root.height / 6)) }
|
||||
transform: Translate { y: Math.round((1 - root.opacity) * (-root.height / 6)) }
|
||||
|
||||
asynchronous: true
|
||||
sourceComponent: Item {
|
||||
|
|
@ -47,8 +47,8 @@ Loader {
|
|||
// animate
|
||||
Behavior on anchors.topMargin {
|
||||
NumberAnimation {
|
||||
duration: loadTimer.running ? 0 : PlasmaCore.Units.longDuration
|
||||
easing.type: Easing.InOutQuad
|
||||
duration: loadTimer.running ? 0 : PlasmaCore.Units.veryLongDuration
|
||||
easing.type: Easing.InOutExpo
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ Loader {
|
|||
id: root
|
||||
|
||||
required property var lockScreenState
|
||||
property var notificationsModel: []
|
||||
|
||||
property var notificationsModel: []
|
||||
property bool notificationsShown: false
|
||||
|
||||
signal passwordRequested()
|
||||
|
|
|
|||
|
|
@ -14,23 +14,30 @@ import org.kde.plasma.private.mobileshell 1.0 as MobileShell
|
|||
|
||||
import org.kde.notificationmanager 1.0 as NotificationManager
|
||||
|
||||
Rectangle {
|
||||
Loader {
|
||||
id: root
|
||||
required property var lockScreenState
|
||||
|
||||
property var notificationsModel: []
|
||||
property var notificationSettings: NotificationManager.Settings {}
|
||||
|
||||
readonly property bool notificationsShown: notificationsList.hasNotifications
|
||||
|
||||
signal passwordRequested()
|
||||
|
||||
property real leftMargin: 0
|
||||
property real rightMargin: 0
|
||||
property real topMargin: 0
|
||||
property real bottomMargin: 0
|
||||
readonly property bool notificationsShown: item && item.notificationsList.hasNotifications
|
||||
|
||||
color: "transparent"
|
||||
clip: true
|
||||
property var notificationsList: item ? item.notificationsList : null
|
||||
|
||||
signal passwordRequested()
|
||||
|
||||
// perform delayed loading of notifications
|
||||
active: false
|
||||
Timer {
|
||||
interval: 500
|
||||
running: true
|
||||
onTriggered: root.active = true
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: lockScreenState
|
||||
|
|
@ -48,28 +55,34 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
|
||||
PlasmaCore.ColorScope {
|
||||
anchors.fill: parent
|
||||
anchors.topMargin: root.topMargin
|
||||
anchors.bottomMargin: root.bottomMargin
|
||||
anchors.leftMargin: root.leftMargin
|
||||
anchors.rightMargin: root.rightMargin
|
||||
colorGroup: PlasmaCore.Theme.NormalColorGroup
|
||||
sourceComponent: Item {
|
||||
clip: true
|
||||
|
||||
MobileShell.NotificationsWidget {
|
||||
id: notificationsList
|
||||
property alias notificationsList: notificationsList
|
||||
|
||||
PlasmaCore.ColorScope {
|
||||
anchors.fill: parent
|
||||
anchors.topMargin: root.topMargin
|
||||
anchors.bottomMargin: root.bottomMargin
|
||||
anchors.leftMargin: root.leftMargin
|
||||
anchors.rightMargin: root.rightMargin
|
||||
colorGroup: PlasmaCore.Theme.NormalColorGroup
|
||||
|
||||
historyModelType: MobileShell.NotificationsModelType.WatchedNotificationsModel
|
||||
actionsRequireUnlock: true
|
||||
historyModel: root.notificationsModel
|
||||
notificationSettings: root.notificationSettings
|
||||
MobileShell.NotificationsWidget {
|
||||
id: notificationsList
|
||||
anchors.fill: parent
|
||||
|
||||
property bool requestNotificationAction: false
|
||||
historyModelType: MobileShell.NotificationsModelType.WatchedNotificationsModel
|
||||
actionsRequireUnlock: true
|
||||
historyModel: root.notificationsModel
|
||||
notificationSettings: root.notificationSettings
|
||||
|
||||
onUnlockRequested: {
|
||||
requestNotificationAction = true;
|
||||
root.passwordRequested();
|
||||
property bool requestNotificationAction: false
|
||||
|
||||
onUnlockRequested: {
|
||||
requestNotificationAction = true;
|
||||
root.passwordRequested();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue