feat: basic notifications
Also fix pixel issue with notif area background Add more log rules to run script
This commit is contained in:
parent
ed55afbcd8
commit
6b64c8a918
9 changed files with 313 additions and 24 deletions
15
config/NotifsConfig.qml
Normal file
15
config/NotifsConfig.qml
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
pragma Singleton
|
||||||
|
|
||||||
|
import Quickshell
|
||||||
|
import QtQuick
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
readonly property int defaultExpireTimeout: 3000
|
||||||
|
readonly property Sizes sizes: Sizes {}
|
||||||
|
|
||||||
|
component Sizes: QtObject {
|
||||||
|
readonly property int width: 400
|
||||||
|
readonly property int image: 50
|
||||||
|
readonly property int badge: 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
pragma ComponentBehavior: Bound
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import "root:/widgets"
|
||||||
import "root:/config"
|
import "root:/config"
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Widgets
|
import Quickshell.Widgets
|
||||||
|
|
@ -53,18 +54,9 @@ MouseArea {
|
||||||
active: BarConfig.tray.recolourIcons
|
active: BarConfig.tray.recolourIcons
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
|
|
||||||
sourceComponent: MultiEffect {
|
sourceComponent: Colouriser {
|
||||||
source: icon
|
source: icon
|
||||||
colorizationColor: root.colour
|
colorizationColor: root.colour
|
||||||
colorization: 1
|
|
||||||
|
|
||||||
Behavior on colorizationColor {
|
|
||||||
ColorAnimation {
|
|
||||||
duration: Appearance.anim.durations.normal
|
|
||||||
easing.type: Easing.BezierSpline
|
|
||||||
easing.bezierCurve: Appearance.anim.curves.standard
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,10 @@ Shape {
|
||||||
radiusX: root.rounding
|
radiusX: root.rounding
|
||||||
radiusY: root.rounding
|
radiusY: root.rounding
|
||||||
}
|
}
|
||||||
|
PathLine {
|
||||||
|
relativeX: 1
|
||||||
|
relativeY: 0
|
||||||
|
}
|
||||||
|
|
||||||
Behavior on fillColor {
|
Behavior on fillColor {
|
||||||
ColorAnimation {
|
ColorAnimation {
|
||||||
|
|
|
||||||
|
|
@ -7,16 +7,18 @@ import QtQuick
|
||||||
Column {
|
Column {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
// padding: Appearance.padding.large
|
padding: Appearance.padding.large
|
||||||
|
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
|
|
||||||
spacing: Appearance.spacing.normal
|
spacing: Appearance.spacing.normal
|
||||||
|
|
||||||
StyledRect {
|
Repeater {
|
||||||
width: 300
|
model: ScriptModel {
|
||||||
height: 100
|
values: [...Notifs.list]
|
||||||
// color: Qt.rgba(255, 0, 0, 0.4)
|
}
|
||||||
|
|
||||||
|
Notification {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
188
modules/notifications/Notification.qml
Normal file
188
modules/notifications/Notification.qml
Normal file
|
|
@ -0,0 +1,188 @@
|
||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import "root:/widgets"
|
||||||
|
import "root:/services"
|
||||||
|
import "root:/config"
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Effects
|
||||||
|
|
||||||
|
StyledRect {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
required property Notifs.Notif modelData
|
||||||
|
readonly property bool hasImage: modelData.image.length > 0
|
||||||
|
readonly property bool hasAppIcon: modelData.appIcon.length > 0
|
||||||
|
readonly property int imageSize: summary.height + bodyPreview.height
|
||||||
|
property bool expanded
|
||||||
|
|
||||||
|
color: Colours.palette.m3surfaceContainer
|
||||||
|
radius: Appearance.rounding.normal
|
||||||
|
implicitWidth: NotifsConfig.sizes.width
|
||||||
|
|
||||||
|
Item {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.margins: Appearance.padding.normal
|
||||||
|
|
||||||
|
implicitHeight: summary.height + bodyPreview.height + anchors.margins * 2
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: image
|
||||||
|
|
||||||
|
active: root.hasImage
|
||||||
|
asynchronous: true
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.verticalCenterOffset: -parent.anchors.margins
|
||||||
|
width: root.hasImage || root.hasAppIcon ? root.imageSize : 0
|
||||||
|
height: root.hasImage || root.hasAppIcon ? root.imageSize : 0
|
||||||
|
visible: root.hasImage || root.hasAppIcon
|
||||||
|
|
||||||
|
sourceComponent: ClippingRectangle {
|
||||||
|
radius: Appearance.rounding.full
|
||||||
|
width: root.imageSize
|
||||||
|
height: root.imageSize
|
||||||
|
|
||||||
|
Image {
|
||||||
|
anchors.fill: parent
|
||||||
|
source: Qt.resolvedUrl(root.modelData.image)
|
||||||
|
fillMode: Image.PreserveAspectCrop
|
||||||
|
cache: false
|
||||||
|
asynchronous: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
states: State {
|
||||||
|
name: "expanded"
|
||||||
|
when: root.expanded
|
||||||
|
|
||||||
|
AnchorChanges {
|
||||||
|
anchors.verticalCenter: undefined
|
||||||
|
anchors.top: image.parent.top
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: appIcon
|
||||||
|
|
||||||
|
active: root.hasAppIcon
|
||||||
|
asynchronous: true
|
||||||
|
|
||||||
|
anchors.horizontalCenter: root.hasImage ? undefined : image.horizontalCenter
|
||||||
|
anchors.verticalCenter: root.hasImage ? undefined : image.verticalCenter
|
||||||
|
anchors.right: root.hasImage ? image.right : undefined
|
||||||
|
anchors.bottom: root.hasImage ? image.bottom : undefined
|
||||||
|
|
||||||
|
sourceComponent: StyledRect {
|
||||||
|
radius: Appearance.rounding.full
|
||||||
|
color: Colours.palette.m3tertiaryContainer
|
||||||
|
implicitWidth: root.hasImage ? NotifsConfig.sizes.badge : root.imageSize
|
||||||
|
implicitHeight: root.hasImage ? NotifsConfig.sizes.badge : root.imageSize
|
||||||
|
|
||||||
|
IconImage {
|
||||||
|
id: icon
|
||||||
|
|
||||||
|
anchors.centerIn: parent
|
||||||
|
visible: false
|
||||||
|
implicitSize: Math.round(parent.width * 0.6)
|
||||||
|
source: Quickshell.iconPath(root.modelData.appIcon)
|
||||||
|
asynchronous: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Colouriser {
|
||||||
|
anchors.fill: icon
|
||||||
|
source: icon
|
||||||
|
colorizationColor: Colours.palette.m3onTertiaryContainer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
states: State {
|
||||||
|
name: "expanded"
|
||||||
|
when: !root.hasImage && root.expanded
|
||||||
|
|
||||||
|
AnchorChanges {
|
||||||
|
anchors.verticalCenter: undefined
|
||||||
|
anchors.top: image.parent.top
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: summary
|
||||||
|
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: image.right
|
||||||
|
anchors.leftMargin: Appearance.spacing.small
|
||||||
|
|
||||||
|
text: root.modelData.summary
|
||||||
|
maximumLineCount: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: timeSep
|
||||||
|
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: summary.right
|
||||||
|
anchors.leftMargin: Appearance.spacing.small
|
||||||
|
|
||||||
|
text: "•"
|
||||||
|
color: Colours.palette.m3onSurfaceVariant
|
||||||
|
font.pointSize: Appearance.font.size.small
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: time
|
||||||
|
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: timeSep.right
|
||||||
|
anchors.leftMargin: Appearance.spacing.small
|
||||||
|
|
||||||
|
animate: true
|
||||||
|
horizontalAlignment: Text.AlignLeft
|
||||||
|
text: root.modelData.timeStr
|
||||||
|
color: Colours.palette.m3onSurfaceVariant
|
||||||
|
font.pointSize: Appearance.font.size.small
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledRect {
|
||||||
|
id: expandBtn
|
||||||
|
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: parent.top
|
||||||
|
|
||||||
|
MaterialIcon {
|
||||||
|
animate: true
|
||||||
|
text: root.expanded ? "expand_less" : "expand_more"
|
||||||
|
font.pointSize: Appearance.font.size.smaller
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: bodyPreview
|
||||||
|
|
||||||
|
anchors.left: summary.left
|
||||||
|
anchors.right: expandBtn.left
|
||||||
|
anchors.top: summary.bottom
|
||||||
|
anchors.rightMargin: Appearance.spacing.small
|
||||||
|
|
||||||
|
text: bodyPreviewMetrics.elidedText
|
||||||
|
color: Colours.palette.m3onSurfaceVariant
|
||||||
|
font.pointSize: Appearance.font.size.small
|
||||||
|
}
|
||||||
|
|
||||||
|
TextMetrics {
|
||||||
|
id: bodyPreviewMetrics
|
||||||
|
|
||||||
|
text: root.modelData.body
|
||||||
|
font.family: bodyPreview.font.family
|
||||||
|
font.pointSize: bodyPreview.font.pointSize
|
||||||
|
elide: Text.ElideRight
|
||||||
|
elideWidth: bodyPreview.width
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -12,14 +12,7 @@ Variants {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
required property ShellScreen modelData
|
required property ShellScreen modelData
|
||||||
property bool osdVisible
|
property bool osdVisible: Notifs.list.length > 0
|
||||||
|
|
||||||
Timer {
|
|
||||||
// running: true
|
|
||||||
repeat: true
|
|
||||||
interval: 2000
|
|
||||||
onTriggered: root.osdVisible = !root.osdVisible
|
|
||||||
}
|
|
||||||
|
|
||||||
LazyLoader {
|
LazyLoader {
|
||||||
loading: true
|
loading: true
|
||||||
|
|
@ -38,7 +31,9 @@ Variants {
|
||||||
|
|
||||||
anchors.top: true
|
anchors.top: true
|
||||||
anchors.bottom: true
|
anchors.bottom: true
|
||||||
|
anchors.left: true
|
||||||
anchors.right: true
|
anchors.right: true
|
||||||
|
|
||||||
width: wrapper.width
|
width: wrapper.width
|
||||||
height: wrapper.height
|
height: wrapper.height
|
||||||
|
|
||||||
|
|
|
||||||
4
run.fish
4
run.fish
|
|
@ -2,10 +2,12 @@
|
||||||
|
|
||||||
set -l dbus 'quickshell.dbus.properties.warning = false' # System tray dbus property errors
|
set -l dbus 'quickshell.dbus.properties.warning = false' # System tray dbus property errors
|
||||||
set -l text_input 'qt.qpa.wayland.textinput.warning = false' # Text input focus when open window
|
set -l text_input 'qt.qpa.wayland.textinput.warning = false' # Text input focus when open window
|
||||||
|
set -l notifs 'quickshell.service.notifications.warning = false' # Notification server warnings on reload
|
||||||
|
set -l sni 'quickshell.service.sni.host.warning = false' # StatusNotifierItem warnings on reload
|
||||||
set -l hypr 'invalid nullptr parameter' # Error that always pops up on Hyprland
|
set -l hypr 'invalid nullptr parameter' # Error that always pops up on Hyprland
|
||||||
set -l intercept '^qsintercept:.*(:[0-9]+){2}$' # Empty qsintercept lines
|
set -l intercept '^qsintercept:.*(:[0-9]+){2}$' # Empty qsintercept lines
|
||||||
set -l loop 'Binding loop detected' # Binding loops
|
set -l loop 'Binding loop detected' # Binding loops
|
||||||
set -l process 'QProcess: Destroyed while process' # Long running processes on reload
|
set -l process 'QProcess: Destroyed while process' # Long running processes on reload
|
||||||
set -l async_loader 'items in the process of being created at engine destruction' # Async loaders on reload
|
set -l async_loader 'items in the process of being created at engine destruction' # Async loaders on reload
|
||||||
|
|
||||||
qs -c caelestia --log-rules "$dbus;$text_input" | grep -vE -e $hypr -e $intercept -e $loop -e $process -e $async_loader
|
qs -c caelestia --log-rules "$dbus;$text_input;$notifs;$sni" | grep -vE -e $hypr -e $intercept -e $loop -e $process -e $async_loader
|
||||||
|
|
|
||||||
76
services/Notifs.qml
Normal file
76
services/Notifs.qml
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
pragma Singleton
|
||||||
|
|
||||||
|
import "root:/config"
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Services.Notifications
|
||||||
|
import QtQuick
|
||||||
|
|
||||||
|
Singleton {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
readonly property list<Notif> list: []
|
||||||
|
|
||||||
|
NotificationServer {
|
||||||
|
id: server
|
||||||
|
|
||||||
|
keepOnReload: false
|
||||||
|
actionsSupported: true
|
||||||
|
bodyHyperlinksSupported: true
|
||||||
|
bodyImagesSupported: true
|
||||||
|
bodyMarkupSupported: true
|
||||||
|
imageSupported: true
|
||||||
|
|
||||||
|
onNotification: notif => {
|
||||||
|
notif.tracked = true;
|
||||||
|
|
||||||
|
root.list.push(notifComp.createObject(root, {
|
||||||
|
popup: true,
|
||||||
|
notification: notif
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component Notif: QtObject {
|
||||||
|
id: notif
|
||||||
|
|
||||||
|
property bool popup
|
||||||
|
readonly property date time: new Date()
|
||||||
|
readonly property string timeStr: {
|
||||||
|
const diff = Time.date.getTime() - time.getTime();
|
||||||
|
const m = Math.floor(diff / 60000);
|
||||||
|
const h = Math.floor(m / 60);
|
||||||
|
|
||||||
|
if (h < 1 && m < 1)
|
||||||
|
return "now";
|
||||||
|
if (h < 1)
|
||||||
|
return `${m}m`;
|
||||||
|
return `${h}h`;
|
||||||
|
}
|
||||||
|
|
||||||
|
required property Notification notification
|
||||||
|
readonly property string summary: notification.summary
|
||||||
|
readonly property string body: notification.body
|
||||||
|
readonly property string appIcon: notification.appIcon
|
||||||
|
readonly property string image: notification.image
|
||||||
|
|
||||||
|
readonly property Timer timer: Timer {
|
||||||
|
running: true
|
||||||
|
interval: notif.notification.expireTimeout > 0 ? notif.notification.expireTimeout : NotifsConfig.defaultExpireTimeout
|
||||||
|
onTriggered: notif.popup = false
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property Connections conn: Connections {
|
||||||
|
target: notif.notification
|
||||||
|
|
||||||
|
function onClosed(): void {
|
||||||
|
notif.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: notifComp
|
||||||
|
|
||||||
|
Notif {}
|
||||||
|
}
|
||||||
|
}
|
||||||
15
widgets/Colouriser.qml
Normal file
15
widgets/Colouriser.qml
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
import "root:/config"
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Effects
|
||||||
|
|
||||||
|
MultiEffect {
|
||||||
|
colorization: 1
|
||||||
|
|
||||||
|
Behavior on colorizationColor {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: Appearance.anim.durations.normal
|
||||||
|
easing.type: Easing.BezierSpline
|
||||||
|
easing.bezierCurve: Appearance.anim.curves.standard
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue