From 62ef2fc4215797d15d6d76835d33a68f32e3b396 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 15 Apr 2025 20:10:52 +0200 Subject: [PATCH] bluetooth and wifi --- .config/quickshell/modules/bar/Bar.qml | 23 ++++++- .config/quickshell/modules/bar/SysTray.qml | 12 ++++ .config/quickshell/modules/bar/Workspaces.qml | 11 +++ .../quickshell/modules/common/Bluetooth.qml | 69 +++++++++++++++++++ .../modules/common/ConfigOptions.qml | 9 +++ .config/quickshell/modules/common/Network.qml | 53 ++++++++++++++ .../modules/common/widgets/StyledToolTip.qml | 4 +- .../modules/sidebarRight/SidebarRight.qml | 54 ++++----------- .../sidebarRight/quickToggles/Bluetooth.qml | 41 +++++++++++ .../sidebarRight/quickToggles/GameMode.qml | 30 ++++++++ .../quickToggles/IdleInhibitor.qml | 20 ++++++ .../sidebarRight/quickToggles/Network.qml | 47 +++++++++++++ .../sidebarRight/quickToggles/NightLight.qml | 42 +++++++++++ 13 files changed, 373 insertions(+), 42 deletions(-) create mode 100644 .config/quickshell/modules/common/Bluetooth.qml create mode 100644 .config/quickshell/modules/common/Network.qml create mode 100644 .config/quickshell/modules/sidebarRight/quickToggles/Bluetooth.qml create mode 100644 .config/quickshell/modules/sidebarRight/quickToggles/GameMode.qml create mode 100644 .config/quickshell/modules/sidebarRight/quickToggles/IdleInhibitor.qml create mode 100644 .config/quickshell/modules/sidebarRight/quickToggles/Network.qml create mode 100644 .config/quickshell/modules/sidebarRight/quickToggles/NightLight.qml diff --git a/.config/quickshell/modules/bar/Bar.qml b/.config/quickshell/modules/bar/Bar.qml index 25fe1ddf5..dd1ecdab2 100644 --- a/.config/quickshell/modules/bar/Bar.qml +++ b/.config/quickshell/modules/bar/Bar.qml @@ -136,9 +136,27 @@ Scope { spacing: 20 layoutDirection: Qt.RightToLeft - Item { // TODO make this wifi & bluetooth - Layout.leftMargin: Appearance.rounding.screenRounding + RowLayout { // TODO make this wifi & bluetooth + Layout.rightMargin: Appearance.rounding.screenRounding Layout.fillWidth: false + spacing: 15 + + MaterialSymbol { + text: (Network.networkName.length > 0 && Network.networkName != "lo") ? ( + Network.networkStrength > 80 ? "signal_wifi_4_bar" : + Network.networkStrength > 60 ? "network_wifi_3_bar" : + Network.networkStrength > 40 ? "network_wifi_2_bar" : + Network.networkStrength > 20 ? "network_wifi_1_bar" : + "signal_wifi_0_bar" + ) : "signal_wifi_off" + font.pointSize: Appearance.font.pointSize.larger + color: Appearance.colors.colOnLayer0 + } + MaterialSymbol { + text: Bluetooth.bluetoothConnected ? "bluetooth_connected" : Bluetooth.bluetoothEnabled ? "bluetooth" : "bluetooth_disabled" + font.pointSize: Appearance.font.pointSize.larger + color: Appearance.colors.colOnLayer0 + } } SysTray { @@ -155,6 +173,7 @@ Scope { MouseArea { anchors.fill: rightSection acceptedButtons: Qt.LeftButton + propagateComposedEvents: true onPressed: (event) => { if (event.button === Qt.LeftButton) { toggleSidebarRight.running = true diff --git a/.config/quickshell/modules/bar/SysTray.qml b/.config/quickshell/modules/bar/SysTray.qml index 5bf3e823f..e5dbdfe28 100644 --- a/.config/quickshell/modules/bar/SysTray.qml +++ b/.config/quickshell/modules/bar/SysTray.qml @@ -7,6 +7,7 @@ import Quickshell.Services.SystemTray import Quickshell.Wayland import Quickshell.Widgets +// TODO: More fancy animation Item { id: root @@ -34,6 +35,17 @@ Item { } + StyledText { + Layout.alignment: Qt.AlignVCenter + font.pointSize: Appearance.font.pointSize.larger + color: Appearance.colors.colSubtext + text: "•" + visible: { + console.log("SystemTray.values.length", SystemTray.items.values.length) + SystemTray.items.values.length > 0 + } + } + } } diff --git a/.config/quickshell/modules/bar/Workspaces.qml b/.config/quickshell/modules/bar/Workspaces.qml index d502c5a20..ec01dcfa9 100644 --- a/.config/quickshell/modules/bar/Workspaces.qml +++ b/.config/quickshell/modules/bar/Workspaces.qml @@ -12,6 +12,7 @@ Item { required property var bar readonly property HyprlandMonitor monitor: Hyprland.monitorFor(bar.screen) readonly property Toplevel activeWindow: ToplevelManager.activeToplevel + readonly property int workspaceGroup: Math.floor((monitor.activeWorkspace?.id - 1) / ConfigOptions.bar.workspacesShown) property list workspaceOccupied: [] property int widgetPadding: 4 @@ -70,6 +71,16 @@ Item { acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad } + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.BackButton + onPressed: (event) => { + if (event.button === Qt.BackButton) { + Hyprland.dispatch(`togglespecialworkspace`); + } + } + } + // Workspaces - background RowLayout { id: rowLayout diff --git a/.config/quickshell/modules/common/Bluetooth.qml b/.config/quickshell/modules/common/Bluetooth.qml new file mode 100644 index 000000000..fb654c417 --- /dev/null +++ b/.config/quickshell/modules/common/Bluetooth.qml @@ -0,0 +1,69 @@ +pragma Singleton + +import Quickshell; +import Quickshell.Io; +import QtQuick; + +Singleton { + id: root + + property int updateInterval: 1000 + property string bluetoothDeviceName: "" + property string bluetoothDeviceAddress: "" + property bool bluetoothEnabled: false + property bool bluetoothConnected: false + + function update() { + updateBluetoothDevice.running = true + updateBluetoothStatus.running = true + updateBluetoothEnabled.running = true + } + + Timer { + interval: 10 + running: true + repeat: true + onTriggered: { + update() + interval = root.updateInterval + } + } + + // Check if Bluetooth is enabled (controller powered on) + Process { + id: updateBluetoothEnabled + command: ["sh", "-c", "bluetoothctl show | grep -q 'Powered: yes' && echo 1 || echo 0"] + running: true + stdout: SplitParser { + onRead: data => { + root.bluetoothEnabled = (parseInt(data) === 1) + } + } + } + + // Get the name and address of the first connected Bluetooth device + Process { + id: updateBluetoothDevice + command: ["sh", "-c", "bluetoothctl info | awk -F': ' '/Name: /{name=$2} /Device /{addr=$2} END{print name \":\" addr}'"] + running: true + stdout: SplitParser { + onRead: data => { + let parts = data.split(":") + root.bluetoothDeviceName = parts[0] || "" + root.bluetoothDeviceAddress = parts[1] || "" + } + } + } + + // Check if any device is connected + Process { + id: updateBluetoothStatus + command: ["sh", "-c", "bluetoothctl info | grep -q 'Connected: yes' && echo 1 || echo 0"] + running: true + stdout: SplitParser { + onRead: data => { + root.bluetoothConnected = (parseInt(data) === 1) + } + } + } +} diff --git a/.config/quickshell/modules/common/ConfigOptions.qml b/.config/quickshell/modules/common/ConfigOptions.qml index 216a7dfd0..f225d9737 100644 --- a/.config/quickshell/modules/common/ConfigOptions.qml +++ b/.config/quickshell/modules/common/ConfigOptions.qml @@ -7,6 +7,15 @@ Singleton { property int fakeScreenRounding: 1 // 0: None | 1: Always | 2: When not fullscreen } + property QtObject apps: QtObject { + property string bluetooth: "blueberry" + property string imageViewer: "loupe" + property string network: "XDG_CURRENT_DESKTOP=\"gnome\" gnome-control-center wifi" + property string settings: "XDG_CURRENT_DESKTOP=\"gnome\" gnome-control-center" + property string taskManager: "gnome-usage" + property string terminal: "foot" // This is only for shell actions + } + property QtObject bar: QtObject { property int workspacesShown: 10 property int batteryLowThreshold: 20 diff --git a/.config/quickshell/modules/common/Network.qml b/.config/quickshell/modules/common/Network.qml new file mode 100644 index 000000000..9a3a30345 --- /dev/null +++ b/.config/quickshell/modules/common/Network.qml @@ -0,0 +1,53 @@ +pragma Singleton + +import Quickshell; +import Quickshell.Io; +import Quickshell.Services.Pipewire; +import QtQuick; + +Singleton { + id: root + + property int updateInterval: 1000 + property string networkName: ""; + property int networkStrength; + function update() { + updateNetworkName.running = true + updateNetworkStrength.running = true + } + + Timer { + interval: 10 + running: true + repeat: true + onTriggered: { + update() + interval = root.updateInterval; + } + } + + Process { + id: updateNetworkName + command: ["sh", "-c", "nmcli -t -f NAME c show --active | head -1"] + running: true; + stdout: SplitParser { + onRead: data => { + root.networkName = data + // console.log("Network: " + data); + } + } + } + + Process { + id: updateNetworkStrength + running: true + command: ["sh", "-c", "nmcli -f IN-USE,SIGNAL,SSID device wifi | awk '/^\*/{if (NR!=1) {print $2}}'"]; + stdout: SplitParser { + onRead: data => { + root.networkStrength = parseInt(data); + // console.log("Network Strength: " + data); + } + } + } +} + diff --git a/.config/quickshell/modules/common/widgets/StyledToolTip.qml b/.config/quickshell/modules/common/widgets/StyledToolTip.qml index 31b67ad34..407fecf78 100644 --- a/.config/quickshell/modules/common/widgets/StyledToolTip.qml +++ b/.config/quickshell/modules/common/widgets/StyledToolTip.qml @@ -12,10 +12,12 @@ ToolTip { background: Rectangle { color: Appearance.colors.colTooltip radius: Appearance.rounding.small + implicitWidth: tooltipText.implicitWidth + 2 * padding } StyledText { - text: content id: tooltipText + text: content color: Appearance.colors.colOnTooltip + wrapMode: Text.WordWrap } } \ No newline at end of file diff --git a/.config/quickshell/modules/sidebarRight/SidebarRight.qml b/.config/quickshell/modules/sidebarRight/SidebarRight.qml index 82e86dd9c..ec5dfc989 100644 --- a/.config/quickshell/modules/sidebarRight/SidebarRight.qml +++ b/.config/quickshell/modules/sidebarRight/SidebarRight.qml @@ -1,5 +1,6 @@ import "root:/modules/common" import "root:/modules/common/widgets" +import "./quickToggles/" import QtQuick import QtQuick.Controls import QtQuick.Layouts @@ -128,45 +129,11 @@ Scope { anchors.margins: 5 spacing: 5 - QuickToggleButton { - property bool enabled: false - buttonIcon: "gamepad" - toggled: enabled - onClicked: { - enabled = !enabled - if (enabled) { - gameModeOn.running = true - } else { - gameModeOff.running = true - } - } - Process { - id: gameModeOn - command: ['bash', '-c', `hyprctl --batch "keyword animations:enabled 0; keyword decoration:shadow:enabled 0; keyword decoration:blur:enabled 0; keyword general:gaps_in 0; keyword general:gaps_out 0; keyword general:border_size 1; keyword decoration:rounding 0; keyword general:allow_tearing 1"`] - } - Process { - id: gameModeOff - command: ['bash', '-c', `hyprctl reload`] - } - StyledToolTip { - content: "Game mode" - } - } - - QuickToggleButton { - toggled: idleInhibitor.running - buttonIcon: "coffee" - onClicked: { - idleInhibitor.running = !idleInhibitor.running - } - Process { - id: idleInhibitor - command: ["bash", "-c", "${XDG_CONFIG_HOME:-$HOME/.config}/quickshell/scripts/wayland-idle-inhibitor.py"] - } - StyledToolTip { - content: "Keep system awake" - } - } + Network {} + Bluetooth {} + NightLight {} + GameMode {} + IdleInhibitor {} } } @@ -203,6 +170,15 @@ Scope { } } } + + function close(): void { + for (let i = 0; i < sidebarVariants.instances.length; i++) { + let panelWindow = sidebarVariants.instances[i]; + if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) { + panelWindow.visible = false; + } + } + } } } diff --git a/.config/quickshell/modules/sidebarRight/quickToggles/Bluetooth.qml b/.config/quickshell/modules/sidebarRight/quickToggles/Bluetooth.qml new file mode 100644 index 000000000..1517e9689 --- /dev/null +++ b/.config/quickshell/modules/sidebarRight/quickToggles/Bluetooth.qml @@ -0,0 +1,41 @@ +import "../" +import "root:/modules/common" +import "root:/modules/common/widgets" +import QtQuick +import Quickshell +import Quickshell.Io + +QuickToggleButton { + toggled: Bluetooth.bluetoothEnabled + buttonIcon: Bluetooth.bluetoothConnected ? "bluetooth_connected" : Bluetooth.bluetoothEnabled ? "bluetooth" : "bluetooth_disabled" + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.RightButton | Qt.LeftButton + onClicked: { + if (mouse.button === Qt.LeftButton) { + toggleBluetooth.running = true + } + if (mouse.button === Qt.RightButton) { + configureBluetooth.running = true + } + } + hoverEnabled: false + propagateComposedEvents: true + } + Process { + id: configureBluetooth + command: ["bash", "-c", `${ConfigOptions.apps.bluetooth} & qs ipc call sidebarRight close`] + } + Process { + id: toggleBluetooth + command: ["bash", "-c", `bluetoothctl power ${Bluetooth.bluetoothEnabled ? "off" : "on"}`] + onRunningChanged: { + if(!running) { + Bluetooth.update() + } + } + } + StyledToolTip { + content: `${Bluetooth.bluetoothEnabled ? Bluetooth.bluetoothDeviceName : "Bluetooth"} | Right-click to configure` + } +} diff --git a/.config/quickshell/modules/sidebarRight/quickToggles/GameMode.qml b/.config/quickshell/modules/sidebarRight/quickToggles/GameMode.qml new file mode 100644 index 000000000..94f8ebb6c --- /dev/null +++ b/.config/quickshell/modules/sidebarRight/quickToggles/GameMode.qml @@ -0,0 +1,30 @@ +import "root:/modules/common" +import "root:/modules/common/widgets" +import "../" +import Quickshell.Io +import Quickshell + +QuickToggleButton { + property bool enabled: false + buttonIcon: "gamepad" + toggled: enabled + onClicked: { + enabled = !enabled + if (enabled) { + gameModeOn.running = true + } else { + gameModeOff.running = true + } + } + Process { + id: gameModeOn + command: ['bash', '-c', `hyprctl --batch "keyword animations:enabled 0; keyword decoration:shadow:enabled 0; keyword decoration:blur:enabled 0; keyword general:gaps_in 0; keyword general:gaps_out 0; keyword general:border_size 1; keyword decoration:rounding 0; keyword general:allow_tearing 1"`] + } + Process { + id: gameModeOff + command: ['bash', '-c', `hyprctl reload`] + } + StyledToolTip { + content: "Game mode" + } +} \ No newline at end of file diff --git a/.config/quickshell/modules/sidebarRight/quickToggles/IdleInhibitor.qml b/.config/quickshell/modules/sidebarRight/quickToggles/IdleInhibitor.qml new file mode 100644 index 000000000..086836975 --- /dev/null +++ b/.config/quickshell/modules/sidebarRight/quickToggles/IdleInhibitor.qml @@ -0,0 +1,20 @@ +import "root:/modules/common" +import "root:/modules/common/widgets" +import "../" +import Quickshell.Io +import Quickshell + +QuickToggleButton { + toggled: idleInhibitor.running + buttonIcon: "coffee" + onClicked: { + idleInhibitor.running = !idleInhibitor.running + } + Process { + id: idleInhibitor + command: ["bash", "-c", "${XDG_CONFIG_HOME:-$HOME/.config}/quickshell/scripts/wayland-idle-inhibitor.py"] + } + StyledToolTip { + content: "Keep system awake" + } +} diff --git a/.config/quickshell/modules/sidebarRight/quickToggles/Network.qml b/.config/quickshell/modules/sidebarRight/quickToggles/Network.qml new file mode 100644 index 000000000..c3d8173ca --- /dev/null +++ b/.config/quickshell/modules/sidebarRight/quickToggles/Network.qml @@ -0,0 +1,47 @@ +import "root:/modules/common" +import "root:/modules/common/widgets" +import "../" +import Quickshell.Io +import Quickshell +import QtQuick + +QuickToggleButton { + toggled: Network.networkName.length > 0 && Network.networkName != "lo" + buttonIcon: toggled ? ( + Network.networkStrength > 80 ? "signal_wifi_4_bar" : + Network.networkStrength > 60 ? "network_wifi_3_bar" : + Network.networkStrength > 40 ? "network_wifi_2_bar" : + Network.networkStrength > 20 ? "network_wifi_1_bar" : + "signal_wifi_0_bar" + ) : "signal_wifi_off" + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.RightButton | Qt.LeftButton + onClicked: { + if (mouse.button === Qt.LeftButton) { + toggleNetwork.running = true + } + if (mouse.button === Qt.RightButton) { + configureNetwork.running = true + } + } + hoverEnabled: false + propagateComposedEvents: true + } + Process { + id: configureNetwork + command: ["bash", "-c", `${ConfigOptions.apps.network} & qs ipc call sidebarRight close`] + } + Process { + id: toggleNetwork + command: ["bash", "-c", "nmcli radio wifi | grep -q enabled && nmcli radio wifi off || nmcli radio wifi on"] + onRunningChanged: { + if(!running) { + Network.update() + } + } + } + StyledToolTip { + content: `${Network.networkName} | Right-click to configure` + } +} diff --git a/.config/quickshell/modules/sidebarRight/quickToggles/NightLight.qml b/.config/quickshell/modules/sidebarRight/quickToggles/NightLight.qml new file mode 100644 index 000000000..e69a81c04 --- /dev/null +++ b/.config/quickshell/modules/sidebarRight/quickToggles/NightLight.qml @@ -0,0 +1,42 @@ +import "root:/modules/common" +import "root:/modules/common/widgets" +import "../" +import Quickshell.Io +import Quickshell + +QuickToggleButton { + id: nightLightButton + property bool enabled: false + toggled: enabled + buttonIcon: "nightlight" + onClicked: { + nightLightButton.enabled = !nightLightButton.enabled + if (enabled) { + nightLightOn.running = true + } + else { + nightLightOff.running = true + } + } + Process { + id: nightLightOn + command: ["gammastep"] + } + Process { + id: nightLightOff + command: ["pkill", "gammastep"] + } + Process { + id: updateNightLightState + running: true + command: ["pidof", "gammastep"] + stdout: SplitParser { + onRead: (data) => { // if not empty then set toggled to true + nightLightButton.enabled = data.length > 0 + } + } + } + StyledToolTip { + content: "Night Light" + } +}