Commit 35c575be authored by Kai Uwe Broulik's avatar Kai Uwe Broulik 🍇

Implement all the things

- Notification action buttons
- Custom icon pixmaps in notifications
- Popup handling
- Notification urgency (low isn't shown as popup TBD, critical always no matter what)
- History stuff (only expired notifications end up there, none that were closed or revoked)
- Text selection and copy for body text
- Basic tray icon support
and many other things

Application jobs are shown as part of regular notifications:
- They show up as little popup that can be hidden in the history
- When finished progress popup turns into a notification

There's still some glitches and missing features, notably
- Screenshot thumbnails
- "Open" functionality for finished jobs
- History layout
- code cleanup
- and basically every configuration option
parent 7eeb28ec
/*
* Copyright 2018 Kai Uwe Broulik <kde@privat.broulik.de>
* Copyright 2018-2019 Kai Uwe Broulik <kde@privat.broulik.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
......@@ -26,6 +26,12 @@ import org.kde.plasma.components 2.0 as PlasmaComponents
MouseArea {
id: compactRoot
property int activeCount: 0
property int expiredCount: 0
property int jobsCount: 0
property int jobsPercentage: 0
property bool wasExpanded: false
onPressed: wasExpanded = plasmoid.expanded
onClicked: plasmoid.expanded = !wasExpanded
......@@ -37,25 +43,99 @@ MouseArea {
}
PlasmaCore.SvgItem {
id: notificationIcon
anchors.centerIn: parent
width: units.roundToIconSize(Math.min(parent.width, parent.height))
height: width
svg: notificationSvg
// TODO icon depending on unread notifications, active jobs, etc
// TODO use States for the icon handling including animation and what not?
elementId: {
return "notification-disabled"
elementId: "notification-disabled"
Item {
id: jobProgressItem
anchors {
left: parent.left
top: parent.top
bottom: parent.bottom
}
width: notificationIcon.width * (jobsPercentage / 100)
clip: true
visible: false
PlasmaCore.SvgItem {
anchors {
left: parent.left
top: parent.top
bottom: parent.bottom
}
width: notificationIcon.width
svg: notificationSvg
elementId: "notification-progress-active"
}
}
PlasmaComponents.Label {
id: countLabel
anchors {
fill: parent
margins: units.devicePixelRatio * 2
}
height: undefined
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
font.pointSize: 100
fontSizeMode: Text.Fit
minimumPointSize: 7
}
// FIXME just so I can tell the two apart in system tray
Text {
PlasmaComponents.BusyIndicator {
id: busyIndicator
anchors.fill: parent
text: "N" // "New"
visible: false
running: visible
}
}
// TODO progress
// would be lovely if we could get back the circular pie thing back we had in Plasma < 4.10
states: [
State { // active process
when: compactRoot.jobsCount > 0
PropertyChanges {
target: notificationIcon
elementId: "notification-progress-inactive"
}
PropertyChanges {
target: countLabel
text: compactRoot.jobsCount
}
PropertyChanges {
target: busyIndicator
visible: true
}
PropertyChanges {
target: jobProgressItem
visible: true
}
},
State { // active notification
when: compactRoot.activeCount > 0
PropertyChanges {
target: notificationIcon
elementId: "notification-active";
}
},
State { // unread notifications
when: compactRoot.expiredCount > 0
PropertyChanges {
target: notificationIcon
elementId: "notification-empty"
}
PropertyChanges {
target: countLabel
text: compactRoot.expiredCount
}
}
]
}
/*
* Copyright 2019 Kai Uwe Broulik <kde@privat.broulik.de>
*
* 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 <http://www.gnu.org/licenses/>
*/
import QtQuick 2.8
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.components 2.0 as PlasmaComponents
import org.kde.kquickcontrolsaddons 2.0 as KQCAddons
PlasmaComponents.ContextMenu {
id: contextMenu
signal closed
property QtObject __clipboard: KQCAddons.Clipboard { }
// FIXME this was supposed to be able to deal with a Text {} item, too
// but it it isn't used for that not, so all of this typeof mess can be removed
property Item target
property string link
onStatusChanged: {
if (status === PlasmaComponents.DialogStatus.Closed) {
closed();
}
}
PlasmaComponents.MenuItem {
text: i18n("Copy Link Address")
onClicked: __clipboard.content = contextMenu.link
visible: contextMenu.link !== ""
}
PlasmaComponents.MenuItem {
separator: true
visible: contextMenu.link !== ""
}
PlasmaComponents.MenuItem {
text: i18n("Copy")
icon: "edit-copy"
enabled: typeof target.selectionStart !== "undefined"
? target.selectionStart !== target.selectionEnd
: (target.text || "").length > 0
onClicked: {
if (typeof target.copy === "function") {
target.copy();
} else {
__clipboard.content = target.text;
}
}
}
PlasmaComponents.MenuItem {
id: selectAllAction
text: i18n("Select All")
onClicked: target.selectAll()
visible: typeof target.selectAll === "function"
}
}
/*
* Copyright 2018 Kai Uwe Broulik <kde@privat.broulik.de>
* Copyright 2018-2019 Kai Uwe Broulik <kde@privat.broulik.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
......@@ -23,30 +23,126 @@ 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 Components
import org.kde.plasma.components 2.0 as PlasmaComponents
import org.kde.plasma.extras 2.0 as PlasmaExtras
import org.kde.notificationmanager 1.0 as NotificationManager
Item {
id: fullRoot
ColumnLayout {
Layout.preferredWidth: units.gridUnit * 22
Layout.preferredHeight: units.gridUnit * 28
// FIXME use PlasmaComponents 3 everywhere?
PlasmaExtras.ScrollArea {
anchors.fill: parent
RowLayout {
Layout.fillWidth: true
ListView {
id: list
// FIXME use history model or the finalized API with NotificationModel instance
// that you can tel what you want (filtered, sorted by, etc), cf. TasksModel
model: NotificationManager.NotificationModel
MouseArea {
width: dndRow.width + units.gridUnit
height: dndRow.height + units.gridUnit / 2
onClicked: dndCheck.checked = !dndCheck.checked
delegate: NotificationDelegate {
width: list.width
RowLayout {
id: dndRow
anchors.centerIn: parent
summary: model.summary
body: model.body
icon: model.iconName || model.image
PlasmaComponents.CheckBox {
id: dndCheck
tooltip: i18n("Do not disturb")
}
PlasmaCore.IconItem {
// FIXME proper icon
source: "face-quiet"
width: height
Layout.fillHeight: true
}
}
}
Item {
Layout.fillWidth: true
}
PlasmaComponents.ToolButton {
iconName: "configure"
tooltip: plasmoid.action("configure").text
visible: plasmoid.action("configure").enabled
onClicked: plasmoid.action("configure").trigger()
}
}
RowLayout {
Layout.fillWidth: true
PlasmaExtras.DescriptiveLabel {
Layout.fillWidth: true
// TODO
text: "Do not disturb mode is configured to automatically enable between 22:00 and 06:00."
wrapMode: Text.WordWrap
font: theme.smallestFont
}
}
PlasmaCore.SvgItem {
elementId: "horizontal-line"
Layout.fillWidth: true
Layout.preferredHeight: 2 // FIXME
svg: PlasmaCore.Svg {
id: lineSvg
imagePath: "widgets/line"
}
}
Item {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.preferredWidth: units.gridUnit * 18
Layout.preferredHeight: units.gridUnit * 20
PlasmaExtras.Heading {
width: parent.width
level: 3
opacity: 0.6
visible: list.count === 0
text: i18n("No missed notifications.")
}
PlasmaExtras.ScrollArea {
anchors.fill: parent
ListView {
id: list
model: historyModel
// FIXME
/*section {
property: "applicationName"
criteria: ViewSection.FullString
delegate: NotificationDelegate {
width: list.width
applicationName: section
}
}*/
delegate: NotificationDelegate {
width: list.width
applicationName: model.applicationName
applicatonIconSource: model.applicationIconName
time: model.updated || model.created
configurable: model.configurable
summary: model.summary
body: model.body || "" // TODO
icon: model.image || model.iconName
// TODO everything else
onCloseClicked: historyModel.close(historyModel.makeModelIndex(index))
onConfigureClicked: historyModel.configure(historyModel.makeModelIndex(index))
svg: lineSvg
}
}
}
}
......
/*
* Copyright 2018 Kai Uwe Broulik <kde@privat.broulik.de>
* Copyright 2019 Kai Uwe Broulik <kde@privat.broulik.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
......@@ -19,50 +19,25 @@
*/
import QtQuick 2.8
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.notificationmanager 1.0 as NotificationManager
Item {
id: buttonRoot
// TODO turn into MouseArea or MEL for expand/collapse
RowLayout {
id: notificationItem
signal clicked
property alias icon: iconItem.source
property alias text: label.text
property alias ageText: ageLabel.text
property alias closable: closeButton.visible
// TODO property bool/alias configurable
property alias text: button.text
property alias iconSource: button.iconSource
property alias tooltip: button.tooltip
signal closeClicked
spacing: units.smallSpacing
PlasmaCore.IconItem {
id: iconItem
Layout.preferredWidth: units.iconSizes.small
Layout.preferredHeight: units.iconSizes.small
}
PlasmaExtras.DescriptiveLabel {
id: label
Layout.fillWidth: true
textFormat: Text.PlainText
// font.pointSize: theme.smallestFont?
}
// TODO number of notifications in group, expand collapse arrow
PlasmaExtras.DescriptiveLabel {
id: ageLabel
}
width: units.iconSizes.small
height: units.iconSizes.small
PlasmaComponents.ToolButton {
id: closeButton
iconName: "window-close" // FIXME
visible: false
id: button
anchors.centerIn: parent
onClicked: buttonRoot.clicked()
}
}
/*
* Copyright 2019 Kai Uwe Broulik <kde@privat.broulik.de>
*
* 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 <http://www.gnu.org/licenses/>
*/
import QtQuick 2.8
import QtQuick.Layouts 1.1
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.extras 2.0 as PlasmaExtras
import org.kde.kcoreaddons 1.0 as KCoreAddons
import org.kde.notificationmanager 1.0 as NotificationManager
GridLayout {
id: detailsGrid
property QtObject jobDetails
columns: 2
rowSpacing: Math.round(units.smallSpacing / 2)
columnSpacing: units.smallSpacing
// once you use Layout.column/Layout.row *all* of the items in the Layout have to use them
Repeater {
model: [1, 2]
PlasmaExtras.DescriptiveLabel {
Layout.column: 0
Layout.row: index
Layout.alignment: Qt.AlignTop | Qt.AlignRight
text: jobDetails["descriptionLabel" + modelData] && jobDetails["descriptionValue" + modelData]
? i18nc("Row description, e.g. Source", "%1:", jobDetails["descriptionLabel" + modelData]) : ""
font: theme.smallestFont
textFormat: Text.PlainText
visible: text !== ""
}
}
Repeater {
model: [1, 2]
PlasmaExtras.DescriptiveLabel {
Layout.column: 1
Layout.row: index
Layout.fillWidth: true
text: jobDetails["descriptionLabel" + modelData] && jobDetails["descriptionValue" + modelData]
? jobDetails["descriptionValue" + modelData] : ""
font: theme.smallestFont
elide: Text.ElideMiddle
textFormat: Text.PlainText
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
maximumLineCount: 5
visible: text !== ""
}
}
Repeater {
model: ["Bytes", "Files", "Directories"]
PlasmaExtras.DescriptiveLabel {
Layout.column: 1
Layout.row: 2 + index
Layout.fillWidth: true
text: {
var processed = jobDetails["processed" + modelData];
var total = jobDetails["total" + modelData];
if (processed > 0 || total > 1) {
if (processed > 0 && total > 0) {
switch(modelData) {
case "Bytes":
return i18nc("How many bytes have been copied", "%2 of %1",
KCoreAddons.Format.formatByteSize(total),
KCoreAddons.Format.formatByteSize(processed))
case "Files":
return i18ncp("How many files have been copied", "%2 of %1 file", "%2 of %1 files",
total, processed);
case "Directories":
return i18ncp("How many dirs have been copied", "%2 of %1 folder", "%2 of %1 folders",
total, processed);
}
} else {
switch(modelData) {
case "Bytes":
return KCoreAddons.Format.formatByteSize(processed || total)
case "Files":
return i18np("%1 file", "%1 files", (processed || total));
case "Directories":
return i18np("%1 folder", "%1 folders", (processed || total));
}
}
}
return "";
}
font: theme.smallestFont
textFormat: Text.PlainText
visible: text !== ""
}
}
PlasmaExtras.DescriptiveLabel {
Layout.column: 1
Layout.row: 2 + 3
Layout.fillWidth: true
text: jobDetails.speed > 0 ? i18nc("Bytes per second", "%1/s",
KCoreAddons.Format.formatByteSize(jobDetails.speed)) : ""
font: theme.smallestFont
textFormat: Text.PlainText
visible: text !== ""
}
}
/*
* Copyright 2019 Kai Uwe Broulik <kde@privat.broulik.de>
*
* 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 <http://www.gnu.org/licenses/>
*/
import QtQuick 2.8
import QtQuick.Window 2.2
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.notificationmanager 1.0 as NotificationManager
ColumnLayout {
id: jobItem
property int jobState
property int error
property string errorText
property alias percentage: progressBar.value
property alias suspendable: suspendButton.visible
property alias killable: killButton.visible
property bool hovered
property QtObject jobDetails
// TOOD make an alias on visible if we're not doing an animation
property bool showDetails
signal suspendJobClicked
signal resumeJobClicked
signal killJobClicked