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,91 @@
import qs.config
import qs.modules.components
import qs.services
import QtQuick
import Quickshell
import QtQuick.Layouts
StyledRect {
id: root
width: 200
height: 80
radius: Metrics.radius("verylarge")
color: Appearance.m3colors.m3surfaceContainerHigh
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
readonly property bool adapterPresent: Bluetooth.defaultAdapter !== null
readonly property bool enabled: Bluetooth.defaultAdapter?.enabled ?? false
readonly property var activeDevice: Bluetooth.activeDevice
readonly property string iconName: Bluetooth.icon
readonly property string statusText: {
if (!adapterPresent)
return "No adapter";
if (!enabled)
return "Disabled";
if (activeDevice)
return activeDevice.name;
return Bluetooth.defaultAdapter.discovering
? "Scanning…"
: "Enabled";
}
StyledRect {
id: iconBg
width: 50
height: 50
radius: Metrics.radius("verylarge")
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: Metrics.margin("small")
color: {
if (!enabled)
return Appearance.m3colors.m3surfaceContainerHigh;
if (activeDevice)
return Appearance.m3colors.m3primaryContainer;
return Appearance.m3colors.m3secondaryContainer;
}
MaterialSymbol {
anchors.centerIn: parent
iconSize: Metrics.iconSize(35)
icon: iconName
}
}
Column {
anchors.verticalCenter: parent.verticalCenter
anchors.left: iconBg.right
anchors.leftMargin: Metrics.margin("small")
spacing: Metrics.spacing(2)
StyledText {
text: "Bluetooth"
font.pixelSize: Metrics.fontSize("large")
elide: Text.ElideRight
width: root.width - iconBg.width - 30
}
StyledText {
text: statusText
font.pixelSize: Metrics.fontSize("small")
color: Appearance.m3colors.m3onSurfaceVariant
elide: Text.ElideRight
width: root.width - iconBg.width - 30
}
}
MouseArea {
anchors.fill: parent
onClicked: {
if (!adapterPresent)
return;
Bluetooth.defaultAdapter.enabled =
!Bluetooth.defaultAdapter.enabled;
}
}
}

View File

@@ -0,0 +1,28 @@
import qs.config
import qs.modules.components
import qs.services
import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Services.Pipewire
import Quickshell.Widgets
import QtQuick.Controls
StyledSlider {
id: brightnessSlider
Layout.fillWidth: true
from: 0
to: 1
stepSize: 0.01
property var monitor: Brightness.monitors.length > 0 ? Brightness.monitors[0] : null
value: monitor ? monitor.brightness : 0.5
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
property real level: brightnessSlider.value * 100
onMoved: if (monitor) {
monitor.setBrightness(value);
}
}

View File

@@ -0,0 +1,73 @@
import qs.config
import qs.modules.components
import qs.modules.functions
import qs.services
import QtQuick
import Quickshell
import Quickshell.Io
import QtQuick.Layouts
StyledRect {
id: root
width: 150
height: 50
radius: Metrics.radius("verylarge")
color: Appearance.m3colors.m3surfaceContainerHigh
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
property bool flightMode
property string flightModeText: flightMode ? "Enabled" : "Disabled"
Process {
id: toggleflightModeProc
running: false
command: []
function toggle() {
flightMode = !flightMode;
const cmd = flightMode ? "off" : "on";
toggleflightModeProc.command = ["bash", "-c", `nmcli radio all ${cmd}`];
toggleflightModeProc.running = true;
}
}
StyledRect {
id: iconBg
width: 50
height: 50
radius: Metrics.radius("large")
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: Metrics.margin(10)
color: !flightMode ? Appearance.m3colors.m3surfaceContainerHigh : Appearance.m3colors.m3primaryContainer
MaterialSymbol {
anchors.centerIn: parent
iconSize: Metrics.iconSize(35)
icon: "flight"
}
}
Column {
anchors.verticalCenter: parent.verticalCenter
anchors.left: iconBg.right
anchors.leftMargin: Metrics.margin(10)
StyledText {
text: "Flight Mode"
font.pixelSize: Metrics.fontSize(20)
}
StyledText {
text: flightModeText
font.pixelSize: Metrics.fontSize("small")
}
}
MouseArea {
anchors.fill: parent
propagateComposedEvents: true
onClicked: toggleflightModeProc.toggle()
}
}

View File

@@ -0,0 +1,196 @@
import Qt5Compat.GraphicalEffects
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Widgets
import Quickshell.Io
import qs.config
import qs.modules.functions
import qs.modules.interface.notifications
import qs.modules.components
import qs.services
StyledRect {
id: root
Layout.fillWidth: true
radius: Metrics.radius("normal")
color: Appearance.m3colors.m3surfaceContainer
ClippingRectangle {
color: Appearance.colors.colLayer1
radius: Metrics.radius("normal")
implicitHeight: 90
anchors.fill: parent
RowLayout {
anchors.fill: parent
spacing: Metrics.margin("small")
ClippingRectangle {
implicitWidth: 140
implicitHeight: 140
Layout.leftMargin: Metrics.margin("large")
radius: Metrics.radius("normal")
clip: true
color: Appearance.colors.colLayer2
Image {
anchors.fill: parent
source: Mpris.artUrl
fillMode: Image.PreserveAspectCrop
cache: true
}
}
ColumnLayout {
Layout.fillWidth: true
Layout.rightMargin: Metrics.margin("small")
spacing: Metrics.spacing(2)
Text {
text: Mpris.albumTitle
elide: Text.ElideRight
Layout.maximumWidth: 190
font.family: Metrics.fontFamily("title")
font.pixelSize: Metrics.fontSize("hugeass")
font.bold: true
color: Appearance.colors.colOnLayer2
}
Text {
text: Mpris.albumArtist
elide: Text.ElideRight
Layout.maximumWidth: 160
font.family: Metrics.fontFamily("main")
font.pixelSize: Metrics.fontSize("normal")
color: Appearance.colors.colSubtext
}
RowLayout {
Layout.fillWidth: true
spacing: Metrics.spacing(12)
Process {
id: control
}
Button {
Layout.preferredWidth: 36
Layout.preferredHeight: 36
onClicked: Quickshell.execDetached(["playerctl", "previous"])
background: Rectangle {
radius: Metrics.radius("large")
color: Appearance.colors.colLayer2
}
contentItem: MaterialSymbol {
anchors.centerIn: parent
icon: "skip_previous"
font.pixelSize: Metrics.fontSize(24)
color: Appearance.colors.colOnLayer2
fill: 1
}
}
Button {
Layout.preferredWidth: 42
Layout.preferredHeight: 42
onClicked: Quickshell.execDetached(["playerctl", "play-pause"])
background: Rectangle {
radius: Metrics.radius("full")
color: Appearance.colors.colPrimary
}
contentItem: MaterialSymbol {
anchors.bottom: parent.bottom
anchors.top: parent.top
icon: "play_arrow"
font.pixelSize: Metrics.fontSize(36)
color: Appearance.colors.colOnPrimary
fill: 1
}
}
Button {
Layout.preferredWidth: 36
Layout.preferredHeight: 36
onClicked: Quickshell.execDetached(["playerctl", "next"])
background: Rectangle {
radius: Metrics.radius("large")
color: Appearance.colors.colLayer2
}
contentItem: MaterialSymbol {
anchors.centerIn: parent
icon: "skip_next"
font.pixelSize: Metrics.fontSize(24)
color: Appearance.colors.colOnLayer2
fill: 1
}
}
}
RowLayout {
Layout.topMargin: Metrics.margin(15)
Layout.fillWidth: true
spacing: Metrics.spacing(12)
Text {
text: Mpris.formatTime(Mpris.positionSec)
font.pixelSize: Metrics.fontSize("smallest")
color: Appearance.colors.colSubtext
}
Item {
Layout.fillWidth: true
implicitHeight: 20
Rectangle {
anchors.fill: parent
radius: Metrics.radius("full")
color: Appearance.colors.colLayer2
}
Rectangle {
width: parent.width * (Mpris.lengthSec > 0 ? Mpris.positionSec / Mpris.lengthSec : 0)
radius: Metrics.radius("full")
color: Appearance.colors.colPrimary
anchors {
left: parent.left
top: parent.top
bottom: parent.bottom
}
}
}
Text {
text: Mpris.formatTime(Mpris.lengthSec)
font.pixelSize: Metrics.fontSize("smallest")
color: Appearance.colors.colSubtext
}
}
}
}
}
}

View File

@@ -0,0 +1,78 @@
import qs.config
import qs.modules.components
import qs.modules.functions
import qs.services
import QtQuick
import Quickshell
import QtQuick.Layouts
StyledRect {
id: root
width: 150
height: 50
radius: Metrics.radius("verylarge")
color: Appearance.m3colors.m3surfaceContainerHigh
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
// Service bindings
readonly property bool wifiEnabled: Network.wifiEnabled
readonly property bool hasActive: Network.active !== null
readonly property string iconName: Network.icon
readonly property string titleText: Network.label
readonly property string statusText: Network.status
// Icon background
StyledRect {
id: iconBg
width: 50
height: 50
radius: Metrics.radius("large")
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: Metrics.margin(10)
color: {
if (!wifiEnabled)
return Appearance.m3colors.m3surfaceContainerHigh;
if (hasActive)
return Appearance.m3colors.m3primaryContainer;
return Appearance.m3colors.m3secondaryContainer;
}
MaterialSymbol {
anchors.centerIn: parent
icon: iconName
iconSize: Metrics.iconSize(35)
}
}
// Labels
Column {
anchors.verticalCenter: parent.verticalCenter
anchors.left: iconBg.right
anchors.leftMargin: Metrics.margin(10)
spacing: Metrics.spacing(2)
StyledText {
text: titleText
font.pixelSize: Metrics.fontSize(20)
elide: Text.ElideRight
width: root.width - iconBg.width - 30
}
StyledText {
text: statusText
font.pixelSize: Metrics.fontSize("small")
color: Appearance.m3colors.m3onSurfaceVariant
elide: Text.ElideRight
width: root.width - iconBg.width - 30
}
}
// Interaction
MouseArea {
anchors.fill: parent
onClicked: Network.toggleWifi()
}
}

