Commit 659324f2 authored by Marco Martin's avatar Marco Martin
Browse files

Support multiple stacked Passive Notifications

Summary:
add the ability to support more (up to an arbitrary 4) passive notifications shown at once
see also D28130 D28131

Test Plan:
it looks and behaves the same when there is only one. when there are multiple they are stacked on top of each other with the most recent at the top
{F8183194}

Reviewers: #kirigami, broulik

Reviewed By: broulik

Subscribers: broulik, ngraham, plasma-devel

Tags: #kirigami

Differential Revision: https://phabricator.kde.org/D28103
parent d545a715
......@@ -132,8 +132,8 @@ Item {
*/
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 @@ Item {
QtObject {
id: internal
property Item __passiveNotification
property QtObject __passiveNotification
}
}
......@@ -303,7 +303,7 @@ QQC2.ApplicationWindow {
QtObject {
id: internal
property Item __passiveNotification
property QtObject __passiveNotification
}
Shortcut {
......
......@@ -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 {}
}
......@@ -7,4 +7,3 @@ AbstractListItem 2.2 AbstractListItem.qml
ApplicationHeader 2.2 ApplicationHeader.qml
AbstractApplicationHeader 2.2 AbstractApplicationHeader.qml
OverlayDrawer 2.2 OverlayDrawer.qml
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment