mirror of
https://invent.kde.org/marcoa/shift-shell.git
synced 2026-08-02 17:54:45 +00:00
toppanel/notifications: add clean all and do not disturb
Hi, this MR adds "clear all" and "do not disturb" buttons to `NotificationsWidget`. Few other corrections have been made: - Transitions should now work correctly, plus they can be disabled via `MobileShell.MobileShellSettings.animationsEnabled`; - Loaders are now asynchronous; - List items have now their own margins, in order to not clip their shadows.\ It only remains to implement a context menu to set do not disturb mode for a given period of time. I think it's a secondary feature on which we can work later, but in case tell me what to do. Unfortunately I can't test the lockscreen on a phone at the moment, and I cannot take a performance test as well. It would be great if someone tested all these things, otherwise I'll provide as soon as possible. Closes: https://invent.kde.org/plasma/plasma-mobile/-/issues/134
This commit is contained in:
parent
db86c14293
commit
d8399e88be
4 changed files with 194 additions and 45 deletions
|
|
@ -71,6 +71,7 @@ PlasmaCore.ColorScope {
|
||||||
top: mediaWidget.bottom
|
top: mediaWidget.bottom
|
||||||
topMargin: 0
|
topMargin: 0
|
||||||
bottom: parent.bottom
|
bottom: parent.bottom
|
||||||
|
bottomMargin: 0
|
||||||
right: quickSettings.left
|
right: quickSettings.left
|
||||||
left: parent.left
|
left: parent.left
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ PlasmaCore.ColorScope {
|
||||||
color: Qt.rgba(PlasmaCore.Theme.backgroundColor.r,
|
color: Qt.rgba(PlasmaCore.Theme.backgroundColor.r,
|
||||||
PlasmaCore.Theme.backgroundColor.g,
|
PlasmaCore.Theme.backgroundColor.g,
|
||||||
PlasmaCore.Theme.backgroundColor.b,
|
PlasmaCore.Theme.backgroundColor.b,
|
||||||
notificationWidget.hasNotifications ? 0.95 : 0.7)
|
0.95)
|
||||||
Behavior on color { ColorAnimation { duration: PlasmaCore.Units.longDuration } }
|
Behavior on color { ColorAnimation { duration: PlasmaCore.Units.longDuration } }
|
||||||
opacity: Math.max(0, Math.min(1, actionDrawer.offset / root.minimizedQuickSettingsOffset))
|
opacity: Math.max(0, Math.min(1, actionDrawer.offset / root.minimizedQuickSettingsOffset))
|
||||||
}
|
}
|
||||||
|
|
@ -114,11 +114,8 @@ PlasmaCore.ColorScope {
|
||||||
top: quickSettings.top
|
top: quickSettings.top
|
||||||
topMargin: quickSettings.height + translate.y
|
topMargin: quickSettings.height + translate.y
|
||||||
bottom: parent.bottom
|
bottom: parent.bottom
|
||||||
bottomMargin: PlasmaCore.Units.largeSpacing
|
|
||||||
left: parent.left
|
left: parent.left
|
||||||
leftMargin: PlasmaCore.Units.largeSpacing
|
|
||||||
right: parent.right
|
right: parent.right
|
||||||
rightMargin: PlasmaCore.Units.largeSpacing
|
|
||||||
}
|
}
|
||||||
opacity: applyMinMax(root.actionDrawer.offset / root.minimizedQuickSettingsOffset)
|
opacity: applyMinMax(root.actionDrawer.offset / root.minimizedQuickSettingsOffset)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Layouts 1.1
|
import QtQuick.Layouts 1.15
|
||||||
import QtQuick.Window 2.2
|
import QtQuick.Window 2.2
|
||||||
import QtGraphicalEffects 1.12
|
import QtGraphicalEffects 1.12
|
||||||
|
|
||||||
|
|
@ -29,17 +29,17 @@ Item {
|
||||||
/**
|
/**
|
||||||
* The notification model for the widget.
|
* The notification model for the widget.
|
||||||
*/
|
*/
|
||||||
property var historyModel: []
|
property var historyModel
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The type of notification model used for the widget.
|
* The type of notification model used for the widget.
|
||||||
*/
|
*/
|
||||||
property int historyModelType: NotificationsModelType.NotificationsModel
|
property int historyModelType
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The notification model settings for the widget.
|
* The notification model settings for the widget.
|
||||||
*/
|
*/
|
||||||
property var notificationSettings: NotificationManager.Settings {}
|
property var notificationSettings
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether invoking notification actions requires authentiation of some sort.
|
* Whether invoking notification actions requires authentiation of some sort.
|
||||||
|
|
@ -55,6 +55,8 @@ Item {
|
||||||
*/
|
*/
|
||||||
readonly property bool hasNotifications: list.count > 0
|
readonly property bool hasNotifications: list.count > 0
|
||||||
|
|
||||||
|
readonly property bool doNotDisturbModeEnabled: !isNaN(notificationSettings.notificationsInhibitedUntil)
|
||||||
|
|
||||||
enum ModelType {
|
enum ModelType {
|
||||||
NotificationsModel, // used in the logged-in shell
|
NotificationsModel, // used in the logged-in shell
|
||||||
WatchedNotificationsModel // used on the lockscreen
|
WatchedNotificationsModel // used on the lockscreen
|
||||||
|
|
@ -85,6 +87,27 @@ Item {
|
||||||
*/
|
*/
|
||||||
function clearHistory() {
|
function clearHistory() {
|
||||||
historyModel.clear(NotificationManager.Notifications.ClearExpired);
|
historyModel.clear(NotificationManager.Notifications.ClearExpired);
|
||||||
|
|
||||||
|
if (historyModel.count === 0) {
|
||||||
|
backgroundClicked();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggles Do Not Disturb mode.
|
||||||
|
*/
|
||||||
|
function toggleDoNotDisturbMode() {
|
||||||
|
if (doNotDisturbModeEnabled) {
|
||||||
|
notificationSettings.defaults();
|
||||||
|
} else {
|
||||||
|
var until = new Date();
|
||||||
|
|
||||||
|
until.setFullYear(until.getFullYear() + 1);
|
||||||
|
|
||||||
|
notificationSettings.notificationsInhibitedUntil = until;
|
||||||
|
}
|
||||||
|
|
||||||
|
notificationSettings.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -102,35 +125,51 @@ Item {
|
||||||
intervalAlignment: PlasmaCore.Types.AlignToMinute
|
intervalAlignment: PlasmaCore.Types.AlignToMinute
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// implement background clicking signal
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: backgroundClicked()
|
||||||
|
z: -1 // ensure that this is below notification items so we don't steal button clicks
|
||||||
|
}
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
id: list
|
id: list
|
||||||
model: historyModel
|
model: historyModel
|
||||||
currentIndex: -1
|
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
currentIndex: 0
|
||||||
|
|
||||||
property var pendingNotificationWithAction
|
property var pendingNotificationWithAction
|
||||||
|
|
||||||
|
readonly property int animationDuration: MobileShell.MobileShellSettings.animationsEnabled ? PlasmaCore.Units.longDuration : 0
|
||||||
|
|
||||||
|
// If a screen overflow occurs, fix height in order to maintain tool buttons in place.
|
||||||
|
readonly property bool listOverflowing: contentItem.childrenRect.height + toolButtons.height + bottomMargin + spacing >= root.height
|
||||||
|
|
||||||
|
bottomMargin: spacing * 2
|
||||||
|
|
||||||
|
height: count === 0 ? 0 : listOverflowing ? root.height - toolButtons.height - bottomMargin : contentItem.childrenRect.height + spacing
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
|
||||||
boundsBehavior: Flickable.StopAtBounds
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
spacing: Kirigami.Units.largeSpacing
|
spacing: Kirigami.Units.largeSpacing
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
// TODO keyboard focus
|
// TODO keyboard focus
|
||||||
highlightMoveDuration: 0
|
highlightMoveDuration: 0
|
||||||
highlightResizeDuration: 0
|
highlightResizeDuration: 0
|
||||||
highlight: Item {}
|
highlight: Item {}
|
||||||
|
|
||||||
section {
|
section {
|
||||||
property: "isInGroup"
|
property: "isGroup"
|
||||||
criteria: ViewSection.FullString
|
criteria: ViewSection.FullString
|
||||||
}
|
}
|
||||||
|
|
||||||
// implement background clicking signal
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
onClicked: root.backgroundClicked()
|
|
||||||
z: -1 // ensure that this is below notification items so we don't steal button clicks
|
|
||||||
}
|
|
||||||
|
|
||||||
PlasmaExtras.PlaceholderMessage {
|
PlasmaExtras.PlaceholderMessage {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
width: parent.width - (PlasmaCore.Units.largeSpacing * 4)
|
width: parent.width - (PlasmaCore.Units.largeSpacing * 4)
|
||||||
|
|
@ -149,24 +188,13 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Run every time an item is visually added to the list, thus when `Show n more` button is clicked as well.
|
||||||
add: Transition {
|
add: Transition {
|
||||||
SequentialAnimation {
|
NumberAnimation { property: "opacity"; from: 0; to: 1; duration: list.animationDuration }
|
||||||
PropertyAction { property: "opacity"; value: 0 }
|
|
||||||
PauseAnimation { duration: PlasmaCore.Units.longDuration }
|
|
||||||
ParallelAnimation {
|
|
||||||
NumberAnimation { property: "opacity"; from: 0; to: 1; duration: PlasmaCore.Units.longDuration }
|
|
||||||
NumberAnimation { property: "height"; from: 0; duration: PlasmaCore.Units.longDuration }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
addDisplaced: Transition {
|
|
||||||
NumberAnimation { properties: "y"; duration: PlasmaCore.Units.longDuration }
|
|
||||||
}
|
|
||||||
removeDisplaced: Transition {
|
|
||||||
SequentialAnimation {
|
|
||||||
PauseAnimation { duration: PlasmaCore.Units.longDuration }
|
|
||||||
NumberAnimation { properties: "y"; duration: PlasmaCore.Units.longDuration }
|
|
||||||
}
|
}
|
||||||
|
// Run every time an item is displaced, such as when the order is scrambled due to a group expansion.
|
||||||
|
displaced: Transition {
|
||||||
|
NumberAnimation { properties: "y"; duration: list.animationDuration }
|
||||||
}
|
}
|
||||||
|
|
||||||
function isRowExpanded(row) {
|
function isRowExpanded(row) {
|
||||||
|
|
@ -191,16 +219,35 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Instantly re-align items after group expansion.
|
||||||
|
forceLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate: Loader {
|
delegate: Loader {
|
||||||
id: delegateLoader
|
id: delegateLoader
|
||||||
width: list.width
|
|
||||||
|
anchors {
|
||||||
|
left: parent ? parent.left : undefined
|
||||||
|
leftMargin: PlasmaCore.Units.largeSpacing
|
||||||
|
right: parent ? parent.right : undefined
|
||||||
|
rightMargin: PlasmaCore.Units.largeSpacing
|
||||||
|
}
|
||||||
|
|
||||||
|
height: model.isGroup ? groupDelegate.height : notificationDelegate.height
|
||||||
sourceComponent: model.isGroup ? groupDelegate : notificationDelegate
|
sourceComponent: model.isGroup ? groupDelegate : notificationDelegate
|
||||||
|
asynchronous: true
|
||||||
|
|
||||||
required property var model
|
required property var model
|
||||||
required property int index
|
required property int index
|
||||||
|
|
||||||
|
// We have to do this here in order to control the animation before the item is completely removed
|
||||||
|
ListView.onRemove: SequentialAnimation {
|
||||||
|
PropertyAction { target: delegateLoader; property: "ListView.delayRemove"; value: true }
|
||||||
|
NumberAnimation { target: delegateLoader; property: "opacity"; to: 0.0; duration: list.animationDuration }
|
||||||
|
PropertyAction { target: delegateLoader; property: "ListView.delayRemove"; value: false }
|
||||||
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: groupDelegate
|
id: groupDelegate
|
||||||
NotificationGroupHeader {
|
NotificationGroupHeader {
|
||||||
|
|
@ -213,12 +260,16 @@ Item {
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: notificationDelegate
|
id: notificationDelegate
|
||||||
ColumnLayout {
|
|
||||||
|
Column {
|
||||||
spacing: PlasmaCore.Units.smallSpacing
|
spacing: PlasmaCore.Units.smallSpacing
|
||||||
|
|
||||||
|
height: notificationItem.height + showMoreLoader.height
|
||||||
|
|
||||||
NotificationItem {
|
NotificationItem {
|
||||||
id: notificationItem
|
id: notificationItem
|
||||||
Layout.fillWidth: true
|
width: parent.width
|
||||||
|
height: implicitHeight
|
||||||
|
|
||||||
model: delegateLoader.model
|
model: delegateLoader.model
|
||||||
modelIndex: delegateLoader.index
|
modelIndex: delegateLoader.index
|
||||||
|
|
@ -233,16 +284,37 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Every item has got an instance of this loader, but it becomes active only for last ones that take place in big enough groups.
|
||||||
Loader {
|
Loader {
|
||||||
|
id: showMoreLoader
|
||||||
|
|
||||||
height: visible ? implicitHeight : 0
|
height: visible ? implicitHeight : 0
|
||||||
|
opacity: 0.0
|
||||||
visible: active
|
visible: active
|
||||||
|
|
||||||
|
asynchronous: true
|
||||||
|
|
||||||
active: {
|
active: {
|
||||||
// if we have the WatchedNotificationsModel, we don't have notification grouping support
|
// if we have the WatchedNotificationsModel, we don't have notification grouping support
|
||||||
if (typeof model.groupChildrenCount === 'undefined')
|
if (typeof model.groupChildrenCount === 'undefined')
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return (model.groupChildrenCount > model.expandedGroupChildrenCount || model.isGroupExpanded)
|
return (model.groupChildrenCount > model.expandedGroupChildrenCount || model.isGroupExpanded)
|
||||||
&& delegateLoader.ListView.nextSection !== delegateLoader.ListView.section
|
&& delegateLoader.ListView.nextSection != delegateLoader.ListView.section;
|
||||||
|
}
|
||||||
|
|
||||||
|
// state + transition: animates the item when it becomes visible. Fade off is handled by above ListView.onRemove.
|
||||||
|
states: State {
|
||||||
|
name: "VISIBLE"
|
||||||
|
when: showMoreLoader.status == Loader.Ready
|
||||||
|
PropertyChanges { target: showMoreLoader; opacity: 1.0 }
|
||||||
|
}
|
||||||
|
transitions: Transition {
|
||||||
|
to: "VISIBLE"
|
||||||
|
SequentialAnimation {
|
||||||
|
PauseAnimation { duration: list.animationDuration * 2 }
|
||||||
|
NumberAnimation { properties: "opacity"; duration: list.animationDuration }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceComponent: PlasmaComponents3.ToolButton {
|
sourceComponent: PlasmaComponents3.ToolButton {
|
||||||
|
|
@ -250,11 +322,86 @@ Item {
|
||||||
text: model.isGroupExpanded ? i18n("Show Fewer")
|
text: model.isGroupExpanded ? i18n("Show Fewer")
|
||||||
: i18nc("Expand to show n more notifications",
|
: i18nc("Expand to show n more notifications",
|
||||||
"Show %1 More", (model.groupChildrenCount - model.expandedGroupChildrenCount))
|
"Show %1 More", (model.groupChildrenCount - model.expandedGroupChildrenCount))
|
||||||
onClicked: list.setGroupExpanded(model.index, !model.isGroupExpanded)
|
onClicked: {
|
||||||
|
list.setGroupExpanded(model.index, !model.isGroupExpanded)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: toolButtons
|
||||||
|
|
||||||
|
visible: !root.actionsRequireUnlock
|
||||||
|
|
||||||
|
width: root.width
|
||||||
|
height: toolLayout.implicitHeight + spacer.height
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: list.bottom
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: spacer
|
||||||
|
|
||||||
|
visible: list.listOverflowing
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: toolButtons.top
|
||||||
|
left: toolButtons.left
|
||||||
|
right: toolButtons.right
|
||||||
|
}
|
||||||
|
|
||||||
|
height: 1
|
||||||
|
|
||||||
|
opacity: 0.25
|
||||||
|
color: PlasmaCore.Theme.textColor
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: toolLayout
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: spacer.bottom
|
||||||
|
topMargin: list.spacing
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: PlasmaCore.Units.smallSpacing
|
||||||
|
right: parent.right
|
||||||
|
rightMargin: PlasmaCore.Units.smallSpacing
|
||||||
|
bottom: parent.bottom
|
||||||
|
bottomMargin: list.spacing
|
||||||
|
}
|
||||||
|
|
||||||
|
PlasmaComponents3.ToolButton {
|
||||||
|
id: doNotDisturbButton
|
||||||
|
|
||||||
|
Layout.alignment: hasNotifications ? Qt.AlignLeft : Qt.AlignHCenter
|
||||||
|
|
||||||
|
font.bold: true
|
||||||
|
|
||||||
|
icon.name: doNotDisturbModeEnabled ? "notifications" : "notifications-disabled"
|
||||||
|
text: doNotDisturbModeEnabled ? "Enable Notifications" : "Do Not Disturb"
|
||||||
|
onClicked: toggleDoNotDisturbMode()
|
||||||
|
}
|
||||||
|
|
||||||
|
PlasmaComponents3.ToolButton {
|
||||||
|
id: clearButton
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignRight
|
||||||
|
|
||||||
|
visible: hasNotifications
|
||||||
|
|
||||||
|
font.bold: true
|
||||||
|
|
||||||
|
icon.name: "edit-clear-history"
|
||||||
|
text: "Clear All Notifications"
|
||||||
|
onClicked: clearHistory()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,13 @@ import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
import org.kde.notificationmanager 1.1 as Notifications
|
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
|
||||||
|
|
||||||
|
import org.kde.notificationmanager 1.0 as NotificationManager
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: root
|
id: root
|
||||||
required property var lockScreenState
|
required property var lockScreenState
|
||||||
property var notificationsModel: []
|
property var notificationsModel: []
|
||||||
|
property var notificationSettings: NotificationManager.Settings {}
|
||||||
|
|
||||||
readonly property bool notificationsShown: notificationsList.hasNotifications
|
readonly property bool notificationsShown: notificationsList.hasNotifications
|
||||||
|
|
||||||
|
|
@ -61,6 +64,7 @@ Rectangle {
|
||||||
historyModelType: MobileShell.NotificationsModelType.WatchedNotificationsModel
|
historyModelType: MobileShell.NotificationsModelType.WatchedNotificationsModel
|
||||||
actionsRequireUnlock: true
|
actionsRequireUnlock: true
|
||||||
historyModel: root.notificationsModel
|
historyModel: root.notificationsModel
|
||||||
|
notificationSettings: root.notificationSettings
|
||||||
|
|
||||||
property bool requestNotificationAction: false
|
property bool requestNotificationAction: false
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue