From 0c2d5998ff2430bc6f556c297471b84f12df9411 Mon Sep 17 00:00:00 2001 From: Damocles Date: Sun, 12 Apr 2026 18:11:30 +0200 Subject: [PATCH] backlight: hover slider panel, remove osd dependency --- modules/Backlight.qml | 156 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 152 insertions(+), 4 deletions(-) diff --git a/modules/Backlight.qml b/modules/Backlight.qml index 6d562b8..be98a29 100644 --- a/modules/Backlight.qml +++ b/modules/Backlight.qml @@ -1,6 +1,7 @@ import QtQuick import Quickshell import Quickshell.Io +import Quickshell.Wayland import "." as M M.BarSection { @@ -8,11 +9,11 @@ M.BarSection { spacing: M.Theme.moduleSpacing opacity: M.Modules.backlight.enable && percent > 0 ? 1 : 0 visible: opacity > 0 - tooltip: "Brightness: " + root.percent + "%" + tooltip: "" property int percent: 0 - onPercentChanged: if (percent > 0) - M.OsdState.show(percent / 100, "\uF185", root.mapToGlobal(root.width / 2, 0).x - (QsWindow.window?.screen?.x ?? 0), QsWindow.window?.screen ?? null) + property bool _panelHovered: false + readonly property bool _showPanel: root._hovered || _panelHovered Process { id: adjProc @@ -28,7 +29,11 @@ M.BarSection { adjProc.running = true; } - // Detect backlight device dynamically + function setPercent(pct) { + adjProc.cmd = "light -S " + Math.round(Math.max(0, Math.min(100, pct))); + adjProc.running = true; + } + property string _blDev: "" Process { id: detectBl @@ -77,4 +82,147 @@ M.BarSection { WheelHandler { onWheel: event => root.adjust(event.angleDelta.y) } + + PanelWindow { + id: panel + + screen: QsWindow.window?.screen ?? null + visible: _winVisible + color: "transparent" + + property bool _winVisible: false + + WlrLayershell.layer: WlrLayer.Overlay + WlrLayershell.exclusiveZone: 0 + WlrLayershell.namespace: "nova-backlight" + + anchors.top: true + anchors.left: true + + margins.top: 0 + margins.left: Math.max(0, Math.min( + Math.round(root.mapToGlobal(root.width / 2, 0).x - (QsWindow.window?.screen?.x ?? 0) - implicitWidth / 2), + (panel.screen?.width ?? 1920) - implicitWidth + )) + + implicitWidth: panelContent.width + implicitHeight: panelContent.height + + Connections { + target: root + function on_ShowPanelChanged() { + if (root._showPanel) { + panel._winVisible = true; + hideAnim.stop(); + showAnim.start(); + } else { + showAnim.stop(); + hideAnim.start(); + } + } + } + + ParallelAnimation { + id: showAnim + NumberAnimation { target: panelContent; property: "opacity"; to: 1; duration: 120; easing.type: Easing.OutCubic } + NumberAnimation { target: panelContent; property: "y"; to: 0; duration: 150; easing.type: Easing.OutCubic } + } + + ParallelAnimation { + id: hideAnim + NumberAnimation { target: panelContent; property: "opacity"; to: 0; duration: 150; easing.type: Easing.InCubic } + NumberAnimation { target: panelContent; property: "y"; to: -panelContent.height; duration: 150; easing.type: Easing.InCubic } + onFinished: panel._winVisible = false + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + onContainsMouseChanged: root._panelHovered = containsMouse + } + + Rectangle { + x: panelContent.x + y: panelContent.y + width: panelContent.width + height: panelContent.height + color: M.Theme.base01 + opacity: panelContent.opacity * Math.max(M.Theme.barOpacity, 0.85) + topLeftRadius: 0 + topRightRadius: 0 + bottomLeftRadius: M.Theme.radius + bottomRightRadius: M.Theme.radius + } + + Column { + id: panelContent + width: 200 + opacity: 0 + y: -height + + Item { + width: parent.width + height: 36 + + Text { + id: blIcon + anchors.left: parent.left + anchors.leftMargin: 12 + anchors.verticalCenter: parent.verticalCenter + text: "\uF185" + color: M.Theme.base0A + font.pixelSize: M.Theme.fontSize + 2 + font.family: M.Theme.iconFontFamily + } + + Item { + id: slider + anchors.left: blIcon.right + anchors.leftMargin: 8 + anchors.right: blLabel.left + anchors.rightMargin: 8 + anchors.verticalCenter: parent.verticalCenter + height: 6 + + Rectangle { + anchors.fill: parent + color: M.Theme.base02 + radius: 3 + } + + Rectangle { + width: parent.width * root.percent / 100 + height: parent.height + color: M.Theme.base0A + radius: 3 + + Behavior on width { NumberAnimation { duration: 80 } } + } + + MouseArea { + anchors.fill: parent + anchors.margins: -6 + cursorShape: Qt.PointingHandCursor + onPressed: mouse => _set(mouse) + onPositionChanged: mouse => { if (pressed) _set(mouse); } + function _set(mouse) { + root.setPercent(mouse.x / slider.width * 100); + } + } + } + + Text { + id: blLabel + anchors.right: parent.right + anchors.rightMargin: 12 + anchors.verticalCenter: parent.verticalCenter + text: root.percent + "%" + color: M.Theme.base05 + font.pixelSize: M.Theme.fontSize + font.family: M.Theme.fontFamily + width: 30 + } + } + } + } }