// SPDX-FileCopyrightText: 2021-2023 Devin Lin // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL import QtQuick import QtQuick.Effects import QtQuick.Controls import Qt5Compat.GraphicalEffects import org.kde.kirigami as Kirigami import org.kde.plasma.components 3.0 as PlasmaComponents import org.kde.plasma.private.mobileshell as MobileShell Item { id: root /** * The content that goes inside the notification card */ default property Item contentItem /** * The panel background type for this notification. */ property int panelType: MobileShell.PanelBackground.PanelType.Drawer /** * Whether this is a popup notification. */ property bool popupNotification: false /** * Whether this popup notification is tucked underneath the current popup. */ property bool inPopupDrawer: false /** * Whether this notification is within the lockscreen. */ property bool inLockScreen: false /** * The current notification popup height. */ property int currentPopupHeight: 0 /** * The remaining time before the notification popup is dismissed. */ property real remainingTimeProgress: 1 /** * Whether the timer for dismissing the notification popup is running. */ property bool closeTimerRunning: false /** * Whether tapping on this notification is enabled. */ property bool tapEnabled: false /** * Whether swipping on this notification is enabled. */ property bool swipeGestureEnabled: false /** * The current drag offset for this notification. */ property real dragOffset: 0 signal tapped() signal dismissRequested() signal configureClicked() // TODO implement settings button signal dragStart() signal dragEnd() onContentItemChanged: { contentItem.parent = contentParent; contentItem.anchors.fill = contentParent; contentItem.anchors.margins = Kirigami.Units.largeSpacing; contentParent.children.push(contentItem); } implicitHeight: contentParent.implicitHeight NumberAnimation on dragOffset { id: dragAnim duration: Kirigami.Units.longDuration easing.type: Easing.OutCubic onFinished: { if (to !== 0) { root.dismissRequested(); } } } MobileShell.PanelBackground { anchors.fill: mainCard animate: true panelType: root.panelType } // card Item { id: mainCard anchors.left: parent.left anchors.leftMargin: root.dragOffset > 0 ? root.dragOffset : 0 anchors.right: parent.right anchors.rightMargin: root.dragOffset < 0 ? -root.dragOffset : 0 anchors.top: parent.top implicitHeight: inPopupDrawer ? currentPopupHeight : contentParent.implicitHeight Behavior on implicitHeight { NumberAnimation { duration: Kirigami.Units.veryLongDuration easing.type: Easing.OutExpo } } ProgressBar { id: progress anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top width: root.width height: 2 value: remainingTimeProgress opacity: closeTimerRunning ? 1 : 0 Behavior on opacity { NumberAnimation { duration: Kirigami.Units.longDuration easing.type: Easing.OutQuad } } background: Item contentItem: Item { implicitWidth: parent.width height: parent.height clip: true Rectangle { width: Math.min(progress.visualPosition * (parent.width + root.dragOffset), parent.width) height: Math.max(Kirigami.Units.cornerRadius * 2, parent.height) topLeftRadius: Kirigami.Units.cornerRadius topRightRadius: Kirigami.Units.cornerRadius color: Kirigami.ColorUtils.linearInterpolation(Kirigami.Theme.highlightColor, Kirigami.Theme.backgroundColor, 0.8) } Rectangle { width: Math.min(progress.visualPosition * (parent.width + root.dragOffset), parent.width - Kirigami.Units.cornerRadius) height: Math.max(Kirigami.Units.cornerRadius * 2, parent.height) topLeftRadius: Kirigami.Units.cornerRadius color: Kirigami.ColorUtils.linearInterpolation (Kirigami.Theme.highlightColor, Kirigami.Theme.backgroundColor, 0.8) } } } // clip layer.enabled: true // ensure this is behind the content to not interfere MouseArea { id: mouseArea anchors.fill: parent onClicked: { if (root.tapEnabled) { root.tapped() } } } // content parent Item { id: contentParent anchors.top: parent.top anchors.left: root.dragOffset > 0 ? parent.left : undefined anchors.right: root.dragOffset < 0 ? parent.right : undefined width: root.width implicitHeight: contentItem.implicitHeight + contentItem.anchors.topMargin + contentItem.anchors.bottomMargin } } DragHandler { id: dragHandler enabled: root.swipeGestureEnabled yAxis.enabled: false xAxis.enabled: !inPopupDrawer property real startDragOffset: 0 property real startPosition: 0 property bool startActive: false onTranslationChanged: { if (startActive) { startDragOffset = root.dragOffset; startPosition = translation.x; startActive = false; } root.dragOffset = startDragOffset + (translation.x - startPosition); } onActiveChanged: { dragAnim.stop(); startActive = active; if (!active) { // release event root.dragEnd() let threshold = Kirigami.Units.gridUnit * 5; // drag threshold if (root.dragOffset > threshold) { dragAnim.to = root.width; } else if (root.dragOffset < -threshold) { dragAnim.to = -root.width; } else { dragAnim.to = 0; } dragAnim.restart(); } else { root.dragStart() } } } }