Address reviewer follow-ups: powerProfile detect, slider live-value, glyphs

- powerProfile now hides on machines without power-profiles-daemon by
  checking against the published profile set rather than a never-undefined
  enum, and uses mdi-scale-balance for Balanced (not battery-charging).
- networkPanel renames the QML-keyword-shadowing 'signal' property to
  'signalStrength'; ethernet glyph corrected to mdi-ethernet (not the
  access-point icon).
- workspacesPro indicator color guards against null bar during construction.
- Common.Slider keeps an internal liveValue so external value bindings
  survive a drag; sliders fed from PipeWire/brightnessctl now follow
  external changes after the user interacts.
- audioPanel drops the redundant default-sink PwObjectTracker since the
  candidate-sinks tracker already covers it.
- networkPanel guards scan/connect commands against re-entry while the
  previous Process is running.
- notificationCenter only instantiates its NotificationServer when the
  module is opted in via settings.replaceMako (default off) so it does
  not collide with mako out of the box.
- PopupCard fully opaque while open (previously 0.98) — removes a
  visible ghost outline on first render.
- Drop dead code: calendar trailing MouseArea, weatherFlyout's unused
  parseForecast/forecast property, idleInhibitor's holdSeconds,
  media's scrollAnim.position; gate media's scroll animation off in
  vertical bar mode.
