diff --git a/applets/notifications/package/contents/ui/NotificationDelegate.qml b/applets/notifications/package/contents/ui/NotificationDelegate.qml index 0ccf26b5f..4822cfc9f 100644 --- a/applets/notifications/package/contents/ui/NotificationDelegate.qml +++ b/applets/notifications/package/contents/ui/NotificationDelegate.qml @@ -1,193 +1,142 @@ /* * Copyright 2011 Marco Martin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * 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 Library General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.0 import QtQuick.Controls.Private 1.0 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 PlasmaComponents.ListItem { id: notificationItem width: popupFlickable.width property int layoutSpacing: units.smallSpacing property int toolIconSize: units.iconSizes.smallMedium opacity: 1-Math.abs(x)/width Timer { interval: 10*60*1000 repeat: false running: !idleTimeSource.idle onTriggered: { if (!notificationsModel.inserting) notificationsModel.remove(index) } } MouseArea { width: parent.width height: childrenRect.height drag { target: notificationItem axis: Drag.XAxis //kind of an hack over Column being too smart minimumX: -parent.width + 1 maximumX: parent.width - 1 } onReleased: { if (notificationItem.x < -notificationItem.width/2) { removeAnimation.exitFromRight = false removeAnimation.running = true } else if (notificationItem.x > notificationItem.width/2 ) { removeAnimation.exitFromRight = true removeAnimation.running = true } else { resetAnimation.running = true } } SequentialAnimation { id: removeAnimation property bool exitFromRight: true NumberAnimation { target: notificationItem properties: "x" to: removeAnimation.exitFromRight ? notificationItem.width-1 : 1-notificationItem.width duration: units.longDuration easing.type: Easing.InOutQuad } NumberAnimation { target: notificationItem properties: "height" to: 0 duration: units.longDuration easing.type: Easing.InOutQuad } ScriptAction { script: notificationsModel.remove(index) } } SequentialAnimation { id: resetAnimation NumberAnimation { target: notificationItem properties: "x" to: 0 duration: units.longDuration easing.type: Easing.InOutQuad } } NotificationItem { id: notification width: parent.width compact: true icon: appIcon image: model.image summary: model.summary + body: model.body configurable: model.configurable && !Settings.isMobile // model.actions JS array is implicitly turned into a ListModel which we can assign directly actions: model.actions created: model.created - textItem: MouseArea { - id: contextMouseArea - height: bodyText.paintedHeight - - acceptedButtons: Qt.RightButton - preventStealing: true - - onPressed: contextMenu.open(mouse.x, mouse.y) - - PlasmaComponents.ContextMenu { - id: contextMenu - visualParent: contextMouseArea - - PlasmaComponents.MenuItem { - text: i18n("Copy") - onClicked: bodyText.copy() - } - - PlasmaComponents.MenuItem { - text: i18n("Select All") - onClicked: bodyText.selectAll() - } - } - - TextEdit { - id: bodyText - anchors.fill: parent - - text: body - color: PlasmaCore.ColorScope.textColor - selectedTextColor: theme.viewBackgroundColor - selectionColor: theme.viewFocusColor - font.capitalization: theme.defaultFont.capitalization - font.family: theme.defaultFont.family - font.italic: theme.defaultFont.italic - font.letterSpacing: theme.defaultFont.letterSpacing - font.pointSize: theme.defaultFont.pointSize - font.strikeout: theme.defaultFont.strikeout - font.underline: theme.defaultFont.underline - font.weight: theme.defaultFont.weight - font.wordSpacing: theme.defaultFont.wordSpacing - renderType: Text.NativeRendering - enabled: !Settings.isMobile - selectByMouse: true - readOnly: true - wrapMode: Text.Wrap - textFormat: TextEdit.RichText - - onLinkActivated: Qt.openUrlExternally(link) - } - } - onClose: { if (notificationsModel.count > 1) { removeAnimation.running = true } else { closeNotification(model.source) notificationsModel.remove(index) } } onConfigure: { plasmoid.expanded = false configureNotification(model.appRealName, model.eventId) } onAction: { executeAction(source, actionId) actions.clear() } } } //MouseArea Component.onCompleted: { mainScrollArea.height = mainScrollArea.implicitHeight } Component.onDestruction: { mainScrollArea.height = mainScrollArea.implicitHeight } } diff --git a/applets/notifications/package/contents/ui/NotificationItem.qml b/applets/notifications/package/contents/ui/NotificationItem.qml index 6849ae7f3..4d78efc96 100644 --- a/applets/notifications/package/contents/ui/NotificationItem.qml +++ b/applets/notifications/package/contents/ui/NotificationItem.qml @@ -1,228 +1,276 @@ /* * Copyright 2011 Marco Martin * Copyright 2014 Kai Uwe Broulik * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * 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 Library General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.0 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.kquickcontrolsaddons 2.0 Item { id: notificationItem width: parent.width implicitHeight: Math.max(appIconItem.visible || imageItem.visible ? units.iconSizes.large : 0, mainLayout.height) // We need to clip here because we support displaying images through // and if we don't clip, they will be painted over the borders of the dialog/item clip: true signal close signal configure signal action(string actionId) - property alias textItem: textItemLoader.sourceComponent property bool compact: false property alias icon: appIconItem.source property alias image: imageItem.image property alias summary: summaryLabel.text + property alias body: bodyText.text property alias configurable: settingsButton.visible property var created property ListModel actions: ListModel { } function updateTimeLabel() { if (!created || created.getTime() <= 0) { timeLabel.text = "" return } var currentTime = new Date().getTime() var createdTime = created.getTime() var d = (currentTime - createdTime) / 1000 if (d < 10) { timeLabel.text = i18nc("notification was just added, keep short", "Just now") } else if (d < 20) { timeLabel.text = i18nc("10 seconds ago, keep short", "10 s ago"); } else if (d < 40) { timeLabel.text = i18nc("30 seconds ago, keep short", "30 s ago"); } else if (d < 60 * 60) { timeLabel.text = i18ncp("minutes ago, keep short", "%1 min ago", "%1 min ago", Math.round(d / 60)) } else if (d <= 60 * 60 * 23) { timeLabel.text = Qt.formatTime(created, Qt.locale().timeFormat(Locale.ShortFormat).replace(/.ss?/i, "")) } else { var yesterday = new Date() yesterday.setDate(yesterday.getDate() - 1) // this will wrap yesterday.setHours(0) yesterday.setMinutes(0) yesterday.setSeconds(0) if (createdTime > yesterday.getTime()) { timeLabel.text = i18nc("notification was added yesterday, keep short", "Yesterday"); } else { timeLabel.text = i18ncp("notification was added n days ago, keep short", "%1 day ago", "%1 days ago", Math.round((currentTime - yesterday.getTime()) / 1000 / 3600 / 24)); } } } Timer { interval: 15000 running: plasmoid.expanded repeat: true triggeredOnStart: true onTriggered: updateTimeLabel() } PlasmaCore.IconItem { id: appIconItem width: units.iconSizes.large height: units.iconSizes.large anchors { top: parent.top left: parent.left } visible: !imageItem.visible && valid animated: false } QImageItem { id: imageItem anchors.fill: appIconItem smooth: true visible: nativeWidth > 0 } ColumnLayout { id: mainLayout anchors { top: parent.top left: appIconItem.visible || imageItem.visible ? appIconItem.right : parent.left right: parent.right leftMargin: units.smallSpacing * 2 } spacing: units.smallSpacing RowLayout { id: titleBar spacing: units.smallSpacing height: units.iconSizes.smallMedium PlasmaExtras.Heading { id: summaryLabel Layout.fillWidth: true Layout.fillHeight: true verticalAlignment: Text.AlignVCenter level: 4 elide: Text.ElideRight wrapMode: Text.NoWrap } PlasmaExtras.Heading { id: timeLabel Layout.fillHeight: true level: 5 visible: text !== "" verticalAlignment: Text.AlignVCenter PlasmaCore.ToolTipArea { anchors.fill: parent subText: Qt.formatDateTime(created, Qt.DefaultLocaleLongDate) } } PlasmaComponents.ToolButton { id: settingsButton width: units.iconSizes.smallMedium height: width visible: false iconSource: "configure" onClicked: configure() } PlasmaComponents.ToolButton { id: closeButton width: units.iconSizes.smallMedium height: width flat: compact iconSource: "window-close" onClicked: close() } } RowLayout { id: bottomPart Layout.alignment: Qt.AlignTop // Force the whole thing to collapse if the children are invisible // If there is a big notification followed by a small one, the height // of the popup does not always shrink back, so this forces it to // height=0 when those are invisible. -1 means "default to implicitHeight" - Layout.maximumHeight: textItemLoader.visible || actionsColumn.visible ? -1 : 0 + Layout.maximumHeight: bodyText.visible || actionsColumn.visible ? -1 : 0 + + MouseArea { + id: contextMouseArea - Loader { - id: textItemLoader Layout.fillWidth: true - Layout.fillHeight: true - visible: textItemLoader.item && textItemLoader.item.text !== "" + anchors { leftMargin: units.smallSpacing * 2 rightMargin: units.smallSpacing * 2 } - } + implicitHeight: bodyText.paintedHeight + + acceptedButtons: Qt.RightButton + preventStealing: true + + onPressed: contextMenu.open(mouse.x, mouse.y) + + PlasmaComponents.ContextMenu { + id: contextMenu + visualParent: contextMouseArea + + PlasmaComponents.MenuItem { + text: i18n("Copy") + onClicked: bodyText.copy() + } + + PlasmaComponents.MenuItem { + text: i18n("Select All") + onClicked: bodyText.selectAll() + } + } + + TextEdit { + id: bodyText + enabled: !Settings.isMobile + anchors.fill: parent + visible: bodyText.length !== 0 + + color: PlasmaCore.ColorScope.textColor + selectedTextColor: theme.viewBackgroundColor + selectionColor: theme.viewFocusColor + font.capitalization: theme.defaultFont.capitalization + font.family: theme.defaultFont.family + font.italic: theme.defaultFont.italic + font.letterSpacing: theme.defaultFont.letterSpacing + font.pointSize: theme.defaultFont.pointSize + font.strikeout: theme.defaultFont.strikeout + font.underline: theme.defaultFont.underline + font.weight: theme.defaultFont.weight + font.wordSpacing: theme.defaultFont.wordSpacing + renderType: Text.NativeRendering + selectByMouse: true + readOnly: true + wrapMode: Text.Wrap + textFormat: TextEdit.RichText + + onLinkActivated: Qt.openUrlExternally(link) + } + } ColumnLayout { id: actionsColumn Layout.alignment: Qt.AlignTop Layout.maximumWidth: theme.mSize(theme.defaultFont).width * (compact ? 8 : 12) spacing: units.smallSpacing visible: notificationItem.actions && notificationItem.actions.count > 0 Repeater { id: actionRepeater model: notificationItem.actions PlasmaComponents.Button { Layout.maximumWidth: actionsColumn.Layout.maximumWidth text: model.text onClicked: notificationItem.action(model.id) } } } } } } diff --git a/applets/notifications/package/contents/ui/NotificationPopup.qml b/applets/notifications/package/contents/ui/NotificationPopup.qml index c5cf6d886..4aa87b2b5 100644 --- a/applets/notifications/package/contents/ui/NotificationPopup.qml +++ b/applets/notifications/package/contents/ui/NotificationPopup.qml @@ -1,141 +1,130 @@ /* * Copyright 2014 Martin Klapetek * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * 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 Library General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.0 import QtQuick.Controls.Private 1.0 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.kquickcontrolsaddons 2.0 PlasmaCore.Dialog { id: notificationPopup location: PlasmaCore.Types.Floating type: PlasmaCore.Dialog.Notification flags: Qt.WindowDoesNotAcceptFocus property var notificationProperties signal notificationTimeout() onVisibleChanged: { if (!visible) { notificationTimer.stop(); } } onYChanged: { if (visible) { notificationTimer.restart(); } } function populatePopup(notification) { notificationProperties = notification notificationTimer.interval = notification.expireTimeout notificationTimer.restart() // notification.actions is a JS array, but we can easily append that to our model notificationItem.actions.clear() notificationItem.actions.append(notificationProperties.actions) } Behavior on y { NumberAnimation { duration: units.longDuration easing.type: Easing.OutQuad } } mainItem: MouseArea { id: root LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft LayoutMirroring.childrenInherit: true height: notificationItem.implicitHeight + (units.smallSpacing * 2) width: notificationItem.width + (units.smallSpacing * 2) hoverEnabled: true onClicked: { closeNotification(notificationProperties.source) notificationPopup.hide() } onContainsMouseChanged: { if (containsMouse) { notificationTimer.stop() } else if (!containsMouse && visible) { notificationTimer.restart() } } Timer { id: notificationTimer onTriggered: { if (!notificationProperties.isPersistent) { expireNotification(notificationProperties.source) } notificationPopup.notificationTimeout(); } } NotificationItem { id: notificationItem - property string text summary: notificationProperties ? notificationProperties.summary: "" - text: notificationProperties ? notificationProperties.body : "" + body: notificationProperties ? notificationProperties.body : "" icon: notificationProperties ? notificationProperties.appIcon : "" image: notificationProperties ? notificationProperties.image : undefined configurable: (notificationProperties ? notificationProperties.configurable : false) && !Settings.isMobile x: units.smallSpacing y: units.smallSpacing width: Math.round(23 * units.gridUnit) - textItem: PlasmaComponents.Label { - wrapMode: Text.WrapAtWordBoundaryOrAnywhere - elide: Text.ElideRight - verticalAlignment: Text.AlignTop - onLinkActivated: Qt.openUrlExternally(link) - text: notificationItem.text - textFormat: Text.StyledText - maximumLineCount: 4 - } - onClose: { closeNotification(notificationProperties.source) notificationPopup.hide() } onConfigure: { configureNotification(notificationProperties.appRealName, notificationProperties.eventId) notificationPopup.hide() } onAction: { executeAction(notificationProperties.source, actionId) actions.clear() notificationPopup.hide() } } } }