You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
301 lines
10 KiB
301 lines
10 KiB
/* |
|
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 QtQuick.Window 2.2 |
|
import QtQuick.Layouts 1.1 |
|
import QtQml 2.15 |
|
|
|
import org.kde.plasma.core 2.0 as PlasmaCore |
|
import org.kde.plasma.components 3.0 as PlasmaComponents3 |
|
|
|
import org.kde.notificationmanager 1.0 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: jobDragIconItem.parent |
|
|
|
readonly property alias dragging: jobDragArea.dragging |
|
readonly property alias menuOpen: otherFileActionsMenu.visible |
|
|
|
signal suspendJobClicked |
|
signal resumeJobClicked |
|
signal killJobClicked |
|
|
|
signal openUrl(string url) |
|
signal fileActionInvoked(QtObject action) |
|
|
|
spacing: 0 |
|
|
|
Notifications.FileInfo { |
|
id: fileInfo |
|
url: jobItem.totalFiles === 1 && jobItem.url ? jobItem.url : "" |
|
} |
|
|
|
// This item is parented to the NotificationItem iconContainer |
|
Item { |
|
id: jobDragIconItem |
|
readonly property bool shown: jobDragIcon.valid |
|
width: parent ? parent.width : 0 |
|
height: parent ? parent.height : 0 |
|
visible: shown |
|
|
|
Binding { |
|
target: jobDragIconItem.parent |
|
property: "visible" |
|
value: true |
|
when: jobDragIconItem.shown |
|
restoreMode: Binding.RestoreBinding |
|
} |
|
|
|
PlasmaCore.IconItem { |
|
id: jobDragIcon |
|
|
|
anchors.fill: parent |
|
usesPlasmaTheme: false |
|
active: jobDragArea.containsMouse |
|
opacity: busyIndicator.running ? 0.6 : 1 |
|
source: !fileInfo.error ? fileInfo.iconName : "" |
|
|
|
Behavior on opacity { |
|
NumberAnimation { |
|
duration: PlasmaCore.Units.longDuration |
|
easing.type: Easing.InOutQuad |
|
} |
|
} |
|
|
|
DraggableFileArea { |
|
id: jobDragArea |
|
anchors.fill: parent |
|
|
|
hoverEnabled: true |
|
dragParent: jobDragIcon |
|
dragUrl: jobItem.url || "" |
|
dragPixmap: jobDragIcon.source |
|
|
|
onActivated: jobItem.openUrl(jobItem.url) |
|
onContextMenuRequested: { |
|
// avoid menu button glowing if we didn't actually press it |
|
otherFileActionsButton.checked = false; |
|
|
|
otherFileActionsMenu.visualParent = this; |
|
otherFileActionsMenu.open(x, y); |
|
} |
|
} |
|
} |
|
|
|
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 |
|
spacing: PlasmaCore.Units.smallSpacing |
|
|
|
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 |
|
} |
|
|
|
RowLayout { |
|
spacing: 0 |
|
|
|
PlasmaComponents3.ToolButton { |
|
id: suspendButton |
|
icon.name: "media-playback-pause" |
|
onClicked: jobItem.jobState === NotificationManager.Notifications.JobStateSuspended ? jobItem.resumeJobClicked() |
|
: jobItem.suspendJobClicked() |
|
|
|
PlasmaComponents3.ToolTip { |
|
text: i18ndc("plasma_applet_org.kde.plasma.notifications", "Pause running job", "Pause") |
|
} |
|
} |
|
|
|
PlasmaComponents3.ToolButton { |
|
id: killButton |
|
icon.name: "media-playback-stop" |
|
onClicked: jobItem.killJobClicked() |
|
|
|
PlasmaComponents3.ToolTip { |
|
text: i18ndc("plasma_applet_org.kde.plasma.notifications", "Cancel running job", "Cancel") |
|
} |
|
} |
|
|
|
PlasmaComponents3.ToolButton { |
|
id: expandButton |
|
icon.name: checked ? "arrow-down" : (LayoutMirroring.enabled ? "arrow-left" : "arrow-right") |
|
checkable: true |
|
enabled: jobItem.jobDetails && jobItem.jobDetails.hasDetails |
|
|
|
PlasmaComponents3.ToolTip { |
|
text: expandButton.checked ? i18ndc("plasma_applet_org.kde.plasma.notifications", "A button tooltip; hides item details", "Hide Details") |
|
: i18ndc("plasma_applet_org.kde.plasma.notifications", "A button tooltip; expands the item to show details", "Show Details") |
|
} |
|
} |
|
} |
|
} |
|
|
|
Loader { |
|
Layout.fillWidth: true |
|
active: expandButton.checked |
|
// Loader doesn't reset its height when unloaded, just hide it altogether |
|
visible: active |
|
sourceComponent: JobDetails { |
|
jobDetails: jobItem.jobDetails |
|
} |
|
} |
|
|
|
Row { |
|
id: jobActionsRow |
|
Layout.fillWidth: true |
|
spacing: PlasmaCore.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" |
|
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 - jobActionsRow.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" |
|
} |
|
}, |
|
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.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: expandButton |
|
checked: false |
|
} |
|
} |
|
] |
|
}
|
|
|