mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-29 07:52:50 -05:00
Notification updates
This commit is contained in:
@@ -26,6 +26,30 @@ Singleton {
|
|||||||
`, root)
|
`, root)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Format timestamp for display
|
||||||
|
function formatTimestamp(timestamp) {
|
||||||
|
if (!timestamp) return ""
|
||||||
|
|
||||||
|
const now = new Date()
|
||||||
|
const notifTime = new Date(timestamp)
|
||||||
|
const diffMs = now.getTime() - notifTime.getTime()
|
||||||
|
const diffMinutes = Math.floor(diffMs / 60000)
|
||||||
|
const diffHours = Math.floor(diffMs / 3600000)
|
||||||
|
const diffDays = Math.floor(diffMs / 86400000)
|
||||||
|
|
||||||
|
if (diffMinutes < 1) {
|
||||||
|
return "now"
|
||||||
|
} else if (diffMinutes < 60) {
|
||||||
|
return `${diffMinutes}m ago`
|
||||||
|
} else if (diffHours < 24) {
|
||||||
|
return `${diffHours}h ago`
|
||||||
|
} else if (diffDays < 7) {
|
||||||
|
return `${diffDays}d ago`
|
||||||
|
} else {
|
||||||
|
return notifTime.toLocaleDateString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Add a new notification to the appropriate group
|
// Add a new notification to the appropriate group
|
||||||
function addNotification(notificationObj) {
|
function addNotification(notificationObj) {
|
||||||
if (!notificationObj || !notificationObj.appName) {
|
if (!notificationObj || !notificationObj.appName) {
|
||||||
|
|||||||
@@ -28,6 +28,18 @@ PanelWindow {
|
|||||||
bottom: true
|
bottom: true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Timer to update timestamps periodically
|
||||||
|
Timer {
|
||||||
|
id: timestampUpdateTimer
|
||||||
|
interval: 60000 // Update every minute
|
||||||
|
running: visible
|
||||||
|
repeat: true
|
||||||
|
onTriggered: {
|
||||||
|
// Force model refresh to update timestamps
|
||||||
|
groupedNotificationListView.model = NotificationGroupingService.groupedNotifications
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: 400
|
width: 400
|
||||||
height: 500
|
height: 500
|
||||||
@@ -156,11 +168,18 @@ PanelWindow {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
height: parent.height - 120
|
height: parent.height - 120
|
||||||
clip: true
|
clip: true
|
||||||
|
contentWidth: -1 // Fit to width
|
||||||
|
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||||
|
ScrollBar.vertical.policy: ScrollBar.AsNeeded
|
||||||
|
|
||||||
ListView {
|
ListView {
|
||||||
id: groupedNotificationListView
|
id: groupedNotificationListView
|
||||||
model: NotificationGroupingService.groupedNotifications
|
model: NotificationGroupingService.groupedNotifications
|
||||||
spacing: Theme.spacingM
|
spacing: Theme.spacingM
|
||||||
|
interactive: true
|
||||||
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
|
flickDeceleration: 1500
|
||||||
|
maximumFlickVelocity: 2000
|
||||||
|
|
||||||
delegate: Column {
|
delegate: Column {
|
||||||
width: groupedNotificationListView.width
|
width: groupedNotificationListView.width
|
||||||
@@ -178,124 +197,137 @@ PanelWindow {
|
|||||||
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) :
|
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) :
|
||||||
Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08)
|
Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08)
|
||||||
|
|
||||||
Row {
|
// App Icon
|
||||||
anchors.fill: parent
|
Rectangle {
|
||||||
anchors.margins: Theme.spacingM
|
width: 32
|
||||||
spacing: Theme.spacingM
|
height: 32
|
||||||
|
radius: width / 2
|
||||||
|
color: Theme.primaryContainer
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: Theme.spacingM
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
// App Icon
|
// Material icon fallback
|
||||||
Rectangle {
|
Loader {
|
||||||
width: 32
|
active: !model.appIcon || model.appIcon === ""
|
||||||
height: 32
|
anchors.fill: parent
|
||||||
radius: width / 2
|
sourceComponent: Text {
|
||||||
color: Theme.primaryContainer
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
|
|
||||||
// Material icon fallback
|
|
||||||
Loader {
|
|
||||||
active: !model.appIcon || model.appIcon === ""
|
|
||||||
anchors.fill: parent
|
|
||||||
sourceComponent: Text {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
text: "apps"
|
|
||||||
font.family: Theme.iconFont
|
|
||||||
font.pixelSize: 16
|
|
||||||
color: Theme.primaryText
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// App icon
|
|
||||||
Loader {
|
|
||||||
active: model.appIcon && model.appIcon !== ""
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
sourceComponent: IconImage {
|
text: "apps"
|
||||||
width: 24
|
font.family: Theme.iconFont
|
||||||
height: 24
|
font.pixelSize: 16
|
||||||
asynchronous: true
|
color: Theme.primaryText
|
||||||
source: {
|
|
||||||
if (!model.appIcon) return ""
|
|
||||||
if (model.appIcon.startsWith("file://") || model.appIcon.startsWith("/")) {
|
|
||||||
return model.appIcon
|
|
||||||
}
|
|
||||||
return Quickshell.iconPath(model.appIcon, "image-missing")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// App Name and Summary
|
// App icon
|
||||||
Column {
|
Loader {
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
active: model.appIcon && model.appIcon !== ""
|
||||||
width: parent.width - 100
|
anchors.centerIn: parent
|
||||||
spacing: 2
|
sourceComponent: IconImage {
|
||||||
|
width: 24
|
||||||
|
height: 24
|
||||||
|
asynchronous: true
|
||||||
|
source: {
|
||||||
|
if (!model.appIcon) return ""
|
||||||
|
if (model.appIcon.startsWith("file://") || model.appIcon.startsWith("/")) {
|
||||||
|
return model.appIcon
|
||||||
|
}
|
||||||
|
return Quickshell.iconPath(model.appIcon, "image-missing")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// App Name and Summary
|
||||||
|
Column {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: Theme.spacingM + 32 + Theme.spacingM // Icon + spacing
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 80 // Space for buttons
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: 2
|
||||||
|
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
Row {
|
Text {
|
||||||
width: parent.width
|
text: model.appName || "App"
|
||||||
spacing: Theme.spacingS
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Medium
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notification count badge
|
||||||
|
Rectangle {
|
||||||
|
width: Math.max(countText.width + 8, 20)
|
||||||
|
height: 20
|
||||||
|
radius: 10
|
||||||
|
color: Theme.primary
|
||||||
|
visible: model.totalCount > 1
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
text: model.appName || "App"
|
id: countText
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
anchors.centerIn: parent
|
||||||
color: Theme.surfaceText
|
text: model.totalCount.toString()
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.primaryText
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notification count badge
|
|
||||||
Rectangle {
|
|
||||||
width: Math.max(countText.width + 8, 20)
|
|
||||||
height: 20
|
|
||||||
radius: 10
|
|
||||||
color: Theme.primary
|
|
||||||
visible: model.totalCount > 1
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
|
|
||||||
Text {
|
|
||||||
id: countText
|
|
||||||
anchors.centerIn: parent
|
|
||||||
text: model.totalCount.toString()
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
color: Theme.primaryText
|
|
||||||
font.weight: Font.Medium
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Text {
|
|
||||||
text: model.latestNotification ?
|
|
||||||
(model.latestNotification.summary || model.latestNotification.body || "") : ""
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
|
||||||
width: parent.width
|
|
||||||
elide: Text.ElideRight
|
|
||||||
visible: text.length > 0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expand/Collapse Icon
|
Text {
|
||||||
Rectangle {
|
text: model.latestNotification ?
|
||||||
width: 32
|
(model.latestNotification.summary || model.latestNotification.body || "") : ""
|
||||||
height: 32
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
radius: 16
|
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
width: parent.width
|
||||||
color: groupHeaderArea.containsMouse ?
|
elide: Text.ElideRight
|
||||||
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) :
|
visible: text.length > 0
|
||||||
"transparent"
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expand/Collapse Icon
|
||||||
|
Rectangle {
|
||||||
|
id: expandCollapseButton
|
||||||
|
width: 32
|
||||||
|
height: 32
|
||||||
|
radius: 16
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 40 // More space from close button
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
color: expandButtonArea.containsMouse ?
|
||||||
|
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) :
|
||||||
|
"transparent"
|
||||||
|
|
||||||
|
Text {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: isExpanded ? "expand_less" : "expand_more"
|
||||||
|
font.family: Theme.iconFont
|
||||||
|
font.pixelSize: 20
|
||||||
|
color: expandButtonArea.containsMouse ? Theme.primary : Theme.surfaceText
|
||||||
|
|
||||||
Text {
|
Behavior on rotation {
|
||||||
anchors.centerIn: parent
|
NumberAnimation {
|
||||||
text: isExpanded ? "expand_less" : "expand_more"
|
duration: Theme.shortDuration
|
||||||
font.family: Theme.iconFont
|
easing.type: Theme.standardEasing
|
||||||
font.pixelSize: 20
|
|
||||||
color: groupHeaderArea.containsMouse ? Theme.primary : Theme.surfaceText
|
|
||||||
|
|
||||||
Behavior on rotation {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
easing.type: Theme.standardEasing
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: expandButtonArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
NotificationGroupingService.toggleGroupExpansion(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close group button
|
// Close group button
|
||||||
@@ -329,12 +361,28 @@ PanelWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Timestamp positioned under close button
|
||||||
|
Text {
|
||||||
|
id: timestampText
|
||||||
|
text: model.latestNotification ?
|
||||||
|
NotificationGroupingService.formatTimestamp(model.latestNotification.timestamp) : ""
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.5)
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.rightMargin: 6
|
||||||
|
anchors.bottomMargin: 6
|
||||||
|
visible: text.length > 0
|
||||||
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: groupHeaderArea
|
id: groupHeaderArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.rightMargin: 32
|
anchors.rightMargin: 76 // Exclude both expand and close button areas
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
preventStealing: false
|
||||||
|
propagateComposedEvents: true
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
NotificationGroupingService.toggleGroupExpansion(index)
|
NotificationGroupingService.toggleGroupExpansion(index)
|
||||||
@@ -350,13 +398,30 @@ PanelWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Expanded Notifications List
|
// Expanded Notifications List
|
||||||
Loader {
|
Item {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
active: isExpanded
|
height: isExpanded ? expandedContent.height : 0
|
||||||
|
clip: true
|
||||||
|
|
||||||
sourceComponent: Column {
|
Behavior on height {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.mediumDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: expandedContent
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
opacity: isExpanded ? 1.0 : 0.0
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: groupData.notifications
|
model: groupData.notifications
|
||||||
@@ -525,6 +590,13 @@ PanelWindow {
|
|||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
visible: text.length > 0
|
visible: text.length > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: NotificationGroupingService.formatTimestamp(model.timestamp)
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.5)
|
||||||
|
visible: text.length > 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -534,6 +606,8 @@ PanelWindow {
|
|||||||
anchors.rightMargin: 32
|
anchors.rightMargin: 32
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
preventStealing: false
|
||||||
|
propagateComposedEvents: true
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (model && root.handleNotificationClick) {
|
if (model && root.handleNotificationClick) {
|
||||||
|
|||||||
@@ -43,10 +43,114 @@ PanelWindow {
|
|||||||
|
|
||||||
opacity: root.showNotificationPopup ? 1.0 : 0.0
|
opacity: root.showNotificationPopup ? 1.0 : 0.0
|
||||||
|
|
||||||
|
// Transform for swipe animations
|
||||||
|
transform: Translate {
|
||||||
|
id: swipeTransform
|
||||||
|
x: 0
|
||||||
|
}
|
||||||
|
|
||||||
Behavior on opacity {
|
Behavior on opacity {
|
||||||
NumberAnimation { duration: 200; easing.type: Easing.OutQuad }
|
NumberAnimation { duration: 200; easing.type: Easing.OutQuad }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Drag area for swipe gestures
|
||||||
|
DragHandler {
|
||||||
|
id: dragHandler
|
||||||
|
target: null // We'll handle the transform manually
|
||||||
|
acceptedDevices: PointerDevice.TouchScreen | PointerDevice.Mouse
|
||||||
|
|
||||||
|
property real startX: 0
|
||||||
|
property real currentDelta: 0
|
||||||
|
property bool isDismissing: false
|
||||||
|
|
||||||
|
onActiveChanged: {
|
||||||
|
if (active) {
|
||||||
|
startX = centroid.position.x
|
||||||
|
currentDelta = 0
|
||||||
|
isDismissing = false
|
||||||
|
} else {
|
||||||
|
// Handle end of drag
|
||||||
|
let deltaX = centroid.position.x - startX
|
||||||
|
|
||||||
|
if (Math.abs(deltaX) > 80) { // Threshold for swipe action
|
||||||
|
if (deltaX > 0) {
|
||||||
|
// Swipe right - open notification history
|
||||||
|
swipeOpenHistory()
|
||||||
|
} else {
|
||||||
|
// Swipe left - dismiss notification
|
||||||
|
swipeDismiss()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Snap back to original position
|
||||||
|
snapBack()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onCentroidChanged: {
|
||||||
|
if (active) {
|
||||||
|
let deltaX = centroid.position.x - startX
|
||||||
|
currentDelta = deltaX
|
||||||
|
|
||||||
|
// Limit swipe distance and add resistance
|
||||||
|
let maxDistance = 120
|
||||||
|
let resistance = 0.6
|
||||||
|
|
||||||
|
if (Math.abs(deltaX) > maxDistance) {
|
||||||
|
deltaX = deltaX > 0 ? maxDistance : -maxDistance
|
||||||
|
}
|
||||||
|
|
||||||
|
swipeTransform.x = deltaX * resistance
|
||||||
|
|
||||||
|
// Visual feedback - reduce opacity when swiping left (dismiss)
|
||||||
|
if (deltaX < 0) {
|
||||||
|
popupContainer.opacity = Math.max(0.3, 1.0 - Math.abs(deltaX) / 150)
|
||||||
|
} else {
|
||||||
|
popupContainer.opacity = Math.max(0.7, 1.0 - Math.abs(deltaX) / 200)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function swipeOpenHistory() {
|
||||||
|
// Animate to the right and open history
|
||||||
|
swipeAnimation.to = 400
|
||||||
|
swipeAnimation.onFinished = function() {
|
||||||
|
root.notificationHistoryVisible = true
|
||||||
|
Utils.hideNotificationPopup()
|
||||||
|
snapBack()
|
||||||
|
}
|
||||||
|
swipeAnimation.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
function swipeDismiss() {
|
||||||
|
// Animate to the left and dismiss
|
||||||
|
swipeAnimation.to = -400
|
||||||
|
swipeAnimation.onFinished = function() {
|
||||||
|
Utils.hideNotificationPopup()
|
||||||
|
snapBack()
|
||||||
|
}
|
||||||
|
swipeAnimation.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
function snapBack() {
|
||||||
|
swipeAnimation.to = 0
|
||||||
|
swipeAnimation.onFinished = function() {
|
||||||
|
popupContainer.opacity = Qt.binding(() => root.showNotificationPopup ? 1.0 : 0.0)
|
||||||
|
}
|
||||||
|
swipeAnimation.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swipe animation
|
||||||
|
NumberAnimation {
|
||||||
|
id: swipeAnimation
|
||||||
|
target: swipeTransform
|
||||||
|
property: "x"
|
||||||
|
duration: 200
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tap area for notification interaction
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.rightMargin: 36 // Don't overlap with close button
|
anchors.rightMargin: 36 // Don't overlap with close button
|
||||||
@@ -58,15 +162,9 @@ PanelWindow {
|
|||||||
console.log("Popup clicked!")
|
console.log("Popup clicked!")
|
||||||
if (root.activeNotification) {
|
if (root.activeNotification) {
|
||||||
root.handleNotificationClick(root.activeNotification)
|
root.handleNotificationClick(root.activeNotification)
|
||||||
// Remove notification from history entirely
|
// Don't remove from history - just hide popup
|
||||||
for (let i = 0; i < notificationHistory.count; i++) {
|
|
||||||
if (notificationHistory.get(i).id === root.activeNotification.id) {
|
|
||||||
notificationHistory.remove(i)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Always hide popup after click
|
// Hide popup but keep in history
|
||||||
Utils.hideNotificationPopup()
|
Utils.hideNotificationPopup()
|
||||||
mouse.accepted = true // Prevent event propagation
|
mouse.accepted = true // Prevent event propagation
|
||||||
}
|
}
|
||||||
@@ -110,11 +208,77 @@ PanelWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Small dismiss button - bottom right corner
|
||||||
|
Rectangle {
|
||||||
|
width: 60
|
||||||
|
height: 18
|
||||||
|
radius: 9
|
||||||
|
color: dismissButtonArea.containsMouse ?
|
||||||
|
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) :
|
||||||
|
Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.1)
|
||||||
|
border.color: dismissButtonArea.containsMouse ?
|
||||||
|
Theme.primary :
|
||||||
|
Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.5)
|
||||||
|
border.width: 1
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.rightMargin: 12
|
||||||
|
anchors.bottomMargin: 10
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: 4
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "archive"
|
||||||
|
font.family: Theme.iconFont
|
||||||
|
font.pixelSize: 10
|
||||||
|
color: dismissButtonArea.containsMouse ? Theme.primary : Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "Dismiss"
|
||||||
|
font.pixelSize: 10
|
||||||
|
color: dismissButtonArea.containsMouse ? Theme.primary : Theme.surfaceText
|
||||||
|
font.weight: Font.Medium
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: dismissButtonArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
// Just hide the popup, keep in history
|
||||||
|
Utils.hideNotificationPopup()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on border.color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Content layout
|
// Content layout
|
||||||
Row {
|
Row {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: 12
|
anchors.margins: 12
|
||||||
anchors.rightMargin: 32
|
anchors.rightMargin: 32
|
||||||
|
anchors.bottomMargin: 6 // Reduced bottom margin to account for dismiss button
|
||||||
spacing: 12
|
spacing: 12
|
||||||
|
|
||||||
// Notification icon based on EXAMPLE NotificationAppIcon pattern
|
// Notification icon based on EXAMPLE NotificationAppIcon pattern
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
import QtQuick 6.5
|
|
||||||
import QtQuick.Controls 6.5
|
|
||||||
|
|
||||||
TopBar 1.0 TopBar/TopBar.qml
|
TopBar 1.0 TopBar/TopBar.qml
|
||||||
TrayMenuPopup 1.0 TrayMenuPopup.qml
|
TrayMenuPopup 1.0 TrayMenuPopup.qml
|
||||||
NotificationPopup 1.0 NotificationPopup.qml
|
NotificationPopup 1.0 NotificationPopup.qml
|
||||||
|
|||||||
Reference in New Issue
Block a user