waffles: osd

This commit is contained in:
end-4
2025-11-23 11:39:11 +01:00
parent 6e986fa8b0
commit 3087e5da92
11 changed files with 337 additions and 10 deletions
@@ -0,0 +1,21 @@
import QtQuick
import Quickshell
import Quickshell.Hyprland
import qs.services
import qs.modules.waffle.looks
OSDValue {
id: root
property var focusedScreen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name)
property var brightnessMonitor: Brightness.getMonitorForScreen(focusedScreen)
iconName: "weather-sunny"
value: brightnessMonitor?.brightness ?? 0
showNumber: false
Connections {
target: Brightness
function onBrightnessChanged() {
root.timer.restart();
}
}
}
@@ -0,0 +1,65 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import qs
import qs.services
import qs.modules.common
import qs.modules.common.functions
import qs.modules.waffle.looks
WBarAttachedPanelContent {
id: root
required property string iconName
property real value
property bool showNumber: true
borderColor: Looks.colors.ambientShadow
property Timer timer: Timer {
id: autoCloseTimer
running: true
interval: Config.options.osd.timeout
repeat: false
onTriggered: {
root.close()
}
}
contentItem: Rectangle {
anchors.centerIn: parent
color: Looks.colors.bg1Base
radius: Looks.radius.medium
implicitWidth: root.showNumber ? 192 : 170
implicitHeight: 46
RowLayout {
id: contentRow
anchors.fill: parent
anchors.margins: 12
spacing: 12
FluentIcon {
Layout.alignment: Qt.AlignVCenter
icon: root.iconName
implicitSize: 18
}
WProgressBar {
id: progressBar
value: root.value
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
Layout.rightMargin: root.showNumber ? 0 : 3
}
WTextWithFixedWidth {
visible: root.showNumber
text: Math.round(root.value * 100)
// longestText: "100"
implicitWidth: 16
horizontalAlignment: Text.AlignHCenter
}
}
}
}
@@ -0,0 +1,22 @@
import QtQuick
import qs.services
import qs.modules.waffle.looks
OSDValue {
id: root
iconName: WIcons.volumeIcon
value: Audio.sink?.audio.volume ?? 0
Connections {
// Listen to volume changes
target: Audio.sink?.audio ?? null
function onVolumeChanged() {
if (Audio.ready)
root.timer.restart();
}
function onMutedChanged() {
if (Audio.ready)
root.timer.restart();
}
}
}
@@ -0,0 +1,154 @@
import QtQuick
import Quickshell
import Quickshell.Io
import Quickshell.Wayland
import Quickshell.Hyprland
import qs
import qs.services
import qs.modules.common
import qs.modules.common.widgets
import qs.modules.waffle.looks
Scope {
id: root
property var focusedScreen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name)
property string currentIndicator: "volume"
property var indicators: [
{
id: "volume",
sourceUrl: "VolumeOSD.qml",
globalStateValue: "osdVolumeOpen"
},
{
id: "brightness",
sourceUrl: "BrightnessOSD.qml",
globalStateValue: "osdBrightnessOpen"
},
]
function triggerBrightnessOsd() {
root.currentIndicator = "brightness";
GlobalStates.osdBrightnessOpen = true;
}
function triggerVolumeOSD() {
root.currentIndicator = "volume";
GlobalStates.osdVolumeOpen = true;
}
// Listen to brightness changes
Connections {
target: Brightness
function onBrightnessChanged() {
root.triggerBrightnessOsd();
}
}
// Listen to volume changes
Connections {
target: Audio.sink?.audio ?? null
function onVolumeChanged() {
if (Audio.ready)
root.triggerVolumeOSD();
}
function onMutedChanged() {
if (Audio.ready)
root.triggerVolumeOSD();
}
}
// Open when global state changes
Connections {
target: GlobalStates
function onOsdBrightnessOpenChanged() {
if (GlobalStates.osdBrightnessOpen)
panelLoader.active = true;
}
function onOsdVolumeOpenChanged() {
if (GlobalStates.osdVolumeOpen)
panelLoader.active = true;
}
}
// The actual thing
Loader {
id: panelLoader
active: GlobalStates.osdBrightnessOpen || GlobalStates.osdVolumeOpen
sourceComponent: PanelWindow {
id: panelWindow
Connections {
target: root
function onFocusedScreenChanged() {
osdRoot.screen = root.focusedScreen;
}
}
color: "transparent"
exclusiveZone: 0
WlrLayershell.namespace: "quickshell:wOnScreenDisplay"
WlrLayershell.layer: WlrLayer.Overlay
anchors {
top: !Config.options.waffles.bar.bottom
bottom: Config.options.waffles.bar.bottom
}
mask: Region {
item: osdIndicatorLoader
}
implicitWidth: osdIndicatorLoader.implicitWidth + osdIndicatorLoader.item.visualMargin * 2
implicitHeight: osdIndicatorLoader.implicitHeight + osdIndicatorLoader.item.visualMargin * 2
Loader {
id: osdIndicatorLoader
anchors.fill: parent
anchors.margins: item.visualMargin
source: root.indicators.find(i => i.id === root.currentIndicator)?.sourceUrl
Connections {
target: osdIndicatorLoader.item
function onClosed() {
panelLoader.active = false;
GlobalStates[root.indicators.find(i => i.id === root.currentIndicator)?.globalStateValue] = false;
}
}
Behavior on source {
id: switchBehavior
SequentialAnimation {
id: switchAnim
// Animate close of current indicator
ScriptAction {
script: {
osdIndicatorLoader.item.close()
}
}
// Wait for close anim
PauseAnimation {
duration: osdIndicatorLoader.item.closeAnimDuration
}
PropertyAction {} // The source change happens here
}
}
}
}
}
IpcHandler {
target: "osd"
function trigger() {
root.trigger();
}
}
GlobalShortcut {
name: "osdTrigger"
description: "Triggers OSD display"
onPressed: root.trigger()
}
}