actiondrawer: Add restricted permission mode, and implement on lockscreen

This commit is contained in:
Devin Lin 2022-04-07 14:11:08 -04:00
parent 3969973e51
commit 57192b772b
18 changed files with 323 additions and 116 deletions

View file

@ -68,6 +68,7 @@ void MobileShellPlugin::registerTypes(const char *uri)
// /actiondrawer // /actiondrawer
qmlRegisterType(resolvePath("actiondrawer/ActionDrawer.qml"), uri, 1, 0, "ActionDrawer"); qmlRegisterType(resolvePath("actiondrawer/ActionDrawer.qml"), uri, 1, 0, "ActionDrawer");
qmlRegisterType(resolvePath("actiondrawer/ActionDrawerOpenSurface.qml"), uri, 1, 0, "ActionDrawerOpenSurface"); qmlRegisterType(resolvePath("actiondrawer/ActionDrawerOpenSurface.qml"), uri, 1, 0, "ActionDrawerOpenSurface");
qmlRegisterType(resolvePath("actiondrawer/ActionDrawerWindow.qml"), uri, 1, 0, "ActionDrawerWindow");
// /components // /components
qmlRegisterType(resolvePath("components/BaseItem.qml"), uri, 1, 0, "BaseItem"); qmlRegisterType(resolvePath("components/BaseItem.qml"), uri, 1, 0, "BaseItem");

View file

