quickshell and hyprland additions

This commit is contained in:
2026-03-15 13:56:00 +02:00
parent c9c27d1554
commit 1ad06b82a6
509 changed files with 68371 additions and 19 deletions

View File

@@ -0,0 +1,144 @@
import qs.config
import qs.modules.components
import qs.services
import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Widgets
import Quickshell.Wayland
Rectangle {
id: root
property bool startAnim: false
property string title: "No Title"
property string body: "No content"
property var rawNotif: null
property bool tracked: false
property string image: ""
property var buttons: [
{ label: "Okay!", onClick: () => console.log("Okay") }
]
opacity: tracked ? 1 : (startAnim ? 1 : 0)
Behavior on opacity {
enabled: Config.runtime.appearance.animations.enabled
NumberAnimation {
duration: Metrics.chronoDuration("small")
easing.type: Easing.InOutExpo
}
}
Layout.fillWidth: true
radius: Metrics.radius("large")
property bool hovered: mouseHandler.containsMouse
property bool clicked: mouseHandler.containsPress
color: hovered ? (clicked ? Appearance.m3colors.m3surfaceContainerHigh : Appearance.m3colors.m3surfaceContainerLow) : Appearance.m3colors.m3surface
Behavior on color {
enabled: Config.runtime.appearance.animations.enabled
ColorAnimation {
duration: Metrics.chronoDuration("small")
easing.type: Easing.InOutExpo
}
}
implicitHeight: Math.max(content.implicitHeight + 30, 80)
RowLayout {
id: content
anchors.fill: parent
anchors.margins: Metrics.margin(10)
spacing: Metrics.spacing(10)
ClippingRectangle {
width: 50
height: 50
radius: Metrics.radius("large")
clip: true
color: root.image === "" ? Appearance.m3colors.m3surfaceContainer : "transparent"
Image {
anchors.fill: parent
source: root.image
fillMode: Image.PreserveAspectCrop
smooth: true
}
MaterialSymbol {
icon: "chat"
color: Appearance.m3colors.m3onSurfaceVariant
anchors.centerIn: parent
visible: root.image === ""
iconSize: Metrics.iconSize(22)
}
}
ColumnLayout {
StyledText {
text: root.title
font.bold: true
font.pixelSize: Metrics.fontSize(18)
wrapMode: Text.Wrap
color: Appearance.m3colors.m3onSurface
Layout.fillWidth: true
}
StyledText {
text: root.body.length > 123 ? root.body.substr(0, 120) + "..." : root.body
visible: root.body.length > 0
font.pixelSize: Metrics.fontSize(12)
color: Appearance.m3colors.m3onSurfaceVariant
wrapMode: Text.Wrap
Layout.fillWidth: true
}
RowLayout {
visible: root.buttons.length > 1
Layout.preferredHeight: 40
Layout.fillWidth: true
spacing: Metrics.spacing(10)
Repeater {
model: buttons
StyledButton {
Layout.fillWidth: true
implicitHeight: 30
implicitWidth: 0
text: modelData.label
base_bg: index !== 0
? Appearance.m3colors.m3secondaryContainer
: Appearance.m3colors.m3primary
base_fg: index !== 0
? Appearance.m3colors.m3onSecondaryContainer
: Appearance.m3colors.m3onPrimary
onClicked: modelData.onClick()
}
}
}
}
}
MouseArea {
id: mouseHandler
anchors.fill: parent
hoverEnabled: true
visible: root.buttons.length === 0 || root.buttons.length === 1
cursorShape: Qt.PointingHandCursor
onClicked: {
if (root.buttons.length === 1 && root.buttons[0].onClick) {
root.buttons[0].onClick()
root.rawNotif?.notification.dismiss()
} else if (root.buttons.length === 0) {
console.log("[Notification] Dismissed a notification with no action.")
root.rawNotif.notification.tracked = false
root.rawNotif.popup = false
root.rawNotif?.notification.dismiss()
} else {
console.log("[Notification] Dismissed a notification with multiple actions.")
root.rawNotif?.notification.dismiss()
}
}
}
Component.onCompleted: {
startAnim = true
}
}

View File

@@ -0,0 +1,154 @@
import QtQuick
import QtQuick.Effects
import QtQuick.Layouts
import Quickshell
import Quickshell.Services.Notifications
import Quickshell.Wayland
import Quickshell.Widgets
import qs.services
import qs.config
import qs.modules.components
Scope {
id: root
property int innerSpacing: Metrics.spacing(10)
PanelWindow {
id: window
implicitWidth: 520
visible: true
color: "transparent"
WlrLayershell.layer: WlrLayer.Overlay
WlrLayershell.exclusionMode: ExclusionMode.Normal
WlrLayershell.namespace: "nucleus:notification"
anchors {
top: true
left: Config.runtime.notifications.position.endsWith("left")
bottom: true
right: Config.runtime.notifications.position.endsWith("right")
}
Item {
id: notificationList
anchors.leftMargin: 0
anchors.topMargin: Metrics.margin(10)
anchors.rightMargin: 0
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
Rectangle {
id: bgRectangle
layer.enabled: true
anchors.top: parent.top
anchors.left: parent.left
anchors.leftMargin: Metrics.margin(20)
anchors.rightMargin: Metrics.margin(20)
anchors.right: parent.right
height: window.mask.height > 0 ? window.mask.height + 40 : 0
color: Appearance.m3colors.m3background
radius: Metrics.radius("large")
layer.effect: MultiEffect {
shadowEnabled: true
shadowOpacity: 1
shadowColor: Appearance.m3colors.m3shadow
shadowBlur: 1
shadowScale: 1
}
Behavior on height {
enabled: Config.runtime.appearance.animations.enabled
NumberAnimation {
duration: Metrics.chronoDuration("small")
easing.type: Easing.InOutExpo
}
}
}
Item {
id: notificationColumn
anchors.left: parent.left
anchors.right: parent.right
Repeater {
id: rep
model: (!Config.runtime.notifications.doNotDisturb && Config.runtime.notifications.enabled) ? NotifServer.popups : []
NotificationChild {
id: child
width: notificationColumn.width - 80
anchors.horizontalCenter: notificationColumn.horizontalCenter
y: {
var pos = 0;
for (let i = 0; i < index; i++) {
var prev = rep.itemAt(i);
if (prev)
pos += prev.height + root.innerSpacing;
}
return pos + 20;
}
Component.onCompleted: {
if (!modelData.shown)
modelData.shown = true;
}
title: modelData.summary
body: modelData.body
image: modelData.image || modelData.appIcon
rawNotif: modelData
tracked: modelData.shown
buttons: modelData.actions.map((action) => {
return ({
"label": action.text,
"onClick": () => {
return action.invoke();
}
});
})
Behavior on y {
enabled: Config.runtime.appearance.animations.enabled
NumberAnimation {
duration: Metrics.chronoDuration("normal")
easing.type: Easing.InOutExpo
}
}
}
}
}
}
mask: Region {
width: window.width
height: {
var total = 0;
for (let i = 0; i < rep.count; i++) {
var child = rep.itemAt(i);
if (child)
total += child.height + (i < rep.count - 1 ? root.innerSpacing : 0);
}
return total;
}
}
}
}