import QtQuick import Quickshell import Quickshell.Io import Quickshell.Wayland import "." as M M.BarSection { id: root spacing: M.Theme.moduleSpacing opacity: M.Modules.backlight.enable && percent > 0 ? 1 : 0 visible: opacity > 0 tooltip: "" property int percent: 0 property bool _panelHovered: false property bool _osdActive: false readonly property bool _showPanel: root._hovered || _panelHovered || _osdActive onPercentChanged: if (percent > 0) _flashPanel() function _flashPanel() { _osdActive = true; _osdTimer.restart(); } Timer { id: _osdTimer interval: 1500 onTriggered: root._osdActive = false } Process { id: adjProc property string cmd: "" command: ["sh", "-c", cmd] onRunningChanged: if (!running && cmd !== "") current.reload() } function adjust(delta) { const step = M.Modules.backlight.step || 5; adjProc.cmd = delta > 0 ? "light -A " + step : "light -U " + step; adjProc.running = true; } 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 running: true command: ["sh", "-c", "ls /sys/class/backlight/ 2>/dev/null | head -1"] stdout: StdioCollector { onStreamFinished: { const dev = text.trim(); if (dev) root._blDev = "/sys/class/backlight/" + dev; } } } FileView { id: current path: root._blDev ? root._blDev + "/brightness" : "" watchChanges: true onFileChanged: reload() onLoaded: root._update() } FileView { id: max path: root._blDev ? root._blDev + "/max_brightness" : "" onLoaded: root._update() } function _update() { const c = parseInt(current.text()); const m = parseInt(max.text()); if (m > 0) root.percent = Math.round((c / m) * 100); } M.BarIcon { icon: "\uF185" color: M.Theme.base0A anchors.verticalCenter: parent.verticalCenter } M.BarLabel { label: root.percent + "%" minText: "100%" color: M.Theme.base0A anchors.verticalCenter: parent.verticalCenter } 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 } } } } }