diff --git a/applets/notifications/package/contents/ui/FullRepresentation.qml b/applets/notifications/package/contents/ui/FullRepresentation.qml --- a/applets/notifications/package/contents/ui/FullRepresentation.qml +++ b/applets/notifications/package/contents/ui/FullRepresentation.qml @@ -34,595 +34,588 @@ 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 + } } } }