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