This commit is contained in:
Ryan Hughes
2026-05-14 02:21:47 -04:00
parent fe73986a01
commit 55a9075415
12 changed files with 63 additions and 70 deletions
+1 -1
View File
@@ -77,7 +77,7 @@ PopupWindow {
border.color: root.bar ? root.bar.foreground : "#cacccc" border.color: root.bar ? root.bar.foreground : "#cacccc"
border.width: 1 border.width: 1
radius: 0 radius: 0
opacity: root.open ? 0.98 : 0 opacity: root.open ? 1.0 : 0
Behavior on opacity { Behavior on opacity {
NumberAnimation { duration: 140; easing.type: Easing.OutCubic } NumberAnimation { duration: 140; easing.type: Easing.OutCubic }
+10 -6
View File
@@ -14,6 +14,9 @@ Item {
property color knobColor: bar ? bar.foreground : "#cacccc" property color knobColor: bar ? bar.foreground : "#cacccc"
property bool dragging: false property bool dragging: false
property real trackHeight: 4 property real trackHeight: 4
property real liveValue: value
onValueChanged: if (!dragging) liveValue = value
signal moved(real value) signal moved(real value)
signal released(real value) signal released(real value)
@@ -22,7 +25,7 @@ Item {
implicitHeight: 22 implicitHeight: 22
readonly property real range: Math.max(0.0001, maximum - minimum) readonly property real range: Math.max(0.0001, maximum - minimum)
readonly property real progress: Math.max(0, Math.min(1, (value - minimum) / range)) readonly property real progress: Math.max(0, Math.min(1, (liveValue - minimum) / range))
Rectangle { Rectangle {
id: track id: track
@@ -87,24 +90,25 @@ Item {
onPressed: function(mouse) { onPressed: function(mouse) {
root.dragging = true root.dragging = true
var next = valueFromX(mouse.x) var next = valueFromX(mouse.x)
root.value = next root.liveValue = next
root.moved(next) root.moved(next)
} }
onPositionChanged: function(mouse) { onPositionChanged: function(mouse) {
if (!root.dragging) return if (!root.dragging) return
var next = valueFromX(mouse.x) var next = valueFromX(mouse.x)
root.value = next root.liveValue = next
root.moved(next) root.moved(next)
} }
onReleased: function(mouse) { onReleased: function(mouse) {
root.dragging = false root.dragging = false
root.released(root.value) root.released(root.liveValue)
root.liveValue = root.value
} }
onWheel: function(wheel) { onWheel: function(wheel) {
var delta = wheel.angleDelta.y > 0 ? root.step : -root.step var delta = wheel.angleDelta.y > 0 ? root.step : -root.step
var next = Math.max(root.minimum, Math.min(root.maximum, root.value + delta)) var next = Math.max(root.minimum, Math.min(root.maximum, root.liveValue + delta))
if (root.integer) next = Math.round(next) if (root.integer) next = Math.round(next)
root.value = next root.liveValue = next
root.moved(next) root.moved(next)
root.released(next) root.released(next)
} }
+2 -1
View File
@@ -273,7 +273,8 @@ ShellRoot {
"workspacesPro": true, "workspacesPro": true,
"powerMenu": true, "powerMenu": true,
"idleInhibitor": true, "idleInhibitor": true,
"microphone": true "microphone": true,
"notificationCenter": true
}) })
function firstPartyWidgetSource(name) { function firstPartyWidgetSource(name) {
@@ -81,7 +81,6 @@ Item {
PwObjectTracker { objects: root.candidateSinks } PwObjectTracker { objects: root.candidateSinks }
PwObjectTracker { objects: root.candidateStreams } PwObjectTracker { objects: root.candidateStreams }
PwObjectTracker { objects: root.sink ? [root.sink] : [] }
Common.WidgetButton { Common.WidgetButton {
id: button id: button
@@ -184,9 +184,4 @@ Item {
} }
} }
MouseArea {
anchors.fill: parent
visible: false
enabled: false
}
} }
@@ -11,7 +11,6 @@ Item {
property var settings: ({}) property var settings: ({})
property bool active: false property bool active: false
property int holdSeconds: 0
readonly property string icon: active ? "󰛊" : "󰾪" readonly property string icon: active ? "󰛊" : "󰾪"
+1 -3
View File
@@ -74,12 +74,10 @@ Item {
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
property bool needsScroll: implicitWidth > scrollClip.width property bool needsScroll: implicitWidth > scrollClip.width
x: needsScroll ? scrollAnim.position : 0
NumberAnimation on x { NumberAnimation on x {
id: scrollAnim id: scrollAnim
property real position: 0 running: labelText.needsScroll && !root.popupOpen && !root.bar.vertical
running: labelText.needsScroll && !root.popupOpen
loops: Animation.Infinite loops: Animation.Infinite
duration: Math.max(6000, labelText.implicitWidth * 25) duration: Math.max(6000, labelText.implicitWidth * 25)
from: scrollClip.width from: scrollClip.width
@@ -18,15 +18,15 @@ Item {
readonly property string kind: bar ? bar.networkKind : "disconnected" readonly property string kind: bar ? bar.networkKind : "disconnected"
readonly property string label: bar ? bar.networkLabel : "" readonly property string label: bar ? bar.networkLabel : ""
readonly property int signal: bar ? bar.networkSignal : -1 readonly property int signalStrength: bar ? bar.networkSignal : -1
readonly property string icon: { readonly property string icon: {
if (kind === "wifi") { if (kind === "wifi") {
var icons = ["󰤯", "󰤟", "󰤢", "󰤥", "󰤨"] var icons = ["󰤯", "󰤟", "󰤢", "󰤥", "󰤨"]
var index = Math.max(0, Math.min(4, Math.ceil(signal / 20) - 1)) var index = Math.max(0, Math.min(4, Math.ceil(signalStrength / 20) - 1))
return icons[index] return icons[index]
} }
if (kind === "ethernet") return "󰀂" if (kind === "ethernet") return "󰈀"
return "󰤮" return "󰤮"
} }
@@ -48,21 +48,21 @@ Item {
list.push({ list.push({
inUse: parts[0] === "*", inUse: parts[0] === "*",
ssid: parts[1], ssid: parts[1],
signal: parseInt(parts[2], 10) || 0, signalStrength: parseInt(parts[2], 10) || 0,
security: parts[3] || "" security: parts[3] || ""
}) })
} }
list.sort(function(a, b) { list.sort(function(a, b) {
if (a.inUse !== b.inUse) return a.inUse ? -1 : 1 if (a.inUse !== b.inUse) return a.inUse ? -1 : 1
return b.signal - a.signal return b.signalStrength - a.signalStrength
}) })
networks = list networks = list
scanning = false scanning = false
} }
function wifiIconFor(signal) { function wifiIconFor(signalStrength) {
var icons = ["󰤯", "󰤟", "󰤢", "󰤥", "󰤨"] var icons = ["󰤯", "󰤟", "󰤢", "󰤥", "󰤨"]
var index = Math.max(0, Math.min(4, Math.ceil(signal / 20) - 1)) var index = Math.max(0, Math.min(4, Math.ceil(signalStrength / 20) - 1))
return icons[index] return icons[index]
} }
@@ -141,7 +141,7 @@ Item {
Text { Text {
visible: root.kind !== "disconnected" visible: root.kind !== "disconnected"
text: root.kind === "wifi" ? "Wi-Fi · " + root.signal + "%" : "Ethernet" text: root.kind === "wifi" ? "Wi-Fi · " + root.signalStrength + "%" : "Ethernet"
color: Qt.darker(root.bar.foreground, 1.4) color: Qt.darker(root.bar.foreground, 1.4)
font.family: root.bar.fontFamily font.family: root.bar.fontFamily
font.pixelSize: 10 font.pixelSize: 10
@@ -155,6 +155,7 @@ Item {
verticalPadding: 4 verticalPadding: 4
active: root.scanning active: root.scanning
onClicked: { onClicked: {
if (scanProc.running) return
scanProc.command = ["bash", "-lc", "command -v nmcli >/dev/null && nmcli device wifi rescan 2>/dev/null; nmcli -t -f IN-USE,SSID,SIGNAL,SECURITY device wifi list --rescan no 2>/dev/null"] scanProc.command = ["bash", "-lc", "command -v nmcli >/dev/null && nmcli device wifi rescan 2>/dev/null; nmcli -t -f IN-USE,SSID,SIGNAL,SECURITY device wifi list --rescan no 2>/dev/null"]
root.refresh() root.refresh()
} }
@@ -178,7 +179,7 @@ Item {
required property var modelData required property var modelData
width: parent.width width: parent.width
iconText: root.wifiIconFor(modelData.signal) iconText: root.wifiIconFor(modelData.signalStrength)
text: (modelData.ssid || "Hidden") + (modelData.security ? " · " : "") + (modelData.security || "") text: (modelData.ssid || "Hidden") + (modelData.security ? " · " : "") + (modelData.security || "")
foreground: root.bar.foreground foreground: root.bar.foreground
horizontalPadding: 10 horizontalPadding: 10
@@ -187,6 +188,7 @@ Item {
onClicked: { onClicked: {
if (modelData.inUse) return if (modelData.inUse) return
if (actionProc.running) return
actionProc.command = ["bash", "-lc", "command -v nmcli >/dev/null && nmcli device wifi connect " + root.bar.shellQuote(modelData.ssid)] actionProc.command = ["bash", "-lc", "command -v nmcli >/dev/null && nmcli device wifi connect " + root.bar.shellQuote(modelData.ssid)]
actionProc.running = true actionProc.running = true
root.popupOpen = false root.popupOpen = false
@@ -23,31 +23,42 @@ Item {
return "󰂚" return "󰂚"
} }
NotificationServer { property bool replaceMako: settings && settings.replaceMako === true
id: server
keepOnReload: false
bodySupported: true
actionsSupported: true
imageSupported: true
onNotification: function(notification) { Loader {
if (root.dnd) { active: root.replaceMako
notification.expire() sourceComponent: serverComponent
return }
Component {
id: serverComponent
NotificationServer {
id: server
keepOnReload: false
bodySupported: true
actionsSupported: true
imageSupported: true
onNotification: function(notification) {
if (root.dnd) {
notification.expire()
return
}
notification.tracked = true
var snapshot = {
id: notification.id,
app: notification.appName,
summary: notification.summary,
body: notification.body,
time: new Date(),
ref: notification
}
var next = root.stored.slice()
next.unshift(snapshot)
if (next.length > 30) next.pop()
root.stored = next
} }
notification.tracked = true
var snapshot = {
id: notification.id,
app: notification.appName,
summary: notification.summary,
body: notification.body,
time: new Date(),
ref: notification
}
var next = root.stored.slice()
next.unshift(snapshot)
if (next.length > 30) next.pop()
root.stored = next
} }
} }
@@ -16,7 +16,7 @@ Item {
readonly property var profileGlyphs: ({ readonly property var profileGlyphs: ({
[PowerProfile.PowerSaver]: "󰌪", [PowerProfile.PowerSaver]: "󰌪",
[PowerProfile.Balanced]: "󰂄", [PowerProfile.Balanced]: "󰗑",
[PowerProfile.Performance]: "󰓅" [PowerProfile.Performance]: "󰓅"
}) })
@@ -26,7 +26,7 @@ Item {
[PowerProfile.Performance]: "Performance" [PowerProfile.Performance]: "Performance"
}) })
readonly property bool available: PowerProfiles.profile !== undefined readonly property bool available: PowerProfiles.hasPerformanceProfile || PowerProfiles.profile === PowerProfile.PowerSaver || PowerProfiles.profile === PowerProfile.Balanced
readonly property int current: PowerProfiles.profile readonly property int current: PowerProfiles.profile
visible: available visible: available
@@ -70,7 +70,7 @@ Item {
Repeater { Repeater {
model: [ model: [
{ profile: PowerProfile.PowerSaver, label: "Power Saver", glyph: "󰌪" }, { profile: PowerProfile.PowerSaver, label: "Power Saver", glyph: "󰌪" },
{ profile: PowerProfile.Balanced, label: "Balanced", glyph: "󰂄" }, { profile: PowerProfile.Balanced, label: "Balanced", glyph: "󰗑" },
{ profile: PowerProfile.Performance, label: "Performance", glyph: "󰓅" } { profile: PowerProfile.Performance, label: "Performance", glyph: "󰓅" }
] ]
@@ -13,7 +13,6 @@ Item {
property bool popupOpen: false property bool popupOpen: false
function closePopout() { popupOpen = false } function closePopout() { popupOpen = false }
property var forecast: ({})
property string fullReport: "" property string fullReport: ""
readonly property string label: bar ? bar.weatherText : "" readonly property string label: bar ? bar.weatherText : ""
@@ -27,21 +26,6 @@ Item {
if (!forecastProc.running) forecastProc.running = true if (!forecastProc.running) forecastProc.running = true
} }
function parseForecast(text) {
var lines = String(text || "").split("\n")
var data = { location: "", current: "", hourly: [], daily: [] }
var section = ""
for (var i = 0; i < lines.length; i++) {
var line = lines[i]
if (i === 0 && line.indexOf("Weather report:") === 0) {
data.location = line.replace("Weather report:", "").trim()
} else if (line.match(/^[├└]\s/)) {
section = "daily"
}
}
return data
}
Process { Process {
id: forecastProc id: forecastProc
@@ -36,7 +36,7 @@ Item {
width: vertical ? root.slotSize - 8 : root.slotSize - 6 width: vertical ? root.slotSize - 8 : root.slotSize - 6
height: vertical ? root.slotSize - 6 : root.slotSize - 8 height: vertical ? root.slotSize - 6 : root.slotSize - 8
radius: 4 radius: 4
color: root.bar.foreground color: root.bar ? root.bar.foreground : "#cacccc"
opacity: root.focusedIndex >= 0 ? 0.25 : 0 opacity: root.focusedIndex >= 0 ? 0.25 : 0
x: vertical ? (root.width - width) / 2 : 3 + root.focusedIndex * (root.slotSize + root.spacing) + (root.slotSize - width) / 2 x: vertical ? (root.width - width) / 2 : 3 + root.focusedIndex * (root.slotSize + root.spacing) + (root.slotSize - width) / 2
y: vertical ? 3 + root.focusedIndex * (root.slotSize + root.spacing) + (root.slotSize - height) / 2 : (root.height - height) / 2 y: vertical ? 3 + root.focusedIndex * (root.slotSize + root.spacing) + (root.slotSize - height) / 2 : (root.height - height) / 2