diff --git a/applets/notifications/package/contents/ui/FullRepresentation.qml b/applets/notifications/package/contents/ui/FullRepresentation.qml index 404dadcae..ad9eb4e25 100644 --- a/applets/notifications/package/contents/ui/FullRepresentation.qml +++ b/applets/notifications/package/contents/ui/FullRepresentation.qml @@ -1,630 +1,623 @@ /* * Copyright 2018-2019 Kai Uwe Broulik * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see */ import QtQuick 2.10 import QtQuick.Layouts 1.1 import org.kde.plasma.plasmoid 2.0 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 2.0 as PlasmaComponents import org.kde.plasma.components 3.0 as PlasmaComponents3 import org.kde.plasma.extras 2.0 as PlasmaExtras import org.kde.kirigami 2.11 as Kirigami import org.kde.kcoreaddons 1.0 as KCoreAddons import org.kde.notificationmanager 1.0 as NotificationManager import "global" -ColumnLayout{ - // FIXME fix popup size when resizing panel smaller (so it collapses) - //Layout.preferredWidth: units.gridUnit * 18 - //Layout.preferredHeight: units.gridUnit * 24 - //Layout.minimumWidth: units.gridUnit * 10 - //Layout.minimumHeight: units.gridUnit * 15 - Layout.fillHeight: plasmoid.formFactor === PlasmaCore.Types.Vertical - - spacing: units.smallSpacing - +PlasmaComponents3.Page { // TODO these should be configurable in the future readonly property int dndMorningHour: 6 readonly property int dndEveningHour: 20 + Layout.fillHeight: plasmoid.formFactor === PlasmaCore.Types.Vertical - // HACK forward focus to the list - onActiveFocusChanged: { - if (activeFocus) { - list.forceActiveFocus(); - } - } - - Connections { - target: plasmoid - onExpandedChanged: { - if (plasmoid.expanded) { - list.positionViewAtBeginning(); - list.currentIndex = -1; - } - } - } - - // header - ColumnLayout { - id: header - visible: !Kirigami.Settings.isMobile - Layout.fillWidth: true - Layout.leftMargin: units.smallSpacing - spacing: 0 - - RowLayout { + header: PlasmaExtras.PlasmoidHeading { + ColumnLayout { + anchors.fill: parent + id: header + visible: !Kirigami.Settings.isMobile Layout.fillWidth: true + Layout.leftMargin: units.smallSpacing spacing: 0 RowLayout { - id: dndRow - spacing: units.smallSpacing - enabled: NotificationManager.Server.valid + Layout.fillWidth: true + spacing: 0 - PlasmaComponents3.CheckBox { - id: dndCheck - text: i18n("Do not disturb") + RowLayout { + id: dndRow spacing: units.smallSpacing - checkable: true - checked: Globals.inhibited - - // Let the menu open on press - onPressed: { - if (!Globals.inhibited) { - dndMenu.date = new Date(); - // shows ontop of CheckBox to hide the fact that it's unchecked - // until you actually select something :) - dndMenu.open(0, 0); - } - } - // but disable only on click - onClicked: { - if (Globals.inhibited) { - Globals.revokeInhibitions(); + enabled: NotificationManager.Server.valid + + PlasmaComponents3.CheckBox { + id: dndCheck + text: i18n("Do not disturb") + spacing: units.smallSpacing + checkable: true + checked: Globals.inhibited + + // Let the menu open on press + onPressed: { + if (!Globals.inhibited) { + dndMenu.date = new Date(); + // shows ontop of CheckBox to hide the fact that it's unchecked + // until you actually select something :) + dndMenu.open(0, 0); + } } - } - - contentItem: RowLayout { - spacing: dndCheck.spacing - - PlasmaCore.IconItem { - Layout.leftMargin: dndCheck.mirrored ? 0 : dndCheck.indicator.width + dndCheck.spacing - Layout.rightMargin: dndCheck.mirrored ? dndCheck.indicator.width + dndCheck.spacing : 0 - source: "notifications-disabled" - Layout.preferredWidth: units.iconSizes.smallMedium - Layout.preferredHeight: units.iconSizes.smallMedium + // but disable only on click + onClicked: { + if (Globals.inhibited) { + Globals.revokeInhibitions(); + } } - PlasmaComponents.Label { - text: i18n("Do not disturb") - } - } + contentItem: RowLayout { + spacing: dndCheck.spacing - PlasmaComponents.ModelContextMenu { - id: dndMenu - property date date - visualParent: dndCheck + PlasmaCore.IconItem { + Layout.leftMargin: dndCheck.mirrored ? 0 : dndCheck.indicator.width + dndCheck.spacing + Layout.rightMargin: dndCheck.mirrored ? dndCheck.indicator.width + dndCheck.spacing : 0 + source: "notifications-disabled" + Layout.preferredWidth: units.iconSizes.smallMedium + Layout.preferredHeight: units.iconSizes.smallMedium + } - onClicked: { - notificationSettings.notificationsInhibitedUntil = model.date; - notificationSettings.save(); + PlasmaComponents.Label { + text: i18n("Do not disturb") + } } - model: { - var model = []; + PlasmaComponents.ModelContextMenu { + id: dndMenu + property date date + visualParent: dndCheck - // For 1 hour - var d = dndMenu.date; - d.setHours(d.getHours() + 1); - d.setSeconds(0); - model.push({date: d, text: i18n("For 1 hour")}); + onClicked: { + notificationSettings.notificationsInhibitedUntil = model.date; + notificationSettings.save(); + } - d = dndMenu.date; - d.setHours(d.getHours() + 4); - d.setSeconds(0); - model.push({date: d, text: i18n("For 4 hours")}); + model: { + var model = []; - // Until this evening - if (dndMenu.date.getHours() < dndEveningHour) { - d = dndMenu.date; - // TODO make the user's preferred time schedule configurable - d.setHours(dndEveningHour); - d.setMinutes(0); + // For 1 hour + var d = dndMenu.date; + d.setHours(d.getHours() + 1); d.setSeconds(0); - model.push({date: d, text: i18n("Until this evening")}); - } + model.push({date: d, text: i18n("For 1 hour")}); - // Until next morning - if (dndMenu.date.getHours() > dndMorningHour) { d = dndMenu.date; - d.setDate(d.getDate() + 1); - d.setHours(dndMorningHour); - d.setMinutes(0); + d.setHours(d.getHours() + 4); d.setSeconds(0); - model.push({date: d, text: i18n("Until tomorrow morning")}); - } + model.push({date: d, text: i18n("For 4 hours")}); + + // Until this evening + if (dndMenu.date.getHours() < dndEveningHour) { + d = dndMenu.date; + // TODO make the user's preferred time schedule configurable + d.setHours(dndEveningHour); + d.setMinutes(0); + d.setSeconds(0); + model.push({date: d, text: i18n("Until this evening")}); + } - // Until Monday - // show Friday and Saturday, Sunday is "0" but for that you can use "until tomorrow morning" - if (dndMenu.date.getDay() >= 5) { - d = dndMenu.date; - d.setHours(dndMorningHour); - // wraps around if necessary - d.setDate(d.getDate() + (7 - d.getDay() + 1)); - d.setMinutes(0); - d.setSeconds(0); - model.push({date: d, text: i18n("Until Monday")}); - } + // Until next morning + if (dndMenu.date.getHours() > dndMorningHour) { + d = dndMenu.date; + d.setDate(d.getDate() + 1); + d.setHours(dndMorningHour); + d.setMinutes(0); + d.setSeconds(0); + model.push({date: d, text: i18n("Until tomorrow morning")}); + } - // Until "turned off" - d = dndMenu.date; - // Just set it to one year in the future so we don't need yet another "do not disturb enabled" property - d.setFullYear(d.getFullYear() + 1); - model.push({date: d, text: i18n("Until turned off")}); + // Until Monday + // show Friday and Saturday, Sunday is "0" but for that you can use "until tomorrow morning" + if (dndMenu.date.getDay() >= 5) { + d = dndMenu.date; + d.setHours(dndMorningHour); + // wraps around if necessary + d.setDate(d.getDate() + (7 - d.getDay() + 1)); + d.setMinutes(0); + d.setSeconds(0); + model.push({date: d, text: i18n("Until Monday")}); + } - return model; + // Until "turned off" + d = dndMenu.date; + // Just set it to one year in the future so we don't need yet another "do not disturb enabled" property + d.setFullYear(d.getFullYear() + 1); + model.push({date: d, text: i18n("Until turned off")}); + + return model; + } } } } - } - Item { - Layout.fillWidth: true - } + Item { + Layout.fillWidth: true + } - PlasmaComponents.ToolButton { - iconName: "configure" - // remove mnemonics - tooltip: plasmoid.action("openKcm").text.replace(/([^&]*)&(.)([^&]*)/g, function (match, p1, p2, p3) { - return p1.concat(p2, p3); - }); - visible: plasmoid.action("openKcm").enabled - onClicked: plasmoid.action("openKcm").trigger() - } + PlasmaComponents.ToolButton { + iconName: "configure" + // remove mnemonics + tooltip: plasmoid.action("openKcm").text.replace(/([^&]*)&(.)([^&]*)/g, function (match, p1, p2, p3) { + return p1.concat(p2, p3); + }); + visible: plasmoid.action("openKcm").enabled + onClicked: plasmoid.action("openKcm").trigger() + } - PlasmaComponents.ToolButton { - iconName: "edit-clear-history" - tooltip: i18n("Clear History") - enabled: plasmoid.action("clearHistory").visible - onClicked: action_clearHistory() + PlasmaComponents.ToolButton { + iconName: "edit-clear-history" + tooltip: i18n("Clear History") + enabled: plasmoid.action("clearHistory").visible + onClicked: action_clearHistory() + } } - } - PlasmaExtras.DescriptiveLabel { - Layout.leftMargin: dndCheck.mirrored ? 0 : dndCheck.indicator.width + 2 * dndCheck.spacing + units.iconSizes.smallMedium - Layout.rightMargin: dndCheck.mirrored ? dndCheck.indicator.width + 2 * dndCheck.spacing + units.iconSizes.smallMedium : 0 - Layout.fillWidth: true - wrapMode: Text.WordWrap - textFormat: Text.PlainText - text: { - if (!Globals.inhibited) { - return ""; - } + PlasmaExtras.DescriptiveLabel { + Layout.leftMargin: dndCheck.mirrored ? 0 : dndCheck.indicator.width + 2 * dndCheck.spacing + units.iconSizes.smallMedium + Layout.rightMargin: dndCheck.mirrored ? dndCheck.indicator.width + 2 * dndCheck.spacing + units.iconSizes.smallMedium : 0 + Layout.fillWidth: true + wrapMode: Text.WordWrap + textFormat: Text.PlainText + text: { + if (!Globals.inhibited) { + return ""; + } - var inhibitedUntil = notificationSettings.notificationsInhibitedUntil; - var inhibitedByApp = notificationSettings.notificationsInhibitedByApplication; - var inhibitedByMirroredScreens = notificationSettings.inhibitNotificationsWhenScreensMirrored - && notificationSettings.screensMirrored; + var inhibitedUntil = notificationSettings.notificationsInhibitedUntil; + var inhibitedByApp = notificationSettings.notificationsInhibitedByApplication; + var inhibitedByMirroredScreens = notificationSettings.inhibitNotificationsWhenScreensMirrored + && notificationSettings.screensMirrored; - var sections = []; + var sections = []; - // Show until time if valid but not if too far int he future - if (!isNaN(inhibitedUntil.getTime()) && inhibitedUntil.getTime() - new Date().getTime() < 365 * 24 * 60 * 60 * 1000 /* 1 year*/) { - sections.push(i18nc("Do not disturb until date", "Until %1", - KCoreAddons.Format.formatRelativeDateTime(inhibitedUntil, Locale.ShortFormat))); - } + // Show until time if valid but not if too far int he future + if (!isNaN(inhibitedUntil.getTime()) && inhibitedUntil.getTime() - new Date().getTime() < 365 * 24 * 60 * 60 * 1000 /* 1 year*/) { + sections.push(i18nc("Do not disturb until date", "Until %1", + KCoreAddons.Format.formatRelativeDateTime(inhibitedUntil, Locale.ShortFormat))); + } - if (inhibitedByApp) { - var inhibitionAppNames = notificationSettings.notificationInhibitionApplications; - var inhibitionAppReasons = notificationSettings.notificationInhibitionReasons; + if (inhibitedByApp) { + var inhibitionAppNames = notificationSettings.notificationInhibitionApplications; + var inhibitionAppReasons = notificationSettings.notificationInhibitionReasons; - for (var i = 0, length = inhibitionAppNames.length; i < length; ++i) { - var name = inhibitionAppNames[i]; - var reason = inhibitionAppReasons[i]; + for (var i = 0, length = inhibitionAppNames.length; i < length; ++i) { + var name = inhibitionAppNames[i]; + var reason = inhibitionAppReasons[i]; - if (reason) { - sections.push(i18nc("Do not disturb until app has finished (reason)", "While %1 is active (%2)", name, reason)); - } else { - sections.push(i18nc("Do not disturb until app has finished", "While %1 is active", name)); + if (reason) { + sections.push(i18nc("Do not disturb until app has finished (reason)", "While %1 is active (%2)", name, reason)); + } else { + sections.push(i18nc("Do not disturb until app has finished", "While %1 is active", name)); + } } } - } - if (inhibitedByMirroredScreens) { - sections.push(i18nc("Do not disturb because external mirrored screens connected", "Screens are mirrored")) - } + if (inhibitedByMirroredScreens) { + sections.push(i18nc("Do not disturb because external mirrored screens connected", "Screens are mirrored")) + } - return sections.join(" · "); + return sections.join(" · "); + } + visible: text !== "" } - visible: text !== "" } } - PlasmaCore.SvgItem { - visible: header.visible - elementId: "horizontal-line" - Layout.fillWidth: true - // why is this needed here but not in the delegate? - Layout.preferredHeight: naturalSize.height - svg: PlasmaCore.Svg { - id: lineSvg - imagePath: "widgets/line" + ColumnLayout{ + // FIXME fix popup size when resizing panel smaller (so it collapses) + //Layout.preferredWidth: units.gridUnit * 18 + //Layout.preferredHeight: units.gridUnit * 24 + //Layout.minimumWidth: units.gridUnit * 10 + //Layout.minimumHeight: units.gridUnit * 15 + anchors.fill: parent + + spacing: units.smallSpacing + + // HACK forward focus to the list + onActiveFocusChanged: { + if (activeFocus) { + list.forceActiveFocus(); + } } - } - // actual notifications - PlasmaExtras.ScrollArea { - Layout.fillWidth: true - Layout.fillHeight: true - Layout.preferredWidth: units.gridUnit * 18 - Layout.preferredHeight: units.gridUnit * 24 - Layout.leftMargin: units.smallSpacing - - ListView { - id: list - model: historyModel - currentIndex: -1 - - Keys.onDeletePressed: { - var idx = historyModel.index(currentIndex, 0); - if (historyModel.data(idx, NotificationManager.Notifications.ClosableRole)) { - historyModel.close(idx); - // TODO would be nice to stay inside the current group when deleting an item + Connections { + target: plasmoid + onExpandedChanged: { + if (plasmoid.expanded) { + list.positionViewAtBeginning(); + list.currentIndex = -1; } } - Keys.onEnterPressed: Keys.onReturnPressed(event) - Keys.onReturnPressed: { - // Trigger default action, if any - var idx = historyModel.index(currentIndex, 0); - if (historyModel.data(idx, NotificationManager.Notifications.HasDefaultActionRole)) { - historyModel.invokeDefaultAction(idx); - return; - } + } - // Trigger thumbnail URL if there's one - var urls = historyModel.data(idx, NotificationManager.Notifications.UrlsRole); - if (urls && urls.length === 1) { - Qt.openUrlExternally(urls[0]); - historyModel.expire(idx); - return; + // actual notifications + PlasmaExtras.ScrollArea { + Layout.fillWidth: true + Layout.fillHeight: true + Layout.preferredWidth: units.gridUnit * 18 + Layout.preferredHeight: units.gridUnit * 24 + Layout.leftMargin: units.smallSpacing + + ListView { + id: list + model: historyModel + currentIndex: -1 + + Keys.onDeletePressed: { + var idx = historyModel.index(currentIndex, 0); + if (historyModel.data(idx, NotificationManager.Notifications.ClosableRole)) { + historyModel.close(idx); + // TODO would be nice to stay inside the current group when deleting an item + } } + Keys.onEnterPressed: Keys.onReturnPressed(event) + Keys.onReturnPressed: { + // Trigger default action, if any + var idx = historyModel.index(currentIndex, 0); + if (historyModel.data(idx, NotificationManager.Notifications.HasDefaultActionRole)) { + historyModel.invokeDefaultAction(idx); + return; + } - // TODO for finished jobs trigger "Open" or "Open Containing Folder" action - } - Keys.onLeftPressed: setGroupExpanded(currentIndex, LayoutMirroring.enabled) - Keys.onRightPressed: setGroupExpanded(currentIndex, !LayoutMirroring.enabled) - - Keys.onPressed: { - switch (event.key) { - case Qt.Key_Home: - currentIndex = 0; - break; - case Qt.Key_End: - currentIndex = count - 1; - break; + // Trigger thumbnail URL if there's one + var urls = historyModel.data(idx, NotificationManager.Notifications.UrlsRole); + if (urls && urls.length === 1) { + Qt.openUrlExternally(urls[0]); + historyModel.expire(idx); + return; + } + + // TODO for finished jobs trigger "Open" or "Open Containing Folder" action + } + Keys.onLeftPressed: setGroupExpanded(currentIndex, LayoutMirroring.enabled) + Keys.onRightPressed: setGroupExpanded(currentIndex, !LayoutMirroring.enabled) + + Keys.onPressed: { + switch (event.key) { + case Qt.Key_Home: + currentIndex = 0; + break; + case Qt.Key_End: + currentIndex = count - 1; + break; + } } - } - function isRowExpanded(row) { - var idx = historyModel.index(row, 0); - return historyModel.data(idx, NotificationManager.Notifications.IsGroupExpandedRole); - } + function isRowExpanded(row) { + var idx = historyModel.index(row, 0); + return historyModel.data(idx, NotificationManager.Notifications.IsGroupExpandedRole); + } - function setGroupExpanded(row, expanded) { - var rowIdx = historyModel.index(row, 0); - var persistentRowIdx = historyModel.makePersistentModelIndex(rowIdx); - var persistentGroupIdx = historyModel.makePersistentModelIndex(historyModel.groupIndex(rowIdx)); + function setGroupExpanded(row, expanded) { + var rowIdx = historyModel.index(row, 0); + var persistentRowIdx = historyModel.makePersistentModelIndex(rowIdx); + var persistentGroupIdx = historyModel.makePersistentModelIndex(historyModel.groupIndex(rowIdx)); - historyModel.setData(rowIdx, expanded, NotificationManager.Notifications.IsGroupExpandedRole); + historyModel.setData(rowIdx, expanded, NotificationManager.Notifications.IsGroupExpandedRole); - // If the current item went away when the group collapsed, scroll to the group heading - if (!persistentRowIdx || !persistentRowIdx.valid) { - if (persistentGroupIdx && persistentGroupIdx.valid) { - list.positionViewAtIndex(persistentGroupIdx.row, ListView.Contain); - // When closed via keyboard, also set a sane current index - if (list.currentIndex > -1) { - list.currentIndex = persistentGroupIdx.row; + // If the current item went away when the group collapsed, scroll to the group heading + if (!persistentRowIdx || !persistentRowIdx.valid) { + if (persistentGroupIdx && persistentGroupIdx.valid) { + list.positionViewAtIndex(persistentGroupIdx.row, ListView.Contain); + // When closed via keyboard, also set a sane current index + if (list.currentIndex > -1) { + list.currentIndex = persistentGroupIdx.row; + } } } } - } - highlightMoveDuration: 0 - highlightResizeDuration: 0 - // Not using PlasmaComponents.Highlight as this is only for indicating keyboard focus - highlight: PlasmaCore.FrameSvgItem { - imagePath: "widgets/listitem" - prefix: "pressed" - } + highlightMoveDuration: 0 + highlightResizeDuration: 0 + // Not using PlasmaComponents.Highlight as this is only for indicating keyboard focus + highlight: PlasmaCore.FrameSvgItem { + imagePath: "widgets/listitem" + prefix: "pressed" + } - add: Transition { - SequentialAnimation { - PropertyAction { property: "opacity"; value: 0 } - PauseAnimation { duration: units.longDuration } - ParallelAnimation { - NumberAnimation { property: "opacity"; from: 0; to: 1; duration: units.longDuration } - NumberAnimation { property: "height"; from: 0; duration: units.longDuration } + add: Transition { + SequentialAnimation { + PropertyAction { property: "opacity"; value: 0 } + PauseAnimation { duration: units.longDuration } + ParallelAnimation { + NumberAnimation { property: "opacity"; from: 0; to: 1; duration: units.longDuration } + NumberAnimation { property: "height"; from: 0; duration: units.longDuration } + } } } - } - addDisplaced: Transition { - NumberAnimation { properties: "y"; duration: units.longDuration } - } + addDisplaced: Transition { + NumberAnimation { properties: "y"; duration: units.longDuration } + } - remove: Transition { - id: removeTransition - ParallelAnimation { - NumberAnimation { property: "opacity"; to: 0; duration: units.longDuration } - NumberAnimation { - id: removeXAnimation - property: "x" - to: list.width - duration: units.longDuration + remove: Transition { + id: removeTransition + ParallelAnimation { + NumberAnimation { property: "opacity"; to: 0; duration: units.longDuration } + NumberAnimation { + id: removeXAnimation + property: "x" + to: list.width + duration: units.longDuration + } } } - } - removeDisplaced: Transition { - SequentialAnimation { - PauseAnimation { duration: units.longDuration } - NumberAnimation { properties: "y"; duration: units.longDuration } + removeDisplaced: Transition { + SequentialAnimation { + PauseAnimation { duration: units.longDuration } + NumberAnimation { properties: "y"; duration: units.longDuration } + } } - } - // This is so the delegates can detect the change in "isInGroup" and show a separator - section { - property: "isInGroup" - criteria: ViewSection.FullString - } + // This is so the delegates can detect the change in "isInGroup" and show a separator + section { + property: "isInGroup" + criteria: ViewSection.FullString + } - delegate: DraggableDelegate { - id: delegate - width: list.width - contentItem: delegateLoader + delegate: DraggableDelegate { + id: delegate + width: list.width + contentItem: delegateLoader - draggable: !model.isGroup && model.type != NotificationManager.Notifications.JobType + draggable: !model.isGroup && model.type != NotificationManager.Notifications.JobType - onDismissRequested: { - // Setting the animation target explicitly before removing the notification: - // Using ViewTransition.item.x to get the x position in the animation - // causes random crash in attached property access (cf. Bug 414066) - if (x < 0) { - removeXAnimation.to = -list.width; - } + onDismissRequested: { + // Setting the animation target explicitly before removing the notification: + // Using ViewTransition.item.x to get the x position in the animation + // causes random crash in attached property access (cf. Bug 414066) + if (x < 0) { + removeXAnimation.to = -list.width; + } - historyModel.close(historyModel.index(index, 0)); - } + historyModel.close(historyModel.index(index, 0)); + } - Loader { - id: delegateLoader - width: list.width - sourceComponent: model.isGroup ? groupDelegate : notificationDelegate + Loader { + id: delegateLoader + width: list.width + sourceComponent: model.isGroup ? groupDelegate : notificationDelegate - Component { - id: groupDelegate - NotificationHeader { - applicationName: model.applicationName - applicationIconSource: model.applicationIconName - originName: model.originName || "" + Component { + id: groupDelegate + NotificationHeader { + applicationName: model.applicationName + applicationIconSource: model.applicationIconName + originName: model.originName || "" - // don't show timestamp for group + // don't show timestamp for group - configurable: model.configurable - closable: model.closable - closeButtonTooltip: i18n("Close Group") + configurable: model.configurable + closable: model.closable + closeButtonTooltip: i18n("Close Group") - onCloseClicked: { - historyModel.close(historyModel.index(index, 0)) - if (list.count === 0) { - root.closePassivePlasmoid(); + onCloseClicked: { + historyModel.close(historyModel.index(index, 0)) + if (list.count === 0) { + root.closePassivePlasmoid(); + } } - } - onConfigureClicked: historyModel.configure(historyModel.index(index, 0)) + onConfigureClicked: historyModel.configure(historyModel.index(index, 0)) + } } - } - Component { - id: notificationDelegate - ColumnLayout { - spacing: units.smallSpacing - - RowLayout { - Item { - id: groupLineContainer - Layout.fillHeight: true - Layout.topMargin: units.smallSpacing - width: units.iconSizes.small - visible: model.isInGroup - - PlasmaCore.SvgItem { - elementId: "vertical-line" - svg: lineSvg - anchors.horizontalCenter: parent.horizontalCenter + Component { + id: notificationDelegate + ColumnLayout { + spacing: units.smallSpacing + + RowLayout { + Item { + id: groupLineContainer + Layout.fillHeight: true + Layout.topMargin: units.smallSpacing width: units.iconSizes.small - height: parent.height + visible: model.isInGroup + + PlasmaCore.SvgItem { + elementId: "vertical-line" + svg: lineSvg + anchors.horizontalCenter: parent.horizontalCenter + width: units.iconSizes.small + height: parent.height + } } - } - NotificationItem { - Layout.fillWidth: true - - notificationType: model.type - - inGroup: model.isInGroup - - applicationName: model.applicationName - applicationIconSource: model.applicationIconName - originName: model.originName || "" - - time: model.updated || model.created - - // configure button on every single notifications is bit overwhelming - configurable: !inGroup && model.configurable - - dismissable: model.type === NotificationManager.Notifications.JobType - && model.jobState !== NotificationManager.Notifications.JobStateStopped - && model.dismissed - // TODO would be nice to be able to undismiss jobs even when they autohide - && notificationSettings.permanentJobPopups - dismissed: model.dismissed || false - closable: model.closable - - summary: model.summary - body: model.body || "" - icon: model.image || model.iconName - - urls: model.urls || [] - - jobState: model.jobState || 0 - percentage: model.percentage || 0 - jobError: model.jobError || 0 - suspendable: !!model.suspendable - killable: !!model.killable - jobDetails: model.jobDetails || null - - configureActionLabel: model.configureActionLabel || "" - // In the popup the default action is triggered by clicking on the popup - // however in the list this is undesirable, so instead show a clickable button - // in case you have a non-expired notification in history (do not disturb mode) - // unless it has the same label as an action - readonly property bool addDefaultAction: (model.hasDefaultAction - && model.defaultActionLabel - && (model.actionLabels || []).indexOf(model.defaultActionLabel) === -1) ? true : false - actionNames: { - var actions = (model.actionNames || []); - if (addDefaultAction) { - actions.unshift("default"); // prepend + NotificationItem { + Layout.fillWidth: true + + notificationType: model.type + + inGroup: model.isInGroup + + applicationName: model.applicationName + applicationIconSource: model.applicationIconName + originName: model.originName || "" + + time: model.updated || model.created + + // configure button on every single notifications is bit overwhelming + configurable: !inGroup && model.configurable + + dismissable: model.type === NotificationManager.Notifications.JobType + && model.jobState !== NotificationManager.Notifications.JobStateStopped + && model.dismissed + // TODO would be nice to be able to undismiss jobs even when they autohide + && notificationSettings.permanentJobPopups + dismissed: model.dismissed || false + closable: model.closable + + summary: model.summary + body: model.body || "" + icon: model.image || model.iconName + + urls: model.urls || [] + + jobState: model.jobState || 0 + percentage: model.percentage || 0 + jobError: model.jobError || 0 + suspendable: !!model.suspendable + killable: !!model.killable + jobDetails: model.jobDetails || null + + configureActionLabel: model.configureActionLabel || "" + // In the popup the default action is triggered by clicking on the popup + // however in the list this is undesirable, so instead show a clickable button + // in case you have a non-expired notification in history (do not disturb mode) + // unless it has the same label as an action + readonly property bool addDefaultAction: (model.hasDefaultAction + && model.defaultActionLabel + && (model.actionLabels || []).indexOf(model.defaultActionLabel) === -1) ? true : false + actionNames: { + var actions = (model.actionNames || []); + if (addDefaultAction) { + actions.unshift("default"); // prepend + } + return actions; } - return actions; - } - actionLabels: { - var labels = (model.actionLabels || []); - if (addDefaultAction) { - labels.unshift(model.defaultActionLabel); + actionLabels: { + var labels = (model.actionLabels || []); + if (addDefaultAction) { + labels.unshift(model.defaultActionLabel); + } + return labels; } - return labels; - } - onCloseClicked: { - historyModel.close(historyModel.index(index, 0)); - if (list.count === 0) { + onCloseClicked: { + historyModel.close(historyModel.index(index, 0)); + if (list.count === 0) { + root.closePassivePlasmoid(); + } + } + onDismissClicked: { + model.dismissed = false; root.closePassivePlasmoid(); } - } - onDismissClicked: { - model.dismissed = false; - root.closePassivePlasmoid(); - } - onConfigureClicked: historyModel.configure(historyModel.index(index, 0)) - - onActionInvoked: { - if (actionName === "default") { - historyModel.invokeDefaultAction(historyModel.index(index, 0)); - } else { - historyModel.invokeAction(historyModel.index(index, 0), actionName); + onConfigureClicked: historyModel.configure(historyModel.index(index, 0)) + + onActionInvoked: { + if (actionName === "default") { + historyModel.invokeDefaultAction(historyModel.index(index, 0)); + } else { + historyModel.invokeAction(historyModel.index(index, 0), actionName); + } + // Keep it in the history + historyModel.expire(historyModel.index(index, 0)); } - // Keep it in the history - historyModel.expire(historyModel.index(index, 0)); - } - onOpenUrl: { - Qt.openUrlExternally(url); - historyModel.expire(historyModel.index(index, 0)); - } - onFileActionInvoked: historyModel.expire(historyModel.index(index, 0)) + onOpenUrl: { + Qt.openUrlExternally(url); + historyModel.expire(historyModel.index(index, 0)); + } + onFileActionInvoked: historyModel.expire(historyModel.index(index, 0)) - onSuspendJobClicked: historyModel.suspendJob(historyModel.index(index, 0)) - onResumeJobClicked: historyModel.resumeJob(historyModel.index(index, 0)) - onKillJobClicked: historyModel.killJob(historyModel.index(index, 0)) + onSuspendJobClicked: historyModel.suspendJob(historyModel.index(index, 0)) + onResumeJobClicked: historyModel.resumeJob(historyModel.index(index, 0)) + onKillJobClicked: historyModel.killJob(historyModel.index(index, 0)) + } } - } - PlasmaComponents.ToolButton { - Layout.preferredWidth: minimumWidth - iconName: model.isGroupExpanded ? "arrow-up" : "arrow-down" - text: model.isGroupExpanded ? i18n("Show Fewer") - : i18nc("Expand to show n more notifications", - "Show %1 More", (model.groupChildrenCount - model.expandedGroupChildrenCount)) - visible: (model.groupChildrenCount > model.expandedGroupChildrenCount || model.isGroupExpanded) - && delegate.ListView.nextSection !== delegate.ListView.section - onClicked: list.setGroupExpanded(model.index, !model.isGroupExpanded) - } + PlasmaComponents.ToolButton { + Layout.preferredWidth: minimumWidth + iconName: model.isGroupExpanded ? "arrow-up" : "arrow-down" + text: model.isGroupExpanded ? i18n("Show Fewer") + : i18nc("Expand to show n more notifications", + "Show %1 More", (model.groupChildrenCount - model.expandedGroupChildrenCount)) + visible: (model.groupChildrenCount > model.expandedGroupChildrenCount || model.isGroupExpanded) + && delegate.ListView.nextSection !== delegate.ListView.section + onClicked: list.setGroupExpanded(model.index, !model.isGroupExpanded) + } - PlasmaCore.SvgItem { - Layout.fillWidth: true - Layout.bottomMargin: units.smallSpacing - elementId: "horizontal-line" - svg: lineSvg + PlasmaCore.SvgItem { + Layout.fillWidth: true + Layout.bottomMargin: units.smallSpacing + elementId: "horizontal-line" + svg: lineSvg - // property is only atached to the delegate itself (the Loader in our case) - visible: (!model.isInGroup || delegate.ListView.nextSection !== delegate.ListView.section) - && delegate.ListView.nextSection !== "" // don't show after last item + // property is only atached to the delegate itself (the Loader in our case) + visible: (!model.isInGroup || delegate.ListView.nextSection !== delegate.ListView.section) + && delegate.ListView.nextSection !== "" // don't show after last item + } } } } } - } - - PlasmaExtras.Heading { - anchors.fill: parent - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - wrapMode: Text.WordWrap - level: 3 - text: i18n("No unread notifications") - visible: list.count === 0 && NotificationManager.Server.valid - enabled: false - } - - ColumnLayout { - id: serverUnavailableColumn - - width: list.width - visible: list.count === 0 && !NotificationManager.Server.valid PlasmaExtras.Heading { - Layout.fillWidth: true - level: 3 - opacity: 0.6 - text: i18n("Notification service not available") + anchors.fill: parent + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter wrapMode: Text.WordWrap + level: 3 + text: i18n("No unread notifications") + visible: list.count === 0 && NotificationManager.Server.valid + enabled: false } - PlasmaComponents.Label { - // Checking valid to avoid creating ServerInfo object if everything is alright - readonly property NotificationManager.ServerInfo currentOwner: !NotificationManager.Server.valid ? NotificationManager.Server.currentOwner - : null + ColumnLayout { + id: serverUnavailableColumn - Layout.fillWidth: true - wrapMode: Text.WordWrap - text: currentOwner ? i18nc("Vendor and product name", - "Notifications are currently provided by '%1 %2'", - currentOwner.vendor, - currentOwner.name) - : "" - visible: currentOwner && currentOwner.vendor && currentOwner.name + width: list.width + visible: list.count === 0 && !NotificationManager.Server.valid + + PlasmaExtras.Heading { + Layout.fillWidth: true + level: 3 + opacity: 0.6 + text: i18n("Notification service not available") + wrapMode: Text.WordWrap + } + + PlasmaComponents.Label { + // Checking valid to avoid creating ServerInfo object if everything is alright + readonly property NotificationManager.ServerInfo currentOwner: !NotificationManager.Server.valid ? NotificationManager.Server.currentOwner + : null + + Layout.fillWidth: true + wrapMode: Text.WordWrap + text: currentOwner ? i18nc("Vendor and product name", + "Notifications are currently provided by '%1 %2'", + currentOwner.vendor, + currentOwner.name) + : "" + visible: currentOwner && currentOwner.vendor && currentOwner.name + } } } } } }