mirror of
https://invent.kde.org/marcoa/shift-shell.git
synced 2026-06-11 16:57:43 +00:00
Filter @other out of the history blacklist so senders without a\ndesktop entry still land in the drawer. Keep the convergence empty\nstate, dismiss path, and popup geometry aligned with the current\nframe and dock layout.
230 lines
6.9 KiB
QML
230 lines
6.9 KiB
QML
// SPDX-FileCopyrightText: 2021-2023 Devin Lin <devin@kde.org>
|
|
// 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
|
|
readonly property int longAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.Standard)
|
|
readonly property int veryLongAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.SpatialSlow)
|
|
|
|
signal tapped()
|
|
signal dismissRequested()
|
|
signal configureClicked() // TODO implement settings button
|
|
signal dragStart()
|
|
signal dragEnd()
|
|
|
|
onContentItemChanged: {
|
|
if (!contentItem) {
|
|
return;
|
|
}
|
|
contentItem.parent = contentParent;
|
|
contentItem.anchors.fill = contentParent;
|
|
contentItem.anchors.margins = Kirigami.Units.largeSpacing;
|
|
}
|
|
|
|
implicitHeight: contentParent.implicitHeight
|
|
|
|
MobileShell.MotionNumberAnimation on dragOffset {
|
|
id: dragAnim
|
|
type: MobileShell.Motion.StandardDecel
|
|
duration: root.longAnimationDuration
|
|
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 {
|
|
MobileShell.MotionNumberAnimation {
|
|
duration: root.veryLongAnimationDuration
|
|
type: MobileShell.Motion.SpatialSlow
|
|
}
|
|
}
|
|
|
|
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 {
|
|
MobileShell.MotionNumberAnimation {
|
|
duration: root.longAnimationDuration
|
|
type: MobileShell.Motion.StandardDecel
|
|
}
|
|
}
|
|
|
|
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()
|
|
}
|
|
}
|
|
}
|
|
}
|