@ -13,29 +13,35 @@ import QtQuick.Window 2.2
import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.components 3.0 as PlasmaComponents import org.kde.plasma.components 3.0 as PlasmaComponents
import org.kde.plasma.private.nanoshell 2.0 as NanoShell import org.kde.plasma.private.nanoshell 2.0 as NanoShell
import org.kde.plasma.private.mobileshell 1.0 as MobileShell
import "../components" as Components import "../components" as Components
/** Item {
* Swipe top left - minimized quick settings, fully shown notifications list id: root
* Swipe top right - full quick settings, minimized notifications list
* Swiping up and down on notifications list toggle minimized/maximized
* Swiping up and down on panel hides and shows the panel
*/
NanoShell.FullScreenOverlay {
id: window
/** /**
* The model for the notification widget. * The model for the notification widget.
*/ */
property var notificationModel property var notificationModel
/**
* The model type for the notification widget.
*/
property var notificationModelType: MobileShell.NotificationsModelType.NotificationsModel
/** /**
* The notification settings object to be used in the notification widget. * The notification settings object to be used in the notification widget.
*/ */
property var notificationSettings property var notificationSettings
/**
* Whether actions should be subject to restricted permissions (ex. lockscreen).
*
* The permissionsRequested() signal emits when authentication is requested.
*/
property bool restrictedPermissions: false
/** /**
* The amount of pixels moved by touch/mouse in the process of opening/closing the panel. * The amount of pixels moved by touch/mouse in the process of opening/closing the panel.
*/ */
@ -56,6 +62,9 @@ NanoShell.FullScreenOverlay {
*/ */
property int direction: Components.Direction.None property int direction: Components.Direction.None
/**
* The mode of the action drawer (portrait or landscape).
*/
property int mode: (height > width && width <= largePortraitThreshold) ? ActionDrawer.Portrait : ActionDrawer.Landscape property int mode: (height > width && width <= largePortraitThreshold) ? ActionDrawer.Portrait : ActionDrawer.Landscape
/** /**
@ -68,34 +77,48 @@ NanoShell.FullScreenOverlay {
Landscape Landscape
} }
width: Screen.width /**
height: Screen.height * Emitted when the drawer has closed.
*/
signal drawerClosed()
color: "transparent" /**
* Emitted when the drawer has opened.
*/
signal drawerOpened()
/**
* Emitted when permissions are requested (ex. unlocking the phone).
*
* Only gets emitted when restrictedPermissions is set to true.
*/
signal permissionsRequested()
/**
* Runs the held notification action that was pending for authentication.
*
* Should be called by users if authentication is successful after permissionsRequested() was emitted.
*/
signal runPendingNotificationAction()
onOpenedChanged: { onOpenedChanged: {
if (opened) flickable.focus = true; if (opened) flickable.focus = true;
} }
onActiveChanged: {
if (!active) {
close();
}
}
property real oldOffset property real oldOffset
onOffsetChanged: { onOffsetChanged: {
if (offset < 0) { if (offset < 0) {
offset = 0; offset = 0;
} }
window.direction = (oldOffset === offset) root.direction = (oldOffset === offset)
? Components.Direction.None ? Components.Direction.None
: (offset > oldOffset ? Components.Direction.Down : Components.Direction.Up); : (offset > oldOffset ? Components.Direction.Down : Components.Direction.Up);
oldOffset = offset; oldOffset = offset;
// close panel immediately after panel is not shown, and the flickable is not being dragged // close panel immediately after panel is not shown, and the flickable is not being dragged
if (opened && window.offset <= 0 && !flickable.dragging && !closeAnim.running && !openAnim.running) { if (opened && root.offset <= 0 && !flickable.dragging && !closeAnim.running && !openAnim.running) {
window.updateState(); root.updateState();
focus = false; focus = false;
} }
} }
@ -125,25 +148,25 @@ NanoShell.FullScreenOverlay {
cancelAnimations(); cancelAnimations();
let openThreshold = PlasmaCore.Units.gridUnit; let openThreshold = PlasmaCore.Units.gridUnit;
if (window.offset <= 0) { if (root.offset <= 0) {
// close immediately, so that we don't have to wait PlasmaCore.Units.longDuration // close immediately, so that we don't have to wait PlasmaCore.Units.longDuration
window.visible = false; root.visible = false;
close(); close();
} else if (window.direction === Components.Direction.None || !window.opened) { } else if (root.direction === Components.Direction.None || !root.opened) {
if (window.offset < openThreshold) { if (root.offset < openThreshold) {
close(); close();
} else { } else {
open(); open();
} }
} else if (window.offset > contentContainerLoader.maximizedQuickSettingsOffset) { } else if (root.offset > contentContainerLoader.maximizedQuickSettingsOffset) {
expand(); expand();
} else if (window.offset > contentContainerLoader.minimizedQuickSettingsOffset) { } else if (root.offset > contentContainerLoader.minimizedQuickSettingsOffset) {
if (window.direction === Components.Direction.Down) { if (root.direction === Components.Direction.Down) {
expand(); expand();
} else { } else {
open(); open();
} }
} else if (window.direction === Components.Direction.Down) { } else if (root.direction === Components.Direction.Down) {
open(); open();
} else { } else {
close(); close();
@ -161,8 +184,8 @@ NanoShell.FullScreenOverlay {
easing.type: Easing.InOutQuad easing.type: Easing.InOutQuad
to: 0 to: 0
onFinished: { onFinished: {
window.visible = false; root.visible = false;
window.opened = false; root.opened = false;
} }
} }
PropertyAnimation on offset { PropertyAnimation on offset {
@ -170,31 +193,31 @@ NanoShell.FullScreenOverlay {
duration: PlasmaCore.Units.longDuration duration: PlasmaCore.Units.longDuration
easing.type: Easing.InOutQuad easing.type: Easing.InOutQuad
to: contentContainerLoader.minimizedQuickSettingsOffset to: contentContainerLoader.minimizedQuickSettingsOffset
onFinished: window.opened = true onFinished: root.opened = true
} }
PropertyAnimation on offset { PropertyAnimation on offset {
id: expandAnim id: expandAnim
duration: PlasmaCore.Units.longDuration duration: PlasmaCore.Units.longDuration
easing.type: Easing.InOutQuad easing.type: Easing.InOutQuad
to: contentContainerLoader.maximizedQuickSettingsOffset to: contentContainerLoader.maximizedQuickSettingsOffset
onFinished: window.opened = true; onFinished: root.opened = true;
} }
Flickable { Flickable {
id: flickable id: flickable
anchors.fill: parent anchors.fill: parent
contentWidth: window.width contentWidth: root.width
contentHeight: window.height + 999999 contentHeight: root.height + 999999
contentY: contentHeight / 2 contentY: contentHeight / 2
// if the recent window.offset change was due to this flickable // if the recent root.offset change was due to this flickable
property bool offsetChangedDueToContentY: false property bool offsetChangedDueToContentY: false
Connections { Connections {
target: window target: root
function onOffsetChanged() { function onOffsetChanged() {
if (!flickable.offsetChangedDueToContentY) { if (!flickable.offsetChangedDueToContentY) {
// ensure the flickable's contentY is not moving when other sources change window.offset // ensure the flickable's contentY is not moving when other sources change root.offset
flickable.cancelFlick(); flickable.cancelFlick();
} }
flickable.offsetChangedDueToContentY = false; flickable.offsetChangedDueToContentY = false;
@ -204,34 +227,34 @@ NanoShell.FullScreenOverlay {
property real oldContentY property real oldContentY
onContentYChanged: { onContentYChanged: {
offsetChangedDueToContentY = true; offsetChangedDueToContentY = true;
window.offset += oldContentY - contentY; root.offset += oldContentY - contentY;
oldContentY = contentY; oldContentY = contentY;
} }
onMovementStarted: { onMovementStarted: {
window.cancelAnimations(); root.cancelAnimations();
window.dragging = true; root.dragging = true;
} }
onFlickStarted: window.dragging = true; onFlickStarted: root.dragging = true;
onMovementEnded: { onMovementEnded: {
window.dragging = false; root.dragging = false;
window.updateState(); root.updateState();
} }
onFlickEnded: { onFlickEnded: {
window.dragging = true; root.dragging = true;
window.updateState(); root.updateState();
} }
onDraggingChanged: { onDraggingChanged: {
if (!dragging) { if (!dragging) {
window.dragging = false; root.dragging = false;
flickable.cancelFlick(); flickable.cancelFlick();
window.updateState(); root.updateState();
} }
} }
// the flickable is only used to measure drag changes, we implement our own UI component movements // the flickable is only used to measure drag changes, we implement our own UI component movements
// the window element is not affected by contentY changes (it's effectively anchored to the flickable) // the root element is not affected by contentY changes (it's effectively anchored to the flickable)
Loader { Loader {
id: contentContainerLoader id: contentContainerLoader
@ -239,27 +262,27 @@ NanoShell.FullScreenOverlay {
property real maximizedQuickSettingsOffset: item ? item.maximizedQuickSettingsOffset : 0 property real maximizedQuickSettingsOffset: item ? item.maximizedQuickSettingsOffset : 0
y: flickable.contentY y: flickable.contentY
width: window.width width: root.width
height: window.height height: root.height
sourceComponent: window.mode == ActionDrawer.Portrait ? portraitContentContainer : landscapeContentContainer sourceComponent: root.mode == ActionDrawer.Portrait ? portraitContentContainer : landscapeContentContainer
} }
Component { Component {
id: portraitContentContainer id: portraitContentContainer
PortraitContentContainer { PortraitContentContainer {
actionDrawer: window actionDrawer: root
width: window.width width: root.width
height: window.height height: root.height
} }
} }
Component { Component {
id: landscapeContentContainer id: landscapeContentContainer
LandscapeContentContainer { LandscapeContentContainer {
actionDrawer: window actionDrawer: root
width: window.width width: root.width
height: window.height height: root.height
} }
} }
} }

View file

@ -0,0 +1,48 @@
/*
* SPDX-FileCopyrightText: 2022 Devin Lin <devin@kde.org>
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.1
import QtQuick.Window 2.2
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.components 3.0 as PlasmaComponents
import org.kde.plasma.private.nanoshell 2.0 as NanoShell
import "../components" as Components
/**
* Window with the ActionDrawer component embedded in it.
*
* Used for overlaying the ActionDrawer if the original window does not cover
* the whole screen.
*/
NanoShell.FullScreenOverlay {
id: window
/**
* The ActionDrawer component.
*/
property alias actionDrawer: drawer
visible: drawer.visible
width: Screen.width
height: Screen.height
color: "transparent"
onActiveChanged: {
if (!active) {
drawer.close();
}
}
ActionDrawer {
id: drawer
anchors.fill: parent
}
}

View file

@ -95,7 +95,18 @@ PlasmaCore.ColorScope {
MobileShell.NotificationsWidget { MobileShell.NotificationsWidget {
id: notificationWidget id: notificationWidget
historyModel: root.actionDrawer.notificationModel historyModel: root.actionDrawer.notificationModel
historyModelType: root.actionDrawer.notificationModelType
notificationSettings: root.actionDrawer.notificationSettings notificationSettings: root.actionDrawer.notificationSettings
actionsRequireUnlock: root.actionDrawer.restrictedPermissions
onUnlockRequested: root.actionDrawer.permissionsRequested()
Connections {
target: root.actionDrawer
function onRunPendingNotificationAction() {
notificationWidget.runPendingAction();
}
}
// don't allow notifications widget to get too wide // don't allow notifications widget to get too wide
Layout.maximumWidth: PlasmaCore.Units.gridUnit * 25 Layout.maximumWidth: PlasmaCore.Units.gridUnit * 25

View file

@ -79,7 +79,18 @@ PlasmaCore.ColorScope {
MobileShell.NotificationsWidget { MobileShell.NotificationsWidget {
id: notificationWidget id: notificationWidget
historyModel: root.actionDrawer.notificationModel historyModel: root.actionDrawer.notificationModel
historyModelType: root.actionDrawer.notificationModelType
notificationSettings: root.actionDrawer.notificationSettings notificationSettings: root.actionDrawer.notificationSettings
actionsRequireUnlock: root.actionDrawer.restrictedPermissions
onUnlockRequested: root.actionDrawer.permissionsRequested()
Connections {
target: root.actionDrawer
function onRunPendingNotificationAction() {
notificationWidget.runPendingAction();
}
}
anchors { anchors {
top: quickSettings.top top: quickSettings.top

View file

@ -69,6 +69,8 @@ Item {
padding: PlasmaCore.Units.smallSpacing padding: PlasmaCore.Units.smallSpacing
contentItem: QuickSettingsFullDelegate { contentItem: QuickSettingsFullDelegate {
restrictedPermissions: actionDrawer.restrictedPermissions
text: modelData.text text: modelData.text
status: modelData.status status: modelData.status
icon: modelData.icon icon: modelData.icon
@ -119,6 +121,8 @@ Item {
visible: index <= root.minimizedColumns visible: index <= root.minimizedColumns
contentItem: QuickSettingsMinimizedDelegate { contentItem: QuickSettingsMinimizedDelegate {
restrictedPermissions: actionDrawer.restrictedPermissions
text: modelData.text text: modelData.text
status: modelData.status status: modelData.status
icon: modelData.icon icon: modelData.icon

View file

@ -20,6 +20,8 @@ import "../../components" as Components
Components.BaseItem { Components.BaseItem {
id: root id: root
required property bool restrictedPermissions
// Model interface // Model interface
required property string text required property string text
required property string status required property string status
@ -61,7 +63,7 @@ Components.BaseItem {
root.toggle(); root.toggle();
} else if (root.toggleFunction) { } else if (root.toggleFunction) {
root.toggleFunction(); root.toggleFunction();
} else if (root.settingsCommand) { } else if (root.settingsCommand && !root.restrictedPermissions) {
closeRequested(); closeRequested();
MobileShell.HomeScreenControls.openAppLaunchAnimation( MobileShell.HomeScreenControls.openAppLaunchAnimation(
root.icon, root.icon,
@ -74,7 +76,7 @@ Components.BaseItem {
} }
function delegatePressAndHold() { function delegatePressAndHold() {
if (root.settingsCommand) { if (root.settingsCommand && !root.restrictedPermissions) {
closeRequested(); closeRequested();
MobileShell.HomeScreenControls.openAppLaunchAnimation( MobileShell.HomeScreenControls.openAppLaunchAnimation(
root.icon, root.icon,

View file

@ -85,6 +85,9 @@ Components.BaseItem {
backgroundColor: "transparent" backgroundColor: "transparent"
showSecondRow: true showSecondRow: true
showDropShadow: false showDropShadow: false
// security reasons, system tray also doesn't work on lockscreen
disableSystemTray: actionDrawer.restrictedPermissions
} }
QuickSettings { QuickSettings {

View file

@ -77,6 +77,9 @@ Components.BaseItem {
showSecondRow: false showSecondRow: false
showDropShadow: false showDropShadow: false
showTime: false showTime: false
// security reasons, system tray also doesn't work on lockscreen
disableSystemTray: actionDrawer.restrictedPermissions
} }
PlasmaComponents.ScrollView { PlasmaComponents.ScrollView {

View file

@ -60,8 +60,17 @@ Item {
WatchedNotificationsModel // used on the lockscreen WatchedNotificationsModel // used on the lockscreen
} }
/**
* Signal emitted when authentication is requested for an action.
* Listeners should call runPendingAction() if authentication is successful.
*
* Only emitted if actionsRequireUnlock is enabled.
*/
signal unlockRequested() signal unlockRequested()
/**
* Run pending action that was pending for authentication when unlockRequested() was emitted.
*/
function runPendingAction() { function runPendingAction() {
list.pendingNotificationWithAction.runPendingAction(); list.pendingNotificationWithAction.runPendingAction();
} }

View file

@ -16,6 +16,7 @@
<file>qml/actiondrawer/ActionDrawer.qml</file> <file>qml/actiondrawer/ActionDrawer.qml</file>
<file>qml/actiondrawer/ActionDrawerOpenSurface.qml</file> <file>qml/actiondrawer/ActionDrawerOpenSurface.qml</file>
<file>qml/actiondrawer/ActionDrawerWindow.qml</file>
<file>qml/actiondrawer/LandscapeContentContainer.qml</file> <file>qml/actiondrawer/LandscapeContentContainer.qml</file>
<file>qml/actiondrawer/PortraitContentContainer.qml</file> <file>qml/actiondrawer/PortraitContentContainer.qml</file>

View file

@ -41,7 +41,7 @@ Item {
Binding { Binding {
target: MobileShell.TopPanelControls target: MobileShell.TopPanelControls
property: "inSwipe" property: "inSwipe"
value: drawer.dragging value: drawer.actionDrawer.dragging
} }
Binding { Binding {
target: MobileShell.TopPanelControls target: MobileShell.TopPanelControls
@ -62,10 +62,10 @@ Item {
swipeArea.updateOffset(offsetY); swipeArea.updateOffset(offsetY);
} }
function onCloseActionDrawer() { function onCloseActionDrawer() {
drawer.close(); drawer.actionDrawer.close();
} }
function onOpenActionDrawer() { function onOpenActionDrawer() {
drawer.open(); drawer.actionDrawer.open();
} }
} }
@ -88,30 +88,30 @@ Item {
MobileShell.ActionDrawerOpenSurface { MobileShell.ActionDrawerOpenSurface {
id: swipeArea id: swipeArea
actionDrawer: drawer actionDrawer: drawer.actionDrawer
anchors.fill: parent anchors.fill: parent
} }
// swipe-down drawer component // swipe-down drawer component
MobileShell.ActionDrawer { MobileShell.ActionDrawerWindow {
id: drawer id: drawer
notificationSettings: NotificationManager.Settings {} actionDrawer.notificationSettings: NotificationManager.Settings {}
notificationModel: NotificationManager.Notifications { actionDrawer.notificationModel: NotificationManager.Notifications {
showExpired: true showExpired: true
showDismissed: true showDismissed: true
showJobs: drawer.notificationSettings.jobsInNotifications showJobs: drawer.actionDrawer.notificationSettings.jobsInNotifications
sortMode: NotificationManager.Notifications.SortByTypeAndUrgency sortMode: NotificationManager.Notifications.SortByTypeAndUrgency
groupMode: NotificationManager.Notifications.GroupApplicationsFlat groupMode: NotificationManager.Notifications.GroupApplicationsFlat
groupLimit: 2 groupLimit: 2
expandUnread: true expandUnread: true
blacklistedDesktopEntries: drawer.notificationSettings.historyBlacklistedApplications blacklistedDesktopEntries: drawer.actionDrawer.notificationSettings.historyBlacklistedApplications
blacklistedNotifyRcNames: drawer.notificationSettings.historyBlacklistedServices blacklistedNotifyRcNames: drawer.actionDrawer.notificationSettings.historyBlacklistedServices
urgencies: { urgencies: {
var urgencies = NotificationManager.Notifications.CriticalUrgency var urgencies = NotificationManager.Notifications.CriticalUrgency
| NotificationManager.Notifications.NormalUrgency; | NotificationManager.Notifications.NormalUrgency;
if (drawer.notificationSettings.lowPriorityHistory) { if (drawer.actionDrawer.notificationSettings.lowPriorityHistory) {
urgencies |= NotificationManager.Notifications.LowUrgency; urgencies |= NotificationManager.Notifications.LowUrgency;
} }
return urgencies; return urgencies;

View file

@ -0,0 +1,87 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Devin Lin <espidev@gmail.com>
* SPDX-License-Identifier: GPL-2.0-or-later
*/
import QtQuick 2.12
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.1
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.private.mobileshell 1.0 as MobileShell
import org.kde.notificationmanager 1.0 as NotificationManager
Loader {
id: root
required property real openFactor
readonly property real statusBarHeight: PlasmaCore.Units.gridUnit * 1.25
property var notificationsModel: []
signal passwordRequested()
active: true
asynchronous: true
sourceComponent: Item {
MobileShell.StatusBar {
id: statusBar
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
height: root.statusBarHeight
colorGroup: PlasmaCore.Theme.ComplementaryColorGroup
backgroundColor: "transparent"
showSecondRow: false
showDropShadow: true
showTime: false
disableSystemTray: true // HACK: prevent SIGABRT
}
MobileShell.ActionDrawerOpenSurface {
id: swipeArea
actionDrawer: drawer
anchors.fill: statusBar
}
// swipe-down 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
onPermissionsRequested: {
requestNotificationAction = true;
root.passwordRequested();
}
}
Connections {
target: authenticator
function onSucceeded() {
// run pending action if successfully unlocked
if (drawer.requestNotificationAction) {
drawer.runPendingAction();
drawer.requestNotificationAction = false;
}
}
function onFailed() {
drawer.requestNotificationAction = false;
}
}
}
}

View file

@ -42,6 +42,17 @@ PlasmaCore.ColorScope {
blur: root.notificationsShown || root.drawerOpen // only blur once animation finished for performance blur: root.notificationsShown || root.drawerOpen // only blur once animation finished for performance
} }
} }
// header bar and action drawer
HeaderComponent {
id: headerBar
z: 1 // on top of flick area
anchors.fill: parent
openFactor: flickable.openFactor
notificationsModel: notifModel
onPasswordRequested: root.askPassword()
}
FlickContainer { FlickContainer {
id: flickable id: flickable
@ -69,23 +80,20 @@ PlasmaCore.ColorScope {
height: flickable.height height: flickable.height
y: flickable.contentY // effectively anchored to the screen y: flickable.contentY // effectively anchored to the screen
// header bar
StatusBarComponent {
id: statusBar
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
opacity: 1 - flickable.openFactor
}
LockScreenNarrowContent { LockScreenNarrowContent {
id: phoneComponent id: phoneComponent
visible: !isWidescreen visible: !isWidescreen
active: visible active: visible
opacity: 1 - flickable.openFactor opacity: 1 - flickable.openFactor
fullHeight: root.height fullHeight: root.height
notificationsModel: notifModel
onNotificationsShownChanged: root.notificationsShown = notificationsShown
onPasswordRequested: root.askPassword()
anchors.top: parent.top anchors.top: parent.top
anchors.bottom: scrollUpIcon.top anchors.bottom: scrollUpIcon.top
anchors.left: parent.left anchors.left: parent.left
@ -101,7 +109,13 @@ PlasmaCore.ColorScope {
active: visible active: visible
opacity: 1 - flickable.openFactor opacity: 1 - flickable.openFactor
anchors.top: statusBar.bottom notificationsModel: notifModel
onNotificationsShownChanged: root.notificationsShown = notificationsShown
onPasswordRequested: root.askPassword()
anchors.topMargin: headerBar.statusBarHeight
anchors.top: parent.top
anchors.bottom: scrollUpIcon.top anchors.bottom: scrollUpIcon.top
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right

View file

@ -14,11 +14,15 @@ import org.kde.plasma.private.mobileshell 1.0 as MobileShell
Loader { Loader {
id: root id: root
asynchronous: true
property var notificationsModel: []
property real fullHeight
property bool notificationsShown: false property bool notificationsShown: false
property real fullHeight
signal passwordRequested()
// avoid topMargin animation when item is being loaded // avoid topMargin animation when item is being loaded
onLoaded: loadTimer.restart(); onLoaded: loadTimer.restart();
Timer { Timer {
@ -29,6 +33,7 @@ Loader {
// move while swiping up // move while swiping up
transform: Translate { y: Math.round((1 - phoneComponent.opacity) * (-root.height / 6)) } transform: Translate { y: Math.round((1 - phoneComponent.opacity) * (-root.height / 6)) }
asynchronous: true
sourceComponent: Item { sourceComponent: Item {
ColumnLayout { ColumnLayout {
id: column id: column
@ -62,6 +67,8 @@ Loader {
NotificationsComponent { NotificationsComponent {
id: notificationComponent id: notificationComponent
notificationsModel: root.notificationsModel
Layout.fillHeight: true Layout.fillHeight: true
Layout.fillWidth: true Layout.fillWidth: true
Layout.maximumWidth: PlasmaCore.Units.gridUnit * (25 + 2) // clip margins Layout.maximumWidth: PlasmaCore.Units.gridUnit * (25 + 2) // clip margins
@ -69,6 +76,7 @@ Loader {
leftMargin: PlasmaCore.Units.gridUnit leftMargin: PlasmaCore.Units.gridUnit
rightMargin: PlasmaCore.Units.gridUnit rightMargin: PlasmaCore.Units.gridUnit
onPasswordRequested: root.passwordRequested()
onNotificationsShownChanged: root.notificationsShown = notificationsShown onNotificationsShownChanged: root.notificationsShown = notificationsShown
} }
} }

View file

@ -13,14 +13,18 @@ import org.kde.notificationmanager 1.1 as Notifications
import org.kde.plasma.private.mobileshell 1.0 as MobileShell import org.kde.plasma.private.mobileshell 1.0 as MobileShell
Loader { Loader {
id: tabletComponent id: root
asynchronous: true
property var notificationsModel: []
property bool notificationsShown: false property bool notificationsShown: false
signal passwordRequested()
asynchronous: true
sourceComponent: Item { sourceComponent: Item {
Item { Item {
id: tabletClockComponent id: clock
width: parent.width / 2 width: parent.width / 2
anchors { anchors {
top: parent.top top: parent.top
@ -54,13 +58,14 @@ Loader {
anchors { anchors {
top: parent.top top: parent.top
bottom: parent.bottom bottom: parent.bottom
left: tabletClockComponent.right left: clock.right
right: parent.right right: parent.right
rightMargin: PlasmaCore.Units.gridUnit rightMargin: PlasmaCore.Units.gridUnit
} }
NotificationsComponent { NotificationsComponent {
id: notificationComponent id: notificationComponent
notificationsModel: root.notificationsModel
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
Layout.fillWidth: true Layout.fillWidth: true
@ -75,6 +80,7 @@ Loader {
bottomMargin: PlasmaCore.Units.gridUnit bottomMargin: PlasmaCore.Units.gridUnit
topMargin: PlasmaCore.Units.gridUnit topMargin: PlasmaCore.Units.gridUnit
onPasswordRequested: root.passwordRequested()
onNotificationsShownChanged: root.notificationsShown = notificationsShown onNotificationsShownChanged: root.notificationsShown = notificationsShown
} }
} }

View file

@ -16,8 +16,12 @@ import org.kde.plasma.private.mobileshell 1.0 as MobileShell
Rectangle { Rectangle {
id: rect id: rect
property var notificationsModel: []
readonly property bool notificationsShown: notificationsList.hasNotifications readonly property bool notificationsShown: notificationsList.hasNotifications
signal passwordRequested()
property real leftMargin: 0 property real leftMargin: 0
property real rightMargin: 0 property real rightMargin: 0
property real topMargin: 0 property real topMargin: 0
@ -54,13 +58,13 @@ Rectangle {
historyModelType: MobileShell.NotificationsModelType.WatchedNotificationsModel historyModelType: MobileShell.NotificationsModelType.WatchedNotificationsModel
actionsRequireUnlock: true actionsRequireUnlock: true
historyModel: notifModel historyModel: rect.notificationsModel
property bool requestNotificationAction: false property bool requestNotificationAction: false
onUnlockRequested: { onUnlockRequested: {
requestNotificationAction = true; requestNotificationAction = true;
root.askPassword(); root.passwordRequested();
} }
} }
} }

View file

@ -1,28 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Devin Lin <espidev@gmail.com>
* SPDX-License-Identifier: GPL-2.0-or-later
*/
import QtQuick 2.12
import QtQuick.Controls 1.1
import QtQuick.Layouts 1.1
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.private.mobileshell 1.0 as MobileShell
Loader {
active: true
asynchronous: true
height: PlasmaCore.Units.gridUnit * 1.25
sourceComponent: MobileShell.StatusBar {
id: statusBar
colorGroup: PlasmaCore.Theme.ComplementaryColorGroup
backgroundColor: "transparent"
showSecondRow: false
showDropShadow: true
showTime: false
disableSystemTray: true // HACK: prevent SIGABRT
}
}