mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
Squash 3 commits
This commit is contained in:
@@ -3,6 +3,7 @@ import QtQuick.Controls
|
||||
import Quickshell
|
||||
import qs.Commons
|
||||
import qs.Modules.Bar.Extras
|
||||
import qs.Modules.Panels.Settings // For SettingsPanel
|
||||
import qs.Services.Networking
|
||||
import qs.Services.UI
|
||||
import qs.Widgets
|
||||
@@ -49,10 +50,15 @@ Item {
|
||||
"action": "toggle-bluetooth",
|
||||
"icon": BluetoothService.enabled ? "bluetooth-off" : "bluetooth"
|
||||
},
|
||||
{
|
||||
"label": I18n.tr("common.bluetooth") + " " + I18n.tr("tooltips.open-settings"),
|
||||
"action": "bluetooth-settings",
|
||||
"icon": "settings"
|
||||
},
|
||||
{
|
||||
"label": I18n.tr("actions.widget-settings"),
|
||||
"action": "widget-settings",
|
||||
"icon": "settings"
|
||||
"icon": "settings-bar"
|
||||
},
|
||||
]
|
||||
|
||||
@@ -62,6 +68,8 @@ Item {
|
||||
|
||||
if (action === "toggle-bluetooth") {
|
||||
BluetoothService.setBluetoothEnabled(!BluetoothService.enabled);
|
||||
} else if (action === "bluetooth-settings") {
|
||||
SettingsPanelService.openToTab(SettingsPanel.Tab.Network, 1, screen);
|
||||
} else if (action === "widget-settings") {
|
||||
BarService.openWidgetSettings(screen, section, sectionWidgetIndex, widgetId, widgetSettings);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ import Quickshell
|
||||
import Quickshell.Bluetooth
|
||||
import qs.Commons
|
||||
import qs.Modules.MainScreen
|
||||
import qs.Modules.Panels.Settings // For SettingsPanel
|
||||
|
||||
import qs.Services.Networking
|
||||
import qs.Services.UI
|
||||
import qs.Widgets
|
||||
@@ -59,25 +61,6 @@ SmartPanel {
|
||||
baseSize: Style.baseWidgetSize * 0.65
|
||||
}
|
||||
|
||||
// Discoverability toggle (advertising)
|
||||
NIconButton {
|
||||
enabled: BluetoothService.enabled
|
||||
icon: BluetoothService.discoverable ? "broadcast" : "broadcast-off"
|
||||
tooltipText: I18n.tr("bluetooth.panel.discoverable")
|
||||
baseSize: Style.baseWidgetSize * 0.8
|
||||
onClicked: {
|
||||
BluetoothService.setDiscoverable(!BluetoothService.discoverable);
|
||||
}
|
||||
}
|
||||
|
||||
NIconButton {
|
||||
enabled: BluetoothService.enabled
|
||||
icon: BluetoothService.scanningActive ? "stop" : "refresh"
|
||||
tooltipText: I18n.tr("tooltips.refresh-devices")
|
||||
baseSize: Style.baseWidgetSize * 0.8
|
||||
onClicked: BluetoothService.toggleDiscovery()
|
||||
}
|
||||
|
||||
NIconButton {
|
||||
icon: "close"
|
||||
tooltipText: I18n.tr("common.close")
|
||||
@@ -150,17 +133,18 @@ SmartPanel {
|
||||
}
|
||||
}
|
||||
|
||||
// Empty state when no devices
|
||||
// Empty state when no paired devices
|
||||
NBox {
|
||||
id: emptyBox
|
||||
visible: {
|
||||
if (!(BluetoothService.adapter && BluetoothService.adapter.enabled && BluetoothService.adapter.devices) || BluetoothService.scanningActive)
|
||||
if (!(BluetoothService.adapter && BluetoothService.adapter.enabled && BluetoothService.adapter.devices))
|
||||
return false;
|
||||
|
||||
var availableCount = BluetoothService.adapter.devices.values.filter(dev => {
|
||||
return dev && !dev.blocked && (dev.signalStrength === undefined || dev.signalStrength > 0);
|
||||
}).length;
|
||||
return (availableCount === 0);
|
||||
// Check for connected or paired/trusted devices
|
||||
var knownCount = BluetoothService.adapter.devices.values.filter(dev => {
|
||||
return dev && !dev.blocked && (dev.connected || dev.paired || dev.trusted);
|
||||
}).length;
|
||||
return (knownCount === 0);
|
||||
}
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: emptyColumn.implicitHeight + Style.marginXL
|
||||
@@ -190,11 +174,12 @@ SmartPanel {
|
||||
}
|
||||
|
||||
NButton {
|
||||
text: I18n.tr("tooltips.refresh-devices")
|
||||
icon: "refresh"
|
||||
text: I18n.tr("common.settings")
|
||||
icon: "settings"
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
onClicked: {
|
||||
BluetoothService.toggleDiscovery();
|
||||
SettingsPanel.openToTab(SettingsPanel.Tab.Bluetooth);
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,153 +220,6 @@ SmartPanel {
|
||||
visible: items.length > 0 && BluetoothService.adapter && BluetoothService.adapter.enabled
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
// Available devices (for pairing)
|
||||
BluetoothDevicesList {
|
||||
label: I18n.tr("bluetooth.panel.available-devices")
|
||||
headerMode: "filter"
|
||||
property var items: {
|
||||
if (!BluetoothService.adapter || !BluetoothService.adapter.devices)
|
||||
return [];
|
||||
var filtered = BluetoothService.adapter.devices.values.filter(dev => dev && !dev.blocked && !dev.paired && !dev.trusted);
|
||||
// Optionally hide devices without a meaningful name when the filter is enabled
|
||||
if (Settings.data && Settings.data.ui && Settings.data.network.bluetoothHideUnnamedDevices) {
|
||||
filtered = filtered.filter(function (dev) {
|
||||
// Extract display name
|
||||
var dn = "";
|
||||
if (dev && dev.name)
|
||||
dn = dev.name;
|
||||
else if (dev && dev.deviceName)
|
||||
dn = dev.deviceName;
|
||||
else
|
||||
dn = "";
|
||||
if (dn === undefined || dn === null)
|
||||
dn = "";
|
||||
var s = String(dn).trim();
|
||||
|
||||
// 1) Hide empty or whitespace-only
|
||||
if (s.length === 0)
|
||||
return false;
|
||||
|
||||
// 2) Hide common placeholders
|
||||
var lower = s.toLowerCase();
|
||||
if (lower === "unknown" || lower === "unnamed" || lower === "n/a" || lower === "na")
|
||||
return false;
|
||||
|
||||
// 3) Hide if the name equals the device address (ignoring separators)
|
||||
var addr = "";
|
||||
if (dev && dev.address)
|
||||
addr = String(dev.address);
|
||||
else if (dev && dev.bdaddr)
|
||||
addr = String(dev.bdaddr);
|
||||
else if (dev && dev.mac)
|
||||
addr = String(dev.mac);
|
||||
if (addr && addr.length > 0) {
|
||||
var normName = s.toLowerCase().replace(/[^0-9a-z]/g, "");
|
||||
var normAddr = addr.toLowerCase().replace(/[^0-9a-z]/g, "");
|
||||
if (normName.length > 0 && normName === normAddr)
|
||||
return false;
|
||||
}
|
||||
|
||||
// 4) Hide address-like strings
|
||||
// - Colon-separated hex: 00:11:22:33:44:55
|
||||
var macColonHex = /^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$/;
|
||||
if (macColonHex.test(s))
|
||||
return false;
|
||||
// - Hyphen-separated hex: 00-11-22-33-44-55
|
||||
var macHyphenHex = /^([0-9A-Fa-f]{2}-){5}[0-9A-Fa-f]{2}$/;
|
||||
if (macHyphenHex.test(s))
|
||||
return false;
|
||||
// - Hyphen-separated alnum pairs (to catch non-hex variants like AB-CD-EF-GH-01-23)
|
||||
var macHyphenAny = /^([0-9A-Za-z]{2}-){5}[0-9A-Za-z]{2}$/;
|
||||
if (macHyphenAny.test(s))
|
||||
return false;
|
||||
// - Cisco dotted hex: 0011.2233.4455
|
||||
var macDotted = /^[0-9A-Fa-f]{4}\.[0-9A-Fa-f]{4}\.[0-9A-Fa-f]{4}$/;
|
||||
if (macDotted.test(s))
|
||||
return false;
|
||||
// - Bare hex: 001122334455
|
||||
var macBare = /^[0-9A-Fa-f]{12}$/;
|
||||
if (macBare.test(s))
|
||||
return false;
|
||||
|
||||
// Keep device otherwise (has a meaningful user-facing name)
|
||||
return true;
|
||||
});
|
||||
}
|
||||
filtered = BluetoothService.dedupeDevices(filtered);
|
||||
return BluetoothService.sortDevices(filtered);
|
||||
}
|
||||
model: items
|
||||
visible: items.length > 0 && BluetoothService.adapter && BluetoothService.adapter.enabled
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
// Fallback - No devices, scanning
|
||||
NBox {
|
||||
id: scanningBox
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: scanningColumn.implicitHeight + Style.marginXL
|
||||
visible: {
|
||||
if (!(BluetoothService.adapter && BluetoothService.adapter.enabled && BluetoothService.adapter.devices) || !BluetoothService.scanningActive) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var availableCount = BluetoothService.adapter.devices.values.filter(dev => {
|
||||
return dev && !dev.paired && !dev.pairing && !dev.blocked && (dev.signalStrength === undefined || dev.signalStrength > 0);
|
||||
}).length;
|
||||
return (availableCount === 0);
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: scanningColumn
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginM
|
||||
spacing: Style.marginL
|
||||
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
spacing: Style.marginXS
|
||||
|
||||
NIcon {
|
||||
icon: "refresh"
|
||||
pointSize: Style.fontSizeXXL * 1.5
|
||||
color: Color.mPrimary
|
||||
|
||||
RotationAnimation on rotation {
|
||||
running: true
|
||||
loops: Animation.Infinite
|
||||
from: 0
|
||||
to: 360
|
||||
duration: Style.animationSlow * 4
|
||||
}
|
||||
}
|
||||
|
||||
NText {
|
||||
text: I18n.tr("bluetooth.panel.scanning")
|
||||
pointSize: Style.fontSizeL
|
||||
color: Color.mOnSurface
|
||||
}
|
||||
}
|
||||
|
||||
NText {
|
||||
text: I18n.tr("bluetooth.panel.pairing-mode")
|
||||
pointSize: Style.fontSizeM
|
||||
color: Color.mOnSurfaceVariant
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,42 +1,43 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs.Commons
|
||||
import qs.Services.Networking
|
||||
import qs.Services.System
|
||||
import qs.Widgets
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
spacing: Style.marginL
|
||||
spacing: 0
|
||||
|
||||
NToggle {
|
||||
label: I18n.tr("actions.enable-wifi")
|
||||
description: I18n.tr("panels.network.wifi-description")
|
||||
checked: ProgramCheckerService.nmcliAvailable && Settings.data.network.wifiEnabled
|
||||
onToggled: checked => NetworkService.setWifiEnabled(checked)
|
||||
enabled: ProgramCheckerService.nmcliAvailable
|
||||
}
|
||||
|
||||
NDivider {
|
||||
NTabBar {
|
||||
id: subTabBar
|
||||
Layout.fillWidth: true
|
||||
Layout.bottomMargin: Style.marginM
|
||||
distributeEvenly: true
|
||||
currentIndex: tabView.currentIndex
|
||||
|
||||
NTabButton {
|
||||
text: I18n.tr("common.wifi")
|
||||
tabIndex: 0
|
||||
checked: subTabBar.currentIndex === 0
|
||||
}
|
||||
NTabButton {
|
||||
text: I18n.tr("common.bluetooth")
|
||||
tabIndex: 1
|
||||
checked: subTabBar.currentIndex === 1
|
||||
}
|
||||
}
|
||||
|
||||
// Bluetooth adapter toggle grouped with its panel settings
|
||||
NToggle {
|
||||
label: I18n.tr("actions.enable-bluetooth")
|
||||
description: I18n.tr("panels.network.bluetooth-description")
|
||||
checked: BluetoothService.enabled
|
||||
onToggled: checked => BluetoothService.setBluetoothEnabled(checked)
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: Style.marginL
|
||||
}
|
||||
|
||||
// Bluetooth signal strength polling (RSSI via bluetoothctl)
|
||||
NToggle {
|
||||
label: I18n.tr("panels.network.bluetooth-rssi-polling-label")
|
||||
description: I18n.tr("panels.network.bluetooth-rssi-polling-description")
|
||||
checked: Settings.data && Settings.data.network && Settings.data.network.bluetoothRssiPollingEnabled
|
||||
enabled: BluetoothService.enabled
|
||||
onToggled: checked => Settings.data.network.bluetoothRssiPollingEnabled = checked
|
||||
NTabView {
|
||||
id: tabView
|
||||
Layout.fillHeight: true
|
||||
currentIndex: subTabBar.currentIndex
|
||||
|
||||
WifiSubTab {}
|
||||
BluetoothSubTab {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs.Commons
|
||||
import qs.Services.Networking
|
||||
import qs.Services.System
|
||||
import qs.Widgets
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
spacing: Style.marginL
|
||||
Layout.fillWidth: true
|
||||
|
||||
NToggle {
|
||||
label: I18n.tr("actions.enable-wifi")
|
||||
description: I18n.tr("panels.network.wifi-description")
|
||||
checked: ProgramCheckerService.nmcliAvailable && Settings.data.network.wifiEnabled
|
||||
onToggled: checked => NetworkService.setWifiEnabled(checked)
|
||||
enabled: ProgramCheckerService.nmcliAvailable
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user