Add notifications and notification center
This commit is contained in:
@@ -10,6 +10,6 @@ IpcHandler {
|
|||||||
item.open();
|
item.open();
|
||||||
}
|
}
|
||||||
function toggle() {
|
function toggle() {
|
||||||
item.visible ? close() : open()
|
item.toggle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,9 @@ PanelWindow {
|
|||||||
visible = true
|
visible = true
|
||||||
list.visible = true
|
list.visible = true
|
||||||
}
|
}
|
||||||
|
function toggle() {
|
||||||
|
launcher.visible ? close() : open();
|
||||||
|
}
|
||||||
|
|
||||||
implicitWidth: 800
|
implicitWidth: 800
|
||||||
exclusiveZone: 0
|
exclusiveZone: 0
|
||||||
@@ -63,7 +66,8 @@ PanelWindow {
|
|||||||
launcher.visible = false
|
launcher.visible = false
|
||||||
break;
|
break;
|
||||||
case Qt.Key_Return:
|
case Qt.Key_Return:
|
||||||
list.currentItem.execute()
|
if (list.currentItem) list.currentItem.execute();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,7 +152,7 @@ PanelWindow {
|
|||||||
position: 0
|
position: 0
|
||||||
}
|
}
|
||||||
GradientStop {
|
GradientStop {
|
||||||
color: Qt.darker(button.color, 4)
|
color: Qt.darker(button.color, 3)
|
||||||
position: 1
|
position: 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
176
NotifCenter.qml
Normal file
176
NotifCenter.qml
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
import Quickshell
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
|
||||||
|
PanelWindow {
|
||||||
|
id: root
|
||||||
|
anchors {
|
||||||
|
top: true
|
||||||
|
right: true
|
||||||
|
bottom: true
|
||||||
|
}
|
||||||
|
visible: false
|
||||||
|
exclusiveZone: 1
|
||||||
|
implicitWidth: 500
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
function open() {
|
||||||
|
root.visible = true;
|
||||||
|
}
|
||||||
|
function close() {
|
||||||
|
exitAnim.start();
|
||||||
|
}
|
||||||
|
function toggle() {
|
||||||
|
if (exitAnim.running) return;
|
||||||
|
if (root.visible) close();
|
||||||
|
else open();
|
||||||
|
}
|
||||||
|
|
||||||
|
required property var tracked
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: "#222"
|
||||||
|
anchors.fill: parent
|
||||||
|
Text {
|
||||||
|
id: title
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
top: parent.top
|
||||||
|
leftMargin: 12
|
||||||
|
topMargin: 12
|
||||||
|
}
|
||||||
|
text: "Notifications"
|
||||||
|
font.pixelSize: 20
|
||||||
|
font.family: "Comfortaa"
|
||||||
|
font.bold: true
|
||||||
|
color: "white"
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
width: clearAllText.width + 32
|
||||||
|
height: clearAllText.height + 30
|
||||||
|
Text {
|
||||||
|
id: clearAllText
|
||||||
|
anchors {
|
||||||
|
right: parent.right
|
||||||
|
top: parent.top
|
||||||
|
bottom: parent.bottom
|
||||||
|
topMargin: 14
|
||||||
|
rightMargin: 16
|
||||||
|
}
|
||||||
|
color: "white"
|
||||||
|
font.pixelSize: 16
|
||||||
|
font.family: "Comfortaa"
|
||||||
|
text: "Clear All"
|
||||||
|
}
|
||||||
|
onClicked: {while (root.tracked.values.length) {
|
||||||
|
root.tracked.values[0].tracked = false;
|
||||||
|
}}
|
||||||
|
property var hover: HoverHandler { }
|
||||||
|
background: Rectangle {
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
topMargin: 6
|
||||||
|
leftMargin: 8
|
||||||
|
bottomMargin: 6
|
||||||
|
rightMargin: 8
|
||||||
|
}
|
||||||
|
radius: 12
|
||||||
|
color: parent.hover.hovered ? "#333" : "#222"
|
||||||
|
Behavior on color { PropertyAnimation {
|
||||||
|
duration: 150
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ListView {
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
topMargin: 48
|
||||||
|
}
|
||||||
|
model: root.tracked
|
||||||
|
spacing: 10
|
||||||
|
delegate: Button {
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
margins: 12
|
||||||
|
}
|
||||||
|
height: notifContent.childrenRect.height + 32
|
||||||
|
Image {
|
||||||
|
id: notifIcon
|
||||||
|
anchors {
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: 12
|
||||||
|
}
|
||||||
|
width: 40
|
||||||
|
height: 40
|
||||||
|
source: Quickshell.iconPath(modelData.appIcon, true)
|
||||||
|
}
|
||||||
|
Column {
|
||||||
|
id: notifContent
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
top: parent.top
|
||||||
|
topMargin: 16
|
||||||
|
leftMargin: 16 + (notifIcon.source == "" ? 0 : 44)
|
||||||
|
rightMargin: 16
|
||||||
|
}
|
||||||
|
spacing: 6
|
||||||
|
Text {
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
color: "white"
|
||||||
|
font.family: "Comfortaa"
|
||||||
|
font.pixelSize: 16
|
||||||
|
text: modelData.summary
|
||||||
|
elide: Text.ElideRight
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
maximumLineCount: 2
|
||||||
|
}
|
||||||
|
Text {
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
color: "white"
|
||||||
|
font.family: "Comfortaa"
|
||||||
|
font.pixelSize: 12
|
||||||
|
text: modelData.body
|
||||||
|
elide: Text.ElideRight
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
maximumLineCount: 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
background: Rectangle {
|
||||||
|
color: "#333"
|
||||||
|
radius: 12
|
||||||
|
}
|
||||||
|
onClicked: modelData.tracked = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
transform: Translate {
|
||||||
|
NumberAnimation on x {
|
||||||
|
running: root.visible
|
||||||
|
from: root.implicitWidth
|
||||||
|
to: 0
|
||||||
|
duration: 300
|
||||||
|
easing.type: Easing.OutQuint
|
||||||
|
}
|
||||||
|
NumberAnimation on x {
|
||||||
|
id: exitAnim
|
||||||
|
to: root.implicitWidth
|
||||||
|
duration: 300
|
||||||
|
easing.type: Easing.InQuint
|
||||||
|
onFinished: root.visible = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
152
Notifs.qml
Normal file
152
Notifs.qml
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
import Quickshell
|
||||||
|
import Quickshell.Services.Notifications
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Effects
|
||||||
|
|
||||||
|
PanelWindow {
|
||||||
|
id: root
|
||||||
|
visible: false
|
||||||
|
anchors {
|
||||||
|
top: true
|
||||||
|
right: true
|
||||||
|
}
|
||||||
|
color: "transparent"
|
||||||
|
aboveWindows: true
|
||||||
|
implicitWidth: 500
|
||||||
|
implicitHeight: 200
|
||||||
|
focusable: false
|
||||||
|
|
||||||
|
property var font: { family: "Comfortaa" }
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: notifPopup
|
||||||
|
Rectangle {
|
||||||
|
id: notifRect
|
||||||
|
required property var modelData
|
||||||
|
|
||||||
|
y: {
|
||||||
|
let res = 0;
|
||||||
|
for (let i = 0; notifsList.children[i] && notifsList.children[i] != this; i++) {
|
||||||
|
if (!notifsList.children[i].exiting) res += notifsList.children[i].height + 10;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
Behavior on y { NumberAnimation {
|
||||||
|
duration: 300
|
||||||
|
easing.type: Easing.InOutCubic
|
||||||
|
}}
|
||||||
|
property bool exiting: false
|
||||||
|
width: parent?.width ?? 0
|
||||||
|
height: notifContent.childrenRect.height + 32
|
||||||
|
radius: 20
|
||||||
|
color: "#181818"
|
||||||
|
|
||||||
|
RectangularShadow {
|
||||||
|
z: -1
|
||||||
|
anchors.fill: parent
|
||||||
|
blur: 12
|
||||||
|
radius: parent.radius
|
||||||
|
}
|
||||||
|
Image {
|
||||||
|
id: notifIcon
|
||||||
|
anchors {
|
||||||
|
verticalCenter: parent.verticalCenter
|
||||||
|
left: parent.left
|
||||||
|
leftMargin: 12
|
||||||
|
}
|
||||||
|
width: 40
|
||||||
|
height: 40
|
||||||
|
source: Quickshell.iconPath(modelData.appIcon, true)
|
||||||
|
}
|
||||||
|
Column {
|
||||||
|
id: notifContent
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
top: parent.top
|
||||||
|
topMargin: 16
|
||||||
|
leftMargin: 16 + (notifIcon.source == "" ? 0 : 44)
|
||||||
|
rightMargin: 16
|
||||||
|
}
|
||||||
|
spacing: 6
|
||||||
|
Text {
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
color: "white"
|
||||||
|
font.family: "Comfortaa"
|
||||||
|
font.pixelSize: 16
|
||||||
|
text: modelData.summary
|
||||||
|
elide: Text.ElideRight
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
maximumLineCount: 2
|
||||||
|
}
|
||||||
|
Text {
|
||||||
|
anchors {
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
color: "white"
|
||||||
|
font.family: "Comfortaa"
|
||||||
|
font.pixelSize: 12
|
||||||
|
text: modelData.body
|
||||||
|
elide: Text.ElideRight
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
maximumLineCount: 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NumberAnimation on x {
|
||||||
|
running: root.visible
|
||||||
|
from: root.width
|
||||||
|
to: 0
|
||||||
|
duration: 300
|
||||||
|
easing.type: Easing.OutBack
|
||||||
|
easing.overshoot: 0.8
|
||||||
|
}
|
||||||
|
NumberAnimation on x {
|
||||||
|
id: exitAnim
|
||||||
|
running: false
|
||||||
|
to: parent.parent.width
|
||||||
|
duration: 300
|
||||||
|
easing.type: Easing.InCubic
|
||||||
|
onStarted: exiting = true
|
||||||
|
onFinished: {
|
||||||
|
if (notifsList.children.length === 1) {
|
||||||
|
root.visible = false
|
||||||
|
root.implicitHeight = 200;
|
||||||
|
}
|
||||||
|
notifRect.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Timer {
|
||||||
|
running: true
|
||||||
|
repeat: false
|
||||||
|
interval: 5000
|
||||||
|
onTriggered: exitAnim.start()
|
||||||
|
}
|
||||||
|
Component.onCompleted: {
|
||||||
|
let sumHeight = 40;
|
||||||
|
for (const notif of notifsList.children) {
|
||||||
|
sumHeight += notif.height + 6;
|
||||||
|
}
|
||||||
|
root.implicitHeight = Math.max(root.implicitHeight, sumHeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: notifsList
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
margins: 12
|
||||||
|
leftMargin: 64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function notify(notif) {
|
||||||
|
if (notif.lastGeneration) return
|
||||||
|
root.visible = true;
|
||||||
|
notifPopup.createObject(notifsList, {modelData: notif});
|
||||||
|
}
|
||||||
|
}
|
||||||
16
shell.qml
16
shell.qml
@@ -1,9 +1,18 @@
|
|||||||
import "." as Osu
|
import "." as Osu
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
|
import Quickshell.Services.Notifications
|
||||||
|
|
||||||
ShellRoot {
|
ShellRoot {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
NotificationServer {
|
||||||
|
id: notificationServer
|
||||||
|
onNotification: (notif) => {
|
||||||
|
notif.tracked = true
|
||||||
|
notifPopup.notify(notif)
|
||||||
|
}
|
||||||
|
}
|
||||||
readonly property var font: {
|
readonly property var font: {
|
||||||
family: "comfortaa"
|
family: "comfortaa"
|
||||||
}
|
}
|
||||||
@@ -15,4 +24,11 @@ ShellRoot {
|
|||||||
target: "lock"
|
target: "lock"
|
||||||
item: Osu.Lock { }
|
item: Osu.Lock { }
|
||||||
}
|
}
|
||||||
|
Osu.IpcToggle {
|
||||||
|
target: "notifications"
|
||||||
|
item: Osu.NotifCenter {
|
||||||
|
tracked: notificationServer.trackedNotifications
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Osu.Notifs { id: notifPopup }
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user