mirror of
https://invent.kde.org/marcoa/shift-shell.git
synced 2026-04-26 14:23:09 +00:00
widgets/notifications: Cleanup, fix some visual issues, and add jobs
Note: There was a lot of whitespace that my IDE is now removing. Fixes job notifications not being dismissable (#208), and imports an implementation from workspace for the progress bar and actions. Fixes notification contents not being clipped when being dragged (https://invent.kde.org/teams/plasma-mobile/issues/-/issues/287) Also fixes notification text being spread over multiple lines unnecessarily (https://invent.kde.org/teams/plasma-mobile/issues/-/issues/302).
This commit is contained in:
parent
b426a7d59e
commit
5d84e6e47d
9 changed files with 659 additions and 160 deletions
|
|
@ -6,8 +6,8 @@
|
||||||
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import QtQuick 2.15
|
import QtQuick
|
||||||
import QtQuick.Window 2.2
|
import QtQuick.Window
|
||||||
|
|
||||||
import org.kde.plasma.components 3.0 as PlasmaComponents
|
import org.kde.plasma.components 3.0 as PlasmaComponents
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ Item {
|
||||||
NumberAnimation on dragOffset {
|
NumberAnimation on dragOffset {
|
||||||
id: dragAnim
|
id: dragAnim
|
||||||
duration: Kirigami.Units.longDuration
|
duration: Kirigami.Units.longDuration
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
onFinished: {
|
onFinished: {
|
||||||
if (to !== 0) {
|
if (to !== 0) {
|
||||||
root.dismissRequested();
|
root.dismissRequested();
|
||||||
|
|
@ -46,16 +47,17 @@ Item {
|
||||||
MultiEffect {
|
MultiEffect {
|
||||||
anchors.fill: mainCard
|
anchors.fill: mainCard
|
||||||
visible: Math.abs(dragOffset) !== root.width
|
visible: Math.abs(dragOffset) !== root.width
|
||||||
source: mainCard
|
source: simpleShadow
|
||||||
blurMax: 16
|
blurMax: 16
|
||||||
shadowEnabled: true
|
shadowEnabled: true
|
||||||
shadowVerticalOffset: 1
|
shadowVerticalOffset: 1
|
||||||
shadowOpacity: 0.5
|
shadowOpacity: 0.3
|
||||||
shadowColor: Qt.lighter(Kirigami.Theme.backgroundColor, 0.1)
|
shadowColor: Qt.lighter(Kirigami.Theme.backgroundColor, 0.2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// shadow
|
// shadow
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
id: simpleShadow
|
||||||
visible: Math.abs(dragOffset) !== root.width
|
visible: Math.abs(dragOffset) !== root.width
|
||||||
anchors.fill: mainCard
|
anchors.fill: mainCard
|
||||||
anchors.leftMargin: -1
|
anchors.leftMargin: -1
|
||||||
|
|
@ -78,7 +80,9 @@ Item {
|
||||||
color: (root.tapEnabled && mouseArea.pressed) ? Qt.darker(Kirigami.Theme.backgroundColor, 1.1) : Kirigami.Theme.backgroundColor
|
color: (root.tapEnabled && mouseArea.pressed) ? Qt.darker(Kirigami.Theme.backgroundColor, 1.1) : Kirigami.Theme.backgroundColor
|
||||||
radius: Kirigami.Units.smallSpacing
|
radius: Kirigami.Units.smallSpacing
|
||||||
implicitHeight: contentParent.implicitHeight
|
implicitHeight: contentParent.implicitHeight
|
||||||
clip: true
|
|
||||||
|
// clip
|
||||||
|
layer.enabled: true
|
||||||
|
|
||||||
// ensure this is behind the content to not interfere
|
// ensure this is behind the content to not interfere
|
||||||
MouseArea {
|
MouseArea {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
SPDX-FileCopyrightText: 2019 Kai Uwe Broulik <kde@privat.broulik.de>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.8
|
||||||
|
|
||||||
|
import org.kde.plasma.extras 2.0 as PlasmaExtras
|
||||||
|
|
||||||
|
import org.kde.kquickcontrolsaddons 2.0 as KQCAddons
|
||||||
|
|
||||||
|
PlasmaExtras.Menu {
|
||||||
|
id: contextMenu
|
||||||
|
|
||||||
|
signal closed
|
||||||
|
|
||||||
|
property QtObject __clipboard: KQCAddons.Clipboard { }
|
||||||
|
|
||||||
|
// can be a Text or TextEdit
|
||||||
|
property Item target
|
||||||
|
|
||||||
|
property string link
|
||||||
|
|
||||||
|
onStatusChanged: {
|
||||||
|
if (status === PlasmaExtras.Menu.Closed) {
|
||||||
|
closed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PlasmaExtras.MenuItem {
|
||||||
|
text: i18ndc("plasma_applet_org.kde.plasma.notifications", "@action:inmenu", "Copy Link Address")
|
||||||
|
icon: "edit-copy-symbolic"
|
||||||
|
onClicked: __clipboard.content = contextMenu.link
|
||||||
|
visible: contextMenu.link !== ""
|
||||||
|
}
|
||||||
|
|
||||||
|
PlasmaExtras.MenuItem {
|
||||||
|
separator: true
|
||||||
|
visible: contextMenu.link !== ""
|
||||||
|
}
|
||||||
|
|
||||||
|
PlasmaExtras.MenuItem {
|
||||||
|
text: i18ndc("plasma_applet_org.kde.plasma.notifications", "@action:inmenu", "Copy")
|
||||||
|
icon: "edit-copy-symbolic"
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PlasmaExtras.MenuItem {
|
||||||
|
id: selectAllAction
|
||||||
|
icon: "edit-select-all-symbolic"
|
||||||
|
text: i18ndc("plasma_applet_org.kde.plasma.notifications", "@action:inmenu", "Select All")
|
||||||
|
onClicked: target.selectAll()
|
||||||
|
visible: typeof target.selectAll === "function"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -20,21 +20,11 @@ import org.kde.coreaddons 1.0 as KCoreAddons
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: notificationHeading
|
id: notificationHeading
|
||||||
property int notificationType
|
|
||||||
|
|
||||||
property var applicationIconSource
|
property var applicationIconSource
|
||||||
property string applicationName
|
property string applicationName
|
||||||
property string originName
|
property string originName
|
||||||
|
|
||||||
property var time
|
|
||||||
property P5Support.DataSource timeSource
|
|
||||||
|
|
||||||
property int jobState
|
|
||||||
property QtObject jobDetails
|
|
||||||
|
|
||||||
property real timeout: 5000
|
|
||||||
property real remainingTime: 0
|
|
||||||
|
|
||||||
spacing: Kirigami.Units.smallSpacing
|
spacing: Kirigami.Units.smallSpacing
|
||||||
Layout.preferredHeight: Math.max(applicationNameLabel.implicitHeight, Kirigami.Units.iconSizes.small)
|
Layout.preferredHeight: Math.max(applicationNameLabel.implicitHeight, Kirigami.Units.iconSizes.small)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,13 +40,6 @@ BaseNotificationItem {
|
||||||
applicationName: notificationItem.applicationName
|
applicationName: notificationItem.applicationName
|
||||||
applicationIconSource: notificationItem.applicationIconSource
|
applicationIconSource: notificationItem.applicationIconSource
|
||||||
originName: notificationItem.originName
|
originName: notificationItem.originName
|
||||||
|
|
||||||
notificationType: notificationItem.notificationType
|
|
||||||
jobState: notificationItem.jobState
|
|
||||||
jobDetails: notificationItem.jobDetails
|
|
||||||
|
|
||||||
time: notificationItem.time
|
|
||||||
timeSource: notificationItem.timeSource
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// notification
|
// notification
|
||||||
|
|
@ -59,7 +52,7 @@ BaseNotificationItem {
|
||||||
|
|
||||||
tapEnabled: notificationItem.hasDefaultAction
|
tapEnabled: notificationItem.hasDefaultAction
|
||||||
onTapped: notificationItem.actionInvoked("default");
|
onTapped: notificationItem.actionInvoked("default");
|
||||||
swipeGestureEnabled: notificationItem.notificationType != NotificationManager.Notifications.JobType
|
swipeGestureEnabled: notificationItem.closable
|
||||||
onDismissRequested: notificationItem.close()
|
onDismissRequested: notificationItem.close()
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
|
|
@ -105,15 +98,9 @@ BaseNotificationItem {
|
||||||
NotificationBodyLabel {
|
NotificationBodyLabel {
|
||||||
id: bodyLabel
|
id: bodyLabel
|
||||||
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
|
Layout.alignment: Qt.AlignTop | Qt.AlignLeft
|
||||||
Layout.fillWidth: true
|
Layout.preferredWidth: column.width - iconContainer.width - Kirigami.Units.smallSpacing
|
||||||
|
|
||||||
// HACK RichText does not allow to specify link color and since LineEdit
|
text: notificationItem.body
|
||||||
// does not support StyledText, we have to inject some CSS to force the color,
|
|
||||||
// cf. QTBUG-81463 and to some extent QTBUG-80354
|
|
||||||
text: "<style>a { color: " + Kirigami.Theme.linkColor + "; }</style>" + notificationItem.body
|
|
||||||
|
|
||||||
// Cannot do text !== "" because RichText adds some HTML tags even when empty
|
|
||||||
visible: notificationItem.body !== ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// notification icon
|
// notification icon
|
||||||
|
|
@ -139,9 +126,37 @@ BaseNotificationItem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Job progress reporting
|
||||||
|
Loader {
|
||||||
|
id: jobLoader
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: item ? item.implicitHeight : 0
|
||||||
|
active: notificationItem.notificationType === NotificationManager.Notifications.JobType
|
||||||
|
visible: active
|
||||||
|
sourceComponent: NotificationJobItem {
|
||||||
|
iconContainerItem: iconContainer
|
||||||
|
|
||||||
|
jobState: notificationItem.jobState
|
||||||
|
jobError: notificationItem.jobError
|
||||||
|
percentage: notificationItem.percentage
|
||||||
|
suspendable: notificationItem.suspendable
|
||||||
|
killable: notificationItem.killable
|
||||||
|
|
||||||
|
jobDetails: notificationItem.jobDetails
|
||||||
|
|
||||||
|
onSuspendJobClicked: notificationItem.suspendJobClicked()
|
||||||
|
onResumeJobClicked: notificationItem.resumeJobClicked()
|
||||||
|
onKillJobClicked: notificationItem.killJobClicked()
|
||||||
|
|
||||||
|
onOpenUrl: notificationItem.openUrl(url)
|
||||||
|
onFileActionInvoked: notificationItem.fileActionInvoked(action)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// notification actions
|
// notification actions
|
||||||
NotificationFooterActions {
|
NotificationFooterActions {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.topMargin: Kirigami.Units.smallSpacing
|
||||||
notification: notificationItem
|
notification: notificationItem
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,148 @@
|
||||||
|
// SPDX-FileCopyrightText: 2019 Kai Uwe Broulik <kde@privat.broulik.de>
|
||||||
|
// SPDX-FileCopyrightText: 2024 Devin Lin <devin@kde.org>
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||||
|
|
||||||
|
import QtQuick 2.8
|
||||||
|
import QtQuick.Layouts 1.1
|
||||||
|
|
||||||
|
import org.kde.plasma.extras 2.0 as PlasmaExtras
|
||||||
|
import org.kde.kirigami 2.20 as Kirigami
|
||||||
|
|
||||||
|
import org.kde.coreaddons 1.0 as KCoreAddons
|
||||||
|
|
||||||
|
GridLayout {
|
||||||
|
id: detailsGrid
|
||||||
|
|
||||||
|
property QtObject jobDetails
|
||||||
|
|
||||||
|
columns: 2
|
||||||
|
rowSpacing: Math.round(Kirigami.Units.smallSpacing / 2)
|
||||||
|
columnSpacing: Kirigami.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]
|
||||||
|
? i18ndc("plasma_applet_org.kde.plasma.notifications", "Row description, e.g. Source", "%1:", jobDetails["descriptionLabel" + modelData]) : ""
|
||||||
|
font: Kirigami.Theme.smallFont
|
||||||
|
textFormat: Text.PlainText
|
||||||
|
visible: text !== ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: [1, 2]
|
||||||
|
|
||||||
|
PlasmaExtras.DescriptiveLabel {
|
||||||
|
id: descriptionValueLabel
|
||||||
|
Layout.column: 1
|
||||||
|
Layout.row: index
|
||||||
|
Layout.fillWidth: true
|
||||||
|
font: Kirigami.Theme.smallFont
|
||||||
|
elide: Text.ElideMiddle
|
||||||
|
textFormat: Text.PlainText
|
||||||
|
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
|
||||||
|
verticalAlignment: Text.AlignTop
|
||||||
|
maximumLineCount: 5
|
||||||
|
visible: text !== ""
|
||||||
|
|
||||||
|
// Only let the label grow, never shrink, to avoid repeatedly resizing the dialog when copying many files
|
||||||
|
onImplicitHeightChanged: {
|
||||||
|
if (implicitHeight > Layout.preferredHeight) {
|
||||||
|
Layout.preferredHeight = implicitHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: bindText()
|
||||||
|
function bindText() {
|
||||||
|
text = Qt.binding(function() {
|
||||||
|
return jobDetails["descriptionValue" + modelData] || "";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
acceptedButtons: Qt.RightButton
|
||||||
|
onPressed: mouse => {
|
||||||
|
// break binding so it doesn't update while the menu is opened
|
||||||
|
descriptionValueLabel.text = descriptionValueLabel.text;
|
||||||
|
descriptionValueMenu.open(mouse.x, mouse.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NotificationEditContextMenu {
|
||||||
|
id: descriptionValueMenu
|
||||||
|
target: descriptionValueLabel
|
||||||
|
// defer re-binding until after the "Copy" action in the menu has triggered
|
||||||
|
onClosed: Qt.callLater(descriptionValueLabel.bindText)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: ["Bytes", "Files", "Directories", "Items"]
|
||||||
|
|
||||||
|
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 && processed <= total) {
|
||||||
|
switch(modelData) {
|
||||||
|
case "Bytes":
|
||||||
|
return i18ndc("plasma_applet_org.kde.plasma.notifications", "How many bytes have been copied", "%2 of %1",
|
||||||
|
KCoreAddons.Format.formatByteSize(total),
|
||||||
|
KCoreAddons.Format.formatByteSize(processed))
|
||||||
|
case "Files":
|
||||||
|
return i18ndcp("plasma_applet_org.kde.plasma.notifications", "How many files have been copied", "%2 of %1 file", "%2 of %1 files",
|
||||||
|
total, processed);
|
||||||
|
case "Directories":
|
||||||
|
return i18ndcp("plasma_applet_org.kde.plasma.notifications", "How many dirs have been copied", "%2 of %1 folder", "%2 of %1 folders",
|
||||||
|
total, processed);
|
||||||
|
case "Items":
|
||||||
|
return i18ndcp("plasma_applet_org.kde.plasma.notifications", "How many items (that includes files and dirs) have been copied", "%2 of %1 item", "%2 of %1 items",
|
||||||
|
total, processed);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch(modelData) {
|
||||||
|
case "Bytes":
|
||||||
|
return KCoreAddons.Format.formatByteSize(processed || total)
|
||||||
|
case "Files":
|
||||||
|
return i18ndp("plasma_applet_org.kde.plasma.notifications", "%1 file", "%1 files", (processed || total));
|
||||||
|
case "Directories":
|
||||||
|
return i18ndp("plasma_applet_org.kde.plasma.notifications", "%1 folder", "%1 folders", (processed || total));
|
||||||
|
case "Items":
|
||||||
|
return i18ndp("plasma_applet_org.kde.plasma.notifications", "%1 item", "%1 items", (processed || total));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
font: Kirigami.Theme.smallFont
|
||||||
|
textFormat: Text.PlainText
|
||||||
|
visible: text !== ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PlasmaExtras.DescriptiveLabel {
|
||||||
|
Layout.column: 1
|
||||||
|
Layout.row: 2 + 4
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: jobDetails.speed > 0 ? i18ndc("plasma_applet_org.kde.plasma.notifications", "Bytes per second", "%1/s",
|
||||||
|
KCoreAddons.Format.formatByteSize(jobDetails.speed)) : ""
|
||||||
|
font: Kirigami.Theme.smallFont
|
||||||
|
textFormat: Text.PlainText
|
||||||
|
visible: text !== ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,277 @@
|
||||||
|
// SPDX-FileCopyrightText: 2019 Kai Uwe Broulik <kde@privat.broulik.de>
|
||||||
|
// SPDX-FileCopyrightText: 2024 Devin Lin <devin@kde.org>
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||||
|
|
||||||
|
import QtQuick 2.8
|
||||||
|
import QtQuick.Window 2.2
|
||||||
|
import QtQuick.Layouts 1.1
|
||||||
|
import QtQml 2.15
|
||||||
|
|
||||||
|
import org.kde.plasma.components 3.0 as PlasmaComponents3
|
||||||
|
import org.kde.kirigami 2.20 as Kirigami
|
||||||
|
|
||||||
|
import org.kde.notificationmanager as NotificationManager
|
||||||
|
|
||||||
|
import org.kde.plasma.private.notifications 2.0 as Notifications
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: jobItem
|
||||||
|
|
||||||
|
property int jobState
|
||||||
|
property int jobError
|
||||||
|
|
||||||
|
property alias percentage: progressBar.value
|
||||||
|
property alias suspendable: suspendButton.visible
|
||||||
|
property alias killable: killButton.visible
|
||||||
|
|
||||||
|
property QtObject jobDetails
|
||||||
|
|
||||||
|
readonly property int totalFiles: jobItem.jobDetails && jobItem.jobDetails.totalFiles || 0
|
||||||
|
readonly property var url: {
|
||||||
|
if (jobItem.jobState !== NotificationManager.Notifications.JobStateStopped
|
||||||
|
|| jobItem.jobError) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For a single file show actions for it
|
||||||
|
// Otherwise the destination folder all of them were copied into
|
||||||
|
const url = totalFiles === 1 ? jobItem.jobDetails.descriptionUrl
|
||||||
|
: jobItem.jobDetails.destUrl;
|
||||||
|
|
||||||
|
// Don't offer opening files in Trash
|
||||||
|
if (url && url.toString().startsWith("trash:")) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
property alias iconContainerItem: busyIndicatorContainer.parent
|
||||||
|
|
||||||
|
readonly property alias menuOpen: otherFileActionsMenu.visible
|
||||||
|
|
||||||
|
signal suspendJobClicked
|
||||||
|
signal resumeJobClicked
|
||||||
|
signal killJobClicked
|
||||||
|
|
||||||
|
signal openUrl(string url)
|
||||||
|
signal fileActionInvoked(QtObject action)
|
||||||
|
|
||||||
|
spacing: Kirigami.Units.smallSpacing
|
||||||
|
|
||||||
|
Notifications.FileInfo {
|
||||||
|
id: fileInfo
|
||||||
|
url: jobItem.totalFiles === 1 && jobItem.url ? jobItem.url : ""
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: busyIndicatorContainer
|
||||||
|
width: parent ? parent.width : 0
|
||||||
|
height: parent ? parent.height : 0
|
||||||
|
|
||||||
|
PlasmaComponents3.BusyIndicator {
|
||||||
|
id: busyIndicator
|
||||||
|
anchors.centerIn: parent
|
||||||
|
running: fileInfo.busy && !delayBusyTimer.running
|
||||||
|
visible: running
|
||||||
|
|
||||||
|
// Avoid briefly flashing the busy indicator
|
||||||
|
Timer {
|
||||||
|
id: delayBusyTimer
|
||||||
|
interval: 500
|
||||||
|
repeat: false
|
||||||
|
running: fileInfo.busy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: progressRow
|
||||||
|
Layout.fillWidth: true
|
||||||
|
// Even when indeterminate, we want to reserve the height for the text, otherwise it's too tightly spaced
|
||||||
|
Layout.minimumHeight: progressText.implicitHeight
|
||||||
|
// We want largeSpacing between the progress bar and the label
|
||||||
|
spacing: Kirigami.Units.largeSpacing
|
||||||
|
|
||||||
|
PlasmaComponents3.ProgressBar {
|
||||||
|
id: progressBar
|
||||||
|
Layout.fillWidth: true
|
||||||
|
from: 0
|
||||||
|
to: 100
|
||||||
|
// TODO do we actually need the window visible check? perhaps I do because it can be in popup or expanded plasmoid
|
||||||
|
indeterminate: visible && Window.window && Window.window.visible && percentage < 1
|
||||||
|
&& jobItem.jobState === NotificationManager.Notifications.JobStateRunning
|
||||||
|
// is this too annoying?
|
||||||
|
&& (jobItem.jobDetails.processedBytes === 0 || jobItem.jobDetails.totalBytes === 0)
|
||||||
|
&& jobItem.jobDetails.processedFiles === 0
|
||||||
|
//&& jobItem.jobDetails.processedDirectories === 0
|
||||||
|
}
|
||||||
|
|
||||||
|
PlasmaComponents3.Label {
|
||||||
|
id: progressText
|
||||||
|
|
||||||
|
visible: !progressBar.indeterminate
|
||||||
|
// the || "0" is a workaround for the fact that 0 as number is falsey, and is wrongly considered a missing argument
|
||||||
|
// BUG: 451807
|
||||||
|
text: i18ndc("plasma_applet_org.kde.plasma.notifications", "Percentage of a job", "%1%", jobItem.percentage || "0")
|
||||||
|
textFormat: Text.PlainText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: jobActionsRow
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
spacing: Kirigami.Units.smallSpacing
|
||||||
|
|
||||||
|
PlasmaComponents3.Button {
|
||||||
|
id: expandButton
|
||||||
|
|
||||||
|
icon.name: checked ? "collapse-symbolic" : "expand-symbolic"
|
||||||
|
text: i18ndc("plasma_applet_org.kde.plasma.notifications", "Hides/expands item details", "Details")
|
||||||
|
checkable: true
|
||||||
|
enabled: jobItem.jobDetails && jobItem.jobDetails.hasDetails
|
||||||
|
}
|
||||||
|
|
||||||
|
Item { Layout.fillWidth: true }
|
||||||
|
|
||||||
|
PlasmaComponents3.Button {
|
||||||
|
id: suspendButton
|
||||||
|
|
||||||
|
icon.name: "media-playback-pause-symbolic"
|
||||||
|
text: i18ndc("plasma_applet_org.kde.plasma.notifications", "Pause running job", "Pause")
|
||||||
|
onClicked: jobItem.jobState === NotificationManager.Notifications.JobStateSuspended ? jobItem.resumeJobClicked()
|
||||||
|
: jobItem.suspendJobClicked()
|
||||||
|
}
|
||||||
|
|
||||||
|
PlasmaComponents3.Button {
|
||||||
|
id: killButton
|
||||||
|
|
||||||
|
icon.name: "dialog-cancel-symbolic"
|
||||||
|
text: i18ndc("plasma_applet_org.kde.plasma.notifications", "Cancel running job", "Cancel")
|
||||||
|
onClicked: jobItem.killJobClicked()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: item ? item.implicitHeight : 0
|
||||||
|
active: expandButton.checked
|
||||||
|
// Loader doesn't reset its height when unloaded, just hide it altogether
|
||||||
|
visible: active
|
||||||
|
sourceComponent: NotificationJobDetails {
|
||||||
|
jobDetails: jobItem.jobDetails
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: fileActionsRow
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: Kirigami.Units.smallSpacing
|
||||||
|
// We want the actions to be right-aligned but Row also reverses
|
||||||
|
// the order of items, so we put them in reverse order
|
||||||
|
layoutDirection: Qt.RightToLeft
|
||||||
|
visible: jobItem.url && jobItem.url.toString() !== "" && !fileInfo.error
|
||||||
|
|
||||||
|
PlasmaComponents3.Button {
|
||||||
|
id: otherFileActionsButton
|
||||||
|
height: Math.max(implicitHeight, openButton.implicitHeight)
|
||||||
|
icon.name: "application-menu-symbolic"
|
||||||
|
checkable: true
|
||||||
|
text: openButton.visible ? "" : Accessible.name
|
||||||
|
Accessible.name: i18nd("plasma_applet_org.kde.plasma.notifications", "More Options…")
|
||||||
|
onPressedChanged: {
|
||||||
|
if (pressed) {
|
||||||
|
checked = Qt.binding(function() {
|
||||||
|
return otherFileActionsMenu.visible;
|
||||||
|
});
|
||||||
|
otherFileActionsMenu.visualParent = this;
|
||||||
|
// -1 tells it to "align bottom left of visualParent (this)"
|
||||||
|
otherFileActionsMenu.open(-1, -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PlasmaComponents3.ToolTip {
|
||||||
|
text: parent.Accessible.name
|
||||||
|
enabled: parent.text === ""
|
||||||
|
}
|
||||||
|
|
||||||
|
Notifications.FileMenu {
|
||||||
|
id: otherFileActionsMenu
|
||||||
|
url: jobItem.url || ""
|
||||||
|
onActionTriggered: jobItem.fileActionInvoked(action)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PlasmaComponents3.Button {
|
||||||
|
id: openButton
|
||||||
|
width: Math.min(implicitWidth, jobItem.width - otherFileActionsButton.width - fileActionsRow.spacing)
|
||||||
|
height: Math.max(implicitHeight, otherFileActionsButton.implicitHeight)
|
||||||
|
text: i18nd("plasma_applet_org.kde.plasma.notifications", "Open")
|
||||||
|
onClicked: jobItem.openUrl(jobItem.url)
|
||||||
|
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
when: jobItem.jobDetails && jobItem.jobDetails.totalFiles !== 1
|
||||||
|
PropertyChanges {
|
||||||
|
target: openButton
|
||||||
|
text: i18nd("plasma_applet_org.kde.plasma.notifications", "Open Containing Folder")
|
||||||
|
icon.name: "folder-open-symbolic"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
when: fileInfo.openAction
|
||||||
|
PropertyChanges {
|
||||||
|
target: openButton
|
||||||
|
text: fileInfo.openAction.text
|
||||||
|
icon.name: fileInfo.openActionIconName
|
||||||
|
visible: fileInfo.openAction.enabled
|
||||||
|
onClicked: {
|
||||||
|
fileInfo.openAction.trigger();
|
||||||
|
jobItem.fileActionInvoked(fileInfo.openAction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
when: jobItem.jobState === NotificationManager.Notifications.JobStateRunning
|
||||||
|
PropertyChanges {
|
||||||
|
target: suspendButton
|
||||||
|
// Explicitly set it to false so it unchecks when pausing from applet
|
||||||
|
// and then the job unpauses programmatically elsewhere.
|
||||||
|
checked: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
when: jobItem.jobState === NotificationManager.Notifications.JobStateSuspended
|
||||||
|
PropertyChanges {
|
||||||
|
target: suspendButton
|
||||||
|
checked: true
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: progressBar
|
||||||
|
enabled: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
when: jobItem.jobState === NotificationManager.Notifications.JobStateStopped
|
||||||
|
PropertyChanges {
|
||||||
|
target: progressRow
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: jobActionsRow
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: expandButton
|
||||||
|
checked: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -254,7 +254,6 @@ Item {
|
||||||
applicationName: model.applicationName
|
applicationName: model.applicationName
|
||||||
applicationIconSource: model.applicationIconName
|
applicationIconSource: model.applicationIconName
|
||||||
originName: model.originName || ""
|
originName: model.originName || ""
|
||||||
timeSource: timeDataSource
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue