From 2fd20c69f94b631d08f0efdbe6057340b8360b23 Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Sat, 16 Aug 2025 15:35:10 +0200 Subject: [PATCH] Add bluetooth panel (wip), add clock / clock+date setting --- Commons/Settings.qml | 1 + Commons/Time.qml | 16 +- Modules/Bar/Bar.qml | 6 +- Modules/Bar/Bluetooth.qml | 58 +++ Modules/Bar/BluetoothMenu.qml | 397 ++++++++++++++++ Modules/SettingsPanel/Tabs/NetworkTab.qml | 1 + Modules/SettingsPanel/Tabs/TimeWeatherTab.qml | 9 + Services/BluetoothService.qml | 424 ++++++++++++++++++ Services/BrightnessService.qml | 8 +- 9 files changed, 911 insertions(+), 9 deletions(-) create mode 100644 Modules/Bar/Bluetooth.qml create mode 100644 Modules/Bar/BluetoothMenu.qml create mode 100644 Services/BluetoothService.qml diff --git a/Commons/Settings.qml b/Commons/Settings.qml index fca47ad..ba38f94 100644 --- a/Commons/Settings.qml +++ b/Commons/Settings.qml @@ -96,6 +96,7 @@ Singleton { property bool useFahrenheit: false property bool reverseDayMonth: false property bool use12HourClock: false + property bool showDateWithClock: false } // screen recorder diff --git a/Commons/Time.qml b/Commons/Time.qml index 8f8d3bf..e027116 100644 --- a/Commons/Time.qml +++ b/Commons/Time.qml @@ -9,8 +9,20 @@ Singleton { id: root property var date: new Date() - property string time: Settings.data.location.use12HourClock ? Qt.formatDateTime(date, "h:mm AP") : Qt.formatDateTime( - date, "HH:mm") + property string time: { + let timeFormat = Settings.data.location.use12HourClock ? "h:mm AP" : "HH:mm" + let timeString = Qt.formatDateTime(date, timeFormat) + + if (Settings.data.location.showDateWithClock) { + let dayName = date.toLocaleDateString(Qt.locale(), "ddd") + dayName = dayName.charAt(0).toUpperCase() + dayName.slice(1) + let day = date.getDate() + let month = date.toLocaleDateString(Qt.locale(), "MMM") + return timeString + " - " + dayName + ", " + day + " " + month + } + + return timeString + } readonly property string dateString: { let now = date let dayName = now.toLocaleDateString(Qt.locale(), "ddd") diff --git a/Modules/Bar/Bar.qml b/Modules/Bar/Bar.qml index 741c842..32d438c 100644 --- a/Modules/Bar/Bar.qml +++ b/Modules/Bar/Bar.qml @@ -109,9 +109,9 @@ Variants { anchors.verticalCenter: parent.verticalCenter } - // Bluetooth { - // anchors.verticalCenter: parent.verticalCenter - // } + Bluetooth { + anchors.verticalCenter: parent.verticalCenter + } Battery { anchors.verticalCenter: parent.verticalCenter } diff --git a/Modules/Bar/Bluetooth.qml b/Modules/Bar/Bluetooth.qml new file mode 100644 index 0000000..b381fb0 --- /dev/null +++ b/Modules/Bar/Bluetooth.qml @@ -0,0 +1,58 @@ +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls +import Quickshell +import Quickshell.Wayland +import qs.Commons +import qs.Services +import qs.Widgets + +NIconButton { + id: root + + readonly property bool bluetoothEnabled: Settings.data.network.bluetoothEnabled + sizeMultiplier: 0.8 + showBorder: false + visible: bluetoothEnabled + + Component.onCompleted: { + console.log("[Bluetooth] Component loaded, bluetoothEnabled:", bluetoothEnabled) + console.log("[Bluetooth] BluetoothService available:", typeof BluetoothService !== 'undefined') + if (typeof BluetoothService !== 'undefined') { + console.log("[Bluetooth] Connected devices:", BluetoothService.connectedDevices.length) + } + } + icon: { + // Show different icons based on connection status + if (BluetoothService.connectedDevices.length > 0) { + return "bluetooth_connected" + } else if (BluetoothService.isDiscovering) { + return "bluetooth_searching" + } else { + return "bluetooth" + } + } + tooltipText: "Bluetooth Devices" + onClicked: { + if (!bluetoothMenuLoader.active) { + bluetoothMenuLoader.isLoaded = true + } + if (bluetoothMenuLoader.item) { + if (bluetoothMenuLoader.item.visible) { + // Panel is visible, hide it with animation + if (bluetoothMenuLoader.item.hide) { + bluetoothMenuLoader.item.hide() + } else { + bluetoothMenuLoader.item.visible = false + } + } else { + // Panel is hidden, show it + bluetoothMenuLoader.item.visible = true + } + } + } + + BluetoothMenu { + id: bluetoothMenuLoader + } +} \ No newline at end of file diff --git a/Modules/Bar/BluetoothMenu.qml b/Modules/Bar/BluetoothMenu.qml new file mode 100644 index 0000000..cfe4dd7 --- /dev/null +++ b/Modules/Bar/BluetoothMenu.qml @@ -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 + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/Modules/SettingsPanel/Tabs/NetworkTab.qml b/Modules/SettingsPanel/Tabs/NetworkTab.qml index a3cd797..f9e66f2 100644 --- a/Modules/SettingsPanel/Tabs/NetworkTab.qml +++ b/Modules/SettingsPanel/Tabs/NetworkTab.qml @@ -58,6 +58,7 @@ ColumnLayout { checked: Settings.data.network.bluetoothEnabled onToggled: checked => { Settings.data.network.bluetoothEnabled = checked + BluetoothService.setBluetoothEnabled(checked) } } } diff --git a/Modules/SettingsPanel/Tabs/TimeWeatherTab.qml b/Modules/SettingsPanel/Tabs/TimeWeatherTab.qml index 7f0b708..157b916 100644 --- a/Modules/SettingsPanel/Tabs/TimeWeatherTab.qml +++ b/Modules/SettingsPanel/Tabs/TimeWeatherTab.qml @@ -95,6 +95,15 @@ ColumnLayout { Settings.data.location.reverseDayMonth = checked } } + + NToggle { + label: "Show Date with Clock" + description: "Display date alongside time (e.g., 18:12 - Sat, 23 Aug)" + checked: Settings.data.location.showDateWithClock + onToggled: checked => { + Settings.data.location.showDateWithClock = checked + } + } } NDivider { diff --git a/Services/BluetoothService.qml b/Services/BluetoothService.qml new file mode 100644 index 0000000..11530df --- /dev/null +++ b/Services/BluetoothService.qml @@ -0,0 +1,424 @@ +pragma Singleton + +import QtQuick +import Quickshell +import Quickshell.Bluetooth +import qs.Commons + +Singleton { + id: root + + // Bluetooth state properties + property bool isEnabled: Settings.data.network.bluetoothEnabled + property bool isDiscovering: false + property var connectedDevices: [] + property var pairedDevices: [] + property var availableDevices: [] + property string lastConnectedDevice: "" + property string connectStatus: "" + property string connectStatusDevice: "" + property string connectError: "" + + // Timer for refreshing device lists + property Timer refreshTimer: Timer { + interval: 5000 // Refresh every 5 seconds when discovery is active + repeat: true + running: root.isEnabled && Bluetooth.defaultAdapter && Bluetooth.defaultAdapter.discovering + onTriggered: root.refreshDevices() + } + + Component.onCompleted: { + console.log("[Bluetooth] Service started") + + if (isEnabled && Bluetooth.defaultAdapter) { + // Ensure adapter is enabled + if (!Bluetooth.defaultAdapter.enabled) { + Bluetooth.defaultAdapter.enabled = true + } + + // Start discovery to find devices + if (!Bluetooth.defaultAdapter.discovering) { + Bluetooth.defaultAdapter.discovering = true + } + + // Refresh devices after a short delay to allow discovery to start + Qt.callLater(function() { + refreshDevices() + }) + } + } + + // Function to enable/disable Bluetooth + function setBluetoothEnabled(enabled) { + + if (enabled) { + // Store the currently connected devices before enabling + for (const device of connectedDevices) { + if (device.connected) { + lastConnectedDevice = device.name || device.deviceName + break + } + } + + // Enable Bluetooth + if (Bluetooth.defaultAdapter) { + Bluetooth.defaultAdapter.enabled = true + + // Start discovery to find devices + if (!Bluetooth.defaultAdapter.discovering) { + Bluetooth.defaultAdapter.discovering = true + } + + // Refresh devices after enabling + Qt.callLater(refreshDevices) + } else { + console.warn("[Bluetooth] No Bluetooth adapter found!") + } + } else { + // Disconnect from current devices before disabling + for (const device of connectedDevices) { + if (device.connected) { + device.disconnect() + } + } + + // Disable Bluetooth + if (Bluetooth.defaultAdapter) { + console.log("[Bluetooth] Disabling Bluetooth adapter") + Bluetooth.defaultAdapter.enabled = false + } + } + + Settings.data.network.bluetoothEnabled = enabled + isEnabled = enabled + } + + // Function to refresh device lists + function refreshDevices() { + if (!isEnabled || !Bluetooth.defaultAdapter) { + connectedDevices = [] + pairedDevices = [] + availableDevices = [] + return + } + + // Remove duplicate check since we already did it above + + const connected = [] + const paired = [] + const available = [] + + let devices = null + + // Try adapter devices first + if (Bluetooth.defaultAdapter.enabled && Bluetooth.defaultAdapter.devices) { + devices = Bluetooth.defaultAdapter.devices + } + + // Fallback to global devices list + if (!devices && Bluetooth.devices) { + devices = Bluetooth.devices + } + + if (!devices) { + connectedDevices = [] + pairedDevices = [] + availableDevices = [] + return + } + + // Use Qt model methods to iterate through the ObjectModel + let deviceFound = false + + try { + // Get the row count using the Qt model method + const rowCount = devices.rowCount() + + if (rowCount > 0) { + // Iterate through each row using the Qt model data() method + for (let i = 0; i < rowCount; i++) { + try { + // Create a model index for this row + const modelIndex = devices.index(i, 0) + if (!modelIndex.valid) continue + + // Get the device object using the Qt.UserRole (typically 256) + const device = devices.data(modelIndex, 256) // Qt.UserRole + if (!device) { + // Try alternative role values + const deviceAlt = devices.data(modelIndex, 0) // Qt.DisplayRole + if (deviceAlt) { + device = deviceAlt + } else { + continue + } + } + + deviceFound = true + + if (device.connected) { + connected.push(device) + } else if (device.paired) { + paired.push(device) + } else { + available.push(device) + } + + } catch (e) { + // Silent error handling + } + } + } + + // Alternative method: try the values property if available + if (!deviceFound && devices.values) { + try { + const values = devices.values + if (values && typeof values === 'object') { + // Try to iterate through values if it's iterable + if (values.length !== undefined) { + for (let i = 0; i < values.length; i++) { + const device = values[i] + if (device) { + deviceFound = true + if (device.connected) { + connected.push(device) + } else if (device.paired) { + paired.push(device) + } else { + available.push(device) + } + } + } + } + } + } catch (e) { + // Silent error handling + } + } + + } catch (e) { + console.warn("[Bluetooth] Error accessing device model:", e) + } + + if (!deviceFound) { + console.log("[Bluetooth] No devices found") + } + + connectedDevices = connected + pairedDevices = paired + availableDevices = available + } + + // Function to start discovery + function startDiscovery() { + if (!isEnabled || !Bluetooth.defaultAdapter) return + + isDiscovering = true + Bluetooth.defaultAdapter.discovering = true + } + + // Function to stop discovery + function stopDiscovery() { + if (!Bluetooth.defaultAdapter) return + + isDiscovering = false + Bluetooth.defaultAdapter.discovering = false + } + + // Function to connect to a device + function connectDevice(device) { + if (!device) return + + // Check if device is still valid (not stale from previous session) + if (!device.connect || typeof device.connect !== 'function') { + console.warn("[Bluetooth] Device object is stale, refreshing devices") + refreshDevices() + return + } + + connectStatus = "connecting" + connectStatusDevice = device.name || device.deviceName + connectError = "" + + try { + device.connect() + } catch (error) { + console.error("[Bluetooth] Error connecting to device:", error) + connectStatus = "error" + connectError = error.toString() + Qt.callLater(refreshDevices) + } + } + + // Function to disconnect from a device + function disconnectDevice(device) { + if (!device) return + + // Check if device is still valid (not stale from previous session) + if (!device.disconnect || typeof device.disconnect !== 'function') { + console.warn("[Bluetooth] Device object is stale, refreshing devices") + refreshDevices() + return + } + + try { + device.disconnect() + // Clear connection status + connectStatus = "" + connectStatusDevice = "" + connectError = "" + } catch (error) { + console.warn("[Bluetooth] Error disconnecting device:", error) + Qt.callLater(refreshDevices) + } + } + + // Function to pair with a device + function pairDevice(device) { + if (!device) return + + // Check if device is still valid (not stale from previous session) + if (!device.pair || typeof device.pair !== 'function') { + console.warn("[Bluetooth] Device object is stale, refreshing devices") + refreshDevices() + return + } + + try { + device.pair() + } catch (error) { + console.warn("[Bluetooth] Error pairing device:", error) + Qt.callLater(refreshDevices) + } + } + + // Function to forget a device + function forgetDevice(device) { + if (!device) return + + // Check if device is still valid (not stale from previous session) + if (!device.forget || typeof device.forget !== 'function') { + console.warn("[Bluetooth] Device object is stale, refreshing devices") + refreshDevices() + return + } + + // Store device info before forgetting (in case device object becomes invalid) + const deviceName = device.name || device.deviceName || "Unknown Device" + + try { + device.forget() + + // Clear any connection status that might be related to this device + if (connectStatusDevice === deviceName) { + connectStatus = "" + connectStatusDevice = "" + connectError = "" + } + + // Refresh devices after a delay to ensure the forget operation is complete + Qt.callLater(refreshDevices, 1000) + + } catch (error) { + console.warn("[Bluetooth] Error forgetting device:", error) + Qt.callLater(refreshDevices, 500) + } + } + + // Function to get device icon + function getDeviceIcon(device) { + if (!device) return "bluetooth" + + // Use device icon if available, otherwise fall back to device type + if (device.icon) { + return device.icon + } + + // Fallback icons based on common device types + const name = (device.name || device.deviceName || "").toLowerCase() + if (name.includes("headphone") || name.includes("earbud") || name.includes("airpods")) { + return "headphones" + } else if (name.includes("speaker")) { + return "speaker" + } else if (name.includes("keyboard")) { + return "keyboard" + } else if (name.includes("mouse")) { + return "mouse" + } else if (name.includes("phone") || name.includes("mobile")) { + return "smartphone" + } else if (name.includes("laptop") || name.includes("computer")) { + return "laptop" + } + + return "bluetooth" + } + + // Function to get device status text + function getDeviceStatus(device) { + if (!device) return "" + + if (device.connected) { + return "Connected" + } else if (device.pairing) { + return "Pairing..." + } else if (device.paired) { + return "Paired" + } else { + return "Available" + } + } + + // Function to get battery level text + function getBatteryText(device) { + if (!device || !device.batteryAvailable) return "" + + const percentage = Math.round(device.battery * 100) + return `${percentage}%` + } + + // Watch for Bluetooth adapter changes + Connections { + target: Bluetooth.defaultAdapter + ignoreUnknownSignals: true + + function onEnabledChanged() { + root.isEnabled = Bluetooth.defaultAdapter.enabled + Settings.data.network.bluetoothEnabled = root.isEnabled + if (root.isEnabled) { + Qt.callLater(refreshDevices) + } else { + connectedDevices = [] + pairedDevices = [] + availableDevices = [] + } + } + + function onDiscoveringChanged() { + root.isDiscovering = Bluetooth.defaultAdapter.discovering + if (Bluetooth.defaultAdapter.discovering) { + Qt.callLater(refreshDevices) + } + } + + function onStateChanged() { + if (Bluetooth.defaultAdapter.state >= 4) { + Qt.callLater(refreshDevices) + } + } + + function onDevicesChanged() { + Qt.callLater(refreshDevices) + } + } + + // Watch for global device changes + Connections { + target: Bluetooth + ignoreUnknownSignals: true + + function onDevicesChanged() { + Qt.callLater(refreshDevices) + } + } +} \ No newline at end of file diff --git a/Services/BrightnessService.qml b/Services/BrightnessService.qml index 85eb9fd..d2ffa31 100644 --- a/Services/BrightnessService.qml +++ b/Services/BrightnessService.qml @@ -156,13 +156,13 @@ Singleton { readonly property Process initProc: Process { stdout: StdioCollector { onStreamFinished: { - console.log("[BrightnessService] Raw brightness data for", monitor.modelData.name + ":", text.trim()) + console.log("[Brightness] Raw brightness data for", monitor.modelData.name + ":", text.trim()) if (monitor.isAppleDisplay) { var val = parseInt(text.trim()) if (!isNaN(val)) { monitor.brightness = val / 101 - console.log("[BrightnessService] Apple display brightness:", monitor.brightness) + console.log("[Brightness] Apple display brightness:", monitor.brightness) } } else if (monitor.isDdc) { var parts = text.trim().split(" ") @@ -171,7 +171,7 @@ Singleton { var max = parseInt(parts[1]) if (!isNaN(current) && !isNaN(max) && max > 0) { monitor.brightness = current / max - console.log("[BrightnessService] DDC brightness:", current + "/" + max + " =", monitor.brightness) + console.log("[Brightness] DDC brightness:", current + "/" + max + " =", monitor.brightness) } } } else { @@ -182,7 +182,7 @@ Singleton { var max = parseInt(parts[1]) if (!isNaN(current) && !isNaN(max) && max > 0) { monitor.brightness = current / max - console.log("[BrightnessService] Internal brightness:", current + "/" + max + " =", monitor.brightness) + console.log("[Brightness] Internal brightness:", current + "/" + max + " =", monitor.brightness) } } }