diff --git a/modules/Backlight.qml b/modules/Backlight.qml index 8eddbf2..a0ccc90 100644 --- a/modules/Backlight.qml +++ b/modules/Backlight.qml @@ -2,13 +2,26 @@ import QtQuick import Quickshell.Io import "." as M -Row { +M.BarSection { id: root spacing: 4 visible: percent > 0 + tooltip: "Brightness: " + root.percent + "%" property int percent: 0 + Process { + id: adjProc + property string cmd: "" + command: ["sh", "-c", cmd] + onRunningChanged: if (!running && cmd !== "") current.reload() + } + + function adjust(delta) { + adjProc.cmd = delta > 0 ? "light -A 5" : "light -U 5"; + adjProc.running = true; + } + FileView { id: current path: "/sys/class/backlight/intel_backlight/brightness" @@ -36,4 +49,8 @@ Row { font.family: M.Theme.fontFamily anchors.verticalCenter: parent.verticalCenter } + + WheelHandler { + onWheel: event => root.adjust(event.angleDelta.y) + } } diff --git a/modules/BarIcon.qml b/modules/BarIcon.qml index 82a4df5..3d22c14 100644 --- a/modules/BarIcon.qml +++ b/modules/BarIcon.qml @@ -1,12 +1,20 @@ import QtQuick +import QtQuick.Controls import "." as M Text { property string icon: "" + property string tooltip: "" text: icon color: M.Theme.base05 font.pixelSize: M.Theme.fontSize + 1 font.family: M.Theme.iconFontFamily verticalAlignment: Text.AlignVCenter + + HoverHandler { id: _hover } + ToolTip { + visible: _hover.hovered && parent.tooltip !== "" + text: parent.tooltip + } } diff --git a/modules/BarLabel.qml b/modules/BarLabel.qml index c16119f..d92d0f3 100644 --- a/modules/BarLabel.qml +++ b/modules/BarLabel.qml @@ -1,12 +1,20 @@ import QtQuick +import QtQuick.Controls import "." as M Text { property string label: "" + property string tooltip: "" text: label color: M.Theme.base05 font.pixelSize: M.Theme.fontSize font.family: M.Theme.fontFamily verticalAlignment: Text.AlignVCenter + + HoverHandler { id: _hover } + ToolTip { + visible: _hover.hovered && parent.tooltip !== "" + text: parent.tooltip + } } diff --git a/modules/BarSection.qml b/modules/BarSection.qml new file mode 100644 index 0000000..7c9b236 --- /dev/null +++ b/modules/BarSection.qml @@ -0,0 +1,12 @@ +import QtQuick +import QtQuick.Controls + +Row { + property string tooltip: "" + + HoverHandler { id: _hover } + ToolTip { + visible: _hover.hovered && parent.tooltip !== "" + text: parent.tooltip + } +} diff --git a/modules/Battery.qml b/modules/Battery.qml index 4290633..11d6c1b 100644 --- a/modules/Battery.qml +++ b/modules/Battery.qml @@ -2,10 +2,17 @@ import QtQuick import Quickshell.Services.UPower import "." as M -Row { +M.BarSection { id: root spacing: 4 visible: UPower.displayDevice?.isLaptopBattery ?? false + tooltip: { + const state = root.charging ? "Charging" : "Discharging"; + const t = root.charging ? root.dev?.timeToFull : root.dev?.timeToEmpty; + const mins = t ? Math.round(t / 60) : 0; + const timeStr = mins > 0 ? "\n" + Math.floor(mins / 60) + "h " + (mins % 60) + "m " + (root.charging ? "until full" : "remaining") : ""; + return state + " \u2014 " + Math.round(root.pct) + "%" + timeStr; + } readonly property var dev: UPower.displayDevice readonly property real pct: (dev?.percentage ?? 0) * 100 diff --git a/modules/Bluetooth.qml b/modules/Bluetooth.qml index a7ac784..07e2c56 100644 --- a/modules/Bluetooth.qml +++ b/modules/Bluetooth.qml @@ -2,9 +2,10 @@ import QtQuick import Quickshell.Io import "." as M -Row { +M.BarSection { id: root spacing: 4 + tooltip: root.status === "connected" ? "Bluetooth: " + root.device : "Bluetooth: on" property string status: "off" property string device: "" diff --git a/modules/Clock.qml b/modules/Clock.qml index 340da86..6506124 100644 --- a/modules/Clock.qml +++ b/modules/Clock.qml @@ -2,15 +2,13 @@ import QtQuick import Quickshell import "." as M -Text { +M.BarLabel { SystemClock { id: clock - precision: SystemClock.Minutes + precision: SystemClock.Seconds } - text: Qt.formatDateTime(clock.date, "ddd, dd. MMM HH:mm") - color: M.Theme.base05 font.pixelSize: M.Theme.fontSize + 1 - font.family: M.Theme.fontFamily - verticalAlignment: Text.AlignVCenter + label: Qt.formatDateTime(clock.date, "ddd, dd. MMM HH:mm") + tooltip: Qt.formatDateTime(clock.date, "dddd, dd. MMMM yyyy\nHH:mm:ss") } diff --git a/modules/Cpu.qml b/modules/Cpu.qml index bb0a860..00a1eb6 100644 --- a/modules/Cpu.qml +++ b/modules/Cpu.qml @@ -2,13 +2,13 @@ import QtQuick import Quickshell.Io import "." as M -Row { +M.BarSection { id: root spacing: 2 + tooltip: "CPU: " + root.usage + "%\n" + root.freqGhz.toFixed(2) + " GHz" property int usage: 0 property real freqGhz: 0 - property var _prev: null FileView { @@ -25,10 +25,7 @@ Row { if (dTotal > 0) root.usage = Math.round((1 - dIdle / dTotal) * 100); } - root._prev = { - idle, - total - }; + root._prev = { idle, total }; } } FileView { @@ -36,8 +33,7 @@ Row { path: "/proc/cpuinfo" onLoaded: { const lines = text().split("\n").filter(l => l.startsWith("cpu MHz")); - if (lines.length === 0) - return; + if (lines.length === 0) return; const sum = lines.reduce((a, l) => a + parseFloat(l.split(":")[1]), 0); root.freqGhz = sum / lines.length / 1000; } @@ -46,10 +42,7 @@ Row { interval: 1000 running: true repeat: true - onTriggered: { - stat.reload(); - cpuinfo.reload(); - } + onTriggered: { stat.reload(); cpuinfo.reload(); } } M.BarIcon { diff --git a/modules/Disk.qml b/modules/Disk.qml index d1e0417..df33abc 100644 --- a/modules/Disk.qml +++ b/modules/Disk.qml @@ -2,9 +2,10 @@ import QtQuick import Quickshell.Io import "." as M -Row { +M.BarSection { id: root spacing: 2 + tooltip: root.freePct + "% free of " + root.totalTb.toFixed(1) + " TB" property int freePct: 0 property real totalTb: 0 diff --git a/modules/IdleInhibitor.qml b/modules/IdleInhibitor.qml index 41423f1..d2b0fbe 100644 --- a/modules/IdleInhibitor.qml +++ b/modules/IdleInhibitor.qml @@ -4,6 +4,7 @@ import "." as M M.BarIcon { id: root + tooltip: "Idle inhibition: " + (root.active ? "active" : "inactive") property bool active: false diff --git a/modules/Memory.qml b/modules/Memory.qml index 9711d86..1faab25 100644 --- a/modules/Memory.qml +++ b/modules/Memory.qml @@ -2,9 +2,10 @@ import QtQuick import Quickshell.Io import "." as M -Row { +M.BarSection { id: root spacing: 2 + tooltip: "Memory: " + root.percent + "% used" property int percent: 0 @@ -15,8 +16,7 @@ Row { const m = {}; text().split("\n").forEach(l => { const [k, v] = l.split(":"); - if (v) - m[k.trim()] = parseInt(v.trim()); + if (v) m[k.trim()] = parseInt(v.trim()); }); const total = m.MemTotal; const avail = m.MemAvailable; diff --git a/modules/Mpris.qml b/modules/Mpris.qml index 52e34f5..5ed5737 100644 --- a/modules/Mpris.qml +++ b/modules/Mpris.qml @@ -2,10 +2,19 @@ import QtQuick import Quickshell.Services.Mpris import "." as M -Row { +M.BarSection { id: root spacing: 4 visible: player !== null + tooltip: { + const p = root.player; + if (!p) return ""; + const parts = []; + if (p.trackTitle) parts.push(p.trackTitle); + if (p.trackArtists?.length) parts.push(p.trackArtists.join(", ")); + if (p.trackAlbum) parts.push(p.trackAlbum); + return parts.join("\n") || p.identity; + } readonly property MprisPlayer player: Mpris.players.values[0] ?? null readonly property bool playing: player?.playbackState === MprisPlaybackState.Playing diff --git a/modules/Network.qml b/modules/Network.qml index 74589e5..e9451ef 100644 --- a/modules/Network.qml +++ b/modules/Network.qml @@ -2,9 +2,15 @@ import QtQuick import Quickshell.Io import "." as M -Row { +M.BarSection { id: root spacing: 4 + tooltip: { + if (root.state === "wifi") return "WiFi: " + root.essid + (root.ifname ? "\nInterface: " + root.ifname : ""); + if (root.state === "eth") return "Ethernet: " + root.ifname; + if (root.state === "linked") return "Linked: " + root.ifname; + return "Disconnected"; + } property string ifname: "" property string essid: "" @@ -44,12 +50,9 @@ Row { M.BarIcon { icon: { - if (root.state === "wifi") - return "\uF1EB"; - if (root.state === "eth") - return "\uDB80\uDE00"; - if (root.state === "linked") - return "\uDB85\uDE16"; + if (root.state === "wifi") return "\uF1EB"; + if (root.state === "eth") return "\uDB80\uDE00"; + if (root.state === "linked") return "\uDB85\uDE16"; return "\uDB82\uDCFD"; } anchors.verticalCenter: parent.verticalCenter diff --git a/modules/Notifications.qml b/modules/Notifications.qml index 63f3890..fdca87d 100644 --- a/modules/Notifications.qml +++ b/modules/Notifications.qml @@ -2,9 +2,15 @@ import QtQuick import Quickshell.Io import "." as M -Row { +M.BarSection { id: root spacing: 4 + tooltip: { + const parts = [root.count + " notification" + (root.count !== 1 ? "s" : "")]; + if (root.dnd) parts.push("Do not disturb"); + if (root.inhibited) parts.push("Inhibited"); + return parts.join("\n"); + } property int count: 0 property bool dnd: false @@ -55,7 +61,5 @@ Row { clicker.running = true; } } - Process { - id: clicker - } + Process { id: clicker } } diff --git a/modules/PowerProfile.qml b/modules/PowerProfile.qml index b26d6ff..e3c0bb0 100644 --- a/modules/PowerProfile.qml +++ b/modules/PowerProfile.qml @@ -4,6 +4,7 @@ import "." as M M.BarIcon { id: root + tooltip: "Power profile: " + (root.profile || "unknown") property string profile: "" @@ -28,4 +29,20 @@ M.BarIcon { repeat: true onTriggered: proc.running = true } + + Process { + id: setter + property string next: "" + command: ["powerprofilesctl", "set", next] + onRunningChanged: if (!running && next !== "") proc.running = true + } + + TapHandler { + onTapped: { + const cycle = ["performance", "balanced", "power-saver"]; + const idx = cycle.indexOf(root.profile); + setter.next = cycle[(idx + 1) % cycle.length]; + setter.running = true; + } + } } diff --git a/modules/Temperature.qml b/modules/Temperature.qml index 62d5dd1..c302962 100644 --- a/modules/Temperature.qml +++ b/modules/Temperature.qml @@ -2,9 +2,10 @@ import QtQuick import Quickshell.Io import "." as M -Row { +M.BarSection { id: root spacing: 2 + tooltip: "Temperature: " + root.celsius + "\u00B0C" property int celsius: 0 @@ -26,7 +27,7 @@ Row { anchors.verticalCenter: parent.verticalCenter } Text { - text: root.celsius + "°C" + text: root.celsius + "\u00B0C" color: root.celsius > 80 ? M.Theme.base08 : M.Theme.base05 font.pixelSize: M.Theme.fontSize font.family: M.Theme.fontFamily diff --git a/modules/Volume.qml b/modules/Volume.qml index 92a5609..f17d676 100644 --- a/modules/Volume.qml +++ b/modules/Volume.qml @@ -2,9 +2,12 @@ import QtQuick import Quickshell.Services.Pipewire import "." as M -Row { +M.BarSection { id: root spacing: 4 + tooltip: (root.sink?.description ?? root.sink?.name ?? "Unknown sink") + + "\nVolume: " + Math.round(root.volume * 100) + "%" + + (root.muted ? "\nMuted" : "") PwObjectTracker { objects: [Pipewire.defaultAudioSink] @@ -26,4 +29,11 @@ Row { TapHandler { onTapped: if (root.sink?.audio) root.sink.audio.muted = !root.sink.audio.muted } + + WheelHandler { + onWheel: event => { + if (!root.sink?.audio) return; + root.sink.audio.volume = Math.max(0, root.sink.audio.volume + (event.angleDelta.y > 0 ? 0.05 : -0.05)); + } + } } diff --git a/modules/Weather.qml b/modules/Weather.qml index 2b65aaf..94f8bd6 100644 --- a/modules/Weather.qml +++ b/modules/Weather.qml @@ -2,10 +2,12 @@ import QtQuick import Quickshell.Io import "." as M -Text { +M.BarSection { id: root + spacing: 4 + tooltip: root.weatherTooltip - property string label: "" + property string weatherTooltip: "" Process { id: proc @@ -15,9 +17,11 @@ Text { onStreamFinished: { try { const data = JSON.parse(text); - root.label = data.text ?? ""; + label.text = data.text ?? ""; + root.weatherTooltip = data.tooltip ?? ""; } catch (e) { - root.label = ""; + label.text = ""; + root.weatherTooltip = ""; } } } @@ -29,9 +33,11 @@ Text { onTriggered: proc.running = true } - text: root.label - color: M.Theme.base05 - font.pixelSize: M.Theme.fontSize - font.family: M.Theme.iconFontFamily - verticalAlignment: Text.AlignVCenter + Text { + id: label + color: M.Theme.base05 + font.pixelSize: M.Theme.fontSize + font.family: M.Theme.iconFontFamily + anchors.verticalCenter: parent.verticalCenter + } } diff --git a/modules/Wlogout.qml b/modules/Wlogout.qml index dfcae8b..48825f3 100644 --- a/modules/Wlogout.qml +++ b/modules/Wlogout.qml @@ -4,6 +4,7 @@ import "." as M M.BarIcon { icon: "\uF011" + tooltip: "Open logout menu" Process { id: proc diff --git a/modules/qmldir b/modules/qmldir index f0c494e..77815c6 100644 --- a/modules/qmldir +++ b/modules/qmldir @@ -1,6 +1,7 @@ module modules singleton Theme 1.0 Theme.qml Bar 1.0 Bar.qml +BarSection 1.0 BarSection.qml Workspaces 1.0 Workspaces.qml WindowTitle 1.0 WindowTitle.qml Clock 1.0 Clock.qml