This commit is contained in:
quadbyte 2025-08-17 11:34:51 -04:00
parent d2acdd1c19
commit 32b6a363af

View file

@ -71,7 +71,7 @@ NLoader {
radius: Style.radiusLarge * scaling radius: Style.radiusLarge * scaling
border.color: Color.mOutlineVariant border.color: Color.mOutlineVariant
border.width: Math.max(1, Style.borderThin * scaling) border.width: Math.max(1, Style.borderThin * scaling)
width: 400 * scaling width: 380 * scaling
height: 500 * scaling height: 500 * scaling
anchors.top: parent.top anchors.top: parent.top
anchors.right: parent.right anchors.right: parent.right
@ -159,203 +159,242 @@ NLoader {
NDivider {} NDivider {}
// Available devices ScrollView {
Column { id: scrollView
id: column
width: parent.width Layout.fillWidth: true
spacing: Style.marginMedium * scaling Layout.fillHeight: true
visible: BluetoothService.adapter && BluetoothService.adapter.enabled clip: true
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
ScrollBar.vertical.policy: ScrollBar.AsNeeded
// Available devices
Column {
id: column
RowLayout {
width: parent.width width: parent.width
spacing: Style.marginMedium * scaling spacing: Style.marginMedium * scaling
visible: BluetoothService.adapter && BluetoothService.adapter.enabled
NText { RowLayout {
text: "Available Devices"
font.pointSize: Style.fontSizeLarge * scaling
color: Color.mOnSurface
font.weight: Style.fontWeightMedium
anchors.verticalCenter: parent.verticalCenter
}
}
Repeater {
model: {
if (!BluetoothService.adapter || !BluetoothService.adapter.discovering || !Bluetooth.devices)
return []
var filtered = Bluetooth.devices.values.filter(dev => {
return dev && !dev.paired && !dev.pairing
&& !dev.blocked && (dev.signalStrength === undefined
|| dev.signalStrength > 0)
})
return BluetoothService.sortDevices(filtered)
}
Rectangle {
property bool canConnect: BluetoothService.canConnect(modelData)
property bool isBusy: BluetoothService.isDeviceBusy(modelData)
width: parent.width width: parent.width
height: 70 spacing: Style.marginMedium * scaling
radius: Style.radiusMedium * scaling
color: {
if (availableDeviceArea.containsMouse && !isBusy)
return Qt.rgba(Color.mPrimary.r, Color.mPrimary.g, Color.mPrimary.b, 0.08)
if (modelData.pairing || modelData.state === BluetoothDeviceState.Connecting) NText {
return Qt.rgba(Color.mError.r, Color.mError.g, Color.mError.b, 0.12) text: "Available Devices"
font.pointSize: Style.fontSizeLarge * scaling
if (modelData.blocked) color: Color.mOnSurface
return Color.mError font.weight: Style.fontWeightMedium
return Color.mSurfaceVariant
} }
border.color: { }
if (modelData.pairing)
return Color.mError
if (modelData.blocked) Repeater {
return Color.mError model: {
if (!BluetoothService.adapter || !BluetoothService.adapter.discovering || !Bluetooth.devices)
return []
return Color.mOutline var filtered = Bluetooth.devices.values.filter(dev => {
} return dev && !dev.paired && !dev.pairing
border.width: 1 && !dev.blocked && (dev.signalStrength === undefined
|| dev.signalStrength > 0)
Row { })
anchors.left: parent.left return BluetoothService.sortDevices(filtered)
anchors.leftMargin: Style.marginMedium * scaling
anchors.verticalCenter: parent.verticalCenter
spacing: Style.marginSmall * scaling
NText {
text: BluetoothService.getDeviceIcon(modelData)
font.family: "Material Symbols Outlined"
font.pointSize: Style.fontSizeXL * scaling
color: {
if (modelData.pairing)
return Color.mError
if (modelData.blocked)
return Color.mError
return Color.mOnSurface
}
anchors.verticalCenter: parent.verticalCenter
}
Column {
spacing: 2
anchors.verticalCenter: parent.verticalCenter
NText {
text: modelData.name || modelData.deviceName
font.pointSize: Style.fonttSizeMedium * scaling
color: {
if (modelData.pairing)
return Color.mError
if (modelData.blocked)
return Color.mError
return Color.mOnSurface
}
font.weight: modelData.pairing ? Style.fontWeightMedium : Font.Normal
}
Row {
spacing: Style.marginTiny * scaling
Row {
spacing: Style.marginSmall * spacing
NText {
text: {
if (modelData.pairing)
return "Pairing..."
if (modelData.blocked)
return "Blocked"
return BluetoothService.getSignalStrength(modelData)
}
font.pointSize: Style.fontSizeSmall * scaling
color: {
if (modelData.pairing)
return Color.mError
if (modelData.blocked)
return Theme.error
return Qt.rgba(Color.mOnSurface.r, Color.mOnSurface.g, Color.mOnSurface.b, 0.7)
}
}
NText {
text: BluetoothService.getSignalIcon(modelData)
font.family: "Material Symbols Outlined"
font.pointSize: Style.fontSizeSmall * scaling
color: Qt.rgba(Color.mOnSurface.r, Color.mOnSurface.g, Color.mOnSurface.b, 0.7)
visible: modelData.signalStrength !== undefined && modelData.signalStrength > 0
&& !modelData.pairing && !modelData.blocked
}
NText {
text: (modelData.signalStrength !== undefined
&& modelData.signalStrength > 0) ? modelData.signalStrength + "%" : ""
font.pointSize: Style.fontSizeSmall * scaling
color: Qt.rgba(Color.mOnSurface.r, Color.mOnSurface.g, Color.mOnSurface.b, 0.5)
visible: modelData.signalStrength !== undefined && modelData.signalStrength > 0
&& !modelData.pairing && !modelData.blocked
}
}
}
}
} }
Rectangle { Rectangle {
width: 80 property bool canConnect: BluetoothService.canConnect(modelData)
height: 28 property bool isBusy: BluetoothService.isDeviceBusy(modelData)
width: parent.width
height: 70
radius: Style.radiusMedium * scaling radius: Style.radiusMedium * scaling
anchors.right: parent.right
anchors.rightMargin: Style.marginMedium * scaling
anchors.verticalCenter: parent.verticalCenter
visible: modelData.state !== BluetoothDeviceState.Connecting
color: { color: {
if (!canConnect && !isBusy) if (availableDeviceArea.containsMouse && !isBusy)
return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3) return Color.mTertiary
if (actionButtonArea.containsMouse && !isBusy) if (modelData.pairing || modelData.state === BluetoothDeviceState.Connecting)
return Qt.rgba(Color.mPrimary.r, Color.mPrimary.g, Color.mPrimary.b, 0.12) return Color.mPrimary
return "transparent" if (modelData.blocked)
return Color.mError
return Color.mSurfaceVariant
} }
border.color: canConnect || isBusy ? Color.mPrimary : Qt.rgba(Theme.outline.r, Theme.outline.g, border.color: Color.mOutline
Theme.outline.b, 0.2) border.width: Math.max(1, Style.borderThin * scaling)
border.width: 1
opacity: canConnect || isBusy ? 1 : 0.5
NText { Row {
anchors.centerIn: parent anchors.left: parent.left
text: { anchors.leftMargin: Style.marginMedium * scaling
if (modelData.pairing) anchors.verticalCenter: parent.verticalCenter
return "Pairing..." spacing: Style.marginSmall * scaling
if (modelData.blocked) // One device BT icon
return "Blocked" NText {
text: BluetoothService.getDeviceIcon(modelData)
font.family: "Material Symbols Outlined"
font.pointSize: Style.fontSizeXL * scaling
color: {
if (availableDeviceArea.containsMouse)
return Color.mOnTertiary
return "Connect" if (modelData.pairing || modelData.state === BluetoothDeviceState.Connecting)
return Color.mOnPrimary
if (modelData.blocked)
return Color.mOnError
return Color.mOnSurface
}
anchors.verticalCenter: parent.verticalCenter
}
Column {
spacing: Style.marginTiniest * scaling
anchors.verticalCenter: parent.verticalCenter
// One device name
NText {
text: modelData.name || modelData.deviceName
font.pointSize: Style.fonttSizeMedium * scaling
elide: Text.ElideRight
color: {
if (availableDeviceArea.containsMouse)
return Color.mOnTertiary
if (modelData.pairing || modelData.state === BluetoothDeviceState.Connecting)
return Color.mOnPrimary
if (modelData.blocked)
return Color.mOnError
return Color.mOnSurface
}
font.weight: Style.fontWeightMedium
}
Row {
spacing: Style.marginTiny * scaling
Row {
spacing: Style.marginSmall * spacing
// One device signal strength - "Unknown" when not connected
NText {
text: {
if (modelData.pairing)
return "Pairing..."
if (modelData.blocked)
return "Blocked"
return BluetoothService.getSignalStrength(modelData)
}
font.pointSize: Style.fontSizeSmall * scaling
color: {
if (availableDeviceArea.containsMouse)
return Color.mOnTertiary
if (modelData.pairing || modelData.state === BluetoothDeviceState.Connecting)
return Color.mOnPrimary
if (modelData.blocked)
return Color.mOnError
return Color.mOnSurface
}
}
NText {
text: BluetoothService.getSignalIcon(modelData)
font.family: "Material Symbols Outlined"
font.pointSize: Style.fontSizeSmall * scaling
color: {
if (availableDeviceArea.containsMouse)
return Color.mOnTertiary
if (modelData.pairing || modelData.state === BluetoothDeviceState.Connecting)
return Color.mOnPrimary
if (modelData.blocked)
return Color.mOnError
return Color.mOnSurface
}
visible: modelData.signalStrength !== undefined && modelData.signalStrength > 0
&& !modelData.pairing && !modelData.blocked
}
NText {
text: (modelData.signalStrength !== undefined
&& modelData.signalStrength > 0) ? modelData.signalStrength + "%" : ""
font.pointSize: Style.fontSizeSmall * scaling
color: {
if (availableDeviceArea.containsMouse)
return Color.mOnTertiary
if (modelData.pairing || modelData.state === BluetoothDeviceState.Connecting)
return Color.mOnPrimary
if (modelData.blocked)
return Color.mOnError
return Color.mOnSurface
}
visible: modelData.signalStrength !== undefined && modelData.signalStrength > 0
&& !modelData.pairing && !modelData.blocked
}
}
}
}
}
Rectangle {
width: 80 * scaling
height: 28 * scaling
radius: Style.radiusMedium * scaling
anchors.right: parent.right
anchors.rightMargin: Style.marginMedium * scaling
anchors.verticalCenter: parent.verticalCenter
visible: modelData.state !== BluetoothDeviceState.Connecting
color: Color.transparent
border.color: {
if (availableDeviceArea.containsMouse) {
return Color.mOnTertiary
} else {
return Color.mPrimary
}
}
border.width: Math.max(1, Style.borderThin * scaling)
opacity: canConnect || isBusy ? 1 : 0.5
// On device connect button
NText {
anchors.centerIn: parent
text: {
if (modelData.pairing)
return "Pairing..."
if (modelData.blocked)
return "Blocked"
return "Connect"
}
font.pointSize: Style.fontSizeSmall * scaling
font.weight: Style.fontWeightMedium
color: {
if (availableDeviceArea.containsMouse) {
return Color.mOnTertiary
} else {
return Color.mPrimary
}
}
} }
font.pointSize: Style.fontSizeSmall * scaling
color: canConnect || isBusy ? Color.mPrimary : Qt.rgba(Color.mOnSurface.r, Color.mOnSurface.g,
Color.mOnSurface.b, 0.5)
font.weight: Style.fontWeightMedium
} }
MouseArea { MouseArea {
id: actionButtonArea id: availableDeviceArea
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
@ -368,102 +407,88 @@ NLoader {
} }
} }
} }
MouseArea {
id: availableDeviceArea
anchors.fill: parent
anchors.rightMargin: 90
hoverEnabled: true
cursorShape: canConnect && !isBusy ? Qt.PointingHandCursor : (isBusy ? Qt.BusyCursor : Qt.ArrowCursor)
enabled: canConnect && !isBusy
onClicked: {
if (modelData)
BluetoothService.connectDeviceWithTrust(modelData)
}
}
}
}
Column {
width: parent.width
spacing: Style.marginMedium * scaling
visible: {
if (!BluetoothService.adapter || !BluetoothService.adapter.discovering || !Bluetooth.devices)
return false
var availableCount = Bluetooth.devices.values.filter(dev => {
return dev && !dev.paired && !dev.pairing
&& !dev.blocked
&& (dev.signalStrength === undefined
|| dev.signalStrength > 0)
}).length
return availableCount === 0
} }
Row { // Fallback if nothing available
anchors.horizontalCenter: parent.horizontalCenter Column {
width: parent.width
spacing: Style.marginMedium * scaling spacing: Style.marginMedium * scaling
visible: {
if (!BluetoothService.adapter || !BluetoothService.adapter.discovering || !Bluetooth.devices)
return false
NText { var availableCount = Bluetooth.devices.values.filter(dev => {
text: "sync" return dev && !dev.paired && !dev.pairing
font.family: "Material Symbols Outlined" && !dev.blocked
font.pointSize: 32 * scaling && (dev.signalStrength === undefined
color: Color.mPrimary || dev.signalStrength > 0)
anchors.verticalCenter: parent.verticalCenter }).length
return availableCount === 0
}
RotationAnimation on rotation { Row {
running: true anchors.horizontalCenter: parent.horizontalCenter
loops: Animation.Infinite spacing: Style.marginMedium * scaling
from: 0
to: 360 NText {
duration: 2000 text: "sync"
font.family: "Material Symbols Outlined"
font.pointSize: Style.fontSizeXLL * 1.5 * scaling
color: Color.mPrimary
anchors.verticalCenter: parent.verticalCenter
RotationAnimation on rotation {
running: true
loops: Animation.Infinite
from: 0
to: 360
duration: 2000
}
}
NText {
text: "Scanning for devices..."
font.pointSize: Style.fontSizeLarge * scaling
color: Color.mOnSurface
font.weight: Style.fontWeightMedium
anchors.verticalCenter: parent.verticalCenter
} }
} }
NText { NText {
text: "Scanning for devices..." text: "Make sure your device is in pairing mode"
font.pointSize: Style.fontSizeLarge * scaling font.pointSize: Style.fontSizeMedium * scaling
color: Color.mOnSurface color: Color.mOnSurfaceVariant
font.weight: Style.fontWeightMedium anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
} }
} }
NText { NText {
text: "Make sure your device is in pairing mode" text: "No devices found. Put your device in pairing mode and click Start Scanning."
font.pointSize: Style.fontSizeMedium * scaling font.pointSize: Style.fontSizeMedium * scaling
color: Qt.rgba(Color.mOnSurface.r, Color.mOnSurface.g, Color.mOnSurface.b, 0.7) color: Color.mOnSurfaceVariant
anchors.horizontalCenter: parent.horizontalCenter visible: {
} if (!BluetoothService.adapter || !Bluetooth.devices)
} return true
NText { var availableCount = Bluetooth.devices.values.filter(dev => {
text: "No devices found. Put your device in pairing mode and click Start Scanning." return dev && !dev.paired && !dev.pairing
font.pointSize: Style.fontSizeMedium * scaling && !dev.blocked
color: Qt.rgba(Color.mOnSurface.r, Color.mOnSurface.g, Color.mOnSurface.b, 0.7) && (dev.signalStrength === undefined
visible: { || dev.signalStrength > 0)
if (!BluetoothService.adapter || !Bluetooth.devices) }).length
return true return availableCount === 0 && !BluetoothService.adapter.discovering
}
var availableCount = Bluetooth.devices.values.filter(dev => { wrapMode: Text.WordWrap
return dev && !dev.paired && !dev.pairing width: parent.width
&& !dev.blocked horizontalAlignment: Text.AlignHCenter
&& (dev.signalStrength === undefined
|| dev.signalStrength > 0)
}).length
return availableCount === 0 && !BluetoothService.adapter.discovering
} }
wrapMode: Text.WordWrap
width: parent.width
horizontalAlignment: Text.AlignHCenter
} }
} }
// This item takes up all the remaining vertical space.
// This item takes up all the remaining vertical space. Item {
Item { Layout.fillHeight: true
Layout.fillHeight: true }
}
} }
} }
} }