mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-10-14 23:34:01 -04:00
deprecation notice to plugin compatibility shims - When qml-common became a thing, we left qs.Widgets shims in place for plugin compatibility - It's annoying to need to maintain them when qml-common has any change, add a warning so we can safely deprecate them in the future - One shot rename the common stuff to D to make it shorter and nicer to write
328 lines
14 KiB
QML
328 lines
14 KiB
QML
pragma ComponentBehavior: Bound
|
|
|
|
import QtQuick
|
|
import Quickshell.Widgets
|
|
import qs.Common
|
|
import qs.Modules.Notifications as Notifications
|
|
import qs.Services
|
|
import qs.DCommon.Widgets
|
|
import qs.DCommon.Session
|
|
|
|
Item {
|
|
id: root
|
|
|
|
property var instanceData: null
|
|
property var lockHost: null
|
|
readonly property var cfg: instanceData?.config ?? ({})
|
|
readonly property int notificationMode: cfg.mode ?? 1
|
|
readonly property bool resizable: true
|
|
readonly property real minWidth: Theme.listItemTwoLineHeight * 2
|
|
readonly property real minHeight: Theme.buttonHeightXS
|
|
readonly property real defaultWidth: LockMetrics.notificationCardWidth
|
|
readonly property real defaultHeight: fullContent ? LockMetrics.notificationMaxHeight : Theme.buttonHeightS
|
|
|
|
readonly property var notifications: NotificationService.groupedNotifications
|
|
readonly property int totalCount: {
|
|
let count = 0;
|
|
for (const group of notifications)
|
|
count += group.count || 0;
|
|
return count;
|
|
}
|
|
readonly property bool hasNotifications: totalCount > 0
|
|
readonly property var appNameGroups: {
|
|
const groups = {};
|
|
for (const group of notifications) {
|
|
const appName = (group.appName || "Unknown").toLowerCase();
|
|
if (!groups[appName]) {
|
|
groups[appName] = {
|
|
appName: group.appName || I18n.tr("Unknown"),
|
|
count: 0,
|
|
latestNotification: group.latestNotification
|
|
};
|
|
}
|
|
groups[appName].count += group.count || 0;
|
|
if (group.latestNotification && (!groups[appName].latestNotification || group.latestNotification.time > groups[appName].latestNotification.time))
|
|
groups[appName].latestNotification = group.latestNotification;
|
|
}
|
|
return Object.values(groups).sort((a, b) => b.count - a.count);
|
|
}
|
|
|
|
readonly property bool editPlaceholder: !hasNotifications && (lockHost?.demoMode ?? false)
|
|
readonly property bool chips: notificationMode === 2 && !editPlaceholder
|
|
readonly property bool fullContent: notificationMode === 3 && !editPlaceholder
|
|
readonly property var visibleGroups: appNameGroups.slice(0, LockMetrics.notificationLimit)
|
|
|
|
implicitWidth: contentLoader.implicitWidth
|
|
implicitHeight: hasNotifications || editPlaceholder ? contentLoader.implicitHeight : 0
|
|
clip: true
|
|
|
|
Loader {
|
|
id: contentLoader
|
|
anchors.fill: parent
|
|
active: root.hasNotifications || root.editPlaceholder
|
|
sourceComponent: {
|
|
if (root.chips)
|
|
return chipRowComponent;
|
|
if (root.fullContent)
|
|
return notificationListComponent;
|
|
return countOnlyComponent;
|
|
}
|
|
}
|
|
|
|
component Chip: Rectangle {
|
|
default property alias content: chipContent.data
|
|
readonly property real contentWidth: chipContent.implicitWidth
|
|
height: Theme.buttonHeightXS
|
|
width: contentWidth + Theme.spacingM * 2
|
|
radius: Theme.cornerRadiusM
|
|
color: Theme.notificationFloatingSurface
|
|
border.width: Theme.layerOutlineWidth
|
|
border.color: Theme.outlineVariant
|
|
|
|
Row {
|
|
id: chipContent
|
|
anchors.centerIn: parent
|
|
spacing: Theme.spacingXS
|
|
}
|
|
}
|
|
|
|
Component {
|
|
id: chipRowComponent
|
|
|
|
Item {
|
|
id: chipHost
|
|
|
|
property int fitCount: root.visibleGroups.length
|
|
// One width for every app chip keeps the row symmetric about the centre whatever the label lengths.
|
|
property real chipWidth: 0
|
|
property bool moreFits: true
|
|
readonly property int hiddenCount: root.appNameGroups.length - fitCount
|
|
implicitWidth: chipRow.implicitWidth
|
|
implicitHeight: chipRow.implicitHeight
|
|
|
|
function measure() {
|
|
let widest = 0;
|
|
for (let i = 0; i < chipRepeater.count; i++)
|
|
widest = Math.max(widest, chipRepeater.itemAt(i)?.contentWidth ?? 0);
|
|
chipWidth = widest + Theme.spacingM * 2;
|
|
const total = count => count * chipWidth + Math.max(0, count - 1) * chipRow.spacing;
|
|
const overflow = count => root.appNameGroups.length > count ? moreMetrics.width + Theme.spacingM * 2 + chipRow.spacing : 0;
|
|
let count = chipRepeater.count;
|
|
while (count > 1 && total(count) + overflow(count) > width)
|
|
count--;
|
|
fitCount = count;
|
|
moreFits = total(count) + overflow(count) <= width;
|
|
}
|
|
|
|
onWidthChanged: Qt.callLater(measure)
|
|
Component.onCompleted: measure()
|
|
|
|
TextMetrics {
|
|
id: moreMetrics
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
font.weight: Theme.fontWeightMedium
|
|
text: I18n.tr("+ %1 more", "lock screen notification list overflow, %1 is a count of hidden apps").arg(root.appNameGroups.length)
|
|
onWidthChanged: Qt.callLater(chipHost.measure)
|
|
}
|
|
|
|
Row {
|
|
id: chipRow
|
|
anchors.centerIn: parent
|
|
spacing: Theme.spacingS
|
|
|
|
Repeater {
|
|
id: chipRepeater
|
|
model: root.visibleGroups
|
|
onItemAdded: Qt.callLater(chipHost.measure)
|
|
onItemRemoved: Qt.callLater(chipHost.measure)
|
|
|
|
Chip {
|
|
id: chip
|
|
|
|
required property var modelData
|
|
required property int index
|
|
readonly property string appIcon: NotificationService.notificationAppIcon(modelData.latestNotification?.appIcon || "", modelData.latestNotification?.desktopEntry || "")
|
|
|
|
visible: index < chipHost.fitCount
|
|
width: chipHost.chipWidth
|
|
Accessible.name: modelData.appName
|
|
onContentWidthChanged: Qt.callLater(chipHost.measure)
|
|
|
|
Item {
|
|
width: Theme.iconSizeSmall
|
|
height: width
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
|
|
Image {
|
|
id: appImage
|
|
anchors.fill: parent
|
|
asynchronous: true
|
|
smooth: true
|
|
fillMode: Image.PreserveAspectFit
|
|
sourceSize: Qt.size(width * 2, height * 2)
|
|
source: NotificationService.notificationImageSource("", chip.appIcon)
|
|
visible: status === Image.Ready
|
|
}
|
|
|
|
AppIconRenderer {
|
|
anchors.fill: parent
|
|
visible: !appImage.visible
|
|
iconValue: NotificationService.notificationFallbackIcon("", chip.appIcon)
|
|
iconSize: Theme.iconSizeSmall
|
|
iconColor: Theme.onSurfaceVariant
|
|
colorOverride: "transparent"
|
|
fallbackText: (chip.modelData.appName || "?").charAt(0).toUpperCase()
|
|
fallbackRadius: Theme.fullRadius(width, height)
|
|
fallbackBackgroundColor: Theme.secondaryContainer
|
|
fallbackTextColor: Theme.onSecondaryContainer
|
|
}
|
|
}
|
|
|
|
StyledText {
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
text: chip.modelData.appName
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
font.weight: Theme.fontWeightMedium
|
|
color: Theme.onSurface
|
|
}
|
|
|
|
Rectangle {
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
visible: chip.modelData.count > 1
|
|
width: Math.max(height, countText.implicitWidth + Theme.spacingXS * 2)
|
|
height: Theme.iconSizeSmall
|
|
radius: Theme.fullRadius(width, height)
|
|
color: Theme.primary
|
|
|
|
StyledText {
|
|
id: countText
|
|
anchors.centerIn: parent
|
|
text: chip.modelData.count
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
font.weight: Theme.fontWeightMedium
|
|
color: Theme.onPrimary
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Chip {
|
|
visible: chipHost.hiddenCount > 0 && chipHost.moreFits
|
|
|
|
StyledText {
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
text: I18n.tr("+ %1 more", "lock screen notification list overflow, %1 is a count of hidden apps").arg(chipHost.hiddenCount)
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
font.weight: Theme.fontWeightMedium
|
|
color: Theme.onSurfaceVariant
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Component {
|
|
id: countOnlyComponent
|
|
|
|
Item {
|
|
implicitWidth: countPill.width
|
|
implicitHeight: countPill.height
|
|
|
|
Rectangle {
|
|
id: countPill
|
|
anchors.centerIn: parent
|
|
width: countRow.implicitWidth + Theme.spacingL * 2
|
|
height: Theme.buttonHeightS
|
|
radius: Theme.fullRadius(width, height)
|
|
color: Theme.notificationFloatingSurface
|
|
border.width: Theme.layerOutlineWidth
|
|
border.color: Theme.outlineVariant
|
|
scale: Math.min(1, parent.width / width, parent.height / height)
|
|
Accessible.name: countLabel.text
|
|
|
|
Row {
|
|
id: countRow
|
|
anchors.centerIn: parent
|
|
spacing: Theme.spacingS
|
|
|
|
DIcon {
|
|
name: "notifications"
|
|
size: Theme.iconSizeSmall + Theme.spacingXXS
|
|
color: Theme.onSurfaceVariant
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
}
|
|
|
|
StyledText {
|
|
id: countLabel
|
|
text: root.editPlaceholder ? I18n.tr("Notifications") : root.totalCount === 1 ? I18n.tr("1 notification") : I18n.tr("%1 notifications").arg(root.totalCount)
|
|
font.pixelSize: Theme.fontSizeMedium
|
|
font.weight: Theme.fontWeightMedium
|
|
color: Theme.onSurface
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Component {
|
|
id: notificationListComponent
|
|
|
|
Item {
|
|
implicitWidth: LockMetrics.notificationCardWidth
|
|
implicitHeight: Math.min(notificationColumn.implicitHeight, LockMetrics.notificationMaxHeight)
|
|
|
|
ClippingRectangle {
|
|
anchors.centerIn: parent
|
|
width: parent.width
|
|
height: Math.min(notificationColumn.implicitHeight, parent.height)
|
|
radius: Theme.groupedListOuterRadius
|
|
color: "transparent"
|
|
|
|
DFlickable {
|
|
anchors.fill: parent
|
|
contentHeight: notificationColumn.implicitHeight
|
|
clip: true
|
|
|
|
Column {
|
|
id: notificationColumn
|
|
width: parent.width
|
|
spacing: Theme.groupedListGap
|
|
|
|
Repeater {
|
|
id: notificationRepeater
|
|
model: root.visibleGroups
|
|
|
|
Notifications.NotificationCard {
|
|
required property var modelData
|
|
required property int index
|
|
width: notificationColumn.width
|
|
notificationData: modelData.latestNotification
|
|
groupCount: modelData.count
|
|
interactive: false
|
|
headerOnly: false
|
|
showActions: false
|
|
showDismiss: false
|
|
showClose: false
|
|
showTime: !headerOnly
|
|
animateHeight: false
|
|
firstInGroup: index === 0
|
|
lastInGroup: index === notificationRepeater.count - 1
|
|
}
|
|
}
|
|
|
|
StyledText {
|
|
width: parent.width
|
|
visible: root.appNameGroups.length > LockMetrics.notificationLimit
|
|
text: I18n.tr("+ %1 more", "lock screen notification list overflow, %1 is a count of hidden apps").arg(root.appNameGroups.length - LockMetrics.notificationLimit)
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
color: Theme.lockScreenContentColor
|
|
horizontalAlignment: Text.AlignHCenter
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|