Notifications n shi

This commit is contained in:
2026-02-26 16:18:06 -08:00
parent a3ffbc80c5
commit 3ed2df2cd5
19 changed files with 331 additions and 17 deletions

View File

@@ -52,7 +52,7 @@ Item {
onCursorPositionChanged: cursorPosition = text.length
Keys.onPressed: (event) => { switch (event.key) {
case Qt.Key_Escape:
root.close();
background.index = 0;
break;
case Qt.Key_Return:
list.currentItem.execute();

View File

@@ -0,0 +1,153 @@
import QtQuick
import Quickshell
import Quickshell.Widgets
import Quickshell.Services.Mpris
import Quickshell.Services.Notifications
import "../Widgets" as Widgets
Item {
anchors {
top: parent.top
horizontalCenter: parent.horizontalCenter
}
width: childrenRect.width + 12
height: childrenRect.height + 12
Row {
spacing: 6
x: 6
y: 6
Rectangle {
width: 400
height: 300
color: "#181818"
radius: 10
Column {
anchors.centerIn: parent
spacing: 2
opacity: !notifServer.trackedNotifications.values.length
Behavior on opacity {NumberAnimation {duration: 150}}
Image {
anchors.horizontalCenter: parent.horizontalCenter
source: Quickshell.shellPath("assets/notif.svg")
width: 24
height: 24
sourceSize {width: width; height: height}
opacity: 0.5
}
Text {
anchors.horizontalCenter: parent.horizontalCenter
color: "#888"
text: "No notifications"
}
}
ListView {
anchors.fill: parent
clip: true
spacing: 4
header: Item {height: 4; width: 1}
model: Array.from(notifServer.trackedNotifications.values)
delegate: Widgets.Notification {
color: "#222"
anchors {
left: parent.left
right: parent.right
margins: 4
}
}
}
}
Item {
id: player
property var modelData: Mpris.players.values[0]
height: 300
width: 250
Column {
anchors.centerIn: parent
spacing: 2
Image {
anchors.horizontalCenter: parent.horizontalCenter
source: Quickshell.shellPath("assets/player-symbolic.svg")
width: 24
height: 24
sourceSize {width: width; height: height}
opacity: 0.5
}
Text {
anchors.horizontalCenter: parent.horizontalCenter
color: "#888"
text: "Nothing playing"
}
}
Column {
anchors.centerIn: parent
opacity: Mpris.players.values.length
ClippingRectangle {
color: "transparent"
width: 180
height: 180
radius: height/2
anchors.horizontalCenter: parent.horizontalCenter
Image {
anchors.fill: parent
source: player.modelData?.trackArtUrl ?? source
}
NumberAnimation on rotation {
id: spin
paused: !player.modelData?.isPlaying || !root.up || background.index !== 3
duration: 30000
loops: Animation.Infinite
from: 0
to: 360
}
}
Item {width: 1; height: 10}
Text {
color: "white"
text: player.modelData?.trackTitle ?? ""
anchors.horizontalCenter: parent.horizontalCenter
font.pixelSize: 16
}
Text {
color: "white"
text: player.modelData?.trackArtist ?? ""
anchors.horizontalCenter: parent.horizontalCenter
font.pixelSize: 12
}
Item {width: 1; height: 8}
Row {
height: childrenRect.height
anchors.horizontalCenter: parent.horizontalCenter
component Button: Rectangle {
id: button
property var action
property var clicked
radius: 12
color: hover.hovered ? "#11ffffff" : "#00ffffff"
property var hover: HoverHandler {}
width: 40
height: width
Image {
anchors.fill: parent
anchors.margins: 8
sourceSize {width: width; height: height}
source: Quickshell.shellPath(`assets/lazer-${action}-symbolic.svg`)
}
TapHandler {onTapped: button.clicked()}
}
Button {
action: "previous"
clicked: player.modelData?.previous
}
Button {
action: player.modelData?.isPlaying ? "pause" : "play"
clicked: player.modelData?.togglePlaying
}
Button {
action: "next"
clicked: player.modelData?.next
}
}
}
}
}
}

28
Panes/Notifications.qml Normal file
View File

@@ -0,0 +1,28 @@
import Quickshell
import QtQuick
import Quickshell.Services.Notifications
import "../Widgets" as Widgets
Item {
anchors {
top: parent.top
horizontalCenter: parent.horizontalCenter
}
width: 350
height: childrenRect.height
Connections {
target: notifServer
function onNotification(notif) {
if (root.up) return
background.index = 4
notifPopup.modelData = notif
root.open()
}
}
Widgets.Notification {
id: notifPopup
anchors.margins: 6
modelData: notifServer.trackedNotifications.values[0] ?? undefined
color: "transparent"
}
}

View File

@@ -3,7 +3,7 @@ import QtQuick
import "../Widgets" as Widgets
Item {
width: 800
width: 900
height: 42
anchors {
top: parent.top
@@ -14,7 +14,6 @@ Item {
top: parent.top
left: parent.left
bottom: parent.bottom
leftMargin: 0
}
Widgets.Battery {}
}
@@ -31,13 +30,7 @@ Item {
top: parent.top
right: parent.right
bottom: parent.bottom
rightMargin: 12
}
Text {
anchors.verticalCenter: parent.verticalCenter
property var clock: SystemClock {}
color: "white"
text: Qt.formatDateTime(clock.date, "ddd MMM dd · hh:mm")
}
Widgets.Time {}
}
}

View File

@@ -17,8 +17,8 @@ PanelWindow {
property var font: {family: "0xProto Nerd Font"}
implicitWidth: 1024
implicitHeight: 200
WlrLayershell.keyboardFocus: (up && !shortcut.pressed) ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None
implicitHeight: 400
WlrLayershell.keyboardFocus: (up && !background.exit.running && !shortcut.pressed && background.index <= 1) ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None
WlrLayershell.layer: WlrLayer.Overlay
exclusionMode: ExclusionMode.Ignore
color: "transparent"
@@ -39,6 +39,7 @@ PanelWindow {
name: "topbar"
description: "Hold to peek, tap to toggle topbar"
onPressed: {
background.index = 0
root.open()
}
onReleased: {
@@ -147,13 +148,14 @@ PanelWindow {
onStarted: entry.stop()
}
}
Pane { opacity: 1; Panes.Status { }}
Pane { opacity: 1; Panes.Status {} }
Pane { Panes.Launcher {
id: launcher
onShowChanged: if (show) background.index = 1; else background.index = 0
}}
Pane { Panes.Battery {
}}
Pane { Panes.Battery {} }
Pane { Panes.NotificationCenter {} }
Pane { Panes.Notifications {} }
property var entry: SequentialAnimation {
PauseAnimation {duration: 2}

View File

@@ -50,7 +50,7 @@ Item {
anchors.verticalCenter: parent.verticalCenter
x: 10
color: "white"
text: `${Math.round(UPower.displayDevice.percentage * 100)}% · ${Math.round(UPower.displayDevice.changeRate)}W`
text: `${Math.round(UPower.displayDevice.percentage * 100)}% · ${UPower.displayDevice.changeRate.toPrecision(2)}W`
}
}
HoverHandler {id: hover}

83
Widgets/Notification.qml Normal file
View File

@@ -0,0 +1,83 @@
import Quickshell
import Quickshell.Widgets
import Quickshell.Services.Notifications
import QtQuick
import QtQuick.Controls
import QtQuick.Effects
Rectangle {
id: notifRect
required property var modelData
property bool exiting: false
width: parent?.width ?? 0
height: notifContent.childrenRect.height + 20
radius: 8
color: "#181818"
Rectangle {
antialiasing: true
color: "transparent"
anchors {
verticalCenter: parent.verticalCenter
left: parent.left
leftMargin: 12
}
radius: 8
width: 40
height: 40
Image {
id: notifIcon
anchors.fill: parent
source: modelData?.image ?? source
}
}
Column {
id: notifContent
anchors {
left: parent.left
right: parent.right
top: parent.top
topMargin: 10
leftMargin: 16 + (notifIcon.source == "" ? 0 : 44)
rightMargin: 16
}
spacing: 4
Text {
anchors {
left: parent.left
right: parent.right
}
color: "white"
font.family: "Comfortaa"
font.pixelSize: 16
text: modelData?.summary ?? text
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 ?? text
elide: Text.ElideRight
wrapMode: Text.Wrap
maximumLineCount: 4
}
}
Timer {
running: modelData?.expireTimeout > 0
interval: (modelData?.expireTimeout > 0) ? modelData?.expireTimeout : 0
onTriggered: modelData.tracked = false
}
TapHandler {
acceptedButtons: Qt.MiddleButton
onTapped: modelData.tracked = false
}
}

48
Widgets/Time.qml Normal file
View File

@@ -0,0 +1,48 @@
import QtQuick
import Quickshell
import Quickshell.Widgets
import Quickshell.Services.Mpris
Item {
id: root
property var player: Mpris.players.values[0]
anchors {
top: parent.top
bottom: parent.bottom
}
width: children[1].width + 24
Rectangle {
anchors.fill: parent
anchors.margins: 4
radius: 8
bottomRightRadius: 12
color: hover.hovered ? "#11ffffff" : "#00ffffff"
Behavior on color {ColorAnimation {duration: 100}}
}
Row {
spacing: 12
anchors.verticalCenter: parent.verticalCenter
x: 12
ClippingRectangle {
visible: !!root.player
anchors.verticalCenter: parent.verticalCenter
width: 24
height: width
radius: 6
Image {
anchors.fill: parent
source: root.player?.trackArtUrl ?? source
}
opacity: root.player?.isPlaying ? 1 : 0.5
Behavior on opacity {NumberAnimation {duration: 150}}
}
Text {
anchors.verticalCenter: parent.verticalCenter
property var clock: SystemClock {}
color: "white"
text: Qt.formatDateTime(clock.date, "ddd MMM dd · hh:mm")
}
}
HoverHandler {id: hover}
TapHandler {onTapped: background.index = 3}
}

View File

@@ -18,7 +18,9 @@ Item {
width: 24
property var workspace: Hyprland.workspaces.values.find(ws => ws.id === modelData+1)
property var icon: {
const appId = workspace?.toplevels.values[0]?.wayland?.appId
const appId = workspace ? Array.from(workspace.toplevels.values).sort(
(a, b) => b.lastIpcObject.focusHistoryId - a.lastIpcObject.focusHistoryId
)[0]?.wayland?.appId : undefined
return DesktopEntries.applications.values.find(
app => app.startupClass === appId || app.id === appId
)?.icon;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 540 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 504 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 279 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

View File

@@ -1,8 +1,13 @@
import Quickshell
import Quickshell.Io
import Quickshell.Services.Notifications
import "." as Shell
ShellRoot {
NotificationServer {
id: notifServer
onNotification: (notif) => notif.tracked = true
}
Shell.Wall {}
Shell.TopBar {}
Shell.Boateye {}