Compare commits
2 Commits
4692ca3377
...
a61945477a
| Author | SHA1 | Date |
|---|---|---|
|
|
a61945477a | |
|
|
f845f724d5 |
|
|
@ -5,7 +5,9 @@ $radius: 12px; // keep in sync with RADIUS in Bar.tsx
|
||||||
$spacing: 8px;
|
$spacing: 8px;
|
||||||
$padding: 8px;
|
$padding: 8px;
|
||||||
$border-width: 1px;
|
$border-width: 1px;
|
||||||
$border-color: $base02;
|
// one step lighter than the card fill so section outlines read against
|
||||||
|
// the dropdown background and the widgets they surround
|
||||||
|
$border-color: $base03;
|
||||||
$border: $border-width solid $border-color;
|
$border: $border-width solid $border-color;
|
||||||
$transition: 180ms;
|
$transition: 180ms;
|
||||||
$fg: $base05;
|
$fg: $base05;
|
||||||
|
|
@ -211,7 +213,7 @@ scrollbar {
|
||||||
.volume,
|
.volume,
|
||||||
.microphone,
|
.microphone,
|
||||||
.brightness {
|
.brightness {
|
||||||
padding: 2px 4px;
|
padding: 0 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
|
|
@ -389,30 +391,6 @@ scale {
|
||||||
background: $hover-overlay;
|
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,
|
.wifi-status,
|
||||||
|
|
@ -442,15 +420,9 @@ scale {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.device-item {
|
.device-row {
|
||||||
padding: 5px 7px;
|
|
||||||
border-radius: 6px;
|
|
||||||
margin: 1px 0;
|
margin: 1px 0;
|
||||||
|
|
||||||
icon {
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.device-name {
|
.device-name {
|
||||||
margin: 0 6px;
|
margin: 0 6px;
|
||||||
}
|
}
|
||||||
|
|
@ -466,10 +438,6 @@ scale {
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
margin-right: 2px;
|
margin-right: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: $hover-overlay;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// media player card
|
// media player card
|
||||||
|
|
|
||||||
|
|
@ -48,64 +48,71 @@ const pillLabel = Variable.derive(
|
||||||
)
|
)
|
||||||
|
|
||||||
// ported from AlfheimOS: icon, name, battery, busy icon while connecting,
|
// 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) {
|
function DeviceItem(device: AstalBluetooth.Device) {
|
||||||
const battery = bind(device, "battery_percentage")
|
const battery = bind(device, "battery_percentage")
|
||||||
const connecting = bind(device, "connecting")
|
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
|
// bluez reports br-connection-busy when attempts pile up, so debounce
|
||||||
let lastAttempt = 0
|
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 (
|
return (
|
||||||
<box className="device-item">
|
<button className="device-row" onClicked={toggle}>
|
||||||
<icon
|
<box>
|
||||||
icon={
|
<icon
|
||||||
device.icon
|
icon={
|
||||||
? `${device.icon}-symbolic`
|
device.icon
|
||||||
: "bluetooth-active-symbolic"
|
? `${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
|
|
||||||
}
|
}
|
||||||
lastAttempt = now
|
/>
|
||||||
// vala async: begin with a callback, finish() throws
|
<label
|
||||||
const done = (_source: AstalBluetooth.Device, res: Gio.AsyncResult) => {
|
className="device-name"
|
||||||
try {
|
label={device.alias ?? device.address}
|
||||||
if (state) device.connect_device_finish(res)
|
halign={Gtk.Align.START}
|
||||||
else device.disconnect_device_finish(res)
|
hexpand
|
||||||
} catch (e) {
|
/>
|
||||||
console.error(e)
|
<label
|
||||||
}
|
className="device-battery"
|
||||||
}
|
visible={battery.as((p) => p > 0)}
|
||||||
if (state) device.connect_device(done)
|
label={battery.as((p) => `${Math.round(p * 100)}%`)}
|
||||||
else device.disconnect_device(done)
|
/>
|
||||||
return true
|
<icon
|
||||||
}}
|
className="device-connecting"
|
||||||
/>
|
icon="view-refresh-symbolic"
|
||||||
</box>
|
visible={connecting}
|
||||||
|
/>
|
||||||
|
<icon
|
||||||
|
visible={connected()}
|
||||||
|
icon="object-select-symbolic"
|
||||||
|
/>
|
||||||
|
</box>
|
||||||
|
</button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue