1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-24 13:32:50 -05:00
Files
DankMaterialShell/quickshell/Modules/Notifications/Center/HistoryNotificationCard.qml
2026-01-11 14:35:34 -05:00

222 lines
8.0 KiB
QML

import QtQuick
import Quickshell
import qs.Common
import qs.Services
import qs.Widgets
Rectangle {
id: root
required property var historyItem
property bool isSelected: false
property bool keyboardNavigationActive: false
property bool descriptionExpanded: NotificationService.expandedMessages[historyItem?.id ? (historyItem.id + "_hist") : ""] || false
readonly property bool compactMode: SettingsData.notificationCompactMode
readonly property real cardPadding: compactMode ? Theme.spacingS : Theme.spacingM
readonly property real iconSize: compactMode ? 48 : 63
readonly property real contentSpacing: compactMode ? Theme.spacingXS : Theme.spacingS
readonly property real collapsedContentHeight: iconSize + cardPadding
readonly property real baseCardHeight: cardPadding * 2 + collapsedContentHeight
width: parent ? parent.width : 400
height: baseCardHeight + contentItem.extraHeight
radius: Theme.cornerRadius
clip: true
color: {
if (isSelected && keyboardNavigationActive)
return Theme.primaryPressed;
return Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency);
}
border.color: {
if (isSelected && keyboardNavigationActive)
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.5);
if (historyItem.urgency === 2)
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.3);
return Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.05);
}
border.width: {
if (isSelected && keyboardNavigationActive)
return 1.5;
if (historyItem.urgency === 2)
return 2;
return 1;
}
Behavior on border.color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
Rectangle {
anchors.fill: parent
radius: parent.radius
visible: historyItem.urgency === 2
gradient: Gradient {
orientation: Gradient.Horizontal
GradientStop {
position: 0.0
color: Theme.primary
}
GradientStop {
position: 0.02
color: Theme.primary
}
GradientStop {
position: 0.021
color: "transparent"
}
}
}
Item {
id: contentItem
readonly property real expandedTextHeight: descriptionText.contentHeight
readonly property real twoLineHeight: descriptionText.font.pixelSize * 1.2 * 2
readonly property real extraHeight: (descriptionExpanded && expandedTextHeight > twoLineHeight + 2) ? (expandedTextHeight - twoLineHeight) : 0
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: cardPadding
anchors.leftMargin: Theme.spacingL
anchors.rightMargin: Theme.spacingL + (compactMode ? 32 : 40)
height: collapsedContentHeight + extraHeight
DankCircularImage {
id: iconContainer
readonly property bool hasNotificationImage: historyItem.image && historyItem.image !== ""
width: iconSize
height: iconSize
anchors.left: parent.left
anchors.top: parent.top
imageSource: {
if (hasNotificationImage)
return historyItem.image;
if (historyItem.appIcon) {
const appIcon = historyItem.appIcon;
if (appIcon.startsWith("file://") || appIcon.startsWith("http://") || appIcon.startsWith("https://"))
return appIcon;
return Quickshell.iconPath(appIcon, true);
}
return "";
}
hasImage: hasNotificationImage
fallbackIcon: ""
fallbackText: {
const appName = historyItem.appName || "?";
return appName.charAt(0).toUpperCase();
}
Rectangle {
anchors.fill: parent
anchors.margins: -2
radius: width / 2
color: "transparent"
border.color: root.color
border.width: 5
visible: parent.hasImage
antialiasing: true
}
}
Rectangle {
anchors.left: iconContainer.right
anchors.leftMargin: Theme.spacingM
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.bottomMargin: contentSpacing
color: "transparent"
Column {
width: parent.width
anchors.top: parent.top
spacing: compactMode ? 1 : 2
StyledText {
width: parent.width
text: {
const timeStr = NotificationService.formatHistoryTime(historyItem.timestamp);
const appName = historyItem.appName || "";
return timeStr.length > 0 ? `${appName} ${timeStr}` : appName;
}
color: Theme.surfaceVariantText
font.pixelSize: Theme.fontSizeSmall
font.weight: Font.Medium
elide: Text.ElideRight
maximumLineCount: 1
visible: text.length > 0
}
StyledText {
text: historyItem.summary || ""
color: Theme.surfaceText
font.pixelSize: Theme.fontSizeMedium
font.weight: Font.Medium
width: parent.width
elide: Text.ElideRight
maximumLineCount: 1
visible: text.length > 0
}
StyledText {
id: descriptionText
property bool hasMoreText: truncated
text: historyItem.htmlBody || historyItem.body || ""
color: Theme.surfaceVariantText
font.pixelSize: Theme.fontSizeSmall
width: parent.width
elide: descriptionExpanded ? Text.ElideNone : Text.ElideRight
maximumLineCount: descriptionExpanded ? -1 : (compactMode ? 1 : 2)
wrapMode: Text.WordWrap
visible: text.length > 0
linkColor: Theme.primary
onLinkActivated: link => Qt.openUrlExternally(link)
MouseArea {
anchors.fill: parent
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : (parent.hasMoreText || descriptionExpanded) ? Qt.PointingHandCursor : Qt.ArrowCursor
onClicked: mouse => {
if (!parent.hoveredLink && (parent.hasMoreText || descriptionExpanded)) {
const messageId = historyItem?.id ? (historyItem.id + "_hist") : "";
NotificationService.toggleMessageExpansion(messageId);
}
}
propagateComposedEvents: true
onPressed: mouse => {
if (parent.hoveredLink)
mouse.accepted = false;
}
onReleased: mouse => {
if (parent.hoveredLink)
mouse.accepted = false;
}
}
}
}
}
}
DankActionButton {
anchors.top: parent.top
anchors.right: parent.right
anchors.topMargin: cardPadding
anchors.rightMargin: Theme.spacingL
iconName: "close"
iconSize: compactMode ? 16 : 18
buttonSize: compactMode ? 24 : 28
onClicked: NotificationService.removeFromHistory(historyItem.id)
}
}