diff --git a/applets/notifications/package/contents/ui/NotificationItem.qml b/applets/notifications/package/contents/ui/NotificationItem.qml deleted file mode 100644 --- a/applets/notifications/package/contents/ui/NotificationItem.qml +++ /dev/null @@ -1,450 +0,0 @@ -/* - * 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.8 -import QtQuick.Layouts 1.1 -import QtQuick.Window 2.2 - -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.kquickcontrolsaddons 2.0 as KQCAddons - -import org.kde.notificationmanager 1.0 as NotificationManager - -ColumnLayout { - id: notificationItem - - property bool hovered: false - property int maximumLineCount: 0 - property alias bodyCursorShape: bodyLabel.cursorShape - - property int notificationType - - property bool inGroup: false - - property alias applicationIconSource: notificationHeading.applicationIconSource - property alias applicationName: notificationHeading.applicationName - property alias originName: notificationHeading.originName - - property string summary - property alias time: notificationHeading.time - - property alias configurable: notificationHeading.configurable - property alias dismissable: notificationHeading.dismissable - property alias dismissed: notificationHeading.dismissed - property alias closable: notificationHeading.closable - - // This isn't an alias because TextEdit RichText adds some HTML tags to it - property string body - property var icon - property var urls: [] - - property int jobState - property int percentage - property int jobError: 0 - property bool suspendable - property bool killable - - property QtObject jobDetails - property bool showDetails - - property alias configureActionLabel: notificationHeading.configureActionLabel - property var actionNames: [] - property var actionLabels: [] - - property bool hasReplyAction - property string replyActionLabel - property string replyPlaceholderText - property string replySubmitButtonText - property string replySubmitButtonIconName - - property int headingLeftPadding: 0 - property int headingRightPadding: 0 - - property int thumbnailLeftPadding: 0 - property int thumbnailRightPadding: 0 - property int thumbnailTopPadding: 0 - property int thumbnailBottomPadding: 0 - - property alias timeout: notificationHeading.timeout - property alias remainingTime: notificationHeading.remainingTime - - readonly property bool menuOpen: bodyLabel.contextMenu !== null - || (thumbnailStripLoader.item && thumbnailStripLoader.item.menuOpen) - || (jobLoader.item && jobLoader.item.menuOpen) - - readonly property bool dragging: (thumbnailStripLoader.item && thumbnailStripLoader.item.dragging) - || (jobLoader.item && jobLoader.item.dragging) - property bool replying: false - - signal bodyClicked(var mouse) - signal closeClicked - signal configureClicked - signal dismissClicked - signal actionInvoked(string actionName) - signal replied(string text) - signal openUrl(string url) - signal fileActionInvoked - - signal suspendJobClicked - signal resumeJobClicked - signal killJobClicked - - spacing: units.smallSpacing - - NotificationHeader { - id: notificationHeading - Layout.fillWidth: true - Layout.leftMargin: notificationItem.headingLeftPadding - Layout.rightMargin: notificationItem.headingRightPadding - - inGroup: notificationItem.inGroup - - notificationType: notificationItem.notificationType - jobState: notificationItem.jobState - jobDetails: notificationItem.jobDetails - - onConfigureClicked: notificationItem.configureClicked() - onDismissClicked: notificationItem.dismissClicked() - onCloseClicked: notificationItem.closeClicked() - } - - RowLayout { - id: defaultHeaderContainer - Layout.fillWidth: true - } - - // Notification body - RowLayout { - id: bodyRow - Layout.fillWidth: true - spacing: units.smallSpacing - - ColumnLayout { - Layout.fillWidth: true - spacing: 0 - - RowLayout { - id: summaryRow - Layout.fillWidth: true - visible: summaryLabel.text !== "" - - PlasmaExtras.Heading { - id: summaryLabel - Layout.fillWidth: true - Layout.preferredHeight: implicitHeight - textFormat: Text.PlainText - maximumLineCount: 3 - wrapMode: Text.WordWrap - elide: Text.ElideRight - level: 4 - text: { - if (notificationItem.notificationType === NotificationManager.Notifications.JobType) { - if (notificationItem.jobState === NotificationManager.Notifications.JobStateSuspended) { - if (notificationItem.summary) { - return i18ndc("plasma_applet_org.kde.plasma.notifications", "Job name, e.g. Copying is paused", "%1 (Paused)", notificationItem.summary); - } - } else if (notificationItem.jobState === NotificationManager.Notifications.JobStateStopped) { - if (notificationItem.jobError) { - if (notificationItem.summary) { - return i18ndc("plasma_applet_org.kde.plasma.notifications", "Job name, e.g. Copying has failed", "%1 (Failed)", notificationItem.summary); - } else { - return i18nd("plasma_applet_org.kde.plasma.notifications", "Job Failed"); - } - } else { - if (notificationItem.summary) { - return i18ndc("plasma_applet_org.kde.plasma.notifications", "Job name, e.g. Copying has finished", "%1 (Finished)", notificationItem.summary); - } else { - return i18nd("plasma_applet_org.kde.plasma.notifications", "Job Finished"); - } - } - } - } - // some apps use their app name as summary, avoid showing the same text twice - // try very hard to match the two - if (notificationItem.summary && notificationItem.summary.toLocaleLowerCase().trim() != notificationItem.applicationName.toLocaleLowerCase().trim()) { - return notificationItem.summary; - } - return ""; - } - visible: text !== "" - } - - // inGroup headerItem is reparented here - } - - RowLayout { - id: bodyTextRow - - Layout.fillWidth: true - spacing: units.smallSpacing - - SelectableLabel { - id: bodyLabel - // FIXME how to assign this via State? target: bodyLabel.Layout doesn't work and just assigning the property doesn't either - Layout.alignment: notificationItem.inGroup ? Qt.AlignTop : Qt.AlignVCenter - Layout.fillWidth: true - - Layout.maximumHeight: notificationItem.maximumLineCount > 0 - ? (theme.mSize(font).height * notificationItem.maximumLineCount) : -1 - text: notificationItem.body - // Cannot do text !== "" because RichText adds some HTML tags even when empty - visible: notificationItem.body !== "" - onClicked: notificationItem.bodyClicked(mouse) - onLinkActivated: Qt.openUrlExternally(link) - } - - // inGroup iconContainer is reparented here - } - } - - Item { - id: iconContainer - - Layout.preferredWidth: units.iconSizes.large - Layout.preferredHeight: units.iconSizes.large - - visible: iconItem.active || imageItem.active - - PlasmaCore.IconItem { - id: iconItem - // don't show two identical icons - readonly property bool active: valid && source != notificationItem.applicationIconSource - anchors.fill: parent - usesPlasmaTheme: false - smooth: true - source: { - var icon = notificationItem.icon; - if (typeof icon !== "string") { // displayed by QImageItem below - return ""; - } - - // don't show a generic "info" icon since this is a notification already - if (icon === "dialog-information") { - return ""; - } - - return icon; - } - visible: active - } - - KQCAddons.QImageItem { - id: imageItem - readonly property bool active: !null && nativeWidth > 0 - anchors.fill: parent - smooth: true - fillMode: KQCAddons.QImageItem.PreserveAspectFit - visible: active - image: typeof notificationItem.icon === "object" ? notificationItem.icon : undefined - } - - // JobItem reparents a file icon here for finished jobs with one total file - } - } - - // Job progress reporting - Loader { - id: jobLoader - Layout.fillWidth: true - active: notificationItem.notificationType === NotificationManager.Notifications.JobType - visible: active - sourceComponent: JobItem { - iconContainerItem: iconContainer - - jobState: notificationItem.jobState - jobError: notificationItem.jobError - percentage: notificationItem.percentage - suspendable: notificationItem.suspendable - killable: notificationItem.killable - - jobDetails: notificationItem.jobDetails - showDetails: notificationItem.showDetails - - onSuspendJobClicked: notificationItem.suspendJobClicked() - onResumeJobClicked: notificationItem.resumeJobClicked() - onKillJobClicked: notificationItem.killJobClicked() - - onOpenUrl: notificationItem.openUrl(url) - onFileActionInvoked: notificationItem.fileActionInvoked() - - hovered: notificationItem.hovered - } - } - - Item { - id: actionContainer - Layout.fillWidth: true - Layout.preferredHeight: Math.max(actionFlow.implicitHeight, replyLoader.height) - visible: actionRepeater.count > 0 - - // Notification actions - Flow { // it's a Flow so it can wrap if too long - id: actionFlow - width: parent.width - spacing: units.smallSpacing - layoutDirection: Qt.RightToLeft - enabled: !replyLoader.active - opacity: replyLoader.active ? 0 : 1 - Behavior on opacity { - NumberAnimation { - duration: units.longDuration - easing.type: Easing.InOutQuad - } - } - - Repeater { - id: actionRepeater - - model: { - var buttons = []; - // HACK We want the actions to be right-aligned but Flow also reverses - var actionNames = (notificationItem.actionNames || []).reverse(); - var actionLabels = (notificationItem.actionLabels || []).reverse(); - for (var i = 0; i < actionNames.length; ++i) { - buttons.push({ - actionName: actionNames[i], - label: actionLabels[i] - }); - } - - if (notificationItem.hasReplyAction) { - buttons.unshift({ - actionName: "inline-reply", - label: notificationItem.replyActionLabel || i18nc("Reply to message", "Reply") - }); - } - - return buttons; - } - - PlasmaComponents.ToolButton { - flat: false - // why does it spit "cannot assign undefined to string" when a notification becomes expired? - text: modelData.label || "" - width: minimumWidth - - onClicked: { - if (modelData.actionName === "inline-reply") { - replyLoader.beginReply(); - return; - } - - notificationItem.actionInvoked(modelData.actionName); - } - } - } - } - - // inline reply field - Loader { - id: replyLoader - width: parent.width - height: active ? item.implicitHeight : 0 - // When there is only one action and it is a reply action, show text field right away - active: notificationItem.replying || (notificationItem.hasReplyAction && (notificationItem.actionNames || []).length === 0) - visible: active - opacity: active ? 1 : 0 - x: active ? 0 : parent.width - Behavior on x { - NumberAnimation { - duration: units.longDuration - easing.type: Easing.InOutQuad - } - } - Behavior on opacity { - NumberAnimation { - duration: units.longDuration - easing.type: Easing.InOutQuad - } - } - - function beginReply() { - notificationItem.replying = true; - - plasmoid.nativeInterface.forceActivateWindow(notificationItem.Window.window); - replyLoader.item.activate(); - } - - sourceComponent: NotificationReplyField { - placeholderText: notificationItem.replyPlaceholderText - buttonIconName: notificationItem.replySubmitButtonIconName - buttonText: notificationItem.replySubmitButtonText - onReplied: notificationItem.replied(text) - - replying: notificationItem.replying - onBeginReplyRequested: replyLoader.beginReply() - } - } - } - - // thumbnails - Loader { - id: thumbnailStripLoader - Layout.leftMargin: notificationItem.thumbnailLeftPadding - Layout.rightMargin: notificationItem.thumbnailRightPadding - // no change in Layout.topMargin to keep spacing to notification text consistent - Layout.topMargin: 0 - Layout.bottomMargin: notificationItem.thumbnailBottomPadding - Layout.fillWidth: true - active: notificationItem.urls.length > 0 - visible: active - sourceComponent: ThumbnailStrip { - leftPadding: -thumbnailStripLoader.Layout.leftMargin - rightPadding: -thumbnailStripLoader.Layout.rightMargin - topPadding: -notificationItem.thumbnailTopPadding - bottomPadding: -thumbnailStripLoader.Layout.bottomMargin - urls: notificationItem.urls - onOpenUrl: notificationItem.openUrl(url) - onFileActionInvoked: notificationItem.fileActionInvoked() - } - } - - states: [ - State { - when: notificationItem.inGroup - PropertyChanges { - target: notificationHeading - parent: summaryRow - } - - PropertyChanges { - target: summaryRow - visible: true - } - PropertyChanges { - target: summaryLabel - visible: true - } - - /*PropertyChanges { - target: bodyLabel.Label - alignment: Qt.AlignTop - }*/ - - PropertyChanges { - target: iconContainer - parent: bodyTextRow - } - } - ] -} diff --git a/applets/notifications/package/contents/ui/NotificationPopup.qml b/applets/notifications/package/contents/ui/NotificationPopup.qml deleted file mode 100644 --- a/applets/notifications/package/contents/ui/NotificationPopup.qml +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright 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.8 -import QtQuick.Layouts 1.1 - -import org.kde.plasma.core 2.0 as PlasmaCore -import org.kde.plasma.components 2.0 as Components - -import org.kde.notificationmanager 1.0 as NotificationManager - -import ".." - -PlasmaCore.Dialog { - id: notificationPopup - - property int popupWidth - - property alias notificationType: notificationItem.notificationType - - property alias applicationName: notificationItem.applicationName - property alias applicationIconSource: notificationItem.applicationIconSource - property alias originName: notificationItem.originName - - property alias time: notificationItem.time - - property alias summary: notificationItem.summary - property alias body: notificationItem.body - property alias icon: notificationItem.icon - property alias urls: notificationItem.urls - - property int urgency - property int timeout - property int dismissTimeout - - property alias jobState: notificationItem.jobState - property alias percentage: notificationItem.percentage - property alias jobError: notificationItem.jobError - property alias suspendable: notificationItem.suspendable - property alias killable: notificationItem.killable - property alias jobDetails: notificationItem.jobDetails - - property alias configureActionLabel: notificationItem.configureActionLabel - property alias configurable: notificationItem.configurable - property alias dismissable: notificationItem.dismissable - property alias closable: notificationItem.closable - - property bool hasDefaultAction - property var defaultActionFallbackWindowIdx - property alias actionNames: notificationItem.actionNames - property alias actionLabels: notificationItem.actionLabels - - property alias hasReplyAction: notificationItem.hasReplyAction - property alias replyActionLabel: notificationItem.replyActionLabel - property alias replyPlaceholderText: notificationItem.replyPlaceholderText - property alias replySubmitButtonText: notificationItem.replySubmitButtonText - property alias replySubmitButtonIconName: notificationItem.replySubmitButtonIconName - - signal configureClicked - signal dismissClicked - signal closeClicked - - signal defaultActionInvoked - signal actionInvoked(string actionName) - signal replied(string text) - signal openUrl(string url) - signal fileActionInvoked - - signal expired - signal hoverEntered - signal hoverExited - - signal suspendJobClicked - signal resumeJobClicked - signal killJobClicked - - property int defaultTimeout: 5000 - readonly property int effectiveTimeout: { - if (timeout === -1) { - return defaultTimeout; - } - if (dismissTimeout) { - return dismissTimeout; - } - return timeout; - } - - location: PlasmaCore.Types.Floating - flags: notificationItem.replying ? 0 : Qt.WindowDoesNotAcceptFocus - - visible: false - - // When notification is updated, restart hide timer - onTimeChanged: { - if (timer.running) { - timer.restart(); - } - } - - mainItem: Item { - width: notificationPopup.popupWidth - height: notificationItem.implicitHeight + notificationItem.y - DraggableDelegate { - id: area - width: parent.width - height: parent.height - hoverEnabled: true - draggable: notificationItem.notificationType != NotificationManager.Notifications.JobType - onDismissRequested: popupNotificationsModel.close(popupNotificationsModel.index(index, 0)) - - cursorShape: hasDefaultAction ? Qt.PointingHandCursor : Qt.ArrowCursor - acceptedButtons: hasDefaultAction || draggable ? Qt.LeftButton : Qt.NoButton - - onClicked: { - if (hasDefaultAction) { - notificationPopup.defaultActionInvoked(); - } - } - onEntered: notificationPopup.hoverEntered() - onExited: notificationPopup.hoverExited() - - LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft - LayoutMirroring.childrenInherit: true - - Timer { - id: timer - interval: notificationPopup.effectiveTimeout - running: notificationPopup.visible && !area.containsMouse && interval > 0 - && !notificationItem.dragging && !notificationItem.menuOpen && !notificationItem.replying - onTriggered: { - if (notificationPopup.dismissTimeout) { - notificationPopup.dismissClicked(); - } else { - notificationPopup.expired(); - } - } - } - - NumberAnimation { - target: notificationItem - property: "remainingTime" - from: timer.interval - to: 0 - duration: timer.interval - running: timer.running && units.longDuration > 1 - } - - NotificationItem { - id: notificationItem - // let the item bleed into the dialog margins so the close button margins cancel out - y: closable || dismissable || configurable ? -notificationPopup.margins.top : 0 - headingRightPadding: -notificationPopup.margins.right - width: parent.width - hovered: area.containsMouse - maximumLineCount: 8 - bodyCursorShape: notificationPopup.hasDefaultAction ? Qt.PointingHandCursor : 0 - - thumbnailLeftPadding: -notificationPopup.margins.left - thumbnailRightPadding: -notificationPopup.margins.right - thumbnailTopPadding: -notificationPopup.margins.top - thumbnailBottomPadding: -notificationPopup.margins.bottom - - timeout: timer.running ? timer.interval : 0 - - closable: true - onBodyClicked: { - if (area.acceptedButtons & mouse.button) { - area.clicked(null /*mouse*/); - } - } - onCloseClicked: notificationPopup.closeClicked() - onDismissClicked: notificationPopup.dismissClicked() - onConfigureClicked: notificationPopup.configureClicked() - onActionInvoked: notificationPopup.actionInvoked(actionName) - onReplied: notificationPopup.replied(text) - onOpenUrl: notificationPopup.openUrl(url) - onFileActionInvoked: notificationPopup.fileActionInvoked() - - onSuspendJobClicked: notificationPopup.suspendJobClicked() - onResumeJobClicked: notificationPopup.resumeJobClicked() - onKillJobClicked: notificationPopup.killJobClicked() - } - } - } -} diff --git a/applets/systemtray/package/contents/ui/ExpandedRepresentation.qml b/applets/systemtray/package/contents/ui/ExpandedRepresentation.qml --- a/applets/systemtray/package/contents/ui/ExpandedRepresentation.qml +++ b/applets/systemtray/package/contents/ui/ExpandedRepresentation.qml @@ -35,11 +35,13 @@ Layout.preferredHeight: Layout.minimumHeight Layout.maximumWidth: Layout.minimumWidth Layout.maximumHeight: Layout.minimumHeight - spacing: 0 // avoid gap between title and content + spacing: toparea.margins.top // compensate -toparea.margins.top of framesvgItem property alias activeApplet: container.activeApplet property alias hiddenLayout: hiddenItemsView.layout + PlasmaComponents.TopArea { + RowLayout { PlasmaExtras.Heading { @@ -59,53 +61,53 @@ } else { return units.smallSpacing; } - } - Layout.rightMargin: { - //Menu mode - if (!activeApplet && hiddenItemsView.visible && LayoutMirroring.enabled) { - return units.smallSpacing; - - //applet open, sidebar - } else if (activeApplet && hiddenItemsView.visible && LayoutMirroring.enabled) { - return hiddenItemsView.width + units.largeSpacing; - - //applet open, no sidebar - } else { - return 0; + Layout.rightMargin: { + //Menu mode + if (!activeApplet && hiddenItemsView.visible && LayoutMirroring.enabled) { + return units.smallSpacing; + + //applet open, sidebar + } else if (activeApplet && hiddenItemsView.visible && LayoutMirroring.enabled) { + return hiddenItemsView.width + units.largeSpacing; + + //applet open, no sidebar + } else { + return 0; + } } - } - visible: activeApplet - text: activeApplet ? activeApplet.title : "" - MouseArea { - anchors.fill: parent - onClicked: { - if (activeApplet) { - activeApplet.expanded = false; - dialog.visible = true; + visible: activeApplet + text: activeApplet ? activeApplet.title : "" + MouseArea { + anchors.fill: parent + onClicked: { + if (activeApplet) { + activeApplet.expanded = false; + dialog.visible = true; + } } } } - } - PlasmaExtras.Heading { - id: noAppletHeading - visible: !activeApplet - Layout.fillWidth: true - level: 1 - text: i18n("Status and Notifications") - } + PlasmaExtras.Heading { + id: noAppletHeading + visible: !activeApplet + Layout.fillWidth: true + level: 1 + text: i18n("Status and Notifications") + } - PlasmaComponents.ToolButton { - id: pinButton - implicitHeight: Math.round(units.gridUnit * 1.25) - implicitWidth: implicitHeight - checkable: true - checked: plasmoid.configuration.pin - onToggled: plasmoid.configuration.pin = checked - icon.name: "window-pin" - PlasmaComponents.ToolTip { - text: i18n("Keep Open") + PlasmaComponents.ToolButton { + id: pinButton + implicitHeight: Math.round(units.gridUnit * 1.25) + implicitWidth: implicitHeight + checkable: true + checked: plasmoid.configuration.pin + onToggled: plasmoid.configuration.pin = checked + icon.name: "window-pin" + PlasmaComponents.ToolTip { + text: i18n("Keep Open") + } } } }