mirror of
https://github.com/kossLAN/dots.git
synced 2025-11-04 22:49:50 -05:00
278 lines
12 KiB
QML
278 lines
12 KiB
QML
pragma Singleton
|
|
pragma ComponentBehavior: Bound
|
|
|
|
import QtQuick
|
|
import Qt5Compat.GraphicalEffects
|
|
import QtQuick.Layouts
|
|
import Quickshell
|
|
import Quickshell.Io
|
|
import Quickshell.Wayland
|
|
import Quickshell.Widgets
|
|
import "../widgets" as Widgets
|
|
import ".."
|
|
|
|
Singleton {
|
|
PersistentProperties {
|
|
id: persist
|
|
property bool notificationsOpen: false
|
|
}
|
|
|
|
IpcHandler {
|
|
id: ipc
|
|
target: "notifications"
|
|
|
|
function open(): void {
|
|
persist.notificationsOpen = true;
|
|
}
|
|
|
|
function close(): void {
|
|
persist.notificationsOpen = false;
|
|
}
|
|
|
|
function toggle(): void {
|
|
persist.notificationsOpen = !persist.notificationsOpen;
|
|
}
|
|
}
|
|
|
|
LazyLoader {
|
|
id: loader
|
|
activeAsync: persist.notificationsOpen
|
|
|
|
PanelWindow {
|
|
id: notificationPanel
|
|
color: "red"
|
|
implicitWidth: 500
|
|
exclusionMode: ExclusionMode.Ignore
|
|
// WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive
|
|
anchors {
|
|
top: true
|
|
right: true
|
|
bottom: true
|
|
}
|
|
|
|
ColumnLayout {
|
|
spacing: 10
|
|
|
|
anchors {
|
|
fill: parent
|
|
margins: 10
|
|
}
|
|
|
|
Text {
|
|
text: "Notifications: " + toastList.count
|
|
Layout.fillWidth: true
|
|
}
|
|
|
|
ListView {
|
|
id: toastList
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
clip: true
|
|
spacing: 5
|
|
|
|
model: ScriptModel {
|
|
values: {
|
|
const notifications = Notifications.notificationServer.trackedNotifications.values.concat();
|
|
|
|
const groupedByApp = notifications.reduce((groups, notification) => {
|
|
const appName = notification.appName;
|
|
|
|
if (!groups[appName]) {
|
|
groups[appName] = {
|
|
appName: appName,
|
|
summaryGroups: {}
|
|
};
|
|
}
|
|
|
|
const summary = notification.summary;
|
|
const image = notification.image;
|
|
|
|
if (!groups[appName].summaryGroups[summary]) {
|
|
groups[appName].summaryGroups[summary] = {
|
|
summary: summary,
|
|
image: image,
|
|
notifications: []
|
|
};
|
|
}
|
|
|
|
groups[appName].summaryGroups[summary].notifications.push(notification);
|
|
|
|
return groups;
|
|
}, {});
|
|
|
|
return Object.values(groupedByApp).map(appGroup => {
|
|
return {
|
|
appName: appGroup.appName,
|
|
summaryGroups: Object.values(appGroup.summaryGroups)
|
|
};
|
|
});
|
|
}
|
|
}
|
|
|
|
delegate: Item {
|
|
id: toastWrapper
|
|
required property var modelData
|
|
width: ListView.view.width
|
|
height: toastContent.height
|
|
|
|
Item {
|
|
id: toastContent
|
|
width: parent.width
|
|
height: contentColumn.implicitHeight
|
|
anchors.centerIn: parent
|
|
|
|
ColumnLayout {
|
|
id: contentColumn
|
|
spacing: 2
|
|
|
|
anchors {
|
|
fill: parent
|
|
margins: 0
|
|
}
|
|
|
|
// Notification content
|
|
Repeater {
|
|
model: toastWrapper.modelData.summaryGroups
|
|
|
|
delegate: Rectangle {
|
|
id: summaryGroup
|
|
required property var modelData
|
|
required property int index
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: groupContent.implicitHeight + 24
|
|
color: ShellSettings.colors["surface_container"]
|
|
antialiasing: true
|
|
|
|
topLeftRadius: index === 0 ? 25 : 5
|
|
topRightRadius: index === 0 ? 25 : 5
|
|
bottomLeftRadius: index === (toastWrapper.modelData.summaryGroups.length - 1) ? 25 : 5
|
|
bottomRightRadius: index === (toastWrapper.modelData.summaryGroups.length - 1) ? 25 : 5
|
|
|
|
ColumnLayout {
|
|
id: groupContent
|
|
spacing: 8
|
|
|
|
anchors {
|
|
fill: parent
|
|
margins: 12
|
|
}
|
|
|
|
RowLayout {
|
|
spacing: 12
|
|
Layout.fillWidth: true
|
|
Layout.alignment: Qt.AlignTop
|
|
|
|
ColumnLayout {
|
|
Layout.alignment: Qt.AlignTop
|
|
spacing: 0
|
|
|
|
Item {
|
|
id: imageContainer
|
|
Layout.preferredWidth: 36
|
|
Layout.preferredHeight: 36
|
|
visible: summaryGroup.modelData.image != ""
|
|
antialiasing: true
|
|
|
|
Image {
|
|
id: notificationImage
|
|
anchors.fill: parent
|
|
source: summaryGroup.modelData.image
|
|
fillMode: Image.PreserveAspectCrop
|
|
|
|
layer.enabled: true
|
|
layer.effect: OpacityMask {
|
|
maskSource: Rectangle {
|
|
width: notificationImage.width
|
|
height: notificationImage.height
|
|
radius: notificationImage.width / 2
|
|
antialiasing: true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Content column
|
|
ColumnLayout {
|
|
Layout.fillWidth: true
|
|
Layout.alignment: Qt.AlignTop
|
|
spacing: 8
|
|
|
|
// Header row
|
|
RowLayout {
|
|
Layout.fillWidth: true
|
|
spacing: 8
|
|
|
|
Text {
|
|
text: summaryGroup.modelData.summary
|
|
font.pixelSize: 16
|
|
font.weight: Font.Medium
|
|
color: ShellSettings.colors["on_surface"]
|
|
wrapMode: Text.WordWrap
|
|
maximumLineCount: 2
|
|
elide: Text.ElideRight
|
|
}
|
|
|
|
Widgets.Separator {}
|
|
|
|
Text {
|
|
text: "now"
|
|
font.pixelSize: 14
|
|
color: ShellSettings.colors["on_surface_variant"]
|
|
Layout.alignment: Qt.AlignVCenter
|
|
}
|
|
}
|
|
|
|
// Notification bodies
|
|
ColumnLayout {
|
|
Layout.fillWidth: true
|
|
spacing: 2
|
|
|
|
Repeater {
|
|
model: summaryGroup.modelData.notifications
|
|
|
|
delegate: ColumnLayout {
|
|
id: bodyDelegate
|
|
required property var modelData
|
|
required property int index
|
|
Layout.fillWidth: true
|
|
spacing: 0
|
|
|
|
Text {
|
|
Layout.fillWidth: true
|
|
text: bodyDelegate.modelData.body
|
|
font.pixelSize: 14
|
|
color: ShellSettings.colors["on_surface_variant"]
|
|
wrapMode: Text.WordWrap
|
|
maximumLineCount: 4
|
|
elide: Text.ElideRight
|
|
lineHeight: 1.3
|
|
visible: bodyDelegate.modelData.body != ""
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// HyprlanFocusGrab {
|
|
// id: grab
|
|
// windows: [notificationPanel]
|
|
// onCleared: {
|
|
// ipc.hide();
|
|
// }
|
|
// }
|
|
}
|
|
}
|
|
|
|
function init() {
|
|
}
|
|
}
|