diff --git a/src/controls/AbstractApplicationItem.qml b/src/controls/AbstractApplicationItem.qml --- a/src/controls/AbstractApplicationItem.qml +++ b/src/controls/AbstractApplicationItem.qml @@ -132,8 +132,8 @@ */ function showPassiveNotification(message, timeout, actionText, callBack) { if (!internal.__passiveNotification) { - var component = Qt.createComponent("templates/private/PassiveNotification.qml"); - internal.__passiveNotification = component.createObject(overlay.parent); + var component = Qt.createComponent("templates/PassiveNotification.qml"); + internal.__passiveNotification = component.createObject(root); } internal.__passiveNotification.showNotification(message, timeout, actionText, callBack); @@ -351,6 +351,6 @@ QtObject { id: internal - property Item __passiveNotification + property QtObject __passiveNotification } } diff --git a/src/controls/AbstractApplicationWindow.qml b/src/controls/AbstractApplicationWindow.qml --- a/src/controls/AbstractApplicationWindow.qml +++ b/src/controls/AbstractApplicationWindow.qml @@ -303,7 +303,7 @@ QtObject { id: internal - property Item __passiveNotification + property QtObject __passiveNotification } Shortcut { diff --git a/src/controls/templates/private/PassiveNotification.qml b/src/controls/templates/private/PassiveNotification.qml --- a/src/controls/templates/private/PassiveNotification.qml +++ b/src/controls/templates/private/PassiveNotification.qml @@ -4,145 +4,198 @@ * SPDX-License-Identifier: LGPL-2.0-or-later */ -import QtQuick 2.5 -import QtQuick.Controls 2.0 as QQC2 +import QtQuick 2.12 +import QtQuick.Controls 2.3 as Controls import QtQuick.Layouts 1.2 import QtQuick.Window 2.2 import QtGraphicalEffects 1.0 -import org.kde.kirigami 2.4 +import org.kde.kirigami 2.12 as Kirigami -MouseArea { +/** + * PassiveNotification is a type for small, passive and inline +notifications in the app. + * used to show messages of limited importance that make sense only when + * the user is using the application and wouldn't be suited as a global + * system-wide notification. + * This is not a full-fledged notification system. the applciation should + * use this with care and only one notification should be visible at once per app. +*/ +Controls.Popup { id: root - z: 9999999 - width: background.width - height: background.height - opacity: 0 - enabled: appearAnimation.appear - - anchors { - horizontalCenter: parent.horizontalCenter - bottom: parent.bottom - bottomMargin: Units.gridUnit * 4 - } + + x: Math.round(parent.width/2 - width/2) + y: parent.height - height - Kirigami.Units.smallSpacing + implicitWidth: Math.max(background ? background.implicitWidth : 0, + contentWidth + leftPadding + rightPadding) + leftInset + rightInset + implicitHeight: Math.max(background ? background.implicitHeight : 0 , + contentHeight + topPadding + bottomPadding)+ topInset + bottomInset + height: implicitHeight + width: implicitWidth + + topPadding: Kirigami.Units.smallSpacing + leftPadding: Kirigami.Units.smallSpacing + rightPadding: Kirigami.Units.smallSpacing + bottomPadding: Kirigami.Units.smallSpacing + + modal: false + closePolicy: Controls.Popup.NoAutoClose + focus: false + clip: false + function showNotification(message, timeout, actionText, callBack) { if (!message) { return; } - appearAnimation.running = false; - appearAnimation.appear = true; - appearAnimation.running = true; + + let interval = 7000; + if (timeout == "short") { - timer.interval = 4000; + interval = 4000; } else if (timeout == "long") { - timer.interval = 12000; + interval = 12000; } else if (timeout > 0) { - timer.interval = timeout; - } else { - timer.interval = 7000; + interval = timeout; } - messageLabel.text = message ? message : ""; - actionButton.text = actionText ? actionText : ""; - actionButton.callBack = callBack ? callBack : ""; - timer.stop(); // stop first to ensure it always starts anew + open(); + + for (let i = 0; i < outerLayout.children.length - 3; ++i) { + outerLayout.children[i].close(); + } - // Only start the timer when the window has focus, to ensure that - // messages don't get missed on the desktop where it's common to - //be working with multiple windows at once - timer.running = Qt.binding(function() { - return root.Window.active; + let delegate = delegateComponent.createObject(outerLayout, { + "text": message, + "actionText": actionText || "", + "callBack": callBack || (function(){}), + "interval": interval }); - } - function hideNotification() { - appearAnimation.running = false; - appearAnimation.appear = false; - appearAnimation.running = true; + // Reorder items to have the last on top + let children = outerLayout.children; + for (let i in children) { + children[i].Layout.row = children.length-1-i; + } } + Kirigami.Theme.colorSet: Kirigami.Theme.Complementary + + background: Item {} - onClicked: { - appearAnimation.appear = false; - appearAnimation.running = true; + contentItem: GridLayout { + id: outerLayout + columns: 1 } - transform: Translate { - id: transform - y: root.height - } + Component { + id: delegateComponent + Controls.Control { + id: delegate + property alias text: label.text + property alias actionText: actionButton.text + property alias interval: timer.interval + property var callBack + Layout.alignment: Qt.AlignCenter + Layout.bottomMargin: -delegate.height + opacity: 0 + function close() { + closeAnim.running = true; + } - Timer { - id: timer - interval: 4000 - onTriggered: { - appearAnimation.appear = false; - appearAnimation.running = true; - } - } - ParallelAnimation { - id: appearAnimation - property bool appear: true - NumberAnimation { - target: root - properties: "opacity" - to: appearAnimation.appear ? 1 : 0 - duration: Units.longDuration - easing.type: Easing.InOutQuad - } - NumberAnimation { - target: transform - properties: "y" - to: appearAnimation.appear ? 0 : background.height - duration: Units.longDuration - easing.type: appearAnimation.appear ? Easing.OutQuad : Easing.InQuad - } - } + Component.onCompleted: openAnim.restart() + ParallelAnimation { + id: openAnim + OpacityAnimator { + target: delegate + from: 0 + to: 1 + duration: Kirigami.Units.longDuration + easing.type: Easing.InOutQuad + } + NumberAnimation { + target: delegate + property: "Layout.bottomMargin" + from: -delegate.height + to: 0 + duration: Kirigami.Units.longDuration + easing.type: Easing.InOutQuad + } + } - Item { - id: background - width: backgroundRect.width + Units.gridUnit - height: backgroundRect.height + Units.gridUnit - Rectangle { - id: backgroundRect - anchors.centerIn: parent - radius: Units.smallSpacing - color: Theme.textColor - opacity: 0.6 - width: mainLayout.width + Math.round((height - mainLayout.height)) - height: Math.max(mainLayout.height + Units.smallSpacing*2, Units.gridUnit*2) - } - RowLayout { - id: mainLayout - anchors.centerIn: parent - QQC2.Label { - id: messageLabel - Layout.maximumWidth: Math.min(root.parent.width - Units.largeSpacing*2, implicitWidth) - elide: Text.ElideRight - wrapMode: Text.WordWrap - maximumLineCount: 4 - color: Theme.backgroundColor + SequentialAnimation { + id: closeAnim + ParallelAnimation { + OpacityAnimator { + target: delegate + from: 1 + to: 0 + duration: Kirigami.Units.longDuration + easing.type: Easing.InOutQuad + } + NumberAnimation { + target: delegate + property: "Layout.bottomMargin" + to: -delegate.height + duration: Kirigami.Units.longDuration + easing.type: Easing.InOutQuad + } + } + ScriptAction { + script: delegate.destroy(); + } } - QQC2.Button { - id: actionButton - property var callBack - visible: text != "" - onClicked: { - appearAnimation.appear = false; - appearAnimation.running = true; - if (callBack) { - callBack(); + + contentItem: RowLayout { + id: mainLayout + + Kirigami.Theme.colorSet: root.Kirigami.Theme.colorSet + width: mainLayout.width + + HoverHandler { + id: hover + } + TapHandler { + acceptedButtons: Qt.LeftButton + onTapped: delegate.close(); + } + Timer { + id: timer + running: mainLayout.Window.active && root.visible && !hover.hovered + onTriggered: delegate.close(); + } + + Controls.Label { + id: label + } + + Controls.Button { + id: actionButton + visible: text.length > 0 + onClicked: { + delegate.close();; + if (delegate.callBack && (typeof delegate.callBack === "function")) { + delegate.callBack(); + } } } } - } - layer.enabled: true - layer.effect: DropShadow { - horizontalOffset: 0 - verticalOffset: 0 - radius: Units.gridUnit - samples: 32 - color: Qt.rgba(0, 0, 0, 0.5) + background: Kirigami.ShadowedRectangle { + Kirigami.Theme.colorSet: root.Kirigami.Theme.colorSet + shadow { + size: Kirigami.Units.gridUnit/2 + color: Qt.rgba(0, 0, 0, 0.4) + yOffset: 2 + } + radius: Kirigami.Units.smallSpacing * 2 + color: Kirigami.Theme.backgroundColor + opacity: 0.6 + } } } + + Controls.Overlay.modal: Rectangle { + color: Qt.rgba(0, 0, 0, 0.4) + } + + Controls.Overlay.modeless: Item {} } diff --git a/src/controls/templates/qmldir b/src/controls/templates/qmldir --- a/src/controls/templates/qmldir +++ b/src/controls/templates/qmldir @@ -7,4 +7,3 @@ ApplicationHeader 2.2 ApplicationHeader.qml AbstractApplicationHeader 2.2 AbstractApplicationHeader.qml OverlayDrawer 2.2 OverlayDrawer.qml -