mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-10-14 23:34:01 -04:00
them towards theme controlCenter: make a real grid, add numerous more tile varieties, add icon size option surfaces: fix elevation levels in general, and shift them down settings: restyles and re-org sliders: scale down universally
292 lines
13 KiB
QML
292 lines
13 KiB
QML
import QtQuick
|
|
import Quickshell
|
|
import qs.Common
|
|
import qs.Services
|
|
import qs.Widgets
|
|
import qs.Modules.Notifications
|
|
import qs.Modules.Notifications as Notifications
|
|
|
|
Item {
|
|
id: root
|
|
|
|
property var notificationGroup
|
|
property bool expanded: NotificationService.expandedGroups[notificationGroup?.key] || false
|
|
property bool animateExpansion: true
|
|
property bool isGroupSelected: false
|
|
property int selectedNotificationIndex: -1
|
|
property bool keyboardNavigationActive: false
|
|
property var transientSurfaceTracker: null
|
|
property bool firstInList: true
|
|
property bool lastInList: true
|
|
property bool nested: false
|
|
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: {
|
|
let total = groupHeader.height;
|
|
for (const child of expandedContent.children) {
|
|
if (child.targetHeight === undefined)
|
|
continue;
|
|
total += Theme.groupedListGap + child.targetHeight;
|
|
}
|
|
return total;
|
|
}
|
|
readonly property real targetHeight: expanded ? expandedTargetHeight : collapsedCard.targetHeight
|
|
readonly property bool isAnimating: heightAnimation.running
|
|
|
|
width: parent ? parent.width : NotificationMetrics.popupWidth
|
|
height: targetHeight
|
|
clip: true
|
|
|
|
function toggleGroup() {
|
|
NotificationService.toggleGroupExpansion(notificationGroup?.key || "");
|
|
}
|
|
|
|
function invokeAction(action) {
|
|
if (!action?.invoke)
|
|
return;
|
|
action.invoke();
|
|
PopoutService.closeNotificationCenter();
|
|
}
|
|
|
|
function openContextMenu(item, x, y) {
|
|
notificationCardContextMenu.popupAnchorItem = item;
|
|
notificationCardContextMenu.showDropdownMenu();
|
|
}
|
|
|
|
Component.onDestruction: transientSurfaceTracker?.unregister(root)
|
|
|
|
Connections {
|
|
target: root.transientSurfaceTracker
|
|
ignoreUnknownSignals: true
|
|
function onCloseRequested() {
|
|
notificationCardContextMenu.closeDropdownMenu();
|
|
}
|
|
}
|
|
|
|
Behavior on height {
|
|
enabled: root.animateExpansion && NotificationMetrics.animationsEnabled
|
|
NumberAnimation {
|
|
id: heightAnimation
|
|
duration: root.expanded ? Theme.notificationExpandDuration : Theme.notificationCollapseDuration
|
|
easing.type: Easing.BezierSpline
|
|
easing.bezierCurve: NotificationMetrics.expandCurve
|
|
}
|
|
}
|
|
|
|
Notifications.NotificationCard {
|
|
id: collapsedCard
|
|
width: parent.width
|
|
surfaceColor: root.cardSurfaceColor
|
|
chipColor: root.cardChipColor
|
|
visible: !root.expanded
|
|
notificationData: root.notificationGroup?.latestNotification ?? null
|
|
groupCount: root.notificationGroup?.count || 0
|
|
descriptionExpanded: NotificationService.expandedMessages[(notificationData?.notification?.id || "") + "_desc"] || false
|
|
firstInGroup: root.firstInList
|
|
lastInGroup: root.lastInList
|
|
keyboardSelected: root.keyboardNavigationActive && root.isGroupSelected
|
|
keyboardHints: keyboardSelected
|
|
animateHeight: false
|
|
onExpandRequested: NotificationService.toggleMessageExpansion((notificationData?.notification?.id || "") + "_desc")
|
|
onGroupToggleRequested: root.toggleGroup()
|
|
onDismissRequested: NotificationService.dismissGroup(root.notificationGroup?.key || "")
|
|
onActionRequested: action => root.invokeAction(action)
|
|
onBodyClicked: {
|
|
if (groupCount > 1) {
|
|
root.toggleGroup();
|
|
return;
|
|
}
|
|
root.invokeAction(contextActions.defaultAction(notificationData));
|
|
}
|
|
onContextMenuRequested: (x, y) => root.openContextMenu(collapsedCard, x, y)
|
|
}
|
|
|
|
Column {
|
|
id: expandedContent
|
|
objectName: "expandedContent"
|
|
property int swipingIndex: -1
|
|
property real swipingOffset: 0
|
|
width: parent.width
|
|
visible: root.expanded
|
|
spacing: Theme.groupedListGap
|
|
|
|
Rectangle {
|
|
id: groupHeader
|
|
width: parent.width
|
|
height: NotificationMetrics.cardPadding * 2 + NotificationMetrics.controlSize
|
|
radius: Theme.groupedListOuterRadius
|
|
color: "transparent"
|
|
border.width: root.keyboardNavigationActive && root.isGroupSelected ? Theme.focusRingWidth : 0
|
|
border.color: Theme.focusRingColor
|
|
StyledText {
|
|
anchors.left: parent.left
|
|
anchors.leftMargin: NotificationMetrics.cardPadding
|
|
anchors.right: groupControls.left
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
text: root.notificationGroup?.appName || ""
|
|
font.pixelSize: Theme.fontSizeMedium
|
|
font.weight: Theme.fontWeightMedium
|
|
color: Theme.primary
|
|
elide: Text.ElideRight
|
|
}
|
|
Row {
|
|
id: groupControls
|
|
anchors.right: parent.right
|
|
anchors.rightMargin: NotificationMetrics.cardPadding
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
spacing: Theme.spacingXS
|
|
DankButton {
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
text: I18n.tr("Dismiss")
|
|
buttonHeight: NotificationMetrics.controlSize
|
|
horizontalPadding: NotificationMetrics.actionPadding
|
|
backgroundColor: "transparent"
|
|
textColor: Theme.primary
|
|
onClicked: NotificationService.dismissGroup(root.notificationGroup?.key || "")
|
|
}
|
|
DankButton {
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
text: (root.notificationGroup?.count || 0).toString()
|
|
iconName: "expand_less"
|
|
buttonHeight: NotificationMetrics.controlSize
|
|
horizontalPadding: Theme.spacingS
|
|
backgroundColor: root.cardChipColor
|
|
textColor: Theme.onSurfaceVariant
|
|
Accessible.name: I18n.tr("Collapse")
|
|
onClicked: root.toggleGroup()
|
|
}
|
|
}
|
|
}
|
|
|
|
Repeater {
|
|
id: notificationRepeater
|
|
objectName: "notificationRepeater"
|
|
model: ScriptModel {
|
|
values: root.expanded ? (root.notificationGroup?.notifications?.slice(0, NotificationMetrics.expandedLimit) || []) : []
|
|
}
|
|
|
|
Item {
|
|
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
|
|
width: expandedContent.width
|
|
height: message.height
|
|
clip: true
|
|
|
|
Notifications.NotificationCard {
|
|
id: message
|
|
surfaceColor: root.cardSurfaceColor
|
|
chipColor: root.cardChipColor
|
|
x: row.swipeOffset + row.adjacentSwipeInfluence
|
|
width: parent.width
|
|
notificationData: row.modelData
|
|
firstInGroup: row.index === 0
|
|
lastInGroup: row.index === notificationRepeater.count - 1
|
|
keyboardSelected: root.keyboardNavigationActive && root.selectedNotificationIndex === row.index
|
|
keyboardHints: keyboardSelected
|
|
descriptionExpanded: NotificationService.expandedMessages[(notificationData?.notification?.id || "") + "_desc"] || false
|
|
onExpandRequested: NotificationService.toggleMessageExpansion((notificationData?.notification?.id || "") + "_desc")
|
|
onDismissRequested: NotificationService.dismissNotification(row.modelData)
|
|
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
|
|
|
|
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"
|
|
value: true
|
|
}
|
|
NumberAnimation {
|
|
target: row
|
|
property: "height"
|
|
to: 0
|
|
duration: NotificationMetrics.animationsEnabled ? Theme.notificationExpandDuration : 0
|
|
easing.type: Easing.BezierSpline
|
|
easing.bezierCurve: NotificationMetrics.expandCurve
|
|
}
|
|
ScriptAction {
|
|
script: NotificationService.dismissNotification(row.notificationToDismiss)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
NotificationActions {
|
|
id: contextActions
|
|
appName: root.notificationGroup?.appName || ""
|
|
desktopEntry: root.notificationGroup?.latestNotification?.desktopEntry || ""
|
|
onDismissRequested: NotificationService.dismissGroup(root.notificationGroup?.key || "")
|
|
onAppMuted: NotificationService.dismissGroup(root.notificationGroup?.key || "")
|
|
}
|
|
|
|
DankDropdown {
|
|
id: notificationCardContextMenu
|
|
showTrigger: false
|
|
popupWidth: NotificationMetrics.menuWidth
|
|
transientSurfaceTracker: root.transientSurfaceTracker
|
|
options: contextActions.items.map(item => item.label)
|
|
onValueChanged: value => {
|
|
const item = contextActions.items.find(item => item.label === value);
|
|
if (item)
|
|
contextActions.trigger(item.action);
|
|
}
|
|
}
|
|
}
|