mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-10-23 19:54:02 -04:00
@@ -224,7 +224,9 @@ DankModal {
|
||||
|
||||
KeyboardNavigatedNotificationList {
|
||||
id: notificationList
|
||||
width: parent.width
|
||||
x: -swipeBleed
|
||||
width: parent.width + swipeBleed * 2
|
||||
swipeBleed: Theme.spacingL
|
||||
height: parent.height - y
|
||||
visible: notificationHeader.currentTab === 0
|
||||
keyboardController: modalKeyboardController
|
||||
@@ -243,6 +245,7 @@ DankModal {
|
||||
width: parent.width
|
||||
height: parent.height - y
|
||||
visible: notificationHeader.currentTab === 1
|
||||
swipeBleed: Theme.spacingL
|
||||
Component.onCompleted: notificationModal.historyListRef = historyList
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ FocusScope {
|
||||
property string entryId: "notifications"
|
||||
property int currentTab: 0
|
||||
property var transientSurfaceTracker: null
|
||||
property real swipeBleed: 0
|
||||
readonly property var surfaces: transientSurfaceTracker ?? localSurfaces
|
||||
readonly property bool hasNotifications: list.count > 0
|
||||
readonly property Item focusTarget: root
|
||||
@@ -85,7 +86,6 @@ FocusScope {
|
||||
|
||||
implicitHeight: DashMetrics.tabMinHeight
|
||||
enabled: interactive
|
||||
clip: true
|
||||
LayoutMirroring.enabled: I18n.isRtl
|
||||
LayoutMirroring.childrenInherit: true
|
||||
|
||||
@@ -128,8 +128,11 @@ FocusScope {
|
||||
KeyboardNavigatedNotificationList {
|
||||
id: list
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: -root.swipeBleed
|
||||
anchors.rightMargin: -root.swipeBleed
|
||||
nested: root.nested
|
||||
anchors.bottomMargin: footer.height + Theme.spacingM
|
||||
swipeBleed: root.swipeBleed
|
||||
visible: root.currentTab === 0
|
||||
showScrollBar: false
|
||||
keyboardController: keyboard
|
||||
@@ -140,13 +143,15 @@ FocusScope {
|
||||
|
||||
Loader {
|
||||
id: historyLoader
|
||||
anchors.fill: list
|
||||
anchors.fill: parent
|
||||
anchors.bottomMargin: footer.height + Theme.spacingM
|
||||
active: root.currentTab === 1
|
||||
visible: active
|
||||
sourceComponent: HistoryNotificationList {
|
||||
focusAllowed: root.activeFocus
|
||||
showScrollBar: false
|
||||
nested: root.nested
|
||||
swipeBleed: root.swipeBleed
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ Card {
|
||||
live: root.live
|
||||
interactive: root.interactive
|
||||
nested: true
|
||||
swipeBleed: root.pad
|
||||
transientSurfaceTracker: root.transientSurfaceTracker
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ Item {
|
||||
property int selectedIndex: -1
|
||||
property bool showKeyboardHints: false
|
||||
property bool nested: false
|
||||
property real swipeBleed: 0
|
||||
property alias showScrollBar: historyListView.showScrollBar
|
||||
|
||||
function getStartOfDay(date) {
|
||||
@@ -237,9 +238,12 @@ Item {
|
||||
|
||||
DankListView {
|
||||
id: historyListView
|
||||
width: parent.width
|
||||
x: -root.swipeBleed
|
||||
width: parent.width + root.swipeBleed * 2
|
||||
height: parent.height - filterChips.height - Theme.spacingS
|
||||
clip: true
|
||||
leftMargin: root.swipeBleed
|
||||
rightMargin: root.swipeBleed
|
||||
spacing: Theme.groupedListGap
|
||||
|
||||
add: NotificationMetrics.animationsEnabled ? DC.ListViewTransitions.add : null
|
||||
@@ -257,90 +261,33 @@ Item {
|
||||
visible: historyListView.count === 0
|
||||
}
|
||||
|
||||
delegate: Item {
|
||||
NotificationSwipeGroup {
|
||||
id: historySwipe
|
||||
}
|
||||
|
||||
delegate: NotificationSwipeRow {
|
||||
id: delegateRoot
|
||||
required property var modelData
|
||||
required property int index
|
||||
|
||||
property real swipeOffset: 0
|
||||
property bool isDismissing: false
|
||||
readonly property real dismissThreshold: width * NotificationMetrics.swipeThreshold
|
||||
property bool __delegateInitialized: false
|
||||
|
||||
Timer {
|
||||
interval: 0
|
||||
running: true
|
||||
onTriggered: delegateRoot.__delegateInitialized = true
|
||||
}
|
||||
|
||||
width: ListView.view.width
|
||||
group: historySwipe
|
||||
bleed: root.swipeBleed
|
||||
width: ListView.view.width - root.swipeBleed * 2
|
||||
height: historyCard.height
|
||||
clip: true
|
||||
onDismissed: root.removeWithScrollPreserve(modelData?.id || "")
|
||||
|
||||
HistoryNotificationCard {
|
||||
id: historyCard
|
||||
width: parent.width
|
||||
x: delegateRoot.swipeOffset
|
||||
x: delegateRoot.offset
|
||||
historyItem: modelData
|
||||
nested: root.nested
|
||||
firstInGroup: index === 0
|
||||
lastInGroup: index === historyListView.count - 1
|
||||
topRoundness: delegateRoot.topRoundness
|
||||
bottomRoundness: delegateRoot.bottomRoundness
|
||||
contentOpacity: delegateRoot.contentOpacity
|
||||
isSelected: root.keyboardActive && root.focusAllowed && root.selectedIndex === index
|
||||
keyboardNavigationActive: root.keyboardActive && root.focusAllowed
|
||||
opacity: Math.max(0, 1 - Math.abs(delegateRoot.swipeOffset) / delegateRoot.width)
|
||||
|
||||
Behavior on x {
|
||||
enabled: !swipeDragHandler.active && !delegateRoot.isDismissing && delegateRoot.__delegateInitialized && NotificationMetrics.animationsEnabled
|
||||
NumberAnimation {
|
||||
duration: Theme.notificationExitDuration
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: NotificationMetrics.dismissCurve
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
enabled: delegateRoot.__delegateInitialized && NotificationMetrics.animationsEnabled
|
||||
NumberAnimation {
|
||||
duration: delegateRoot.__delegateInitialized ? Theme.notificationExitDuration : 0
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Theme.expressiveCurves.expressiveEffects
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
id: swipeDismissAnimation
|
||||
target: delegateRoot
|
||||
property: "swipeOffset"
|
||||
to: delegateRoot.swipeOffset > 0 ? delegateRoot.width : -delegateRoot.width
|
||||
duration: NotificationMetrics.animationsEnabled ? Theme.notificationExitDuration : 0
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: NotificationMetrics.dismissCurve
|
||||
onFinished: root.removeWithScrollPreserve(delegateRoot.modelData?.id || "")
|
||||
}
|
||||
|
||||
DragHandler {
|
||||
id: swipeDragHandler
|
||||
target: null
|
||||
yAxis.enabled: false
|
||||
xAxis.enabled: true
|
||||
|
||||
onActiveChanged: {
|
||||
if (active || delegateRoot.isDismissing)
|
||||
return;
|
||||
if (Math.abs(delegateRoot.swipeOffset) <= delegateRoot.dismissThreshold) {
|
||||
delegateRoot.swipeOffset = 0;
|
||||
return;
|
||||
}
|
||||
delegateRoot.isDismissing = true;
|
||||
swipeDismissAnimation.restart();
|
||||
}
|
||||
|
||||
onTranslationChanged: {
|
||||
if (delegateRoot.isDismissing)
|
||||
return;
|
||||
delegateRoot.swipeOffset = translation.x;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,8 +21,7 @@ DankListView {
|
||||
property bool trackStableContentHeight: true
|
||||
property bool trackSessionContentHeight: false
|
||||
property bool listInitialized: false
|
||||
property int swipingCardIndex: -1
|
||||
property real swipingCardOffset: 0
|
||||
property real swipeBleed: 0
|
||||
property real sessionContentHeight: 0
|
||||
property var transientSurfaceTracker: null
|
||||
property bool nested: false
|
||||
@@ -171,6 +170,8 @@ DankListView {
|
||||
}
|
||||
|
||||
clip: true
|
||||
leftMargin: swipeBleed
|
||||
rightMargin: swipeBleed
|
||||
model: ScriptModel {
|
||||
values: NotificationService.groupedNotifications.map(group => group.key)
|
||||
}
|
||||
@@ -206,6 +207,10 @@ DankListView {
|
||||
visible: listView.count === 0
|
||||
}
|
||||
|
||||
NotificationSwipeGroup {
|
||||
id: swipeGroup
|
||||
}
|
||||
|
||||
onCountChanged: listView.queueSessionContentHeightUpdate()
|
||||
|
||||
onModelChanged: {
|
||||
@@ -216,21 +221,12 @@ DankListView {
|
||||
ensureVisibleTimer.restart();
|
||||
}
|
||||
|
||||
delegate: Item {
|
||||
delegate: NotificationSwipeRow {
|
||||
id: delegateRoot
|
||||
required property string modelData
|
||||
required property int index
|
||||
|
||||
readonly property var notificationGroup: NotificationService.groupedNotifications.find(group => group.key === modelData)
|
||||
readonly property bool isExpanded: NotificationService.expandedGroups[modelData] || false
|
||||
property real swipeOffset: 0
|
||||
property bool isDismissing: false
|
||||
readonly property real dismissThreshold: width * NotificationMetrics.swipeThreshold
|
||||
|
||||
readonly property bool isAdjacentToSwipe: listView.count >= 2 && listView.swipingCardIndex !== -1 && (index === listView.swipingCardIndex - 1 || index === listView.swipingCardIndex + 1)
|
||||
readonly property real adjacentSwipeInfluence: isAdjacentToSwipe ? listView.swipingCardOffset * NotificationMetrics.adjacentSwipeInfluence : 0
|
||||
readonly property real swipeFadeStartOffset: width * NotificationMetrics.swipeFadeStart
|
||||
readonly property real swipeFadeDistance: Math.max(1, width - swipeFadeStartOffset)
|
||||
readonly property real nonAnimHeight: notificationCard.targetHeight
|
||||
|
||||
readonly property bool isCardAnimating: notificationCard.isAnimating
|
||||
@@ -244,14 +240,20 @@ DankListView {
|
||||
}
|
||||
}
|
||||
|
||||
width: ListView.view.width
|
||||
group: swipeGroup
|
||||
bleed: listView.swipeBleed
|
||||
width: ListView.view.width - listView.swipeBleed * 2
|
||||
height: notificationCard.height
|
||||
clip: true
|
||||
onDismissed: NotificationService.dismissGroup(modelData)
|
||||
|
||||
Center.NotificationCard {
|
||||
id: notificationCard
|
||||
width: parent.width
|
||||
x: delegateRoot.swipeOffset + delegateRoot.adjacentSwipeInfluence
|
||||
x: delegateRoot.offset
|
||||
topRoundness: delegateRoot.topRoundness
|
||||
bottomRoundness: delegateRoot.bottomRoundness
|
||||
contentOpacity: delegateRoot.contentOpacity
|
||||
swipeBleed: listView.swipeBleed
|
||||
notificationGroup: delegateRoot.notificationGroup
|
||||
nested: listView.nested
|
||||
firstInList: index === 0
|
||||
@@ -259,13 +261,6 @@ DankListView {
|
||||
keyboardNavigationActive: listView.keyboardActive && listView.focusAllowed
|
||||
animateExpansion: listView.cardAnimateExpansion && listView.listInitialized
|
||||
transientSurfaceTracker: listView.transientSurfaceTracker
|
||||
opacity: {
|
||||
const swipeAmount = Math.abs(delegateRoot.swipeOffset);
|
||||
if (swipeAmount <= delegateRoot.swipeFadeStartOffset)
|
||||
return 1;
|
||||
const fadeProgress = (swipeAmount - delegateRoot.swipeFadeStartOffset) / delegateRoot.swipeFadeDistance;
|
||||
return Math.max(0, 1 - fadeProgress);
|
||||
}
|
||||
onIsAnimatingChanged: {
|
||||
if (!listView.trackStableContentHeight)
|
||||
return;
|
||||
@@ -302,65 +297,6 @@ DankListView {
|
||||
const selection = keyboardController.getCurrentSelection();
|
||||
return (selection.type === "notification" && selection.groupIndex === index) ? selection.notificationIndex : -1;
|
||||
}
|
||||
|
||||
Behavior on x {
|
||||
enabled: NotificationMetrics.animationsEnabled && !swipeDragHandler.active && !delegateRoot.isDismissing && (listView.swipingCardIndex === -1 || !delegateRoot.isAdjacentToSwipe) && listView.listInitialized
|
||||
NumberAnimation {
|
||||
duration: Theme.notificationExitDuration
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: NotificationMetrics.heightCurve
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
enabled: NotificationMetrics.animationsEnabled && listView.listInitialized
|
||||
NumberAnimation {
|
||||
duration: listView.listInitialized ? Theme.notificationExitDuration : 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DragHandler {
|
||||
id: swipeDragHandler
|
||||
target: null
|
||||
yAxis.enabled: false
|
||||
xAxis.enabled: true
|
||||
|
||||
onActiveChanged: {
|
||||
if (active) {
|
||||
listView.swipingCardIndex = index;
|
||||
return;
|
||||
}
|
||||
listView.swipingCardIndex = -1;
|
||||
listView.swipingCardOffset = 0;
|
||||
if (delegateRoot.isDismissing)
|
||||
return;
|
||||
if (Math.abs(delegateRoot.swipeOffset) > delegateRoot.dismissThreshold) {
|
||||
delegateRoot.isDismissing = true;
|
||||
swipeDismissAnim.to = delegateRoot.swipeOffset > 0 ? delegateRoot.width : -delegateRoot.width;
|
||||
swipeDismissAnim.start();
|
||||
} else {
|
||||
delegateRoot.swipeOffset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
onTranslationChanged: {
|
||||
if (delegateRoot.isDismissing)
|
||||
return;
|
||||
delegateRoot.swipeOffset = translation.x;
|
||||
listView.swipingCardOffset = translation.x;
|
||||
}
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
id: swipeDismissAnim
|
||||
target: delegateRoot
|
||||
property: "swipeOffset"
|
||||
to: 0
|
||||
duration: NotificationMetrics.animationsEnabled ? Theme.notificationExitDuration : 0
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: NotificationMetrics.dismissCurve
|
||||
onFinished: NotificationService.dismissGroup(delegateRoot.modelData)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,10 @@ Item {
|
||||
property bool firstInList: true
|
||||
property bool lastInList: true
|
||||
property bool nested: false
|
||||
property real topRoundness: 0
|
||||
property real bottomRoundness: 0
|
||||
property real contentOpacity: 1
|
||||
property real swipeBleed: 0
|
||||
readonly property color cardSurfaceColor: Theme.foregroundColor(nested ? Theme.chipSurface : Theme.cardSurface, Theme.isFloatingWindow(root))
|
||||
readonly property color cardChipColor: nested ? Theme.withAlpha(Theme.onSurface, Theme.stateLayerFocus) : Theme.chipSurface
|
||||
readonly property real expandedTargetHeight: {
|
||||
@@ -35,7 +39,7 @@ Item {
|
||||
|
||||
width: parent ? parent.width : NotificationMetrics.popupWidth
|
||||
height: targetHeight
|
||||
clip: true
|
||||
clip: isAnimating
|
||||
|
||||
function toggleGroup() {
|
||||
NotificationService.toggleGroupExpansion(notificationGroup?.key || "");
|
||||
@@ -55,6 +59,10 @@ Item {
|
||||
|
||||
Component.onDestruction: transientSurfaceTracker?.unregister(root)
|
||||
|
||||
NotificationSwipeGroup {
|
||||
id: rowSwipe
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root.transientSurfaceTracker
|
||||
ignoreUnknownSignals: true
|
||||
@@ -84,6 +92,9 @@ Item {
|
||||
descriptionExpanded: NotificationService.expandedMessages[(notificationData?.notification?.id || "") + "_desc"] || false
|
||||
firstInGroup: root.firstInList
|
||||
lastInGroup: root.lastInList
|
||||
topRoundness: root.topRoundness
|
||||
bottomRoundness: root.bottomRoundness
|
||||
contentOpacity: root.contentOpacity
|
||||
keyboardSelected: root.keyboardNavigationActive && root.isGroupSelected
|
||||
keyboardHints: keyboardSelected
|
||||
animateHeight: false
|
||||
@@ -104,10 +115,9 @@ Item {
|
||||
Column {
|
||||
id: expandedContent
|
||||
objectName: "expandedContent"
|
||||
property int swipingIndex: -1
|
||||
property real swipingOffset: 0
|
||||
width: parent.width
|
||||
visible: root.expanded
|
||||
opacity: root.contentOpacity
|
||||
spacing: Theme.groupedListGap
|
||||
|
||||
Rectangle {
|
||||
@@ -165,30 +175,34 @@ Item {
|
||||
values: root.expanded ? (root.notificationGroup?.notifications?.slice(0, NotificationMetrics.expandedLimit) || []) : []
|
||||
}
|
||||
|
||||
Item {
|
||||
NotificationSwipeRow {
|
||||
id: row
|
||||
required property var modelData
|
||||
required property int index
|
||||
property real swipeOffset: 0
|
||||
property bool dismissing: false
|
||||
property bool collapsing: false
|
||||
property var notificationToDismiss: null
|
||||
readonly property real targetHeight: collapsing ? 0 : message.targetHeight
|
||||
readonly property bool adjacentToSwipe: expandedContent.swipingIndex !== -1 && Math.abs(index - expandedContent.swipingIndex) === 1
|
||||
readonly property real adjacentSwipeInfluence: adjacentToSwipe ? expandedContent.swipingOffset * NotificationMetrics.adjacentSwipeInfluence : 0
|
||||
group: rowSwipe
|
||||
bleed: root.swipeBleed
|
||||
width: expandedContent.width
|
||||
height: message.height
|
||||
clip: true
|
||||
clip: collapsing
|
||||
onDismissed: {
|
||||
notificationToDismiss = modelData;
|
||||
collapseAnimation.start();
|
||||
}
|
||||
|
||||
Notifications.NotificationCard {
|
||||
id: message
|
||||
surfaceColor: root.cardSurfaceColor
|
||||
chipColor: root.cardChipColor
|
||||
x: row.swipeOffset + row.adjacentSwipeInfluence
|
||||
x: row.offset
|
||||
width: parent.width
|
||||
notificationData: row.modelData
|
||||
firstInGroup: row.index === 0
|
||||
lastInGroup: row.index === notificationRepeater.count - 1
|
||||
topRoundness: row.topRoundness
|
||||
bottomRoundness: row.bottomRoundness
|
||||
contentOpacity: row.contentOpacity
|
||||
keyboardSelected: root.keyboardNavigationActive && root.selectedNotificationIndex === row.index
|
||||
keyboardHints: keyboardSelected
|
||||
descriptionExpanded: NotificationService.expandedMessages[(notificationData?.notification?.id || "") + "_desc"] || false
|
||||
@@ -197,56 +211,11 @@ Item {
|
||||
onActionRequested: action => root.invokeAction(action)
|
||||
onBodyClicked: root.invokeAction(contextActions.defaultAction(notificationData))
|
||||
onContextMenuRequested: (x, y) => root.openContextMenu(message, x, y)
|
||||
Behavior on x {
|
||||
enabled: !swipe.active && !row.dismissing && !row.adjacentToSwipe && NotificationMetrics.animationsEnabled
|
||||
NumberAnimation {
|
||||
duration: Theme.notificationExitDuration
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: NotificationMetrics.dismissCurve
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DragHandler {
|
||||
id: swipe
|
||||
target: null
|
||||
yAxis.enabled: false
|
||||
grabPermissions: PointerHandler.CanTakeOverFromItems | PointerHandler.CanTakeOverFromHandlersOfDifferentType
|
||||
onTranslationChanged: {
|
||||
if (row.dismissing)
|
||||
return;
|
||||
row.swipeOffset = translation.x;
|
||||
expandedContent.swipingOffset = translation.x;
|
||||
}
|
||||
onActiveChanged: {
|
||||
if (active) {
|
||||
expandedContent.swipingIndex = row.index;
|
||||
return;
|
||||
}
|
||||
expandedContent.swipingIndex = -1;
|
||||
expandedContent.swipingOffset = 0;
|
||||
if (row.dismissing)
|
||||
return;
|
||||
if (Math.abs(row.swipeOffset) <= row.width * NotificationMetrics.swipeThreshold) {
|
||||
row.swipeOffset = 0;
|
||||
return;
|
||||
}
|
||||
row.notificationToDismiss = row.modelData;
|
||||
row.dismissing = true;
|
||||
dismissAnimation.start();
|
||||
}
|
||||
}
|
||||
SequentialAnimation {
|
||||
id: dismissAnimation
|
||||
id: collapseAnimation
|
||||
|
||||
NumberAnimation {
|
||||
target: row
|
||||
property: "swipeOffset"
|
||||
to: row.swipeOffset > 0 ? row.width : -row.width
|
||||
duration: NotificationMetrics.animationsEnabled ? Theme.notificationExitDuration : 0
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: NotificationMetrics.dismissCurve
|
||||
}
|
||||
PropertyAction {
|
||||
target: row
|
||||
property: "collapsing"
|
||||
|
||||
@@ -122,6 +122,9 @@ Item {
|
||||
|
||||
objectName: "notificationList"
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: -PopoutMetrics.contentPadding
|
||||
anchors.rightMargin: -PopoutMetrics.contentPadding
|
||||
swipeBleed: PopoutMetrics.contentPadding
|
||||
cardAnimateExpansion: root.host.animateCardExpansion ?? true
|
||||
trackStableContentHeight: !root.hostOwnsHeight
|
||||
trackSessionContentHeight: root.hostOwnsHeight
|
||||
@@ -135,6 +138,7 @@ Item {
|
||||
visible: notificationHeader.currentTab === 1
|
||||
width: parent.width
|
||||
height: parent.height - root.cachedHeaderHeight - contentColumnInner.spacing
|
||||
swipeBleed: PopoutMetrics.contentPadding
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,9 @@ Rectangle {
|
||||
property real outerRadius: Theme.groupedListOuterRadius
|
||||
property bool firstInGroup: true
|
||||
property bool lastInGroup: true
|
||||
property real topRoundness: 0
|
||||
property real bottomRoundness: 0
|
||||
property real contentOpacity: 1
|
||||
readonly property bool hasMoreText: bodyText.truncated || summaryText.truncated
|
||||
readonly property bool hasBody: (notificationData?.htmlBody || "").replace(/<[^>]*>/g, "").trim().length > 0
|
||||
readonly property string appIcon: NotificationService.notificationAppIcon(notificationData?.appIcon || "", notificationData?.desktopEntry || "")
|
||||
@@ -69,9 +72,9 @@ Rectangle {
|
||||
implicitHeight: targetHeight
|
||||
height: targetHeight
|
||||
radius: Theme.groupedListInnerRadius
|
||||
topLeftRadius: firstInGroup ? outerRadius : radius
|
||||
topLeftRadius: radius + (outerRadius - radius) * (firstInGroup ? 1 : topRoundness)
|
||||
topRightRadius: topLeftRadius
|
||||
bottomLeftRadius: lastInGroup ? outerRadius : radius
|
||||
bottomLeftRadius: radius + (outerRadius - radius) * (lastInGroup ? 1 : bottomRoundness)
|
||||
bottomRightRadius: bottomLeftRadius
|
||||
color: keyboardSelected ? Theme.selectedContainer : surfaceColor
|
||||
border.width: Theme.layerOutlineWidth
|
||||
@@ -127,6 +130,7 @@ Rectangle {
|
||||
height: parent.height - NotificationMetrics.cardPadding * 2
|
||||
radius: Theme.fullRadius(width, height)
|
||||
color: Theme.primary
|
||||
opacity: root.contentOpacity
|
||||
visible: root.notificationData?.urgency === NotificationUrgency.Critical
|
||||
}
|
||||
|
||||
@@ -151,6 +155,7 @@ Rectangle {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: NotificationMetrics.cardPadding
|
||||
y: NotificationMetrics.cardPadding
|
||||
opacity: root.contentOpacity
|
||||
|
||||
Image {
|
||||
id: appImage
|
||||
@@ -184,6 +189,7 @@ Rectangle {
|
||||
anchors.rightMargin: NotificationMetrics.cardPadding
|
||||
y: NotificationMetrics.cardPadding
|
||||
spacing: NotificationMetrics.contentSpacing
|
||||
opacity: root.contentOpacity
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
|
||||
@@ -35,7 +35,15 @@ Singleton {
|
||||
readonly property real unreadDotSize: Theme.spacingXS + Theme.spacingXXS
|
||||
readonly property real swipeThreshold: 0.35
|
||||
readonly property real swipeFadeStart: 0.75
|
||||
readonly property real adjacentSwipeInfluence: 0.1
|
||||
readonly property real swipeContentFadeEnd: 0.6
|
||||
readonly property real swipeDetachDistance: 72
|
||||
readonly property real swipeAttachDistance: 56
|
||||
readonly property var swipePull: [0.04, 0.12, 0.5, 0.12, 0.04]
|
||||
readonly property var swipeRoundness: [0.5, 0.7, 0.9, 1, 0.9, 0.7, 0.5]
|
||||
readonly property real swipePullRoundnessMax: 0.8
|
||||
readonly property var swipeDetachSpring: swipeSpring(800, 0.95)
|
||||
readonly property var swipeSnapSpring: swipeSpring(550, 0.6)
|
||||
readonly property var swipeAttachSpring: swipeSpring(850, 0.95)
|
||||
readonly property int expandedLimit: 10
|
||||
readonly property int collapsedLines: compact ? 1 : 2
|
||||
readonly property real summarySize: SettingsData.notificationSummaryFontSize || Theme.fontSizeMedium
|
||||
@@ -49,4 +57,11 @@ Singleton {
|
||||
readonly property var stackSpring: Theme.springPreset("default", Theme.notificationStackShiftDuration)
|
||||
readonly property var expandCurve: Theme.expressiveCurves.expressiveDefaultSpatial
|
||||
readonly property var dismissCurve: Theme.expressiveCurves.expressiveFastSpatial
|
||||
|
||||
function swipeSpring(stiffness, dampingRatio) {
|
||||
return {
|
||||
"stiffness": stiffness,
|
||||
"damping": 2 * dampingRatio * Math.sqrt(stiffness)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import QtQuick
|
||||
|
||||
QtObject {
|
||||
property Item row: null
|
||||
property real translation: 0
|
||||
property bool detached: false
|
||||
|
||||
function begin(item) {
|
||||
if (row)
|
||||
return;
|
||||
translation = 0;
|
||||
detached = false;
|
||||
row = item;
|
||||
}
|
||||
|
||||
function drag(item, value) {
|
||||
if (row !== item)
|
||||
return;
|
||||
const distance = Math.abs(value);
|
||||
if (!detached && distance >= NotificationMetrics.swipeDetachDistance)
|
||||
detached = true;
|
||||
else if (detached && distance <= NotificationMetrics.swipeAttachDistance)
|
||||
detached = false;
|
||||
translation = value;
|
||||
}
|
||||
|
||||
function end(item) {
|
||||
if (row !== item)
|
||||
return;
|
||||
row = null;
|
||||
detached = false;
|
||||
translation = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property int index
|
||||
property NotificationSwipeGroup group: null
|
||||
property real bleed: 0
|
||||
property bool dismissing: false
|
||||
property real topRoundness: roundness(true)
|
||||
property real bottomRoundness: roundness(false)
|
||||
readonly property real offset: motion.value
|
||||
readonly property real contentOpacity: 1 - Math.min(1, Math.abs(motion.value) / Math.max(1, width * NotificationMetrics.swipeContentFadeEnd))
|
||||
readonly property bool magnetActive: group !== null && group.row !== null
|
||||
readonly property bool magnetDetached: group !== null && group.detached
|
||||
readonly property real magnetTranslation: group ? group.translation : 0
|
||||
|
||||
signal dismissed
|
||||
|
||||
function slot(values, distance) {
|
||||
const i = distance + (values.length - 1) / 2;
|
||||
return i >= 0 && i < values.length ? values[i] : 0;
|
||||
}
|
||||
|
||||
function roundness(top) {
|
||||
if (dismissing)
|
||||
return 1;
|
||||
if (!group?.row)
|
||||
return 0;
|
||||
const distance = index - group.row.index;
|
||||
if (group.detached)
|
||||
return distance === 0 || distance === (top ? 1 : -1) ? 1 : 0;
|
||||
const reach = (NotificationMetrics.swipeRoundness.length - 1) / 2;
|
||||
if (distance === (top ? -reach : reach))
|
||||
return 0;
|
||||
const pull = Math.abs(slot(NotificationMetrics.swipePull, 0) * group.translation) / NotificationMetrics.swipeDetachDistance;
|
||||
return slot(NotificationMetrics.swipeRoundness, distance) * Math.min(NotificationMetrics.swipePullRoundnessMax, pull);
|
||||
}
|
||||
|
||||
function magnetTarget() {
|
||||
if (!group?.row)
|
||||
return 0;
|
||||
const distance = index - group.row.index;
|
||||
if (group.detached)
|
||||
return distance === 0 ? group.translation : 0;
|
||||
return slot(NotificationMetrics.swipePull, distance) * group.translation;
|
||||
}
|
||||
|
||||
function springTo(spring, target) {
|
||||
if (dismissing)
|
||||
return;
|
||||
motion.stiffness = spring.stiffness;
|
||||
motion.damping = spring.damping;
|
||||
motion.retarget(target);
|
||||
}
|
||||
|
||||
function release() {
|
||||
if (group?.row !== root)
|
||||
return;
|
||||
if (Math.abs(group.translation) > width * NotificationMetrics.swipeThreshold) {
|
||||
dismissing = true;
|
||||
motion.snapTo(motion.value);
|
||||
dismissAnimation.to = (group.translation > 0 ? 1 : -1) * (width + bleed);
|
||||
dismissAnimation.start();
|
||||
}
|
||||
group.end(root);
|
||||
}
|
||||
|
||||
onMagnetTranslationChanged: {
|
||||
if (dismissing)
|
||||
return;
|
||||
if (motion.running) {
|
||||
motion.retarget(magnetTarget());
|
||||
return;
|
||||
}
|
||||
motion.snapTo(magnetTarget());
|
||||
}
|
||||
|
||||
onMagnetDetachedChanged: {
|
||||
if (!group?.row)
|
||||
return;
|
||||
if (!group.detached) {
|
||||
springTo(NotificationMetrics.swipeAttachSpring, magnetTarget());
|
||||
return;
|
||||
}
|
||||
springTo(group.row === root ? NotificationMetrics.swipeDetachSpring : NotificationMetrics.swipeSnapSpring, magnetTarget());
|
||||
}
|
||||
|
||||
onMagnetActiveChanged: {
|
||||
if (magnetActive)
|
||||
return;
|
||||
springTo(NotificationMetrics.swipeSnapSpring, 0);
|
||||
}
|
||||
|
||||
Component.onDestruction: group?.end(root)
|
||||
|
||||
Behavior on topRoundness {
|
||||
enabled: NotificationMetrics.animationsEnabled
|
||||
NumberAnimation {
|
||||
duration: Theme.expressiveDurations.expressiveEffects
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Theme.expressiveCurves.expressiveEffects
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on bottomRoundness {
|
||||
enabled: NotificationMetrics.animationsEnabled
|
||||
NumberAnimation {
|
||||
duration: Theme.expressiveDurations.expressiveEffects
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Theme.expressiveCurves.expressiveEffects
|
||||
}
|
||||
}
|
||||
|
||||
SpringMotion {
|
||||
id: motion
|
||||
reducedMotion: !NotificationMetrics.animationsEnabled
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
id: dismissAnimation
|
||||
target: motion
|
||||
property: "value"
|
||||
duration: NotificationMetrics.animationsEnabled ? Theme.notificationExitDuration : 0
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: NotificationMetrics.dismissCurve
|
||||
onFinished: root.dismissed()
|
||||
}
|
||||
|
||||
DragHandler {
|
||||
target: null
|
||||
yAxis.enabled: false
|
||||
enabled: root.group !== null && !root.dismissing
|
||||
grabPermissions: PointerHandler.CanTakeOverFromItems | PointerHandler.CanTakeOverFromHandlersOfDifferentType
|
||||
onActiveChanged: {
|
||||
if (active) {
|
||||
root.group.begin(root);
|
||||
return;
|
||||
}
|
||||
root.release();
|
||||
}
|
||||
onTranslationChanged: root.group?.drag(root, translation.x)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user