From 5da7005ce728a416eee14c7ba38e8aef017beffc Mon Sep 17 00:00:00 2001 From: Damocles Date: Wed, 22 Apr 2026 21:54:20 +0200 Subject: [PATCH] extract BluetoothApplet, fold BluetoothMenu into BluetoothModule with hover+pin --- .../BluetoothApplet.qml} | 56 ++---------- shell/modules/Bar.qml | 4 +- shell/modules/BluetoothModule.qml | 91 ++++++++++++++----- shell/modules/qmldir | 1 - 4 files changed, 79 insertions(+), 73 deletions(-) rename shell/{modules/BluetoothMenu.qml => applets/BluetoothApplet.qml} (68%) diff --git a/shell/modules/BluetoothMenu.qml b/shell/applets/BluetoothApplet.qml similarity index 68% rename from shell/modules/BluetoothMenu.qml rename to shell/applets/BluetoothApplet.qml index 9bbb6a4..64d2584 100644 --- a/shell/modules/BluetoothMenu.qml +++ b/shell/applets/BluetoothApplet.qml @@ -1,46 +1,10 @@ import QtQuick -import Quickshell -import "." as M import "../services" as S -M.HoverPanel { - id: menuWindow +Column { + id: root - contentWidth: 250 - panelNamespace: "nova-bluetooth" - popupMode: true - panelTitle: "Bluetooth" - titleActionsComponent: Component { - Item { - width: 20 - height: 20 - - Text { - anchors.centerIn: parent - text: "\uF011" - color: S.BluetoothService.enabled ? menuWindow.accentColor : S.Theme.base04 - font.pixelSize: S.Theme.fontSize - font.family: S.Theme.iconFontFamily - - Behavior on color { - ColorAnimation { - duration: 100 - } - } - } - - HoverHandler { - cursorShape: Qt.PointingHandCursor - } - - TapHandler { - onTapped: S.BluetoothService.setPower(!S.BluetoothService.enabled) - } - } - } - - onVisibleChanged: if (visible) - S.BluetoothService.refresh() + required property color accentColor Repeater { model: S.BluetoothService.devices @@ -52,7 +16,7 @@ M.HoverPanel { readonly property bool _pending: S.BluetoothService.pendingMac === entry.modelData.mac - width: menuWindow.contentWidth + width: root.width height: 32 Rectangle { @@ -69,7 +33,7 @@ M.HoverPanel { anchors.leftMargin: 12 anchors.verticalCenter: parent.verticalCenter text: "\uF294" - color: entry._pending ? menuWindow.accentColor : entry.modelData.connected ? menuWindow.accentColor : S.Theme.base04 + color: entry._pending || entry.modelData.connected ? root.accentColor : S.Theme.base04 font.pixelSize: S.Theme.fontSize + 1 font.family: S.Theme.iconFontFamily } @@ -81,7 +45,7 @@ M.HoverPanel { anchors.rightMargin: 4 anchors.verticalCenter: parent.verticalCenter text: entry.modelData.name - color: entry._pending ? menuWindow.accentColor : entry.modelData.connected ? menuWindow.accentColor : S.Theme.base05 + color: entry._pending || entry.modelData.connected ? root.accentColor : S.Theme.base05 font.pixelSize: S.Theme.fontSize font.family: S.Theme.fontFamily font.bold: entry.modelData.connected || entry._pending @@ -94,7 +58,7 @@ M.HoverPanel { anchors.rightMargin: 12 anchors.verticalCenter: parent.verticalCenter text: entry._pending ? (entry.modelData.connected ? "disconnecting..." : "connecting...") : (entry.modelData.battery >= 0 ? entry.modelData.battery + "%" : "") - color: entry._pending ? S.Theme.base04 : S.Theme.base04 + color: S.Theme.base04 font.pixelSize: S.Theme.fontSize - 1 font.family: S.Theme.fontFamily font.italic: entry._pending @@ -125,10 +89,8 @@ M.HoverPanel { } TapHandler { onTapped: { - if (!entry._pending) { + if (!entry._pending) S.BluetoothService.toggleDevice(entry.modelData.mac, !entry.modelData.connected); - menuWindow.keepOpen(500); - } } } } @@ -136,7 +98,7 @@ M.HoverPanel { Text { visible: S.BluetoothService.devices.length === 0 - width: menuWindow.contentWidth + width: root.width height: 32 horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter diff --git a/shell/modules/Bar.qml b/shell/modules/Bar.qml index 944e22b..fd86f92 100644 --- a/shell/modules/Bar.qml +++ b/shell/modules/Bar.qml @@ -183,9 +183,7 @@ PanelWindow { M.NetworkModule { visible: S.Modules.network.enable } - M.BluetoothModule { - bar: bar - } + M.BluetoothModule {} } // Controls diff --git a/shell/modules/BluetoothModule.qml b/shell/modules/BluetoothModule.qml index 81e715e..d107f71 100644 --- a/shell/modules/BluetoothModule.qml +++ b/shell/modules/BluetoothModule.qml @@ -2,18 +2,30 @@ import QtQuick import Quickshell import "." as M import "../services" as S +import "../applets" as C M.BarSection { id: root spacing: S.Theme.moduleSpacing opacity: S.Modules.bluetooth.enable && S.BluetoothService.state !== "unavailable" ? 1 : 0 visible: opacity > 0 - tooltip: { - if (S.BluetoothService.state === "off") - return "Bluetooth: off"; - if (S.BluetoothService.state === "connected") - return "Bluetooth: " + S.BluetoothService.device + (S.BluetoothService.batteryPct >= 0 ? "\nBattery: " + S.BluetoothService.batteryPct + "%" : ""); - return "Bluetooth: on"; + tooltip: "" + + property bool _pinned: false + readonly property bool _anyHover: root._hovered || hoverPanel.panelHovered + readonly property bool _showPanel: _anyHover || _pinned + + on_AnyHoverChanged: { + if (_anyHover) + _unpinTimer.stop(); + else if (_pinned) + _unpinTimer.start(); + } + + Timer { + id: _unpinTimer + interval: 500 + onTriggered: root._pinned = false } M.BarIcon { @@ -21,10 +33,7 @@ M.BarSection { color: S.BluetoothService.state === "off" ? S.Theme.base04 : root.accentColor anchors.verticalCenter: parent.verticalCenter TapHandler { - onTapped: { - M.FlyoutState.visible = false; - btLoader.active = true; - } + onTapped: root._pinned = !root._pinned } } M.BarLabel { @@ -32,23 +41,61 @@ M.BarSection { label: S.BluetoothService.device + (S.BluetoothService.batteryPct >= 0 ? " " + S.BluetoothService.batteryPct + "%" : "") anchors.verticalCenter: parent.verticalCenter TapHandler { - onTapped: { - M.FlyoutState.visible = false; - btLoader.active = true; - } + onTapped: root._pinned = !root._pinned } } - required property var bar + M.HoverPanel { + id: hoverPanel + showPanel: root._showPanel + screen: QsWindow.window?.screen ?? null + anchorItem: root + accentColor: root.accentColor + panelNamespace: "nova-bluetooth" + panelTitle: "Bluetooth" + contentWidth: 250 + titleActionsComponent: Component { + Item { + width: 20 + height: 20 - LazyLoader { - id: btLoader - active: false - M.BluetoothMenu { + Text { + anchors.centerIn: parent + text: "\uF011" + color: S.BluetoothService.enabled ? hoverPanel.accentColor : S.Theme.base04 + font.pixelSize: S.Theme.fontSize + font.family: S.Theme.iconFontFamily + + Behavior on color { + ColorAnimation { + duration: 100 + } + } + } + + HoverHandler { + cursorShape: Qt.PointingHandCursor + } + + TapHandler { + onTapped: S.BluetoothService.setPower(!S.BluetoothService.enabled) + } + } + } + + onVisibleChanged: if (visible) + S.BluetoothService.refresh() + + Connections { + target: S.BluetoothService + function onDevicesChanged() { + hoverPanel.keepOpen(500); + } + } + + C.BluetoothApplet { + width: hoverPanel.contentWidth accentColor: root.accentColor - screen: QsWindow.window?.screen ?? null - anchorX: root.mapToGlobal(root.width / 2, 0).x - (QsWindow.window?.screen?.x ?? 0) - onDismissed: btLoader.active = false } } } diff --git a/shell/modules/qmldir b/shell/modules/qmldir index a0d5f63..b4a55ee 100644 --- a/shell/modules/qmldir +++ b/shell/modules/qmldir @@ -8,7 +8,6 @@ BarIcon 1.0 BarIcon.qml BarLabel 1.0 BarLabel.qml BarSection 1.0 BarSection.qml BatteryModule 1.0 BatteryModule.qml -BluetoothMenu 1.0 BluetoothMenu.qml BluetoothModule 1.0 BluetoothModule.qml ClockModule 1.0 ClockModule.qml CpuModule 1.0 CpuModule.qml