mirror of
https://invent.kde.org/marcoa/shift-shell.git
synced 2026-08-03 10:14:45 +00:00
actiondrawer: Add restricted permission mode, and implement on lockscreen
This commit is contained in:
parent
3969973e51
commit
57192b772b
18 changed files with 323 additions and 116 deletions
|
|
@ -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");
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
87
look-and-feel/contents/lockscreen/HeaderComponent.qml
Normal file
87
look-and-feel/contents/lockscreen/HeaderComponent.qml
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in a new issue