fix to issue #165

This commit is contained in:
Oleksiy Nedobiychuk 2025-08-31 20:51:04 +02:00
parent 46ef2b6e53
commit 3151b1634c
3 changed files with 99 additions and 30 deletions

View file

@ -12,6 +12,7 @@ ColumnLayout {
id: root id: root
property string label: "" property string label: ""
property string tooltipText: ""
property var model: { property var model: {
} }
@ -29,12 +30,15 @@ ColumnLayout {
} }
Repeater { Repeater {
id: deviceList
Layout.fillWidth: true Layout.fillWidth: true
model: root.model model: root.model
visible: BluetoothService.adapter && BluetoothService.adapter.enabled visible: BluetoothService.adapter && BluetoothService.adapter.enabled
Rectangle { Rectangle {
id: bluetoothDeviceRectangle
property bool canConnect: BluetoothService.canConnect(modelData) property bool canConnect: BluetoothService.canConnect(modelData)
property bool canDisconnect: BluetoothService.canDisconnect(modelData)
property bool isBusy: BluetoothService.isDeviceBusy(modelData) property bool isBusy: BluetoothService.isDeviceBusy(modelData)
Layout.fillWidth: true Layout.fillWidth: true
@ -42,13 +46,19 @@ ColumnLayout {
radius: Style.radiusM * scaling radius: Style.radiusM * scaling
color: { color: {
if (availableDeviceArea.containsMouse && !isBusy) if (availableDeviceArea.containsMouse){
return Color.mTertiary if (canDisconnect && !isBusy)
return Color.mError
if(!isBusy)
return Color.mTertiary
return Color.mPrimary
}
if (modelData.pairing || modelData.state === BluetoothDeviceState.Connecting) if (modelData.pairing || modelData.state === BluetoothDeviceState.Connecting)
return Color.mPrimary return Color.mPrimary
if (modelData.blocked) if (modelData.blocked )
return Color.mError return Color.mError
return Color.mSurfaceVariant return Color.mSurfaceVariant
@ -56,6 +66,13 @@ ColumnLayout {
border.color: Color.mOutline border.color: Color.mOutline
border.width: Math.max(1, Style.borderS * scaling) border.width: Math.max(1, Style.borderS * scaling)
NTooltip {
id: tooltip
target: bluetoothDeviceRectangle
positionAbove: Settings.data.bar.position === "bottom"
text: root.tooltipText
}
RowLayout { RowLayout {
anchors.fill: parent anchors.fill: parent
anchors.margins: Style.marginM * scaling anchors.margins: Style.marginM * scaling
@ -193,6 +210,7 @@ ColumnLayout {
Layout.fillWidth: true Layout.fillWidth: true
} }
// Call to action // Call to action
Rectangle { Rectangle {
Layout.preferredWidth: 80 * scaling Layout.preferredWidth: 80 * scaling
@ -204,12 +222,14 @@ ColumnLayout {
border.color: { border.color: {
if (availableDeviceArea.containsMouse) { if (availableDeviceArea.containsMouse) {
return Color.mOnTertiary return Color.mOnTertiary
} else {
return Color.mPrimary
} }
if (bluetoothDeviceRectangle.canDisconnect && !isBusy) {
return Color.mError
}
return Color.mPrimary
} }
border.width: Math.max(1, Style.borderS * scaling) border.width: Math.max(1, Style.borderS * scaling)
opacity: canConnect || isBusy ? 1 : 0.5 opacity: canConnect || isBusy || canDisconnect ? 1 : 0.5
NText { NText {
anchors.centerIn: parent anchors.centerIn: parent
@ -220,7 +240,7 @@ ColumnLayout {
if (modelData.blocked) { if (modelData.blocked) {
return "Blocked" return "Blocked"
} }
if (modelData.paired || modelData.trusted) { if(modelData.connected){
return "Disconnect" return "Disconnect"
} }
return "Connect" return "Connect"
@ -228,8 +248,13 @@ ColumnLayout {
font.pointSize: Style.fontSizeXS * scaling font.pointSize: Style.fontSizeXS * scaling
font.weight: Style.fontWeightMedium font.weight: Style.fontWeightMedium
color: { color: {
if (availableDeviceArea.containsMouse) { if (availableDeviceArea.containsMouse) {
return Color.mOnTertiary return Color.mOnTertiary
}
if (bluetoothDeviceRectangle.canDisconnect && !isBusy) {
return Color.mError
} else { } else {
return Color.mPrimary return Color.mPrimary
} }
@ -240,20 +265,39 @@ ColumnLayout {
MouseArea { MouseArea {
id: availableDeviceArea id: availableDeviceArea
acceptedButtons: Qt.LeftButton | Qt.RightButton
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: canConnect && !isBusy ? Qt.PointingHandCursor : (isBusy ? Qt.BusyCursor : Qt.ArrowCursor) cursorShape: (canConnect || canDisconnect) && !isBusy ? Qt.PointingHandCursor : (isBusy ? Qt.BusyCursor : Qt.ArrowCursor)
enabled: canConnect && !isBusy onEntered: {
if (root.tooltipText && !isBusy) {
tooltip.show()
}
}
onExited: {
if(root.tooltipText && !isBusy) {
tooltip.hide()
}
}
onClicked: { onClicked: {
if (!modelData || modelData.pairing) { if (!modelData || modelData.pairing) {
return return
} }
if (modelData.paired || modelData.trusted) { if (root.tooltipText && !isBusy) {
BluetoothService.disconnectDevice(modelData) tooltip.hide()
} else { }
BluetoothService.connectDeviceWithTrust(modelData)
if (mouse.button === Qt.LeftButton){
if (modelData.connected) {
BluetoothService.disconnectDevice(modelData)
} else {
BluetoothService.connectDeviceWithTrust(modelData)
}
} else if (mouse.button === Qt.RightButton) {
BluetoothService.forgetDevice(modelData)
} }
} }
} }

View file

@ -83,30 +83,40 @@ NPanel {
// Connected devices // Connected devices
BluetoothDevicesList { BluetoothDevicesList {
label: "Connected devices" label: "Connected devices"
model: { property var items: {
if (!BluetoothService.adapter || !Bluetooth.devices) if (!BluetoothService.adapter || !Bluetooth.devices) return []
return [] var filtered = Bluetooth.devices.values.filter(dev => dev && !dev.blocked && dev.connected)
var filtered = Bluetooth.devices.values.filter(dev => {
return dev && !dev.blocked && (dev.paired || dev.trusted)
})
return BluetoothService.sortDevices(filtered) return BluetoothService.sortDevices(filtered)
} }
model: items
visible: items.length > 0
Layout.fillWidth: true
}
// Known devices
BluetoothDevicesList {
label: "Known devices"
tooltipText: "Left click to connect, right click to forget"
property var items: {
if (!BluetoothService.adapter || !Bluetooth.devices) return []
var filtered = Bluetooth.devices.values.filter(dev => dev && !dev.blocked && !dev.connected && (dev.paired || dev.trusted))
return BluetoothService.sortDevices(filtered)
}
model: items
visible: items.length > 0
Layout.fillWidth: true Layout.fillWidth: true
} }
// Available devices // Available devices
BluetoothDevicesList { BluetoothDevicesList {
label: "Available devices" label: "Available devices"
model: { property var items: {
if (!BluetoothService.adapter || !Bluetooth.devices) if (!BluetoothService.adapter || !Bluetooth.devices) return []
return [] var filtered = Bluetooth.devices.values.filter(dev => dev && !dev.blocked && !dev.paired && !dev.trusted)
var filtered = Bluetooth.devices.values.filter(dev => {
return dev && !dev.blocked && !dev.paired && !dev.trusted
})
return BluetoothService.sortDevices(filtered) return BluetoothService.sortDevices(filtered)
} }
model: items
visible: items.length > 0
Layout.fillWidth: true Layout.fillWidth: true
} }

View file

@ -3,6 +3,7 @@ pragma Singleton
import QtQuick import QtQuick
import Quickshell import Quickshell
import Quickshell.Bluetooth import Quickshell.Bluetooth
import qs.Commons
Singleton { Singleton {
id: root id: root
@ -85,8 +86,23 @@ Singleton {
function canConnect(device) { function canConnect(device) {
if (!device) if (!device)
return false return false
/*
Paired
return !device.paired && !device.pairing && !device.blocked Means youve successfully exchanged keys with the device.
The devices remember each other and can authenticate without repeating the pairing process.
Example: once your headphones are paired, you dont need to type a PIN every time.
Hence, instead of !device.paired, should be device.connected
*/
return !device.connected && !device.pairing && !device.blocked
}
function canDisconnect(device) {
if (!device)
return false
return device.connected && !device.pairing && !device.blocked
} }
function getSignalStrength(device) { function getSignalStrength(device) {
@ -162,7 +178,6 @@ Singleton {
return return
} }
device.trusted = false
device.disconnect() device.disconnect()
} }