1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-24 21:42:51 -05:00

notifications: add compact mode, expansion in history, expansion in

popup
fixes #1282
This commit is contained in:
bbedward
2026-01-11 12:11:44 -05:00
parent 510ea5d2e4
commit 3c38e17472
7 changed files with 359 additions and 283 deletions

View File

@@ -18,17 +18,17 @@ Rectangle {
property int selectedNotificationIndex: -1
property bool keyboardNavigationActive: 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 badgeSize: compactMode ? 16 : 18
readonly property real actionButtonHeight: compactMode ? 20 : 24
readonly property real baseCardHeight: cardPadding * 2 + collapsedContentHeight
readonly property real collapsedContentHeight: iconSize + cardPadding
width: parent ? parent.width : 400
height: {
if (expanded) {
return expandedContent.height + 28;
}
const baseHeight = 116;
if (descriptionExpanded) {
return baseHeight + descriptionText.contentHeight - (descriptionText.font.pixelSize * 1.2 * 2);
}
return baseHeight;
}
height: expanded ? (expandedContent.height + cardPadding * 2) : (baseCardHeight + collapsedContent.extraHeight)
radius: Theme.cornerRadius
Behavior on border.color {
@@ -97,24 +97,28 @@ Rectangle {
Item {
id: collapsedContent
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: 12
anchors.leftMargin: 16
anchors.rightMargin: 56
height: 92
anchors.topMargin: cardPadding
anchors.leftMargin: Theme.spacingL
anchors.rightMargin: Theme.spacingL + (compactMode ? 32 : 40)
height: collapsedContentHeight + extraHeight
visible: !expanded
DankCircularImage {
id: iconContainer
readonly property bool hasNotificationImage: notificationGroup?.latestNotification?.image && notificationGroup.latestNotification.image !== ""
width: 63
height: 63
width: iconSize
height: iconSize
anchors.left: parent.left
anchors.top: parent.top
anchors.topMargin: 14
anchors.verticalCenter: parent.verticalCenter
imageSource: {
if (hasNotificationImage)
@@ -147,9 +151,9 @@ Rectangle {
}
Rectangle {
width: 18
height: 18
radius: 9
width: badgeSize
height: badgeSize
radius: badgeSize / 2
color: Theme.primary
anchors.top: parent.top
anchors.right: parent.right
@@ -161,7 +165,7 @@ Rectangle {
anchors.centerIn: parent
text: (notificationGroup?.count || 0) > 99 ? "99+" : (notificationGroup?.count || 0).toString()
color: Theme.primaryText
font.pixelSize: 9
font.pixelSize: compactMode ? 8 : 9
font.weight: Font.Bold
}
}
@@ -171,87 +175,78 @@ Rectangle {
id: textContainer
anchors.left: iconContainer.right
anchors.leftMargin: 12
anchors.leftMargin: Theme.spacingM
anchors.right: parent.right
anchors.rightMargin: 0
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.bottomMargin: 8
anchors.bottomMargin: contentSpacing
color: "transparent"
Item {
Column {
width: parent.width
height: parent.height
anchors.top: parent.top
anchors.topMargin: -2
anchors.verticalCenter: parent.verticalCenter
spacing: compactMode ? 1 : 2
Column {
StyledText {
width: parent.width
spacing: 2
text: {
const timeStr = (notificationGroup && notificationGroup.latestNotification && notificationGroup.latestNotification.timeStr) || "";
const appName = (notificationGroup && notificationGroup.appName) || "";
return timeStr.length > 0 ? `${appName} ${timeStr}` : appName;
}
color: Theme.surfaceVariantText
font.pixelSize: Theme.fontSizeSmall
font.weight: Font.Medium
elide: Text.ElideRight
maximumLineCount: 1
}
StyledText {
width: parent.width
text: {
const timeStr = (notificationGroup && notificationGroup.latestNotification && notificationGroup.latestNotification.timeStr) || "";
const appName = (notificationGroup && notificationGroup.appName) || "";
return timeStr.length > 0 ? `${appName} ${timeStr}` : appName;
StyledText {
text: (notificationGroup && notificationGroup.latestNotification && notificationGroup.latestNotification.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 string fullText: (notificationGroup && notificationGroup.latestNotification && notificationGroup.latestNotification.htmlBody) || ""
property bool hasMoreText: truncated
text: fullText
color: Theme.surfaceVariantText
font.pixelSize: Theme.fontSizeSmall
width: parent.width
elide: 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 = (notificationGroup && notificationGroup.latestNotification && notificationGroup.latestNotification.notification && notificationGroup.latestNotification.notification.id) ? (notificationGroup.latestNotification.notification.id + "_desc") : "";
NotificationService.toggleMessageExpansion(messageId);
}
}
color: Theme.surfaceVariantText
font.pixelSize: Theme.fontSizeSmall
font.weight: Font.Medium
elide: Text.ElideRight
maximumLineCount: 1
}
StyledText {
text: (notificationGroup && notificationGroup.latestNotification && notificationGroup.latestNotification.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 string fullText: (notificationGroup && notificationGroup.latestNotification && notificationGroup.latestNotification.htmlBody) || ""
property bool hasMoreText: truncated
text: fullText
color: Theme.surfaceVariantText
font.pixelSize: Theme.fontSizeSmall
width: parent.width
elide: Text.ElideRight
maximumLineCount: descriptionExpanded ? -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 = (notificationGroup && notificationGroup.latestNotification && notificationGroup.latestNotification.notification && notificationGroup.latestNotification.notification.id) ? (notificationGroup.latestNotification.notification.id + "_desc") : "";
NotificationService.toggleMessageExpansion(messageId);
}
}
propagateComposedEvents: true
onPressed: mouse => {
if (parent.hoveredLink) {
mouse.accepted = false;
}
}
onReleased: mouse => {
if (parent.hoveredLink) {
mouse.accepted = false;
}
}
propagateComposedEvents: true
onPressed: mouse => {
if (parent.hoveredLink)
mouse.accepted = false;
}
onReleased: mouse => {
if (parent.hoveredLink)
mouse.accepted = false;
}
}
}
@@ -265,21 +260,20 @@ Rectangle {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 14
anchors.bottomMargin: 14
anchors.topMargin: cardPadding
anchors.leftMargin: Theme.spacingL
anchors.rightMargin: Theme.spacingL
spacing: -1
spacing: compactMode ? Theme.spacingXS : Theme.spacingS
visible: expanded
Item {
width: parent.width
height: 40
height: compactMode ? 32 : 40
Row {
anchors.left: parent.left
anchors.right: parent.right
anchors.rightMargin: 56
anchors.rightMargin: Theme.spacingL + (compactMode ? 32 : 40)
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingS
@@ -294,9 +288,9 @@ Rectangle {
}
Rectangle {
width: 18
height: 18
radius: 9
width: badgeSize
height: badgeSize
radius: badgeSize / 2
color: Theme.primary
visible: (notificationGroup?.count || 0) > 1
anchors.verticalCenter: parent.verticalCenter
@@ -305,7 +299,7 @@ Rectangle {
anchors.centerIn: parent
text: (notificationGroup?.count || 0) > 99 ? "99+" : (notificationGroup?.count || 0).toString()
color: Theme.primaryText
font.pixelSize: 9
font.pixelSize: compactMode ? 8 : 9
font.weight: Font.Bold
}
}
@@ -314,7 +308,7 @@ Rectangle {
Column {
width: parent.width
spacing: 16
spacing: compactMode ? Theme.spacingS : Theme.spacingL
Repeater {
id: notificationRepeater
@@ -328,23 +322,23 @@ Rectangle {
required property int index
readonly property bool messageExpanded: NotificationService.expandedMessages[modelData?.notification?.id] || false
readonly property bool isSelected: root.selectedNotificationIndex === index
readonly property real expandedIconSize: compactMode ? 40 : 48
readonly property real expandedItemPadding: compactMode ? Theme.spacingS : Theme.spacingM
readonly property real expandedBaseHeight: expandedItemPadding * 2 + expandedIconSize + actionButtonHeight + contentSpacing * 2
width: parent.width
height: {
const baseHeight = 120;
if (messageExpanded) {
const twoLineHeight = bodyText.font.pixelSize * 1.2 * 2;
if (bodyText.implicitHeight > twoLineHeight + 2) {
const extraHeight = bodyText.implicitHeight - twoLineHeight;
return baseHeight + extraHeight;
}
}
return baseHeight;
if (!messageExpanded)
return expandedBaseHeight;
const twoLineHeight = bodyText.font.pixelSize * 1.2 * 2;
if (bodyText.implicitHeight > twoLineHeight + 2)
return expandedBaseHeight + bodyText.implicitHeight - twoLineHeight;
return expandedBaseHeight;
}
radius: Theme.cornerRadius
color: isSelected ? Theme.primaryPressed : Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
border.color: isSelected ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.4) : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.05)
border.width: isSelected ? 1 : 1
border.width: 1
Behavior on border.color {
ColorAnimation {
@@ -359,19 +353,19 @@ Rectangle {
Item {
anchors.fill: parent
anchors.margins: 12
anchors.bottomMargin: 8
anchors.margins: compactMode ? Theme.spacingS : Theme.spacingM
anchors.bottomMargin: contentSpacing
DankCircularImage {
id: messageIcon
readonly property bool hasNotificationImage: modelData?.image && modelData.image !== ""
width: 48
height: 48
width: expandedIconSize
height: expandedIconSize
anchors.left: parent.left
anchors.top: parent.top
anchors.topMargin: 32
anchors.topMargin: compactMode ? Theme.spacingM : Theme.spacingXL
imageSource: {
if (hasNotificationImage)
@@ -397,9 +391,9 @@ Rectangle {
Item {
anchors.left: messageIcon.right
anchors.leftMargin: 12
anchors.leftMargin: Theme.spacingM
anchors.right: parent.right
anchors.rightMargin: 12
anchors.rightMargin: Theme.spacingM
anchors.top: parent.top
anchors.bottom: parent.bottom
@@ -408,8 +402,8 @@ Rectangle {
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: buttonArea.top
anchors.bottomMargin: 4
spacing: 2
anchors.bottomMargin: contentSpacing
spacing: compactMode ? 1 : 2
StyledText {
width: parent.width
@@ -477,12 +471,12 @@ Rectangle {
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
height: 30
height: actionButtonHeight + contentSpacing
Row {
anchors.right: parent.right
anchors.bottom: parent.bottom
spacing: 8
spacing: contentSpacing
Repeater {
model: modelData?.actions || []
@@ -490,18 +484,17 @@ Rectangle {
Rectangle {
property bool isHovered: false
width: Math.max(actionText.implicitWidth + 12, 50)
height: 24
radius: 4
width: Math.max(actionText.implicitWidth + Theme.spacingM, compactMode ? 40 : 50)
height: actionButtonHeight
radius: Theme.spacingXS
color: isHovered ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.1) : "transparent"
StyledText {
id: actionText
text: {
const baseText = modelData.text || "View";
if (keyboardNavigationActive && (isGroupSelected || selectedNotificationIndex >= 0)) {
if (keyboardNavigationActive && (isGroupSelected || selectedNotificationIndex >= 0))
return `${baseText} (${index + 1})`;
}
return baseText;
}
color: parent.isHovered ? Theme.primary : Theme.surfaceVariantText
@@ -518,9 +511,8 @@ Rectangle {
onEntered: parent.isHovered = true
onExited: parent.isHovered = false
onClicked: {
if (modelData && modelData.invoke) {
if (modelData && modelData.invoke)
modelData.invoke();
}
}
}
}
@@ -529,9 +521,9 @@ Rectangle {
Rectangle {
property bool isHovered: false
width: Math.max(clearText.implicitWidth + 12, 50)
height: 24
radius: 4
width: Math.max(clearText.implicitWidth + Theme.spacingM, compactMode ? 40 : 50)
height: actionButtonHeight
radius: Theme.spacingXS
color: isHovered ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.1) : "transparent"
StyledText {
@@ -564,10 +556,10 @@ Rectangle {
Row {
visible: !expanded
anchors.right: clearButton.left
anchors.rightMargin: 8
anchors.rightMargin: contentSpacing
anchors.bottom: parent.bottom
anchors.bottomMargin: 8
spacing: 8
anchors.bottomMargin: contentSpacing
spacing: contentSpacing
Repeater {
model: notificationGroup?.latestNotification?.actions || []
@@ -575,9 +567,9 @@ Rectangle {
Rectangle {
property bool isHovered: false
width: Math.max(actionText.implicitWidth + 12, 50)
height: 24
radius: 4
width: Math.max(actionText.implicitWidth + Theme.spacingM, compactMode ? 40 : 50)
height: actionButtonHeight
radius: Theme.spacingXS
color: isHovered ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.1) : "transparent"
StyledText {
@@ -619,12 +611,12 @@ Rectangle {
visible: !expanded
anchors.right: parent.right
anchors.rightMargin: 16
anchors.rightMargin: Theme.spacingL
anchors.bottom: parent.bottom
anchors.bottomMargin: 8
width: Math.max(clearText.implicitWidth + 12, 50)
height: 24
radius: 4
anchors.bottomMargin: contentSpacing
width: Math.max(clearText.implicitWidth + Theme.spacingM, compactMode ? 40 : 50)
height: actionButtonHeight
radius: Theme.spacingXS
color: isHovered ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.1) : "transparent"
StyledText {
@@ -660,18 +652,18 @@ Rectangle {
id: fixedControls
anchors.top: parent.top
anchors.right: parent.right
anchors.topMargin: 12
anchors.rightMargin: 16
width: 60
height: 28
anchors.topMargin: cardPadding
anchors.rightMargin: Theme.spacingL
width: compactMode ? 52 : 60
height: compactMode ? 24 : 28
DankActionButton {
anchors.left: parent.left
anchors.top: parent.top
visible: (notificationGroup?.count || 0) > 1
iconName: expanded ? "expand_less" : "expand_more"
iconSize: 18
buttonSize: 28
iconSize: compactMode ? 16 : 18
buttonSize: compactMode ? 24 : 28
onClicked: {
root.userInitiatedExpansion = true;
NotificationService.toggleGroupExpansion(notificationGroup?.key || "");
@@ -682,8 +674,8 @@ Rectangle {
anchors.right: parent.right
anchors.top: parent.top
iconName: "close"
iconSize: 18
buttonSize: 28
iconSize: compactMode ? 16 : 18
buttonSize: compactMode ? 24 : 28
onClicked: NotificationService.dismissGroup(notificationGroup?.key || "")
}
}