1/*
2 *  SPDX-FileCopyrightText: 2015 Marco Martin <mart@kde.org>
3 *
4 *  SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6
7import QtQuick 2.12
8import QtQuick.Controls 2.3 as Controls
9import QtQuick.Layouts 1.2
10import QtQuick.Window 2.2
11import QtGraphicalEffects 1.0
12import org.kde.kirigami 2.12 as Kirigami
13
14/*
15 * PassiveNotification is a type for small, passive and inline
16notifications in the app.
17 * used to show messages of limited importance that make sense only when
18 * the user is using the application and wouldn't be suited as a global
19 * system-wide notification.
20 * This is not a full-fledged notification system. the applciation should
21 * use this with care and only one notification should be visible at once per app.
22*/
23Controls.Popup {
24    id: root
25
26    x: Math.round(parent.width/2 - width/2)
27    y: parent.height - height - Kirigami.Units.smallSpacing
28    implicitWidth: Math.max(background ? background.implicitWidth : 0,
29                            contentWidth + leftPadding + rightPadding) + leftInset + rightInset
30    implicitHeight: Math.max(background ? background.implicitHeight : 0 ,
31                             contentHeight + topPadding + bottomPadding)+ topInset + bottomInset
32    height: implicitHeight
33    width: implicitWidth
34
35    topPadding: Kirigami.Units.smallSpacing
36    leftPadding: Kirigami.Units.smallSpacing
37    rightPadding: Kirigami.Units.smallSpacing
38    bottomPadding: Kirigami.Units.smallSpacing
39
40    modal: false
41    closePolicy: Controls.Popup.NoAutoClose
42    focus: false
43    clip: false
44
45    function showNotification(message, timeout, actionText, callBack) {
46        if (!message) {
47            return;
48        }
49
50        let interval = 7000;
51
52        if (timeout == "short") {
53            interval = 4000;
54        } else if (timeout == "long") {
55            interval = 12000;
56        } else if (timeout > 0) {
57            interval = timeout;
58        }
59
60        open();
61
62        for (let i = 0; i < outerLayout.children.length - 3; ++i) {
63            outerLayout.children[i].close();
64        }
65
66        let delegate = delegateComponent.createObject(outerLayout, {
67            "text": message,
68            "actionText": actionText || "",
69            "callBack": callBack || (function(){}),
70            "interval": interval
71        });
72
73        // Reorder items to have the last on top
74        let children = outerLayout.children;
75        for (let i in children) {
76            children[i].Layout.row = children.length-1-i;
77        }
78    }
79
80    Kirigami.Theme.inherit: false
81    Kirigami.Theme.colorSet: Kirigami.Theme.Complementary
82
83    background: Item {}
84
85    contentItem: GridLayout {
86        id: outerLayout
87        columns: 1
88    }
89
90    Component {
91        id: delegateComponent
92        Controls.Control {
93            id: delegate
94            property alias text: label.text
95            property alias actionText: actionButton.text
96            property alias interval: timer.interval
97            property var callBack
98            Layout.alignment: Qt.AlignCenter
99            Layout.bottomMargin: -delegate.height
100            opacity: 0
101            function close() {
102                closeAnim.running = true;
103            }
104
105            leftPadding: Kirigami.Units.largeSpacing
106            rightPadding: Kirigami.Units.largeSpacing
107            topPadding: Kirigami.Units.largeSpacing
108            bottomPadding: Kirigami.Units.largeSpacing
109
110            Component.onCompleted: openAnim.restart()
111            ParallelAnimation {
112                id: openAnim
113                OpacityAnimator {
114                    target: delegate
115                    from: 0
116                    to: 1
117                    duration: Kirigami.Units.longDuration
118                    easing.type: Easing.InOutQuad
119                }
120                NumberAnimation {
121                    target: delegate
122                    property: "Layout.bottomMargin"
123                    from: -delegate.height
124                    to: 0
125                    duration: Kirigami.Units.longDuration
126                    easing.type: Easing.InOutQuad
127                }
128            }
129
130            SequentialAnimation {
131                id: closeAnim
132                ParallelAnimation {
133                    OpacityAnimator {
134                        target: delegate
135                        from: 1
136                        to: 0
137                        duration: Kirigami.Units.longDuration
138                        easing.type: Easing.InOutQuad
139                    }
140                    NumberAnimation {
141                        target: delegate
142                        property: "Layout.bottomMargin"
143                        to: -delegate.height
144                        duration: Kirigami.Units.longDuration
145                        easing.type: Easing.InOutQuad
146                    }
147                }
148                ScriptAction {
149                    script: delegate.destroy();
150                }
151            }
152
153            contentItem: RowLayout {
154                id: mainLayout
155
156                Kirigami.Theme.inherit: false
157                Kirigami.Theme.colorSet: root.Kirigami.Theme.colorSet
158                width: mainLayout.width
159                //FIXME: why this is not automatic?
160                implicitHeight: Math.max(label.implicitHeight, actionButton.implicitHeight)
161                HoverHandler {
162                    id: hover
163                }
164                TapHandler {
165                    acceptedButtons: Qt.LeftButton
166                    onTapped: delegate.close();
167                }
168                Timer {
169                    id: timer
170                    running: root.visible && !hover.hovered
171                    onTriggered: delegate.close();
172                }
173
174                Controls.Label {
175                    id: label
176                    Layout.maximumWidth: Math.min(root.parent.width - Kirigami.Units.largeSpacing * 4, implicitWidth)
177                    elide: Text.ElideRight
178                    wrapMode: Text.WordWrap
179                    maximumLineCount: 4
180                }
181
182                Controls.Button {
183                    id: actionButton
184                    visible: text.length > 0
185                    onClicked: {
186                        delegate.close();;
187                        if (delegate.callBack && (typeof delegate.callBack === "function")) {
188                            delegate.callBack();
189                        }
190                    }
191                }
192            }
193            background: Kirigami.ShadowedRectangle {
194                Kirigami.Theme.inherit: false
195                Kirigami.Theme.colorSet: root.Kirigami.Theme.colorSet
196                shadow {
197                    size: Kirigami.Units.gridUnit/2
198                    color: Qt.rgba(0, 0, 0, 0.4)
199                    yOffset: 2
200                }
201                radius: Kirigami.Units.smallSpacing * 2
202                color: Kirigami.Theme.backgroundColor
203                opacity: 0.9
204            }
205        }
206    }
207
208    Controls.Overlay.modal: Rectangle {
209        color: Qt.rgba(0, 0, 0, 0.4)
210    }
211
212    Controls.Overlay.modeless: Item {}
213}
214
215