Add bluetooth panel (wip), add clock / clock+date setting
This commit is contained in:
parent
56ad08816e
commit
2fd20c69f9
9 changed files with 911 additions and 9 deletions
397
Modules/Bar/BluetoothMenu.qml
Normal file
397
Modules/Bar/BluetoothMenu.qml
Normal file
|
|
@ -0,0 +1,397 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import qs.Commons
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
// Loader for Bluetooth menu
|
||||
NLoader {
|
||||
id: root
|
||||
|
||||
content: Component {
|
||||
NPanel {
|
||||
id: bluetoothPanel
|
||||
|
||||
function hide() {
|
||||
bluetoothMenuRect.scaleValue = 0.8
|
||||
bluetoothMenuRect.opacityValue = 0.0
|
||||
hideTimer.start()
|
||||
}
|
||||
|
||||
// Connect to NPanel's dismissed signal to handle external close events
|
||||
Connections {
|
||||
target: bluetoothPanel
|
||||
ignoreUnknownSignals: true
|
||||
function onDismissed() {
|
||||
// Start hide animation
|
||||
bluetoothMenuRect.scaleValue = 0.8
|
||||
bluetoothMenuRect.opacityValue = 0.0
|
||||
// Hide after animation completes
|
||||
hideTimer.start()
|
||||
}
|
||||
}
|
||||
|
||||
// Also handle visibility changes from external sources
|
||||
onVisibleChanged: {
|
||||
if (visible && Settings.data.network.bluetoothEnabled) {
|
||||
// Always refresh devices when menu opens to get fresh device objects
|
||||
BluetoothService.refreshDevices()
|
||||
} else if (bluetoothMenuRect.opacityValue > 0) {
|
||||
// Start hide animation
|
||||
bluetoothMenuRect.scaleValue = 0.8
|
||||
bluetoothMenuRect.opacityValue = 0.0
|
||||
// Hide after animation completes
|
||||
hideTimer.start()
|
||||
}
|
||||
}
|
||||
|
||||
// Timer to hide panel after animation
|
||||
Timer {
|
||||
id: hideTimer
|
||||
interval: Style.animationSlow
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
bluetoothPanel.visible = false
|
||||
bluetoothPanel.dismissed()
|
||||
}
|
||||
}
|
||||
|
||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
|
||||
|
||||
Rectangle {
|
||||
id: bluetoothMenuRect
|
||||
color: Colors.mSurface
|
||||
radius: Style.radiusLarge * scaling
|
||||
border.color: Colors.mOutlineVariant
|
||||
border.width: Math.max(1, Style.borderThin * scaling)
|
||||
width: 340 * scaling
|
||||
height: 500 * scaling
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: Style.marginTiny * scaling
|
||||
anchors.rightMargin: Style.marginTiny * scaling
|
||||
|
||||
// Animation properties
|
||||
property real scaleValue: 0.8
|
||||
property real opacityValue: 0.0
|
||||
|
||||
scale: scaleValue
|
||||
opacity: opacityValue
|
||||
|
||||
// Animate in when component is completed
|
||||
Component.onCompleted: {
|
||||
scaleValue = 1.0
|
||||
opacityValue = 1.0
|
||||
}
|
||||
|
||||
// Animation behaviors
|
||||
Behavior on scale {
|
||||
NumberAnimation {
|
||||
duration: Style.animationSlow
|
||||
easing.type: Easing.OutExpo
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Style.animationNormal
|
||||
easing.type: Easing.OutQuad
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginLarge * scaling
|
||||
spacing: Style.marginMedium * scaling
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginMedium * scaling
|
||||
|
||||
NText {
|
||||
text: "bluetooth"
|
||||
font.family: "Material Symbols Outlined"
|
||||
font.pointSize: Style.fontSizeXL * scaling
|
||||
color: Colors.mPrimary
|
||||
}
|
||||
|
||||
NText {
|
||||
text: "Bluetooth"
|
||||
font.pointSize: Style.fontSizeLarge * scaling
|
||||
font.bold: true
|
||||
color: Colors.mOnSurface
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
NIconButton {
|
||||
icon: "refresh"
|
||||
tooltipText: "Refresh Devices"
|
||||
sizeMultiplier: 0.8
|
||||
enabled: Settings.data.network.bluetoothEnabled && !BluetoothService.isDiscovering
|
||||
onClicked: {
|
||||
BluetoothService.refreshDevices()
|
||||
}
|
||||
}
|
||||
|
||||
NIconButton {
|
||||
icon: "close"
|
||||
tooltipText: "Close"
|
||||
sizeMultiplier: 0.8
|
||||
onClicked: {
|
||||
bluetoothPanel.hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NDivider {}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
// Loading indicator
|
||||
ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
visible: Settings.data.network.bluetoothEnabled && BluetoothService.isDiscovering
|
||||
spacing: Style.marginMedium * scaling
|
||||
|
||||
NBusyIndicator {
|
||||
running: BluetoothService.isDiscovering
|
||||
color: Colors.mPrimary
|
||||
size: Style.baseWidgetSize * scaling
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
|
||||
NText {
|
||||
text: "Scanning for devices..."
|
||||
font.pointSize: Style.fontSizeNormal * scaling
|
||||
color: Colors.mOnSurfaceVariant
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
}
|
||||
|
||||
// Bluetooth disabled message
|
||||
ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
visible: !Settings.data.network.bluetoothEnabled
|
||||
spacing: Style.marginMedium * scaling
|
||||
|
||||
NText {
|
||||
text: "bluetooth_disabled"
|
||||
font.family: "Material Symbols Outlined"
|
||||
font.pointSize: Style.fontSizeXXL * scaling
|
||||
color: Colors.mOnSurfaceVariant
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
|
||||
NText {
|
||||
text: "Bluetooth is disabled"
|
||||
font.pointSize: Style.fontSizeLarge * scaling
|
||||
color: Colors.mOnSurfaceVariant
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
|
||||
NText {
|
||||
text: "Enable Bluetooth to see available devices"
|
||||
font.pointSize: Style.fontSizeNormal * scaling
|
||||
color: Colors.mOnSurfaceVariant
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
}
|
||||
|
||||
// Device list
|
||||
ListView {
|
||||
id: deviceList
|
||||
anchors.fill: parent
|
||||
visible: Settings.data.network.bluetoothEnabled && !BluetoothService.isDiscovering
|
||||
model: []
|
||||
spacing: Style.marginMedium * scaling
|
||||
clip: true
|
||||
|
||||
// Combine all devices into a single list for the ListView
|
||||
property var allDevices: {
|
||||
const devices = []
|
||||
|
||||
// Add connected devices first
|
||||
for (const device of BluetoothService.connectedDevices) {
|
||||
devices.push({
|
||||
device: device,
|
||||
type: 'connected',
|
||||
section: 'Connected Devices'
|
||||
})
|
||||
}
|
||||
|
||||
// Add paired devices
|
||||
for (const device of BluetoothService.pairedDevices) {
|
||||
devices.push({
|
||||
device: device,
|
||||
type: 'paired',
|
||||
section: 'Paired Devices'
|
||||
})
|
||||
}
|
||||
|
||||
// Add available devices
|
||||
for (const device of BluetoothService.availableDevices) {
|
||||
devices.push({
|
||||
device: device,
|
||||
type: 'available',
|
||||
section: 'Available Devices'
|
||||
})
|
||||
}
|
||||
|
||||
return devices
|
||||
}
|
||||
|
||||
// Update model when devices change
|
||||
onAllDevicesChanged: {
|
||||
deviceList.model = allDevices
|
||||
}
|
||||
|
||||
// Also watch for changes in the service arrays
|
||||
Connections {
|
||||
target: BluetoothService
|
||||
function onConnectedDevicesChanged() {
|
||||
deviceList.model = deviceList.allDevices
|
||||
}
|
||||
function onPairedDevicesChanged() {
|
||||
deviceList.model = deviceList.allDevices
|
||||
}
|
||||
function onAvailableDevicesChanged() {
|
||||
deviceList.model = deviceList.allDevices
|
||||
}
|
||||
}
|
||||
|
||||
delegate: Item {
|
||||
width: parent ? parent.width : 0
|
||||
height: Style.baseWidgetSize * 1.5 * scaling
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: Style.radiusMedium * scaling
|
||||
color: modelData.device.connected ? Colors.mPrimary : (deviceMouseArea.containsMouse ? Colors.mTertiary : "transparent")
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginSmall * scaling
|
||||
spacing: Style.marginSmall * scaling
|
||||
|
||||
NText {
|
||||
text: BluetoothService.getDeviceIcon(modelData.device)
|
||||
font.family: "Material Symbols Outlined"
|
||||
font.pointSize: Style.fontSizeXL * scaling
|
||||
color: modelData.device.connected ? Colors.mSurface : (deviceMouseArea.containsMouse ? Colors.mSurface : Colors.mOnSurface)
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginTiny * scaling
|
||||
|
||||
NText {
|
||||
text: modelData.device.name || modelData.device.deviceName || "Unknown Device"
|
||||
font.pointSize: Style.fontSizeNormal * scaling
|
||||
elide: Text.ElideRight
|
||||
Layout.fillWidth: true
|
||||
color: modelData.device.connected ? Colors.mSurface : (deviceMouseArea.containsMouse ? Colors.mSurface : Colors.mOnSurface)
|
||||
}
|
||||
|
||||
NText {
|
||||
text: {
|
||||
if (modelData.device.connected) {
|
||||
return "Connected"
|
||||
} else if (modelData.device.paired) {
|
||||
return "Paired"
|
||||
} else {
|
||||
return "Available"
|
||||
}
|
||||
}
|
||||
font.pointSize: Style.fontSizeSmall * scaling
|
||||
color: modelData.device.connected ? Colors.mSurface : (deviceMouseArea.containsMouse ? Colors.mSurface : Colors.mOnSurfaceVariant)
|
||||
}
|
||||
|
||||
NText {
|
||||
text: BluetoothService.getBatteryText(modelData.device)
|
||||
font.pointSize: Style.fontSizeSmall * scaling
|
||||
color: modelData.device.connected ? Colors.mSurface : (deviceMouseArea.containsMouse ? Colors.mSurface : Colors.mOnSurfaceVariant)
|
||||
visible: modelData.device.batteryAvailable
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.preferredWidth: Style.baseWidgetSize * 0.7 * scaling
|
||||
Layout.preferredHeight: Style.baseWidgetSize * 0.7 * scaling
|
||||
visible: modelData.device.pairing || modelData.device.state === 2 // Connecting state
|
||||
|
||||
NBusyIndicator {
|
||||
visible: modelData.device.pairing || modelData.device.state === 2
|
||||
running: modelData.device.pairing || modelData.device.state === 2
|
||||
color: Colors.mPrimary
|
||||
anchors.centerIn: parent
|
||||
size: Style.baseWidgetSize * 0.7 * scaling
|
||||
}
|
||||
}
|
||||
|
||||
NText {
|
||||
visible: modelData.device.connected
|
||||
text: "connected"
|
||||
font.pointSize: Style.fontSizeSmall * scaling
|
||||
color: modelData.device.connected ? Colors.mSurface : (deviceMouseArea.containsMouse ? Colors.mSurface : Colors.mOnSurface)
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: deviceMouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onClicked: {
|
||||
if (modelData.device.connected) {
|
||||
BluetoothService.disconnectDevice(modelData.device)
|
||||
} else if (modelData.device.paired) {
|
||||
BluetoothService.connectDevice(modelData.device)
|
||||
} else {
|
||||
BluetoothService.pairDevice(modelData.device)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Empty state when no devices found
|
||||
ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
visible: Settings.data.network.bluetoothEnabled &&
|
||||
!BluetoothService.isDiscovering &&
|
||||
deviceList.count === 0
|
||||
spacing: Style.marginMedium * scaling
|
||||
|
||||
NText {
|
||||
text: "bluetooth_disabled"
|
||||
font.family: "Material Symbols Outlined"
|
||||
font.pointSize: Style.fontSizeXXL * scaling
|
||||
color: Colors.mOnSurfaceVariant
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
|
||||
NText {
|
||||
text: "No Bluetooth devices"
|
||||
font.pointSize: Style.fontSizeLarge * scaling
|
||||
color: Colors.mOnSurfaceVariant
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
|
||||
NText {
|
||||
text: "Click the refresh button to discover devices"
|
||||
font.pointSize: Style.fontSizeNormal * scaling
|
||||
color: Colors.mOnSurfaceVariant
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue