From 169450aa2e9d9058686aab0f7cf54b728ecd6efa Mon Sep 17 00:00:00 2001 From: 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> Date: Tue, 29 Apr 2025 18:47:45 +1000 Subject: feat: bar bluetooth devices --- modules/bar/Pills.qml | 8 ++-- modules/bar/components/Clock.qml | 2 +- modules/bar/components/StatusIcons.qml | 38 +++++++++++++++++- services/Bluetooth.qml | 70 ++++++++++++++++++++++++++++++++++ services/Hyprland.qml | 4 +- utils/Icons.qml | 10 +++++ widgets/AnchorText.qml | 2 +- widgets/BoxLayout.qml | 2 +- widgets/StyledRect.qml | 2 +- 9 files changed, 127 insertions(+), 11 deletions(-) create mode 100644 services/Bluetooth.qml diff --git a/modules/bar/Pills.qml b/modules/bar/Pills.qml index 814643a..042e9c1 100644 --- a/modules/bar/Pills.qml +++ b/modules/bar/Pills.qml @@ -35,9 +35,9 @@ Item { vertical: BarConfig.vertical anchors.left: root.get(osIcon.right, undefined) - anchors.leftMargin: root.get(Appearance.padding.normal, 0) + anchors.leftMargin: root.get(Appearance.padding.large, 0) anchors.top: root.get(undefined, osIcon.bottom) - anchors.topMargin: root.get(0, Appearance.padding.normal) + anchors.topMargin: root.get(0, Appearance.padding.large) anchors.horizontalCenter: root.get(undefined, parent.horizontalCenter) anchors.verticalCenter: root.get(parent.verticalCenter, undefined) @@ -70,9 +70,9 @@ Item { StatusIcons { anchors.left: root.get(clock.right, undefined) - anchors.leftMargin: root.get(Appearance.padding.normal, 0) + anchors.leftMargin: root.get(Appearance.padding.large, 0) anchors.top: root.get(undefined, clock.bottom) - anchors.topMargin: root.get(0, Appearance.padding.normal) + anchors.topMargin: root.get(0, Appearance.padding.large) anchors.horizontalCenter: root.get(undefined, parent.horizontalCenter) anchors.verticalCenter: root.get(parent.verticalCenter, undefined) diff --git a/modules/bar/components/Clock.qml b/modules/bar/components/Clock.qml index 91e978c..c4764a5 100644 --- a/modules/bar/components/Clock.qml +++ b/modules/bar/components/Clock.qml @@ -19,7 +19,7 @@ StyledRect { prevAnchor: icon horizontalAlignment: StyledText.AlignHCenter - text: root.vertical ? Time.format("hh\nmm") : Time.format("dd/MM/yy hh:mm") + text: root.vertical ? Time.format("hh\nmm") : Time.format("hh:mm • dddd, dd MMMM") font.pointSize: Appearance.font.size.smaller font.family: Appearance.font.family.mono color: root.colour diff --git a/modules/bar/components/StatusIcons.qml b/modules/bar/components/StatusIcons.qml index d3df67d..dd63fc6 100644 --- a/modules/bar/components/StatusIcons.qml +++ b/modules/bar/components/StatusIcons.qml @@ -3,6 +3,7 @@ import "root:/services" import "root:/utils" import "root:/config" import QtQuick +import QtQuick.Controls StyledRect { id: root @@ -10,9 +11,44 @@ StyledRect { readonly property color colour: Appearance.colours.rosewater MaterialIcon { - id: icon + id: network + animate: true text: Icons.getNetworkIcon(Network.active.strength) color: root.colour } + + AnchorText { + id: bluetooth + + prevAnchor: network + + animate: true + text: Bluetooth.powered ? "bluetooth" : "bluetooth_disabled" + color: root.colour + font.family: Appearance.font.family.material + font.pointSize: Appearance.font.size.larger + } + + BoxLayout { + anchors.left: vertical ? undefined : bluetooth.right + anchors.leftMargin: vertical ? 0 : Appearance.padding.smaller + anchors.top: vertical ? bluetooth.bottom : undefined + anchors.topMargin: vertical ? Appearance.padding.smaller : 0 + + anchors.horizontalCenter: vertical ? bluetooth.horizontalCenter : undefined + anchors.verticalCenter: vertical ? undefined : bluetooth.verticalCenter + + Repeater { + model: Bluetooth.connected + + MaterialIcon { + required property Bluetooth.Device modelData + + animate: true + text: Icons.getBluetoothIcon(modelData.icon) + color: root.colour + } + } + } } diff --git a/services/Bluetooth.qml b/services/Bluetooth.qml new file mode 100644 index 0000000..d749879 --- /dev/null +++ b/services/Bluetooth.qml @@ -0,0 +1,70 @@ +pragma Singleton + +import Quickshell +import Quickshell.Io +import QtQuick + +Singleton { + id: root + + property bool powered + property bool discovering + readonly property list devices: [] + readonly property list connected: devices.filter(d => d.connected) + + Process { + running: true + command: ["bluetoothctl"] + stdout: SplitParser { + onRead: getInfo.running = true + } + } + + Process { + id: getInfo + running: true + command: ["sh", "-c", "bluetoothctl show | paste -s"] + stdout: SplitParser { + onRead: data => { + root.powered = data.includes("Powered: yes"); + root.discovering = data.includes("Discovering: yes"); + } + } + } + + Process { + id: getDevices + running: true + command: ["fish", "-c", `for a in (bluetoothctl devices | cut -d ' ' -f 2); bluetoothctl info $a | jq -R 'reduce (inputs / ":") as [$key, $value] ({}; .[$key | ltrimstr("\t")] = ($value | ltrimstr(" ")))' | jq -c --arg addr $a '.Address = $addr'; end`] + stdout: SplitParser { + onRead: data => { + const d = JSON.parse(data); + root.devices.push(deviceComp.createObject(root, { + name: d.Name, + alias: d.Alias, + address: d.Address, + icon: d.Icon, + connected: d.Connected === "yes", + paired: d.Paired === "yes", + trusted: d.Trusted === "yes" + })); + } + } + } + + component Device: QtObject { + property string name + property string alias + property string address + property string icon + property bool connected + property bool paired + property bool trusted + } + + Component { + id: deviceComp + + Device {} + } +} diff --git a/services/Hyprland.qml b/services/Hyprland.qml index d786e62..df2f0ba 100644 --- a/services/Hyprland.qml +++ b/services/Hyprland.qml @@ -39,7 +39,7 @@ Singleton { Process { id: getClients - command: ["bash", "-c", "hyprctl -j clients | jq -c"] + command: ["sh", "-c", "hyprctl -j clients | jq -c"] stdout: SplitParser { onRead: data => { const clients = JSON.parse(data); @@ -58,7 +58,7 @@ Singleton { Process { id: getActiveClient - command: ["bash", "-c", "hyprctl -j activewindow | jq -c"] + command: ["sh", "-c", "hyprctl -j activewindow | jq -c"] stdout: SplitParser { onRead: data => { const client = JSON.parse(data); diff --git a/utils/Icons.qml b/utils/Icons.qml index ac19b4e..92d8de8 100644 --- a/utils/Icons.qml +++ b/utils/Icons.qml @@ -177,6 +177,16 @@ Singleton { return "signal_wifi_0_bar"; } + function getBluetoothIcon(icon: string): string { + if (icon.includes("headset") || icon.includes("headphones")) + return "headphones"; + if (icon.includes("audio")) + return "speaker"; + if (icon.includes("phone")) + return "smartphone"; + return "bluetooth"; + } + FileView { path: "/etc/os-release" onLoaded: { diff --git a/widgets/AnchorText.qml b/widgets/AnchorText.qml index a0342e8..03514dd 100644 --- a/widgets/AnchorText.qml +++ b/widgets/AnchorText.qml @@ -5,7 +5,7 @@ StyledText { id: root required property Item prevAnchor - property bool vertical + property bool vertical: parent.vertical ?? false anchors.left: vertical ? undefined : prevAnchor.right anchors.leftMargin: vertical ? 0 : Appearance.padding.smaller diff --git a/widgets/BoxLayout.qml b/widgets/BoxLayout.qml index 2163000..d000bda 100644 --- a/widgets/BoxLayout.qml +++ b/widgets/BoxLayout.qml @@ -2,7 +2,7 @@ import "root:/config" import QtQuick.Layouts GridLayout { - property bool vertical: false + property bool vertical: parent.vertical ?? false // Propagate from parent property bool homogenous: false property int spacing: Appearance.spacing.small diff --git a/widgets/StyledRect.qml b/widgets/StyledRect.qml index 07c1e77..bb9824c 100644 --- a/widgets/StyledRect.qml +++ b/widgets/StyledRect.qml @@ -5,7 +5,7 @@ Rectangle { id: root property bool animate: false - property bool vertical: false // Convenience property for propagation to children + property bool vertical: parent.vertical ?? false // Convenience property for propagation to children color: "transparent" implicitWidth: childrenRect.width -- cgit v1.2.3-freya