diff --git a/applets/notifications/package/contents/ui/NotificationPopup.qml b/applets/notifications/package/contents/ui/NotificationPopup.qml index 8690d1034..c408ef74a 100644 --- a/applets/notifications/package/contents/ui/NotificationPopup.qml +++ b/applets/notifications/package/contents/ui/NotificationPopup.qml @@ -1,139 +1,138 @@ /* * Copyright 2014 Martin Klapetek * * 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 QtQuick.Layouts 1.1 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 import org.kde.kquickcontrolsaddons 2.0 as KQuickControlsAddons PlasmaCore.Dialog { id: notificationPopup location: PlasmaCore.Types.Floating type: PlasmaCore.Dialog.Notification flags: Qt.WindowDoesNotAcceptFocus property var notificationProperties signal notificationTimeout() onVisibleChanged: { if (!visible) { notificationTimer.stop(); } } onYChanged: { if (visible) { notificationTimer.restart(); } } function populatePopup(notification) { notificationProperties = notification notificationTimer.interval = notification.expireTimeout notificationTimer.restart() // notification.actions is a JS array, but we can easily append that to our model notificationItem.actions.clear() notificationItem.actions.append(notificationProperties.actions) } Behavior on y { NumberAnimation { duration: units.longDuration easing.type: Easing.OutQuad } } mainItem: KQuickControlsAddons.MouseEventListener { id: root LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft LayoutMirroring.childrenInherit: true width: notificationItem.width + 2 * notificationItem.x height: notificationItem.implicitHeight + 2 * notificationItem.y hoverEnabled: true onClicked: { // the MEL would close the notification before the action button // onClicked handler would fire effectively breaking notification actions if (notificationItem.pressedAction()) { return } closeNotification(notificationProperties.source) - notificationPopup.hide() + // the popup will be closed in response to sourceRemoved } onContainsMouseChanged: { if (containsMouse) { notificationTimer.stop() } else if (!containsMouse && visible) { notificationTimer.restart() } } Timer { id: notificationTimer onTriggered: { if (!notificationProperties.isPersistent) { expireNotification(notificationProperties.source) } notificationPopup.notificationTimeout(); } } NotificationItem { id: notificationItem summary: notificationProperties ? notificationProperties.summary: "" body: notificationProperties ? notificationProperties.body : "" icon: notificationProperties ? notificationProperties.appIcon : "" image: notificationProperties ? notificationProperties.image : undefined configurable: (notificationProperties ? notificationProperties.configurable : false) && !Settings.isMobile x: units.smallSpacing y: units.smallSpacing width: Math.round(23 * units.gridUnit) maximumTextHeight: theme.mSize(theme.defaultFont).height * 10 onClose: { closeNotification(notificationProperties.source) - notificationPopup.hide() + // the popup will be closed in response to sourceRemoved } onConfigure: { configureNotification(notificationProperties.appRealName, notificationProperties.eventId) - notificationPopup.hide() + notificationPositioner.closePopup(notificationProperties.source); } onAction: { executeAction(notificationProperties.source, actionId) actions.clear() - notificationPopup.hide() } } } } diff --git a/applets/notifications/package/contents/ui/Notifications.qml b/applets/notifications/package/contents/ui/Notifications.qml index 6826938cc..d0f253408 100644 --- a/applets/notifications/package/contents/ui/Notifications.qml +++ b/applets/notifications/package/contents/ui/Notifications.qml @@ -1,216 +1,217 @@ /* * Copyright 2012 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 org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.plasma.private.notifications 1.0 Column { id: notificationsRoot anchors { left: parent.left right: parent.right } property QtObject notificationPopup property alias count: notificationsRepeater.count Component.onCompleted: { // Create the popup components and pass them to the C++ plugin for (var i = 0; i < 3; i++) { var popup = notificationPopupComponent.createObject(); notificationPositioner.addNotificationPopup(popup); } } function addNotification(notification) { // Do not show duplicated notifications for (var i = 0; i < notificationsModel.count; ++i) { if (notificationsModel.get(i).source == notification.source && notificationsModel.get(i).appName == notification.appName && notificationsModel.get(i).summary == notification.summary && notificationsModel.get(i).body == notification.body) { return } } for (var i = 0; i < notificationsModel.count; ++i) { if (notificationsModel.get(i).source == notification.source || (notificationsModel.get(i).appName == notification.appName && notificationsModel.get(i).summary == notification.summary && notificationsModel.get(i).body == notification.body)) { notificationsModel.remove(i) break } } if (notificationsModel.count > 20) { notificationsModel.remove(notificationsModel.count-1) } if (notification.isPersistent) { notification.created = new Date(); notificationsModel.inserting = true; notificationsModel.insert(0, notification); notificationsModel.inserting = false; } notificationPositioner.displayNotification(notification); } function executeAction(source, id) { //try to use the service if (id.indexOf("jobUrl#") === -1) { var service = notificationsSource.serviceForSource(source) var op = service.operationDescription("invokeAction") op["actionId"] = id service.startOperationCall(op) //try to open the id as url } else if (id.indexOf("jobUrl#") !== -1) { Qt.openUrlExternally(id.slice(7)); - notificationPositioner.closePopup(source); } + + notificationPositioner.closePopup(source); } function configureNotification(appRealName, eventId) { var service = notificationsSource.serviceForSource("notification") var op = service.operationDescription("configureNotification") op.appRealName = appRealName op.eventId = eventId service.startOperationCall(op) } function createNotification(data) { var service = notificationsSource.serviceForSource("notification"); var op = service.operationDescription("createNotification"); // add everything from "data" to "op" for (var attrname in data) { op[attrname] = data[attrname]; } service.startOperationCall(op); } function closeNotification(source) { var service = notificationsSource.serviceForSource(source) var op = service.operationDescription("userClosed") service.startOperationCall(op) } function expireNotification(source) { var service = notificationsSource.serviceForSource(source) var op = service.operationDescription("expireNotification") service.startOperationCall(op) } function clearNotifications() { for (var i = 0, length = notificationsSource.sources.length; i < length; ++i) { var source = notificationsSource.sources[i]; closeNotification(source) notificationPositioner.closePopup(source); } notificationsModel.clear() } Component { id: notificationPopupComponent NotificationPopup { } } ListModel { id: notificationsModel property bool inserting: false } PlasmaCore.DataSource { id: idleTimeSource property bool idle: data["UserActivity"]["IdleTime"] > 300000 engine: "powermanagement" interval: 30000 connectedSources: ["UserActivity"] //Idle with more than 5 minutes of user inactivity } PlasmaCore.DataSource { id: notificationsSource engine: "notifications" interval: 0 onSourceAdded: { connectSource(source); } onSourceRemoved: { notificationPositioner.closePopup(source); for (var i = 0; i < notificationsModel.count; ++i) { if (notificationsModel.get(i).source == source) { notificationsModel.remove(i) break } } } onNewData: { var _data = data; // Temp copy to avoid lots of context switching var actions = [] if (data["actions"] && data["actions"].length % 2 == 0) { for (var i = 0; i < data["actions"].length; i += 2) { actions.push({ id: data["actions"][i], text: data["actions"][i+1] }) } } _data["source"] = sourceName _data["actions"] = actions notificationsRoot.addNotification(_data) } } Connections { target: plasmoid.nativeInterface onAvailableScreenRectChanged: { notificationPositioner.setPlasmoidScreenGeometry(availableScreenRect); } } NotificationsHelper { id: notificationPositioner popupLocation: plasmoid.nativeInterface.screenPosition Component.onCompleted: { notificationPositioner.setPlasmoidScreenGeometry(plasmoid.nativeInterface.availableScreenRect); } } Repeater { id: notificationsRepeater model: notificationsModel delegate: NotificationDelegate { toolIconSize: notificationsApplet.toolIconSize } } }