diff --git a/applets/notifications/package/contents/config/main.xml b/applets/notifications/package/contents/config/main.xml --- a/applets/notifications/package/contents/config/main.xml +++ b/applets/notifications/package/contents/config/main.xml @@ -14,6 +14,10 @@ true + + + true + diff --git a/applets/notifications/package/contents/ui/NotificationHistoryDelegate.qml b/applets/notifications/package/contents/ui/NotificationHistoryDelegate.qml new file mode 100644 --- /dev/null +++ b/applets/notifications/package/contents/ui/NotificationHistoryDelegate.qml @@ -0,0 +1,166 @@ +/* + * Copyright 2011 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.0 +import QtQuick.Controls.Private 1.0 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.extras 2.0 as PlasmaExtras +import org.kde.kquickcontrolsaddons 2.0 + +PlasmaComponents.ListItem { + id: notificationItem + width: popupFlickable.width + + opacity: 1-Math.abs(x)/width + + enabled: model.hasDefaultAction + checked: notificationItem.containsMouse + + Timer { + interval: 10*60*1000 + repeat: false + running: !idleTimeSource.idle + onTriggered: { + if (!notificationsHistoryModel.inserting) + notificationsHistoryModel.remove(index) + } + } + + MouseArea { + width: parent.width + height: childrenRect.height + acceptedButtons: Qt.NoButtons + + drag { + target: notificationItem + axis: Drag.XAxis + //kind of an hack over Column being too smart + minimumX: -parent.width + 1 + maximumX: parent.width - 1 + } + onReleased: { + if (notificationItem.x < -notificationItem.width/2) { + removeAnimation.exitFromRight = false + removeAnimation.running = true + } else if (notificationItem.x > notificationItem.width/2 ) { + removeAnimation.exitFromRight = true + removeAnimation.running = true + } else { + resetAnimation.running = true + } + } + + SequentialAnimation { + id: removeAnimation + property bool exitFromRight: true + NumberAnimation { + target: notificationItem + properties: "x" + to: removeAnimation.exitFromRight ? notificationItem.width-1 : 1-notificationItem.width + duration: units.longDuration + easing.type: Easing.InOutQuad + } + NumberAnimation { + target: notificationItem + properties: "height" + to: 0 + duration: units.longDuration + easing.type: Easing.InOutQuad + } + ScriptAction { + script: { + closeNotification(model.source); + notificationsHistoryModel.remove(index); + } + } + } + + SequentialAnimation { + id: resetAnimation + NumberAnimation { + target: notificationItem + properties: "x" + to: 0 + duration: units.longDuration + easing.type: Easing.InOutQuad + } + } + + NotificationItem { + id: notification + width: parent.width + + compact: true + icon: appIcon + image: model.image + summary: model.summary + body: model.body + configurable: model.configurable && !Settings.isMobile + // model.actions JS array is implicitly turned into a ListModel which we can assign directly + // we set this to null here, because actions for items in history will be broken once the actual notification closes + actions: null + created: model.created + hasDefaultAction: model.hasDefaultAction + hasConfigureAction: model.hasConfigureAction + urls: { + // QML ListModel tries to be smart and turns our urls Array into a dict with index as key... + var urls = [] + + var modelUrls = model.urls + if (modelUrls) { + for (var key in modelUrls) { + urls.push(modelUrls[key]) + } + } + + return urls + } + + onClose: { + if (notificationsHistoryModel.count > 1) { + removeAnimation.running = true + } else { + closeNotification(model.source) + notificationsHistoryModel.remove(index) + } + } + onConfigure: { + plasmoid.expanded = false + configureNotification(model.appRealName, model.eventId) + } + onAction: { + executeAction(model.source, actionId) + actions.clear() + } + onOpenUrl: { + plasmoid.expanded = false + Qt.openUrlExternally(url) + } + } + + } //MouseArea + + Component.onCompleted: { + mainScrollArea.height = mainScrollArea.implicitHeight + } + Component.onDestruction: { + mainScrollArea.height = mainScrollArea.implicitHeight + } +} diff --git a/applets/notifications/package/contents/ui/NotificationIcon.qml b/applets/notifications/package/contents/ui/NotificationIcon.qml --- a/applets/notifications/package/contents/ui/NotificationIcon.qml +++ b/applets/notifications/package/contents/ui/NotificationIcon.qml @@ -75,28 +75,28 @@ PlasmaComponents.Label { id: notificationCountLabel - property int oldTotalCount: 0 + property int oldActiveItemsCount: 0 // anchors.fill: parent breaks at small sizes for some reason anchors.centerIn: parent width: parent.width - (units.smallSpacing * 2.5 * text.length) height: width horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter - text: notificationsApplet.totalCount + text: notificationsApplet.activeItemsCount font.pointSize: 100 fontSizeMode: Text.Fit minimumPointSize: theme.smallestFont.pointSize visible: false Connections { target: notificationsApplet - onTotalCountChanged: { - if (notificationsApplet.totalCount > notificationCountLabel.oldTotalCount) { + onActiveItemsCountChanged: { + if (notificationsApplet.activeItemsCount > notificationCountLabel.oldActiveItemsCount) { notificationAnimation.running = true } - notificationCountLabel.oldTotalCount = notificationsApplet.totalCount + notificationCountLabel.oldActiveItemsCount = notificationsApplet.activeItemsCount } } diff --git a/applets/notifications/package/contents/ui/Notifications.qml b/applets/notifications/package/contents/ui/Notifications.qml --- a/applets/notifications/package/contents/ui/Notifications.qml +++ b/applets/notifications/package/contents/ui/Notifications.qml @@ -20,6 +20,7 @@ import QtQuick 2.0 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.extras 2.0 as PlasmaExtras import org.kde.plasma.private.notifications 1.0 @@ -32,7 +33,16 @@ } property alias count: notificationsRepeater.count + property alias historyCount: notificationsHistoryRepeater.count + + property bool showHistory + signal popupShown(var popup) + + onShowHistoryChanged: { + if(!showHistory) + clearHistory() + } Component.onCompleted: { // Create the popup components and pass them to the C++ plugin @@ -74,6 +84,13 @@ notificationsModel.insert(0, notification); notificationsModel.inserting = false; } + else if (showHistory) { + notification.created = new Date(); + + notificationsHistoryModel.inserting = true; + notificationsHistoryModel.insert(0, notification); + notificationsHistoryModel.inserting = false; + } notificationPositioner.displayNotification(notification); } @@ -129,6 +146,11 @@ } notificationsModel.clear() + clearHistory() + } + + function clearHistory() { + notificationsHistoryModel.clear() } Component { @@ -140,6 +162,11 @@ id: notificationsModel property bool inserting: false } + + ListModel { + id: notificationsHistoryModel + property bool inserting: false + } PlasmaCore.DataSource { id: idleTimeSource @@ -222,4 +249,18 @@ model: notificationsModel delegate: NotificationDelegate {} } + + PlasmaExtras.Heading { + width: parent.width + level: 3 + opacity: 0.6 + visible: historyCount > 0 + text: i18n("History") + } + + Repeater { + id: notificationsHistoryRepeater + model: notificationsHistoryModel + delegate: NotificationHistoryDelegate {} + } } diff --git a/applets/notifications/package/contents/ui/configNotifications.qml b/applets/notifications/package/contents/ui/configNotifications.qml --- a/applets/notifications/package/contents/ui/configNotifications.qml +++ b/applets/notifications/package/contents/ui/configNotifications.qml @@ -35,6 +35,7 @@ property alias cfg_showNotifications: showNotificationsCheckBox.checked property alias cfg_showJobs: showJobsCheckBox.checked + property alias cfg_showHistory: showHistoryCheckBox.checked QtLayouts.ColumnLayout { anchors.left: parent.left @@ -47,6 +48,12 @@ id: showJobsCheckBox text: i18n("Track file transfers and other jobs") } + + QtControls.CheckBox { + id: showHistoryCheckBox + text: i18n("Show a history of notifications") + } + QtControls.CheckBox { id: useCustomPopupPositionCheckBox text: i18n("Use custom position for the notification popup") diff --git a/applets/notifications/package/contents/ui/main.qml b/applets/notifications/package/contents/ui/main.qml --- a/applets/notifications/package/contents/ui/main.qml +++ b/applets/notifications/package/contents/ui/main.qml @@ -51,26 +51,27 @@ property Item notifications: notificationsLoader.item property Item jobs: jobsLoader.item - + //notifications + jobs - property int totalCount: (notifications ? notifications.count : 0) + (jobs ? jobs.count : 0) + property int activeItemsCount: (notifications ? notifications.count : 0) + (jobs ? jobs.count : 0) + property int totalCount: activeItemsCount + (notifications ? notifications.historyCount : 0) property Item notificationIcon Plasmoid.switchWidth: units.gridUnit * 20 Plasmoid.switchHeight: units.gridUnit * 30 - Plasmoid.status: totalCount > 0 ? PlasmaCore.Types.ActiveStatus : PlasmaCore.Types.PassiveStatus + Plasmoid.status: activeItemsCount > 0 ? PlasmaCore.Types.ActiveStatus : PlasmaCore.Types.PassiveStatus Plasmoid.icon: { if (jobs && jobs.count) { return "notification-active" } - return totalCount ? "notification-inactive" : "notification-disabled" + return activeItemsCount ? "notification-inactive" : "notification-disabled" } Plasmoid.toolTipSubText: { - if (totalCount == 0) { + if (activeItemsCount == 0) { return i18n("No notifications or jobs") } else if (!notifications || !notifications.count) { return i18np("%1 running job", "%1 running jobs", jobs.count) @@ -91,9 +92,9 @@ state: "default" hoverEnabled: !UiProperties.touchInput - onTotalCountChanged: { - print(" totalCountChanged " + totalCount) - if (totalCount > 0) { + onActiveItemsCountChanged: { + print(" activeItemsCountChanged " + activeItemsCount) + if (activeItemsCount > 0) { state = "new-notifications" } else { state = "default" @@ -147,6 +148,9 @@ width: parent.width source: "Notifications.qml" active: notificationsApplet.Plasmoid.configuration.showNotifications + onLoaded: { + notificationsLoader.item.showHistory = Qt.binding(function(){ return notificationsApplet.Plasmoid.configuration.showHistory }) + } } } }