Restyle Bluetooth device rows to match wifi menu

This commit is contained in:
Joshua Yuen 2026-09-12 23:22:34 -04:00
parent 83e9509eb4
commit 0a21b7f63d
Signed by: josh
GPG Key ID: 530DA0331EFE99F5
2 changed files with 59 additions and 86 deletions

View File

@ -389,30 +389,6 @@ scale {
background: $hover-overlay;
}
}
switch {
background: $base03;
border-radius: 999px;
min-width: 34px;
min-height: 16px;
margin: 1px 0;
slider {
background: $base05;
border-radius: 999px;
min-width: 12px;
min-height: 12px;
margin: 2px;
}
}
switch:checked {
background: $primary-bg;
slider {
background: $primary-fg;
}
}
}
.wifi-status,
@ -442,15 +418,9 @@ scale {
}
}
.device-item {
padding: 5px 7px;
border-radius: 6px;
.device-row {
margin: 1px 0;
icon {
font-size: 16px;
}
.device-name {
margin: 0 6px;
}
@ -466,10 +436,6 @@ scale {
opacity: 0.7;
margin-right: 2px;
}
&:hover {
background: $hover-overlay;
}
}
// media player card

View File

@ -48,64 +48,71 @@ const pillLabel = Variable.derive(
)
// ported from AlfheimOS: icon, name, battery, busy icon while connecting,
// switch reflecting the bluez connected state
// whole row toggles the bluez connected state
function DeviceItem(device: AstalBluetooth.Device) {
const battery = bind(device, "battery_percentage")
const connecting = bind(device, "connecting")
const connected = Variable.derive(
[bind(device, "connected"), connecting],
(c, busy) => c && !busy,
)
// bluez reports br-connection-busy when attempts pile up, so debounce
let lastAttempt = 0
function toggle() {
if (connecting.get()) return
// skip retries within the debounce window
const now = Date.now()
if (now - lastAttempt < 1500) return
lastAttempt = now
const state = !device.connected
// bluez rejects connects while the controller is busy scanning
if (state) setDiscovery(false)
// vala async: begin with a callback, finish() throws
const done = (_source: AstalBluetooth.Device, res: Gio.AsyncResult) => {
try {
if (state) device.connect_device_finish(res)
else device.disconnect_device_finish(res)
} catch (e) {
console.error(e)
}
}
if (state) device.connect_device(done)
else device.disconnect_device(done)
}
return (
<box className="device-item">
<icon
icon={
device.icon
? `${device.icon}-symbolic`
: "bluetooth-active-symbolic"
}
/>
<label
className="device-name"
label={device.alias ?? device.address}
halign={Gtk.Align.START}
hexpand
/>
<label
className="device-battery"
visible={battery.as((p) => p > 0)}
label={battery.as((p) => `${Math.round(p * 100)}%`)}
/>
<icon
className="device-connecting"
icon="view-refresh-symbolic"
visible={connecting}
/>
<switch
visible={connecting.as((c) => !c)}
active={bind(device, "connected")}
onStateSet={(_, state) => {
// bluez rejects connects while the controller is busy scanning
if (state) setDiscovery(false)
// skip no-ops and retries within the debounce window
const now = Date.now()
if (state === device.connected || now - lastAttempt < 1500) {
return true
<button className="device-row" onClicked={toggle}>
<box>
<icon
icon={
device.icon
? `${device.icon}-symbolic`
: "bluetooth-active-symbolic"
}
lastAttempt = now
// vala async: begin with a callback, finish() throws
const done = (_source: AstalBluetooth.Device, res: Gio.AsyncResult) => {
try {
if (state) device.connect_device_finish(res)
else device.disconnect_device_finish(res)
} catch (e) {
console.error(e)
}
}
if (state) device.connect_device(done)
else device.disconnect_device(done)
return true
}}
/>
</box>
/>
<label
className="device-name"
label={device.alias ?? device.address}
halign={Gtk.Align.START}
hexpand
/>
<label
className="device-battery"
visible={battery.as((p) => p > 0)}
label={battery.as((p) => `${Math.round(p * 100)}%`)}
/>
<icon
className="device-connecting"
icon="view-refresh-symbolic"
visible={connecting}
/>
<icon
visible={connected()}
icon="object-select-symbolic"
/>
</box>
</button>
)
}