per-group borders, workspace hover, battery notifications, richer network tooltip

This commit is contained in:
Damocles 2026-04-12 16:32:33 +02:00
parent b8ec39f2c9
commit 0eda2c71c9
7 changed files with 69 additions and 16 deletions

View file

@ -40,6 +40,7 @@ PanelWindow {
spacing: M.Theme.barSpacing spacing: M.Theme.barSpacing
M.BarGroup { M.BarGroup {
borderColor: M.Theme.base0D
M.Clock { visible: M.Modules.clock } M.Clock { visible: M.Modules.clock }
M.Notifications { visible: M.Modules.notifications } M.Notifications { visible: M.Modules.notifications }
} }
@ -53,12 +54,15 @@ PanelWindow {
spacing: M.Theme.barSpacing spacing: M.Theme.barSpacing
M.BarGroup { M.BarGroup {
borderColor: M.Theme.base0D
M.Workspaces { bar: bar; visible: M.Modules.workspaces } M.Workspaces { bar: bar; visible: M.Modules.workspaces }
} }
M.BarGroup { M.BarGroup {
borderColor: M.Theme.base0D
M.Tray { bar: bar; visible: M.Modules.tray } M.Tray { bar: bar; visible: M.Modules.tray }
} }
M.BarGroup { M.BarGroup {
borderColor: M.Theme.base0D
M.WindowTitle { M.WindowTitle {
Layout.maximumWidth: 400 Layout.maximumWidth: 400
visible: M.Modules.windowTitle visible: M.Modules.windowTitle
@ -78,18 +82,21 @@ PanelWindow {
// Media // Media
M.BarGroup { M.BarGroup {
borderColor: M.Theme.base0E
M.Mpris {} M.Mpris {}
M.Volume { visible: M.Modules.volume } M.Volume { visible: M.Modules.volume }
} }
// Connectivity // Connectivity
M.BarGroup { M.BarGroup {
borderColor: M.Theme.base0D
M.Network { visible: M.Modules.network } M.Network { visible: M.Modules.network }
M.Bluetooth {} M.Bluetooth {}
} }
// Controls // Controls
M.BarGroup { M.BarGroup {
borderColor: M.Theme.base0A
M.Backlight {} M.Backlight {}
M.PowerProfile { visible: M.Modules.powerProfile } M.PowerProfile { visible: M.Modules.powerProfile }
M.IdleInhibitor { visible: M.Modules.idleInhibitor } M.IdleInhibitor { visible: M.Modules.idleInhibitor }
@ -97,6 +104,7 @@ PanelWindow {
// Stats // Stats
M.BarGroup { M.BarGroup {
borderColor: M.Theme.base08
M.Cpu { visible: M.Modules.cpu } M.Cpu { visible: M.Modules.cpu }
M.Memory { visible: M.Modules.memory } M.Memory { visible: M.Modules.memory }
M.Temperature { visible: M.Modules.temperature } M.Temperature { visible: M.Modules.temperature }
@ -106,6 +114,7 @@ PanelWindow {
// Power // Power
M.BarGroup { M.BarGroup {
borderColor: M.Theme.base08
M.Battery {} M.Battery {}
M.Power { bar: bar; visible: M.Modules.power } M.Power { bar: bar; visible: M.Modules.power }
} }

View file

@ -5,9 +5,10 @@ Rectangle {
id: root id: root
default property alias content: row.children default property alias content: row.children
property color borderColor: M.Theme.base02
color: "transparent" color: "transparent"
border.color: M.Theme.base02 border.color: borderColor
border.width: 1 border.width: 1
radius: M.Theme.radius radius: M.Theme.radius

View file

@ -1,4 +1,5 @@
import QtQuick import QtQuick
import Quickshell.Io
import Quickshell.Services.UPower import Quickshell.Services.UPower
import "." as M import "." as M
@ -21,6 +22,25 @@ M.BarSection {
Behavior on _stateColor { ColorAnimation { duration: 300 } } Behavior on _stateColor { ColorAnimation { duration: 300 } }
property bool _warnSent: false
property bool _critSent: false
onChargingChanged: { _warnSent = false; _critSent = false; }
onPctChanged: {
if (charging) return;
if (pct < 15 && !_critSent) {
_critSent = true; _warnSent = true;
_notif.command = ["notify-send", "--urgency=critical", "--icon=battery-low", "--category=device", "Very Low Battery", "Connect to power now!"];
_notif.running = true;
} else if (pct < 25 && !_warnSent) {
_warnSent = true;
_notif.command = ["notify-send", "--icon=battery-caution", "--category=device", "Low Battery"];
_notif.running = true;
}
}
Process { id: _notif }
M.BarIcon { M.BarIcon {
icon: { icon: {
if (root.charging) if (root.charging)

View file

@ -65,4 +65,5 @@ M.BarSection {
toggle.running = true; toggle.running = true;
} }
} }
} }

View file

@ -6,33 +6,44 @@ M.BarSection {
id: root id: root
spacing: M.Theme.moduleSpacing spacing: M.Theme.moduleSpacing
tooltip: { tooltip: {
if (root.state === "wifi") const parts = [];
return "WiFi: " + root.essid + (root.ifname ? "\nInterface: " + root.ifname : ""); if (root.state === "wifi") {
if (root.state === "eth") parts.push("WiFi: " + root.essid);
return "Ethernet: " + root.ifname; if (root.signal) parts.push("Signal: " + root.signal + "%");
if (root.state === "linked") } else if (root.state === "eth") {
return "Linked: " + root.ifname; parts.push("Ethernet");
return "Disconnected"; } else if (root.state === "linked") {
parts.push("Linked");
} else {
return "Disconnected";
}
if (root.ipAddr) parts.push("IP: " + root.ipAddr);
if (root.ifname) parts.push("Interface: " + root.ifname);
return parts.join("\n");
} }
property string ifname: "" property string ifname: ""
property string essid: "" property string essid: ""
property string state: "disconnected" property string state: "disconnected"
property string ipAddr: ""
property string signal: ""
Process { Process {
id: proc id: proc
running: true running: true
command: ["sh", "-c", "line=$(nmcli -t -f NAME,TYPE,DEVICE connection show --active 2>/dev/null | head -1); if [ -n \"$line\" ]; then echo \"$line\"; else dev=$(nmcli -t -f DEVICE,STATE device 2>/dev/null | grep ':connected' | grep -v ':unmanaged\\|:unavailable\\|:disconnected\\|:connecting' | head -1 | cut -d: -f1); if [ -n \"$dev\" ]; then echo \"linked:linked:$dev\"; fi; fi"] command: ["sh", "-c", "line=$(nmcli -t -f NAME,TYPE,DEVICE connection show --active 2>/dev/null | head -1); if [ -z \"$line\" ]; then dev=$(nmcli -t -f DEVICE,STATE device 2>/dev/null | grep ':connected' | grep -v ':unmanaged\\|:unavailable\\|:disconnected\\|:connecting' | head -1 | cut -d: -f1); [ -n \"$dev\" ] && line=\"linked:linked:$dev\"; fi; [ -z \"$line\" ] && exit 0; echo \"$line\"; dev=$(echo \"$line\" | cut -d: -f3); ip=$(nmcli -t -f IP4.ADDRESS device show \"$dev\" 2>/dev/null | head -1 | cut -d: -f2); echo \"ip:${ip:-}\"; sig=$(nmcli -t -f GENERAL.SIGNAL device show \"$dev\" 2>/dev/null | head -1 | cut -d: -f2); echo \"sig:${sig:-}\""]
stdout: StdioCollector { stdout: StdioCollector {
onStreamFinished: { onStreamFinished: {
const line = text.trim(); const lines = text.trim().split("\n");
if (!line) { if (!lines[0]) {
root.state = "disconnected"; root.state = "disconnected";
root.essid = ""; root.essid = "";
root.ifname = ""; root.ifname = "";
root.ipAddr = "";
root.signal = "";
return; return;
} }
const parts = line.split(":"); const parts = lines[0].split(":");
root.essid = parts[0] || ""; root.essid = parts[0] || "";
root.ifname = parts[2] || ""; root.ifname = parts[2] || "";
if ((parts[1] || "").includes("wireless")) if ((parts[1] || "").includes("wireless"))
@ -41,6 +52,15 @@ M.BarSection {
root.state = "linked"; root.state = "linked";
else else
root.state = "eth"; root.state = "eth";
// Parse extra info lines
root.ipAddr = "";
root.signal = "";
for (let i = 1; i < lines.length; i++) {
if (lines[i].startsWith("ip:"))
root.ipAddr = lines[i].slice(3);
else if (lines[i].startsWith("sig:"))
root.signal = lines[i].slice(4);
}
} }
} }
} }
@ -70,4 +90,5 @@ M.BarSection {
color: root.state === "disconnected" ? M.Theme.base08 : M.Theme.base0C color: root.state === "disconnected" ? M.Theme.base08 : M.Theme.base0C
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }
} }

View file

@ -10,8 +10,7 @@ M.PopupPanel {
property var _currentHandle: handle property var _currentHandle: handle
property var _handleStack: [] property var _handleStack: []
QsMenuOpener { property QsMenuOpener _opener: QsMenuOpener {
id: opener
menu: menuWindow._currentHandle menu: menuWindow._currentHandle
} }
@ -52,7 +51,7 @@ M.PopupPanel {
} }
Repeater { Repeater {
model: opener.children model: menuWindow._opener.children
delegate: Item { delegate: Item {
id: entryItem id: entryItem

View file

@ -69,9 +69,11 @@ Row {
required property var modelData required property var modelData
readonly property bool active: modelData.id === root._activeId readonly property bool active: modelData.id === root._activeId
property bool _hovered: false
HoverHandler { HoverHandler {
onHoveredChanged: { onHoveredChanged: {
pill._hovered = hovered;
const name = pill.modelData.name || ("Workspace " + pill.modelData.idx); const name = pill.modelData.name || ("Workspace " + pill.modelData.idx);
if (hovered) { if (hovered) {
M.FlyoutState.text = name; M.FlyoutState.text = name;
@ -87,7 +89,7 @@ Row {
width: 20 width: 20
height: 20 height: 20
radius: M.Theme.radius radius: M.Theme.radius
color: pill.active ? M.Theme.base0D : M.Theme.base02 color: pill.active ? M.Theme.base0D : (pill._hovered ? M.Theme.base03 : M.Theme.base02)
Behavior on color { ColorAnimation { duration: 150 } } Behavior on color { ColorAnimation { duration: 150 } }
Text { Text {