Use vertical swipe dismiss for top/bottom centered notifications

This commit is contained in:
sima
2026-02-12 17:15:23 +08:00
parent 593f880125
commit c68c9be676
2 changed files with 107 additions and 14 deletions
+52 -3
View File
@@ -212,17 +212,21 @@ Variants {
property real opacityValue: 0.0
property real slideOffset: 0
property real swipeOffset: 0
property real swipeOffsetY: 0
property real pressGlobalX: 0
property real pressGlobalY: 0
property bool isSwiping: false
property bool suppressClick: false
readonly property bool useVerticalSwipe: notifWindow.location === "bottom" || notifWindow.location === "top"
readonly property real swipeStartThreshold: Math.round(18 * Style.uiScaleRatio)
readonly property real swipeDismissThreshold: Math.max(110, cardBackground.width * 0.32)
readonly property real verticalSwipeDismissThreshold: Math.max(70, cardBackground.height * 0.35)
scale: scaleValue
opacity: opacityValue
transform: Translate {
x: card.swipeOffset
y: card.slideOffset
y: card.slideOffset + card.swipeOffsetY
}
readonly property real slideInOffset: notifWindow.isTop ? -slideDistance : slideDistance
@@ -236,6 +240,14 @@ Variants {
return deltaX;
}
function clampVerticalSwipeDelta(deltaY) {
if (notifWindow.isBottom)
return Math.max(0, deltaY);
if (notifWindow.isTop)
return Math.min(0, deltaY);
return deltaY;
}
// Background with border
Rectangle {
id: cardBackground
@@ -326,6 +338,7 @@ Variants {
if (mouse.button === Qt.LeftButton) {
const globalPoint = cardDragArea.mapToGlobal(mouse.x, mouse.y);
card.pressGlobalX = globalPoint.x;
card.pressGlobalY = globalPoint.y;
card.isSwiping = false;
card.suppressClick = false;
}
@@ -334,13 +347,28 @@ Variants {
if (!(mouse.buttons & Qt.LeftButton) || card.isRemoving)
return;
const globalPoint = cardDragArea.mapToGlobal(mouse.x, mouse.y);
const deltaX = card.clampSwipeDelta(globalPoint.x - card.pressGlobalX);
const rawDeltaX = globalPoint.x - card.pressGlobalX;
const rawDeltaY = globalPoint.y - card.pressGlobalY;
const deltaX = card.clampSwipeDelta(rawDeltaX);
const deltaY = card.clampVerticalSwipeDelta(rawDeltaY);
if (!card.isSwiping) {
if (card.useVerticalSwipe) {
if (Math.abs(deltaY) < card.swipeStartThreshold)
return;
card.isSwiping = true;
} else {
if (Math.abs(deltaX) < card.swipeStartThreshold)
return;
card.isSwiping = true;
}
}
if (card.useVerticalSwipe) {
card.swipeOffset = 0;
card.swipeOffsetY = deltaY;
} else {
card.swipeOffset = deltaX;
card.swipeOffsetY = 0;
}
}
onReleased: mouse => {
if (mouse.button === Qt.RightButton) {
@@ -352,10 +380,13 @@ Variants {
return;
if (card.isSwiping) {
if (Math.abs(card.swipeOffset) >= card.swipeDismissThreshold) {
const dismissDistance = card.useVerticalSwipe ? Math.abs(card.swipeOffsetY) : Math.abs(card.swipeOffset);
const threshold = card.useVerticalSwipe ? card.verticalSwipeDismissThreshold : card.swipeDismissThreshold;
if (dismissDistance >= threshold) {
card.dismissBySwipe();
} else {
card.swipeOffset = 0;
card.swipeOffsetY = 0;
}
card.suppressClick = true;
card.isSwiping = false;
@@ -378,6 +409,7 @@ Variants {
onCanceled: {
card.isSwiping = false;
card.swipeOffset = 0;
card.swipeOffsetY = 0;
}
}
// Animation setup
@@ -389,6 +421,7 @@ Variants {
hoverCount = 0;
isSwiping = false;
swipeOffset = 0;
swipeOffsetY = 0;
if (Settings.data.general.animationDisabled) {
slideOffset = 0;
scaleValue = 1.0;
@@ -428,6 +461,7 @@ Variants {
isRemoving = true;
isSwiping = false;
swipeOffset = 0;
swipeOffsetY = 0;
if (!Settings.data.general.animationDisabled) {
slideOffset = slideOutOffset;
scaleValue = 0.8;
@@ -443,11 +477,18 @@ Variants {
isRemoving = true;
isSwiping = false;
if (!Settings.data.general.animationDisabled) {
if (useVerticalSwipe) {
swipeOffset = 0;
swipeOffsetY = swipeOffsetY >= 0 ? cardBackground.height + Style.marginXL : -cardBackground.height - Style.marginXL;
} else {
swipeOffset = swipeOffset >= 0 ? cardBackground.width + Style.marginXL : -cardBackground.width - Style.marginXL;
swipeOffsetY = 0;
}
scaleValue = 0.8;
opacityValue = 0.0;
} else {
swipeOffset = 0;
swipeOffsetY = 0;
}
}
@@ -516,6 +557,14 @@ Variants {
}
}
Behavior on swipeOffsetY {
enabled: !Settings.data.general.animationDisabled && !card.isSwiping
NumberAnimation {
duration: Style.animationFast
easing.type: Easing.OutCubic
}
}
// Content
ColumnLayout {
id: notificationContent
+46 -2
View File
@@ -31,16 +31,21 @@ Item {
property real progress: 1.0
property int hoverCount: 0
property real swipeOffset: 0
property real swipeOffsetY: 0
property real pressGlobalX: 0
property real pressGlobalY: 0
property bool isSwiping: false
readonly property string location: Settings.data.notifications?.location || "top_right"
readonly property bool isLeft: location.endsWith("_left")
readonly property bool isRight: location.endsWith("_right")
readonly property bool useVerticalSwipe: location === "bottom" || location === "top"
readonly property real swipeStartThreshold: Math.round(18 * Style.uiScaleRatio)
readonly property real swipeDismissThreshold: Math.max(110, background.width * 0.32)
readonly property real verticalSwipeDismissThreshold: Math.max(70, background.height * 0.35)
transform: Translate {
x: root.swipeOffset
y: root.swipeOffsetY
}
function clampSwipeDelta(deltaX) {
@@ -51,6 +56,14 @@ Item {
return deltaX;
}
function clampVerticalSwipeDelta(deltaY) {
if (location === "bottom")
return Math.max(0, deltaY);
if (location === "top")
return Math.min(0, deltaY);
return deltaY;
}
onHoverCountChanged: {
if (hoverCount > 0) {
resumeTimer.stop();
@@ -176,6 +189,14 @@ Item {
}
}
Behavior on swipeOffsetY {
enabled: !root.isSwiping
NumberAnimation {
duration: Style.animationFast
easing.type: Easing.OutCubic
}
}
Timer {
id: hideAnimation
interval: Style.animationFast
@@ -206,29 +227,48 @@ Item {
onPressed: mouse => {
const globalPoint = toastDragArea.mapToGlobal(mouse.x, mouse.y);
root.pressGlobalX = globalPoint.x;
root.pressGlobalY = globalPoint.y;
root.isSwiping = false;
}
onPositionChanged: mouse => {
if (!(mouse.buttons & Qt.LeftButton))
return;
const globalPoint = toastDragArea.mapToGlobal(mouse.x, mouse.y);
const deltaX = root.clampSwipeDelta(globalPoint.x - root.pressGlobalX);
const rawDeltaX = globalPoint.x - root.pressGlobalX;
const rawDeltaY = globalPoint.y - root.pressGlobalY;
const deltaX = root.clampSwipeDelta(rawDeltaX);
const deltaY = root.clampVerticalSwipeDelta(rawDeltaY);
if (!root.isSwiping) {
if (root.useVerticalSwipe) {
if (Math.abs(deltaY) < root.swipeStartThreshold)
return;
root.isSwiping = true;
} else {
if (Math.abs(deltaX) < root.swipeStartThreshold)
return;
root.isSwiping = true;
}
}
if (root.useVerticalSwipe) {
root.swipeOffset = 0;
root.swipeOffsetY = deltaY;
} else {
root.swipeOffset = deltaX;
root.swipeOffsetY = 0;
}
}
onReleased: mouse => {
if (mouse.button !== Qt.LeftButton)
return;
if (root.isSwiping) {
root.isSwiping = false;
if (Math.abs(root.swipeOffset) >= root.swipeDismissThreshold) {
const dismissDistance = root.useVerticalSwipe ? Math.abs(root.swipeOffsetY) : Math.abs(root.swipeOffset);
const threshold = root.useVerticalSwipe ? root.verticalSwipeDismissThreshold : root.swipeDismissThreshold;
if (dismissDistance >= threshold) {
root.hide();
} else {
root.swipeOffset = 0;
root.swipeOffsetY = 0;
}
return;
}
@@ -237,6 +277,7 @@ Item {
onCanceled: {
root.isSwiping = false;
root.swipeOffset = 0;
root.swipeOffsetY = 0;
}
cursorShape: Qt.PointingHandCursor
}
@@ -345,6 +386,7 @@ Item {
hoverCount = 0;
isSwiping = false;
swipeOffset = 0;
swipeOffsetY = 0;
// Configure and start animation
progressAnimation.duration = duration;
@@ -357,6 +399,7 @@ Item {
progressAnimation.stop();
isSwiping = false;
swipeOffset = 0;
swipeOffsetY = 0;
opacity = 0;
scale = initialScale;
hideAnimation.restart();
@@ -367,6 +410,7 @@ Item {
progressAnimation.stop();
isSwiping = false;
swipeOffset = 0;
swipeOffsetY = 0;
opacity = 0;
scale = initialScale;
root.hidden();