mirror of
https://invent.kde.org/marcoa/shift-shell.git
synced 2026-04-26 14:23:09 +00:00
The mobile popup system always anchored to top-center which looked out of place on a desktop screen. In convergence mode, position popups at the bottom-right corner and slide them in from below. Swipe-to-dismiss flips to match (drag down to dismiss instead of up). Stacked card previews and the popup drawer tap area are hidden since notifications are already accessible in the action drawer.
626 lines
25 KiB
QML
626 lines
25 KiB
QML
/*
|
|
* SPDX-FileCopyrightText: 2024-2025 Micah Stanley <stanleymicah@proton.me>
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
*/
|
|
|
|
import QtQuick
|
|
import QtQuick.Layouts
|
|
import QtQuick.Window
|
|
|
|
import org.kde.kirigami as Kirigami
|
|
import org.kde.plasma.components 3.0 as PlasmaComponents
|
|
import org.kde.plasma.private.mobileshell as MobileShell
|
|
import org.kde.plasma.private.mobileshell.state as MobileShellState
|
|
|
|
import org.kde.layershell 1.0 as LayerShell
|
|
|
|
import org.kde.notificationmanager as NotificationManager
|
|
import org.kde.plasma.clock
|
|
|
|
Item {
|
|
id: notificationPopup
|
|
|
|
readonly property int popupHeight: notificationItem.implicitHeight
|
|
readonly property bool isClosing: notificationItem.state == "closeWithMove" || notificationItem.state == "closeWithScale"
|
|
readonly property real closedOffset: -(popupHeight + Kirigami.Units.smallSpacing)
|
|
// 'popupWidth' and 'openOffset' is set by the 'notificationPopupManager'
|
|
property int popupWidth
|
|
property real openOffset
|
|
property bool isConvergence: false
|
|
|
|
// In convergence the popup enters from the bottom-right corner
|
|
readonly property real effectiveOpenOffset: isConvergence
|
|
? (Screen.height - openOffset - popupHeight)
|
|
: openOffset
|
|
readonly property real effectiveClosedOffset: isConvergence
|
|
? (Screen.height + Kirigami.Units.smallSpacing)
|
|
: closedOffset
|
|
|
|
// calculate the position needed to at when the expanded drawer is active
|
|
readonly property real fullOpenOffset: popupDrawerOpened ? aboveNotificationFullOffset + aboveNotificationHeight + Kirigami.Units.largeSpacing : 0
|
|
property real aboveNotificationFullOffset: 0
|
|
property int aboveNotificationHeight: 0
|
|
|
|
// set to true when notification is swiped up by user
|
|
property bool closedWithSwipe: false
|
|
|
|
// the drag offset on the current popup notification - used to position notification when stacked underneath
|
|
property real currentDragOffset: {
|
|
let current = popupNotifications.currentPopupIndex == notificationPopup.popupIndex;
|
|
return current || popupDrawerOpened ? 0 : Math.max(popupNotifications.currentDragOffset, 0)
|
|
}
|
|
|
|
// due to it not looking great to have a notification sliding up while another one is sliding down
|
|
// we use a timer so that the current notification can know to use "closeWithScale" instead
|
|
property Timer queueTimer: Timer {
|
|
interval: Kirigami.Units.veryLongDuration
|
|
running: true
|
|
onTriggered: {
|
|
visible = true;
|
|
updateNotificationPopups();
|
|
}
|
|
}
|
|
|
|
// The timer for when the notification will dismiss
|
|
Timer {
|
|
id: hideTimer
|
|
interval: notificationPopup.effectiveTimeout
|
|
running: {
|
|
if (interval <= 0) {
|
|
return false;
|
|
}
|
|
if (notificationPopup.preventDismissTimeout) {
|
|
return false;
|
|
}
|
|
if (notificationPopup.inPopupDrawer) {
|
|
return false;
|
|
}
|
|
if (notificationPopup.popupDrawerOpened) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
onTriggered: notificationPopup.closePopup(popupIndex);
|
|
}
|
|
|
|
// the value of how much time is left, normalized from 1 to 0
|
|
property real remainingTimeProgress: 1
|
|
NumberAnimation on remainingTimeProgress {
|
|
from: 1
|
|
to: 0
|
|
duration: hideTimer.interval
|
|
running: hideTimer.running
|
|
}
|
|
|
|
// set the height and width of the notification container with a extra space for starting a drag
|
|
width: popupWidth + Kirigami.Units.gridUnit
|
|
height: popupHeight + Kirigami.Units.gridUnit
|
|
|
|
visible: false
|
|
|
|
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
|
Kirigami.Theme.inherit: false
|
|
|
|
signal expired()
|
|
signal dismissClicked()
|
|
signal updateTouchArea()
|
|
signal setInputTransparent()
|
|
signal openPopupDrawer()
|
|
signal setKeyboardFocus()
|
|
signal removeKeyboardFocus()
|
|
|
|
// animate the notifications entering and exiting the expanded drawer
|
|
property real fullOffsetAn: fullOpenOffset
|
|
Behavior on fullOffsetAn {
|
|
NumberAnimation {
|
|
duration: Kirigami.Units.veryLongDuration * 1.5
|
|
easing.type: Easing.OutExpo
|
|
}
|
|
}
|
|
|
|
// when a notification is grouped behind the current Notification
|
|
// we need the y origin to be a the bottom
|
|
// however we need it at the center when "closeWithScale" is used
|
|
// animate this value so that the popup in some situations will not jump around
|
|
property real scaleOriginY: inPopupDrawer && !popupDrawerOpened ? popupNotifications.currentPopupHeight : Math.round(popupHeight / 2)
|
|
Behavior on scaleOriginY {
|
|
NumberAnimation {
|
|
duration: Kirigami.Units.veryLongDuration
|
|
easing.type: Easing.OutExpo
|
|
}
|
|
}
|
|
|
|
// the vertical drag offset for the notification popup
|
|
// we drag is released, animate back to 0
|
|
property real dragOffset: 0
|
|
NumberAnimation on dragOffset {
|
|
id: dragOffsetAn
|
|
running: false
|
|
to: 0
|
|
duration: Kirigami.Units.veryLongDuration * 1.5
|
|
easing.type: Easing.OutExpo
|
|
}
|
|
|
|
// if the popup height ever changes, update the notification below with new height
|
|
// also update the allowed touch area for the main window
|
|
onPopupHeightChanged: {
|
|
let abovePopup = popupNotifications.objectAt(popupIndex + 1)
|
|
if (popupIndex + 1 < popupCount && abovePopup) {
|
|
abovePopup.aboveNotificationHeight = popupHeight;
|
|
}
|
|
if (popupNotifications.currentPopupIndex == notificationPopup.popupIndex && notificationItem.state == "open") {
|
|
notificationPopup.updateTouchArea();
|
|
}
|
|
}
|
|
|
|
// if the offset position need in the expanded drawer changes, update the notification below with new offset
|
|
onFullOpenOffsetChanged: {
|
|
let abovePopup = popupNotifications.objectAt(popupIndex + 1)
|
|
if (popupIndex + 1 < popupCount && abovePopup) {
|
|
abovePopup.aboveNotificationFullOffset = fullOpenOffset;
|
|
}
|
|
}
|
|
// if the notification is being dragged and is the current one
|
|
// update 'currentDragOffset' so all notifications can easily access this value
|
|
onDragOffsetChanged: {
|
|
let abovePopup = popupNotifications.objectAt(popupIndex + 1)
|
|
if (popupNotifications.currentPopupIndex == notificationPopup.popupIndex) {
|
|
popupNotifications.currentDragOffset = dragOffset;
|
|
}
|
|
}
|
|
// if a new notification is added, update the above notification values need for the expanded drawer
|
|
onPopupCountChanged: {
|
|
let abovePopup = popupNotifications.objectAt(popupIndex + 1)
|
|
if (popupIndex + 1 < popupCount && abovePopup) {
|
|
abovePopup.aboveNotificationHeight = popupHeight;
|
|
abovePopup.aboveNotificationFullOffset = fullOpenOffset;
|
|
}
|
|
}
|
|
// update the current popup index value if the index ever changes.
|
|
onPopupIndexChanged: {
|
|
if (!isClosing && !inPopupDrawer && !waiting) {
|
|
// if index goes below zero, assume it is being closed externally and move over to the next popup
|
|
if (popupIndex < 0 ) {
|
|
closePopup(0);
|
|
return;
|
|
}
|
|
popupNotifications.currentPopupIndex = popupIndex;
|
|
}
|
|
}
|
|
property bool isActionDrawerOpen: MobileShellState.ShellDBusClient.isActionDrawerOpen
|
|
|
|
property bool waiting: true
|
|
property bool popupDrawerOpened: false
|
|
property bool inPopupDrawer: false
|
|
|
|
property var keyboardInteractivity
|
|
property Instantiator popupNotifications
|
|
property int popupCount: popupNotifications.count
|
|
property int popupIndex
|
|
|
|
property var popupModel
|
|
property var notificationsModel
|
|
property int notificationsModelType
|
|
property var timeDataSource
|
|
|
|
property bool preventDismissTimeout: true
|
|
property int timeout
|
|
property int dismissTimeout
|
|
|
|
property int defaultTimeout: 5000
|
|
readonly property int effectiveTimeout: {
|
|
if (timeout === -1) {
|
|
return defaultTimeout;
|
|
}
|
|
if (dismissTimeout) {
|
|
return dismissTimeout;
|
|
}
|
|
return model.timeout;
|
|
}
|
|
|
|
// show the top most notification in the list and move the rest to the popup drawer
|
|
function updateNotificationPopups() {
|
|
if (popupCount != 1) {
|
|
for (var i = 0; i < popupCount - 1; i++) {
|
|
popupNotifications.objectAt(i + 1).moveToPopupDrawer();
|
|
}
|
|
}
|
|
popupNotifications.objectAt(0).showNotificationPopup();
|
|
visible = true;
|
|
}
|
|
|
|
function showNotificationPopup() {
|
|
if (isClosing) {
|
|
closePopup(popupIndex);
|
|
return;
|
|
}
|
|
if (notificationItem.state != "open") {
|
|
preventDismissTimeout = true;
|
|
}
|
|
waiting = false;
|
|
inPopupDrawer = false;
|
|
popupNotifications.currentPopupIndex = popupIndex;
|
|
visible = true;
|
|
openPopup();
|
|
updateTouchArea();
|
|
}
|
|
|
|
function moveToPopupDrawer() {
|
|
if (isClosing) {
|
|
return;
|
|
}
|
|
waiting = false;
|
|
inPopupDrawer = true;
|
|
if (notificationPopup.popupDrawerOpened && notificationItem.state != "inDrawerClosed" && notificationItem.state != "open") {
|
|
notificationItem.offset = effectiveOpenOffset;
|
|
notificationItem.scale = 0.75;
|
|
notificationItem.popupOpacity = 0.0;
|
|
}
|
|
notificationItem.state = "inDrawerClosed";
|
|
notificationPopup.removeKeyboardFocus();
|
|
visible = true;
|
|
}
|
|
|
|
function openPopup() {
|
|
if (notificationPopup.popupDrawerOpened && notificationItem.state != "open" && notificationItem.state != "inDrawerClosed") {
|
|
notificationItem.offset = effectiveOpenOffset;
|
|
notificationItem.scale = 0.75;
|
|
notificationItem.popupOpacity = 0.0;
|
|
}
|
|
notificationItem.state = "open";
|
|
notificationPopup.removeKeyboardFocus();
|
|
}
|
|
|
|
// if the notification ever expires, close it and move on to the next one in the list.
|
|
property bool isExpired: model.expired
|
|
onIsExpiredChanged: closePopup(popupIndex)
|
|
|
|
// this closes the popup notification with the relvent animation while updating the popup below to show, if any exist
|
|
function closePopup(index: int) {
|
|
notificationPopup.removeKeyboardFocus();
|
|
notificationPopup.setInputTransparent();
|
|
if (index + 1 < popupCount) {
|
|
popupNotifications.objectAt(index + 1).aboveNotificationHeight = 0;
|
|
popupNotifications.objectAt(index + 1).aboveNotificationFullOffset = 0;
|
|
}
|
|
|
|
if (popupCount > 1) {
|
|
let nextNotificationIdx = index + (index < popupCount - 1 ? 1 : -1);
|
|
let nextNotification = popupNotifications.objectAt(nextNotificationIdx);
|
|
|
|
if (nextNotification != null) {
|
|
nextNotification.showNotificationPopup();
|
|
if (!isExpired) {
|
|
if (!dragOffsetAn.running && nextNotification.queueTimer.running) {
|
|
nextNotification.queueTimer.stop();
|
|
notificationItem.state = "closeWithScale";
|
|
} else {
|
|
notificationItem.state = "closeWithMove";
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
if (isExpired) {
|
|
notificationItem.close();
|
|
return;
|
|
}
|
|
notificationItem.state = "closeWithMove";
|
|
}
|
|
|
|
function calculateResistance(value : double, threshold : int) : double {
|
|
if (value > threshold) {
|
|
return threshold + Math.pow(value - threshold + 1, Math.max(0.8 - (value - threshold) / ((longestLength - threshold) * 15), 0.35));
|
|
} else {
|
|
return value;
|
|
}
|
|
}
|
|
|
|
NotificationPopupItem {
|
|
id: notificationItem
|
|
|
|
inPopupDrawer: notificationPopup.inPopupDrawer && !notificationPopup.popupDrawerOpened
|
|
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
anchors.top: parent.top
|
|
|
|
width: notificationPopup.popupWidth
|
|
height: notificationPopup.popupHeight
|
|
|
|
model: notificationPopup.popupModel
|
|
modelIndex: notificationPopup.popupIndex
|
|
notificationsModel: notificationPopup.notificationsModel
|
|
notificationsModelType: notificationPopup.notificationsModelType
|
|
clockSource: notificationPopup.timeDataSource
|
|
panelType: notificationPopup.popupDrawerOpened ?
|
|
MobileShell.PanelBackground.PanelType.Drawer :
|
|
MobileShell.PanelBackground.PanelType.Popup
|
|
|
|
currentPopupHeight: popupNotifications.currentPopupHeight
|
|
|
|
remainingTimeProgress: notificationPopup.remainingTimeProgress
|
|
closeTimerRunning: hideTimer.running
|
|
|
|
onDragStart: preventDismissTimeout = true
|
|
onDragEnd: preventDismissTimeout = (keyboardInteractivity == LayerShell.Window.KeyboardInteractivityOnDemand)
|
|
|
|
onTakeFocus: {
|
|
notificationPopup.setKeyboardFocus();
|
|
preventDismissTimeout = true;
|
|
}
|
|
|
|
onDismissRequested: closePopup(popupIndex)
|
|
|
|
property real offset: notificationPopup.effectiveClosedOffset
|
|
property real scale: 1.0
|
|
property real popupOpacity: 1.0 // controls the opacity of the notification popup when outside the popup drawer
|
|
property real drawerScale: {
|
|
if (notificationPopup.popupDrawerOpened) {
|
|
return 0; // when popup drawer is opened, reset scale to 0
|
|
}
|
|
let index = notificationPopup.popupIndex - popupNotifications.currentPopupIndex;
|
|
// clamp the index value to avoid scaling too much with animations
|
|
let indexClamped = Math.max(Math.min(index, 2), 0);
|
|
return indexClamped * 0.075;
|
|
}
|
|
property real drawerAddedOffset: {
|
|
if (notificationPopup.popupDrawerOpened) {
|
|
return 0; // when popup drawer is opened, reset any added height to 0
|
|
}
|
|
let index = notificationPopup.popupIndex - popupNotifications.currentPopupIndex;
|
|
// clamp the index value to avoid moving too much with animations
|
|
let indexClamped = Math.max(Math.min(index, 2), -1);
|
|
return Kirigami.Units.gridUnit * 0.5 * indexClamped;
|
|
}
|
|
property real drawerOpacity: {
|
|
if (notificationPopup.isConvergence && notificationPopup.inPopupDrawer) {
|
|
return 0;
|
|
}
|
|
let index = notificationPopup.popupIndex - popupNotifications.currentPopupIndex;
|
|
if (index > 2 && !notificationPopup.popupDrawerOpened) {
|
|
return 0; // make this popup invisible if it is below 3 other popups
|
|
} else {
|
|
return 1; // when popup drawer is opened, reset opacity to 1
|
|
}
|
|
}
|
|
Behavior on drawerScale {
|
|
NumberAnimation {
|
|
duration: Kirigami.Units.veryLongDuration * 1.25
|
|
easing.type: Easing.OutQuint
|
|
}
|
|
}
|
|
Behavior on drawerAddedOffset {
|
|
NumberAnimation {
|
|
duration: Kirigami.Units.veryLongDuration * 1.25
|
|
easing.type: Easing.OutQuint
|
|
}
|
|
}
|
|
Behavior on drawerOpacity {
|
|
NumberAnimation {
|
|
duration: Kirigami.Units.veryLongDuration * 1.25
|
|
easing.type: Easing.OutQuint
|
|
}
|
|
}
|
|
|
|
opacity: Math.min(popupOpacity, drawerOpacity)
|
|
|
|
state: ""
|
|
|
|
states: [
|
|
State {
|
|
name: "open"
|
|
PropertyChanges {
|
|
target: notificationItem; offset: notificationPopup.effectiveOpenOffset
|
|
}
|
|
PropertyChanges {
|
|
target: notificationItem; scale: 1.0
|
|
}
|
|
PropertyChanges {
|
|
target: notificationItem; popupOpacity: 1.0
|
|
}
|
|
},
|
|
State {
|
|
name: "closeWithMove"
|
|
PropertyChanges {
|
|
target: notificationItem; offset: notificationPopup.effectiveClosedOffset
|
|
}
|
|
PropertyChanges {
|
|
target: notificationItem; scale: 1.0
|
|
}
|
|
PropertyChanges {
|
|
target: notificationItem; popupOpacity: 1.0
|
|
}
|
|
},
|
|
State {
|
|
name: "closeWithScale"
|
|
PropertyChanges {
|
|
target: notificationItem; offset: notificationPopup.effectiveOpenOffset
|
|
}
|
|
PropertyChanges {
|
|
target: notificationItem; scale: 0.75
|
|
}
|
|
PropertyChanges {
|
|
target: notificationItem; popupOpacity: 0.0
|
|
}
|
|
},
|
|
State {
|
|
name: "inDrawerClosed"
|
|
PropertyChanges {
|
|
target: notificationItem; offset: notificationPopup.effectiveOpenOffset
|
|
}
|
|
PropertyChanges {
|
|
target: notificationItem; scale: 1
|
|
}
|
|
PropertyChanges {
|
|
target: notificationItem; popupOpacity: 1
|
|
}
|
|
}
|
|
]
|
|
|
|
readonly property int notificationEasing: {
|
|
// check whether the popup is the current one or above it
|
|
let topPopup = popupNotifications.currentPopupIndex >= notificationPopup.popupIndex;
|
|
// check whether the popup has any popups below it
|
|
let popupBelow = notificationPopup.popupCount - notificationPopup.popupIndex > 1;
|
|
let popupOpening = notificationItem.state == "open" || notificationItem.state == "inDrawerClosed";
|
|
let popupClosing = notificationItem.state == "closeWithMove" || notificationItem.state == "closeWithScale"
|
|
if (notificationPopup.closedWithSwipe || (topPopup && popupClosing && popupBelow)) {
|
|
// set the easing type to linear when closed with a swipe or if a popup is below when closing
|
|
// as to make sure the popup feels like it is keeping it's momentum
|
|
return Easing.Linear;
|
|
} else if (popupOpening) {
|
|
// set the easing type to 'Out' when opening so the popup will have a gentle landing
|
|
return Easing.OutQuint;
|
|
} else {
|
|
// if above conditions fail, set the easing type to 'In' so the popup will build up speed for it's exit
|
|
return Easing.InQuint;
|
|
}
|
|
}
|
|
|
|
readonly property real notificationDuration: {
|
|
// check whether the popup is the current one or above it
|
|
let topPopup = popupNotifications.currentPopupIndex >= notificationPopup.popupIndex;
|
|
// check whether the popup has any popups below it
|
|
let popupBelow = notificationPopup.popupCount - notificationPopup.popupIndex > 1;
|
|
let popupClosing = notificationItem.state == "closeWithMove" || notificationItem.state == "closeWithScale"
|
|
if (notificationPopup.closedWithSwipe || (topPopup && popupClosing && popupBelow)) {
|
|
// make sure the speed it faster when closed with a swipe or if there is a popup below when closing
|
|
// as to make sure the speed feels comparable with the easing type is set to linear
|
|
return Kirigami.Units.veryLongDuration * 0.5;
|
|
} else {
|
|
return Kirigami.Units.veryLongDuration * 1.25;
|
|
}
|
|
}
|
|
|
|
transitions: Transition {
|
|
SequentialAnimation {
|
|
ParallelAnimation {
|
|
PropertyAnimation {
|
|
properties: "offset"
|
|
easing.type: notificationItem.notificationEasing
|
|
duration: notificationItem.notificationDuration
|
|
}
|
|
PropertyAnimation {
|
|
properties: "scale"
|
|
easing.type: notificationItem.notificationEasing
|
|
duration: notificationItem.notificationDuration
|
|
}
|
|
PropertyAnimation {
|
|
properties: "popupOpacity"
|
|
easing.type: notificationItem.notificationEasing
|
|
duration: notificationItem.notificationDuration
|
|
}
|
|
}
|
|
ScriptAction {
|
|
script: {
|
|
if (notificationItem.state == "open") {
|
|
notificationPopup.preventDismissTimeout = false;
|
|
notificationPopup.updateTouchArea();
|
|
} else if (notificationItem.state == "closeWithMove" || notificationItem.state == "closeWithScale") {
|
|
notificationPopup.preventDismissTimeout = true;
|
|
if (notificationPopup.dismissTimeout) {
|
|
notificationPopup.dismissClicked();
|
|
} else {
|
|
notificationPopup.expired();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
transform: [
|
|
Scale {
|
|
origin.x: Math.round(notificationPopup.popupWidth / 2)
|
|
origin.y: notificationPopup.scaleOriginY
|
|
xScale: notificationItem.scale - notificationItem.drawerScale
|
|
yScale: notificationItem.scale - notificationItem.drawerScale
|
|
}
|
|
]
|
|
}
|
|
|
|
transform: [
|
|
Translate {
|
|
y: notificationItem.offset + notificationPopup.fullOffsetAn + notificationPopup.dragOffset + notificationPopup.currentDragOffset + notificationItem.drawerAddedOffset
|
|
}
|
|
]
|
|
|
|
DragHandler {
|
|
id: dragHandler
|
|
xAxis.enabled: false
|
|
yAxis.enabled: popupNotifications.currentPopupIndex == notificationPopup.popupIndex && !notificationPopup.popupDrawerOpened
|
|
target: null
|
|
|
|
property real lastOffset: 0
|
|
|
|
property real startDragOffset: 0
|
|
property real startPosition: 0
|
|
property bool startActive: false
|
|
|
|
onTranslationChanged: {
|
|
if (notificationItem.state == "closeWithScale" || notificationItem.state == "closeWithMove") {
|
|
return;
|
|
}
|
|
if (startActive) {
|
|
startDragOffset = notificationPopup.dragOffset;
|
|
startPosition = translation.y;
|
|
startActive = false;
|
|
}
|
|
lastOffset = notificationPopup.dragOffset;
|
|
let rawOffset = startDragOffset + (translation.y - startPosition);
|
|
if (notificationPopup.isConvergence) {
|
|
notificationPopup.dragOffset = -calculateResistance(-rawOffset, 0);
|
|
} else {
|
|
notificationPopup.dragOffset = calculateResistance(rawOffset, 0);
|
|
}
|
|
}
|
|
|
|
onActiveChanged: {
|
|
startActive = active;
|
|
notificationPopup.preventDismissTimeout = true;
|
|
if (!active && !(notificationItem.state == "closeWithScale" || notificationItem.state == "closeWithMove")) {
|
|
let dominated = false;
|
|
if (notificationPopup.isConvergence) {
|
|
// convergence: dismiss on swipe down
|
|
dominated = (notificationPopup.dragOffset - lastOffset > 1.0 && notificationPopup.dragOffset > 0)
|
|
|| (notificationPopup.dragOffset > (notificationPopup.effectiveClosedOffset - notificationPopup.effectiveOpenOffset) / 4);
|
|
} else {
|
|
// mobile: dismiss on swipe up
|
|
dominated = (lastOffset - notificationPopup.dragOffset > 1.0 && notificationPopup.dragOffset < 0)
|
|
|| (-(notificationPopup.openOffset - notificationPopup.closedOffset) / 4 > notificationPopup.dragOffset);
|
|
}
|
|
if (dominated) {
|
|
notificationPopup.closedWithSwipe = true;
|
|
notificationPopup.closePopup(popupIndex);
|
|
return;
|
|
}
|
|
dragOffsetAn.running = true;
|
|
if (notificationPopup.dragOffset - lastOffset > 1.0 || Kirigami.Units.gridUnit * 3 < notificationPopup.dragOffset) {
|
|
// this code is called when the notification is swiped or dragged down.
|
|
}
|
|
notificationPopup.preventDismissTimeout = (keyboardInteractivity == LayerShell.Window.KeyboardInteractivityOnDemand);
|
|
} else {
|
|
dragOffsetAn.running = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
MouseArea {
|
|
// capture taps were the notifications are grouping together to open the popup notification drawer
|
|
id: item
|
|
anchors.left: parent.left
|
|
anchors.right: parent.right
|
|
anchors.top: notificationItem.bottom
|
|
|
|
height: Kirigami.Units.gridUnit * 2
|
|
|
|
enabled: !notificationPopup.isConvergence && !notificationPopup.popupDrawerOpened && (notificationPopup.popupCount - popupNotifications.currentPopupIndex > 1)
|
|
|
|
onReleased: {
|
|
notificationPopup.openPopupDrawer();
|
|
notificationPopup.updateTouchArea();
|
|
notificationPopup.setKeyboardFocus();
|
|
}
|
|
}
|
|
}
|