features/adding_quickshell #6
@@ -79,6 +79,7 @@ Item {
|
||||
bonded: !!d.bonded,
|
||||
trusted: !!d.trusted,
|
||||
pairing: !!d.pairing,
|
||||
state: d.state,
|
||||
batteryAvailable: !!d.batteryAvailable,
|
||||
battery: d.battery || 0
|
||||
}
|
||||
@@ -105,19 +106,107 @@ Item {
|
||||
return null
|
||||
}
|
||||
|
||||
// --- Action feedback ---------------------------------------------------
|
||||
// BlueZ operations here are fire-and-forget from QML's side — connect()/
|
||||
// disconnect()/pair()/forget() report failure only via a C++-side log
|
||||
// line (see quickshell's bluetooth/device.cpp), nothing reaches QML. So
|
||||
// "did it work" has to be inferred from the device's own properties
|
||||
// instead of a success/failure callback. Shown as a line right under the
|
||||
// specific row it's about (DeviceRow.statusText below) rather than a
|
||||
// panel-wide banner — actionStatusAddress is how a row knows a message
|
||||
// is meant for it; since it's address-keyed rather than tied to a
|
||||
// particular DeviceRow instance, the message still finds its row even
|
||||
// when the device jumps sections (Paired → Connected on a successful
|
||||
// connect, say), because that's a new DeviceRow for the same address.
|
||||
// - busyStatus is purely reactive — it just reads pairing/state off
|
||||
// whichever row is currently mid-operation, however that operation
|
||||
// was triggered. It's not shown directly (each row already derives
|
||||
// the same "Connecting…"/"Disconnecting…" off its own dev.state);
|
||||
// it only exists here to tell resolvePendingAction when to fire.
|
||||
// - pendingAction+the busyStatus watcher below is how a *specific*
|
||||
// click gets its own outcome message: it snapshots what was asked
|
||||
// for, waits for busyStatus to clear, then checks whether the
|
||||
// expected end state (connected/paired) actually landed.
|
||||
readonly property string busyStatus: {
|
||||
for (const r of root.deviceRows) {
|
||||
if (r.pairing) return "pairing:" + r.address
|
||||
if (r.state === BluetoothDeviceState.Connecting) return "connecting:" + r.address
|
||||
if (r.state === BluetoothDeviceState.Disconnecting) return "disconnecting:" + r.address
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
property var pendingAction: null
|
||||
property string actionStatus: ""
|
||||
property string actionStatusAddress: ""
|
||||
|
||||
// Checks whether pendingAction's expected end state actually landed and
|
||||
// reports it, then clears pendingAction. Called both when busyStatus
|
||||
// clears normally and from pendingTimeoutTimer below — an operation
|
||||
// that resolves inside a single binding re-evaluation (a device already
|
||||
// in range reconnecting near-instantly, say) can flip busyStatus from ""
|
||||
// straight back to "" without ever visibly changing, which would
|
||||
// otherwise leave pendingAction stuck forever and misattribute whatever
|
||||
// busyStatus transition happens to come along next.
|
||||
function resolvePendingAction() {
|
||||
if (!root.pendingAction) return
|
||||
const p = root.pendingAction
|
||||
root.pendingAction = null
|
||||
const d = root.deviceForAddress(p.address)
|
||||
root.actionStatusAddress = p.address
|
||||
if (p.kind === "disconnect") {
|
||||
root.actionStatus = (d && !d.connected) ? "Disconnected" : "Couldn't disconnect"
|
||||
} else if (p.kind === "pair") {
|
||||
root.actionStatus = (d && d.paired) ? "Paired" : "Couldn't pair"
|
||||
} else {
|
||||
root.actionStatus = (d && d.connected) ? "Connected" : "Couldn't connect"
|
||||
}
|
||||
actionStatusTimer.restart()
|
||||
}
|
||||
|
||||
onBusyStatusChanged: if (root.busyStatus === "") root.resolvePendingAction()
|
||||
|
||||
Timer {
|
||||
id: actionStatusTimer
|
||||
interval: 3500
|
||||
repeat: false
|
||||
onTriggered: root.actionStatus = ""
|
||||
}
|
||||
|
||||
// Backstop for the "resolved before busyStatus ever visibly changed"
|
||||
// case above, and a genuine timeout for an operation that's just stuck
|
||||
// (device out of range, etc.) — either way, restarted whenever
|
||||
// pendingAction is (re)set, so it always reflects the latest click.
|
||||
Timer {
|
||||
id: pendingTimeoutTimer
|
||||
interval: 8000
|
||||
repeat: false
|
||||
onTriggered: root.resolvePendingAction()
|
||||
}
|
||||
|
||||
function connectDevice(row) {
|
||||
const d = root.deviceForAddress(row.address)
|
||||
if (!d) return
|
||||
// Already-paired devices just reconnect; a fresh discovery result
|
||||
// has to pair first (BlueZ's own agent handles the SSP/passkey
|
||||
// handshake — there's no PIN-entry surface exposed by this API).
|
||||
if (row.paired || row.bonded || row.trusted) d.connect()
|
||||
else d.pair()
|
||||
if (row.paired || row.bonded || row.trusted) {
|
||||
root.pendingAction = { address: row.address, kind: "connect" }
|
||||
pendingTimeoutTimer.restart()
|
||||
d.connect()
|
||||
} else {
|
||||
root.pendingAction = { address: row.address, kind: "pair" }
|
||||
pendingTimeoutTimer.restart()
|
||||
d.pair()
|
||||
}
|
||||
}
|
||||
|
||||
function disconnectDevice(row) {
|
||||
const d = root.deviceForAddress(row.address)
|
||||
if (d) d.disconnect()
|
||||
if (!d) return
|
||||
root.pendingAction = { address: row.address, kind: "disconnect" }
|
||||
pendingTimeoutTimer.restart()
|
||||
d.disconnect()
|
||||
}
|
||||
|
||||
function forgetDevice(row) {
|
||||
@@ -125,11 +214,18 @@ Item {
|
||||
if (!d) return
|
||||
if (d.connected) d.disconnect()
|
||||
d.forget()
|
||||
// No feedback line here — forgetting removes this row from every
|
||||
// list immediately, so there's nothing left to show a message
|
||||
// under. The row vanishing is the confirmation.
|
||||
}
|
||||
|
||||
function setTrusted(row, trusted) {
|
||||
const d = root.deviceForAddress(row.address)
|
||||
if (d) d.trusted = trusted
|
||||
if (!d) return
|
||||
d.trusted = trusted
|
||||
root.actionStatusAddress = row.address
|
||||
root.actionStatus = trusted ? "Trusting" : "No longer trusting"
|
||||
actionStatusTimer.restart()
|
||||
}
|
||||
|
||||
readonly property bool anyConnected: root.connectedRows.length > 0
|
||||
@@ -301,6 +397,17 @@ Item {
|
||||
Text {
|
||||
readonly property string statusText: {
|
||||
if (devRow.dev.pairing) return "Pairing…"
|
||||
if (devRow.dev.state === BluetoothDeviceState.Connecting) return "Connecting…"
|
||||
if (devRow.dev.state === BluetoothDeviceState.Disconnecting) return "Disconnecting…"
|
||||
// The outcome of the last action taken on this
|
||||
// specific device (connect/disconnect/pair/trust) —
|
||||
// see the "Action feedback" comment above
|
||||
// root.busyStatus. Matched by address rather than by
|
||||
// this being "the same row" that was clicked, since
|
||||
// a successful connect/disconnect moves the device
|
||||
// to a different section (a new DeviceRow entirely).
|
||||
if (root.actionStatusAddress === devRow.dev.address && root.actionStatus !== "")
|
||||
return root.actionStatus
|
||||
if (devRow.dev.connected && devRow.dev.batteryAvailable)
|
||||
return Math.round(devRow.dev.battery * 100) + "%"
|
||||
if (devRow.dev.connected) return "Connected"
|
||||
|
||||
Reference in New Issue
Block a user