Files
s0wlz (Matthias Puchstein) c5f7162ebb quickshell: add initial bar config with per-monitor workspaces
- Vertical bar on DP-2 with rounded-square pills throughout
- Per-monitor workspace groups sorted by screen x position, with
  Nerd Font icons for named workspaces and apex-neon red active indicator
- Bar layout: datetime+weather top, workspaces centered, gamemode+media+notif+system bottom
- Popouts anchor to triggering icon (top-right for datetime/weather, bottom-right for media/notif/system)
- Lock command switched from hyprlock to swaylock
- Hyprland blur/ignore_alpha layerrules for quickshell namespace

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-05 20:00:54 +02:00

179 lines
6.4 KiB
QML

import Quickshell
import Quickshell.Io
import Quickshell.Services.Pipewire
import Quickshell.Wayland
import QtQuick
import QtQuick.Layouts
import "../shared" as Shared
Scope {
id: root
PwObjectTracker { objects: [Pipewire.defaultAudioSink, Pipewire.defaultAudioSource] }
property string osdIcon: ""
property real osdValue: 0
property bool osdMuted: false
property string osdLabel: ""
property bool osdVisible: false
function showOsd(icon, value, muted, label) {
osdIcon = icon;
osdValue = value;
osdMuted = muted;
osdLabel = label;
osdVisible = true;
hideTimer.restart();
}
Timer {
id: hideTimer
interval: 1500
onTriggered: root.osdVisible = false
}
// Event-driven audio change detection
property var sinkAudio: Pipewire.defaultAudioSink?.audio ?? null
property var sourceAudio: Pipewire.defaultAudioSource?.audio ?? null
Connections {
target: root.sinkAudio
function onVolumeChanged() {
let a = root.sinkAudio;
if (a) root.showOsd("\u{f057e}", a.volume, a.muted, "Volume");
}
function onMutedChanged() {
let a = root.sinkAudio;
if (a) root.showOsd(a.muted ? "\u{f057f}" : "\u{f057e}", a.volume, a.muted, a.muted ? "Muted" : "Volume");
}
}
Connections {
target: root.sourceAudio
function onVolumeChanged() {
let a = root.sourceAudio;
if (a) root.showOsd("\u{f036c}", a.volume, a.muted, "Mic");
}
function onMutedChanged() {
let a = root.sourceAudio;
if (a) root.showOsd(a.muted ? "\u{f036d}" : "\u{f036c}", a.volume, a.muted, a.muted ? "Mic muted" : "Mic");
}
}
// Brightness monitoring via brightnessctl (auto-disables if no backlight device)
property real lastBrightness: -1
property bool hasBrightness: false
Process {
id: brightProc
command: ["brightnessctl", "-m"]
stdout: StdioCollector {
onStreamFinished: {
let parts = this.text.trim().split(",");
if (parts.length >= 5) {
root.hasBrightness = true;
let pct = parseInt(parts[4]) / 100;
if (root.lastBrightness >= 0 && Math.abs(pct - root.lastBrightness) > 0.005)
root.showOsd("\u{f00df}", pct, false, "Brightness");
root.lastBrightness = pct;
}
}
}
}
Timer {
interval: 500
running: root.hasBrightness
repeat: true
onTriggered: { brightProc.running = false; brightProc.running = true; }
}
Component.onCompleted: { brightProc.running = true; }
Variants {
model: Quickshell.screens
delegate: Component {
PanelWindow {
required property var modelData
screen: modelData
WlrLayershell.namespace: "quickshell:osd"
surfaceFormat { opaque: false }
visible: modelData.name === Shared.Config.monitor && root.osdVisible
anchors {
top: true
right: true
bottom: true
}
exclusionMode: ExclusionMode.Ignore
implicitWidth: Shared.Theme.barWidth + Shared.Theme.popoutWidth + 12
color: "transparent"
Rectangle {
anchors.right: parent.right
anchors.rightMargin: Shared.Theme.barWidth + 12
anchors.verticalCenter: parent.verticalCenter
width: 44
height: 180
radius: 22
color: Shared.Theme.popoutBackground
border.width: 1
border.color: Shared.Theme.borderSubtle
opacity: root.osdVisible ? 1.0 : 0.0
scale: root.osdVisible ? 1.0 : 0.95
transformOrigin: Item.Right
Behavior on opacity { NumberAnimation { duration: 120; easing.type: Easing.OutCubic } }
Behavior on scale { NumberAnimation { duration: 150; easing.type: Easing.OutCubic } }
ColumnLayout {
anchors.fill: parent
anchors.topMargin: 12
anchors.bottomMargin: 12
spacing: 8
// Percentage
Text {
Layout.alignment: Qt.AlignHCenter
text: root.osdMuted ? "M" : Math.round(root.osdValue * 100)
color: root.osdMuted ? Shared.Theme.overlay0 : Shared.Theme.text
font.pixelSize: Shared.Theme.fontSmall
font.family: Shared.Theme.fontFamily
font.bold: true
}
// Vertical bar (fills bottom-up)
Rectangle {
Layout.fillHeight: true
Layout.alignment: Qt.AlignHCenter
implicitWidth: 6
radius: 3
color: Shared.Theme.surface0
Rectangle {
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
height: root.osdMuted ? 0 : parent.height * Math.min(1, root.osdValue)
radius: 3
color: root.osdMuted ? Shared.Theme.overlay0 : Shared.Theme.sky
Behavior on height { NumberAnimation { duration: 80; easing.type: Easing.OutCubic } }
}
}
// Icon
Text {
Layout.alignment: Qt.AlignHCenter
text: root.osdIcon
color: root.osdMuted ? Shared.Theme.overlay0 : Shared.Theme.sky
font.pixelSize: 16
font.family: Shared.Theme.iconFont
}
}
}
}
}
}
}