View File

@@ -0,0 +1,33 @@
import QtQuick
import QtQuick.Layouts
import Quickshell
import qs.services
import qs.config
import qs.modules.components
Rectangle {
id: root
property bool nightTime
width: 200
height: 80
radius: Metrics.radius("childish")
color: !nightTime ? Appearance.m3colors.m3surfaceContainer : Appearance.m3colors.m3paddingContainer
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
Layout.margins: 0
MaterialSymbol {
anchors.centerIn: parent
iconSize: Metrics.iconSize(35)
icon: "coffee"
}
MouseArea {
anchors.fill: parent
onClicked: {
nightTime = !nightTime;
nightTime ? Quickshell.execDetached(["gammastep", "-O", "4000"]) : Quickshell.execDetached(["killall", "gammastep"]);
}
}
}

View File

@@ -0,0 +1,110 @@
import Qt5Compat.GraphicalEffects
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import qs.modules.functions
import qs.modules.interface.notifications
import qs.services
import qs.config
import qs.modules.components
StyledRect {
id: root
Layout.fillWidth: true
radius: Metrics.radius("normal")
color: Appearance.colors.colLayer1
property bool dndActive: Config.runtime.notifications.doNotDisturb
function toggleDnd() {
Config.updateKey("notifications.doNotDisturb", !dndActive);
}
StyledButton {
id: clearButton
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.bottomMargin: Metrics.margin(10)
anchors.rightMargin: Metrics.margin(10)
icon: "clear_all"
text: "Clear"
implicitHeight: 40
implicitWidth: 100
secondary: true
onClicked: {
for (let i = 0; i < NotifServer.history.length; i++) {
let n = NotifServer.history[i];
if (n?.notification) n.notification.dismiss();
}
}
}
StyledButton {
id: silentButton
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.bottomMargin: Metrics.margin(10)
anchors.rightMargin: clearButton.implicitWidth + Metrics.margin(15)
text: "Silent"
icon: "do_not_disturb_on"
implicitHeight: 40
implicitWidth: 100
secondary: true
checkable: true
checked: Config.runtime.notifications.doNotDisturb
onClicked: {
toggleDnd()
}
}
StyledText {
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.bottomMargin: Metrics.margin(15)
anchors.leftMargin: Metrics.margin(15)
text: NotifServer.history.length + " Notifications"
}
StyledText {
anchors.centerIn: parent
text: "No notifications"
visible: NotifServer.history.length < 1
font.pixelSize: Metrics.fontSize("huge")
}
ListView {
id: notifList
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: clearButton.top
anchors.margins: Metrics.margin(10)
clip: true
spacing: Metrics.spacing(8)
boundsBehavior: Flickable.StopAtBounds
ScrollBar.vertical: ScrollBar { }
model: Config.runtime.notifications.enabled
? NotifServer.history
: []
delegate: NotificationChild {
width: notifList.width
title: model.summary
body: model.body
image: model.image || model.appIcon
rawNotif: model
buttons: model.actions.map((action) => ({
"label": action.text,
"onClick": () => action.invoke()
}))
}
}
}

View File

@@ -0,0 +1,34 @@
import QtQuick
import QtQuick.Layouts
import Quickshell
import qs.services
import qs.config
import qs.modules.components
Rectangle {
id: root
readonly property bool isDark: Config.runtime.appearance.theme === "dark"
property string themestatusicon: isDark ? "dark_mode" : "light_mode"
width: 200
height: 80
radius: Metrics.radius("childish")
color: Appearance.m3colors.m3paddingContainer
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
Layout.margins: 0
MaterialSymbol {
anchors.centerIn: parent
iconSize: Metrics.iconSize(35)
icon: themestatusicon
}
MouseArea {
anchors.fill: parent
onClicked: {
Quickshell.execDetached(["nucleus", "ipc", "call", "global", "toggleTheme"]);
}
}
}

View File

@@ -0,0 +1,34 @@
import qs.config
import qs.modules.components
import qs.services
import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Services.Pipewire
import Quickshell.Widgets
import QtQuick.Controls
StyledSlider {
id: volSlider
Layout.fillWidth: true
from: 0
to: 1
stepSize: 0.01
value: sink ? sink.volume : 0
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
property real level: volSlider.value * 100
PwObjectTracker {
objects: [Pipewire.defaultAudioSink]
}
property var sink: Pipewire.defaultAudioSink?.audio
onMoved: {
if (sink) sink.volume = value
}
}