mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
Merge pull request #2110 from turannul/pr/networking-refactor-pt2
Full wireless enterprise support (networking refactor pt2) + QoL Improvements + Conditional WiFi icons
This commit is contained in:
+23
-14
@@ -550,16 +550,13 @@
|
||||
"width": "Width",
|
||||
"wifi": "Wi-Fi",
|
||||
"windows": "Windows",
|
||||
"yes": "Yes"
|
||||
"yes": "Yes",
|
||||
"username": "Username"
|
||||
},
|
||||
"control-center": {
|
||||
"power-profile": {
|
||||
"tooltip-action": "Power Profile",
|
||||
"tooltip-disabled": "Install power-profiles-daemon to use power profiles"
|
||||
},
|
||||
"wifi": {
|
||||
"label-disconnected": "Wi-Fi disconnected",
|
||||
"label-ethernet": "Ethernet"
|
||||
}
|
||||
},
|
||||
"display-modes": {
|
||||
@@ -1424,9 +1421,7 @@
|
||||
"network": {
|
||||
"bluetooth-description": "Activate Bluetooth management.",
|
||||
"bluetooth-rssi-polling-description": "Periodically sample RSSI for connected devices via bluetoothctl. May not be available for all devices; uses minimal resources when enabled.",
|
||||
"bluetooth-rssi-polling-label": "Bluetooth signal polling",
|
||||
"desc": "Manage Wi-Fi and Bluetooth connections.",
|
||||
"wifi-description": "Manage wireless networks (requires NetworkManager)."
|
||||
"bluetooth-rssi-polling-label": "Bluetooth signal polling"
|
||||
},
|
||||
"notifications": {
|
||||
"duration-critical-urgency-description": "How long critical priority notifications stay visible.",
|
||||
@@ -1972,7 +1967,6 @@
|
||||
"keyboard-layout": "{layout} keyboard layout",
|
||||
"list-view": "List view",
|
||||
"manage-vpn": "VPN connections",
|
||||
"manage-wifi": "Wi-Fi",
|
||||
"max-widgets-reached": "Maximum widgets reached",
|
||||
"microphone-volume-at": "Microphone volume: {volume}%",
|
||||
"move-to-section": "Move to {section}",
|
||||
@@ -2166,22 +2160,37 @@
|
||||
"ipv6": "IPv6",
|
||||
"known-networks": "Known networks",
|
||||
"link-speed": "Link speed",
|
||||
"network-name-ssid": "Network name (SSID)",
|
||||
"no-ethernet-devices": "No Ethernet devices detected",
|
||||
"no-networks": "No Wi‑Fi networks found",
|
||||
"saved": "Saved",
|
||||
"scan-again": "Scan again",
|
||||
"searching": "Searching for networks...",
|
||||
"add-network": "Add network",
|
||||
"show-password": "Show password",
|
||||
"hidden-network": "Hidden network",
|
||||
"network-name-ssid": "Network name (SSID)",
|
||||
"security-open": "Open",
|
||||
"security-wpa": "WPA",
|
||||
"security-wep": "WEP",
|
||||
"security-wpa": "WPA/WPA2/WPA3 Personal",
|
||||
"title": "Wi‑Fi"
|
||||
"security-wpa23": "WPA2/WPA3",
|
||||
"security-wpa3": "WPA3",
|
||||
"security-wpa-ent": "WPA Enterprise",
|
||||
"security-wpa2-ent": "WPA2 Enterprise",
|
||||
"security-wpa3-ent": "WPA3 Enterprise"
|
||||
},
|
||||
"signal": {
|
||||
"excellent": "Excellent",
|
||||
"fair": "Fair",
|
||||
"good": "Good",
|
||||
"poor": "Poor"
|
||||
"poor": "Poor",
|
||||
"weak": "Weak"
|
||||
},
|
||||
"enterprise": {
|
||||
"username": "Identity / Username",
|
||||
"password": "User password",
|
||||
"anonymous-identity": "Anonymous Identity",
|
||||
"eap-method": "EAP Method",
|
||||
"phase2-auth": "Phase 2 Authentication",
|
||||
"ca-cert": "CA Certificate"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,47 +79,12 @@ Item {
|
||||
|
||||
BarPill {
|
||||
id: pill
|
||||
|
||||
screen: root.screen
|
||||
oppositeDirection: BarService.getPillDirection(root)
|
||||
customIconColor: Color.resolveColorKeyOptional(root.iconColorKey)
|
||||
customTextColor: Color.resolveColorKeyOptional(root.textColorKey)
|
||||
icon: {
|
||||
try {
|
||||
if (NetworkService.ethernetConnected) {
|
||||
return NetworkService.internetConnectivity ? "ethernet" : "ethernet-off";
|
||||
}
|
||||
let connected = false;
|
||||
let signalStrength = 0;
|
||||
for (const net in NetworkService.networks) {
|
||||
if (NetworkService.networks[net].connected) {
|
||||
connected = true;
|
||||
signalStrength = NetworkService.networks[net].signal;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return connected ? NetworkService.signalIcon(signalStrength, true) : "wifi-off";
|
||||
} catch (error) {
|
||||
Logger.e("Wi-Fi", "Error getting icon:", error);
|
||||
return "wifi-off";
|
||||
}
|
||||
}
|
||||
text: {
|
||||
try {
|
||||
if (NetworkService.ethernetConnected) {
|
||||
return "";
|
||||
}
|
||||
for (const net in NetworkService.networks) {
|
||||
if (NetworkService.networks[net].connected) {
|
||||
return net;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
} catch (error) {
|
||||
Logger.e("Wi-Fi", "Error getting ssid:", error);
|
||||
return "error";
|
||||
}
|
||||
}
|
||||
icon: NetworkService.getIcon()
|
||||
text: NetworkService.getStatustxt()
|
||||
autoHide: false
|
||||
forceOpen: !isBarVertical && root.displayMode === "alwaysShow"
|
||||
forceClose: isBarVertical || root.displayMode === "alwaysHide" || text === ""
|
||||
@@ -134,31 +99,7 @@ Item {
|
||||
if (PanelService.getPanel("networkPanel", screen)?.isPanelOpen) {
|
||||
return "";
|
||||
}
|
||||
try {
|
||||
if (NetworkService.ethernetConnected) {
|
||||
const d = NetworkService.activeEthernetDetails || ({});
|
||||
let base = "";
|
||||
if (d.ifname && d.ifname.length > 0)
|
||||
base = d.ifname;
|
||||
else if (d.connectionName && d.connectionName.length > 0)
|
||||
base = d.connectionName;
|
||||
else if (NetworkService.activeEthernetIf && NetworkService.activeEthernetIf.length > 0)
|
||||
base = NetworkService.activeEthernetIf;
|
||||
else
|
||||
base = I18n.tr("common.ethernet");
|
||||
const speed = (d.speed && d.speed.length > 0) ? d.speed : "";
|
||||
return speed ? (base + " — " + speed) : base;
|
||||
}
|
||||
// Wi‑Fi tooltip: SSID — link speed (if available)
|
||||
if (pill.text !== "") {
|
||||
const w = NetworkService.activeWifiDetails || ({});
|
||||
const rate = (w.rateShort && w.rateShort.length > 0) ? w.rateShort : (w.rate || "");
|
||||
return rate && rate.length > 0 ? (pill.text + " — " + rate) : pill.text;
|
||||
}
|
||||
} catch (e) {
|
||||
// noop
|
||||
}
|
||||
return I18n.tr("common.wifi");
|
||||
return pill.text; // pill.text is exact copy of getStatustxt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,47 +7,8 @@ import qs.Widgets
|
||||
|
||||
NIconButtonHot {
|
||||
property ShellScreen screen
|
||||
|
||||
icon: {
|
||||
try {
|
||||
if (NetworkService.ethernetConnected) {
|
||||
return NetworkService.internetConnectivity ? "ethernet" : "ethernet-off";
|
||||
}
|
||||
let connected = false;
|
||||
let signalStrength = 0;
|
||||
for (const net in NetworkService.networks) {
|
||||
if (NetworkService.networks[net].connected) {
|
||||
connected = true;
|
||||
signalStrength = NetworkService.networks[net].signal;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return connected ? NetworkService.signalIcon(signalStrength, true) : "wifi-off";
|
||||
} catch (error) {
|
||||
Logger.e("Wi-Fi", "Error getting icon:", error);
|
||||
return "wifi-off";
|
||||
}
|
||||
}
|
||||
|
||||
tooltipText: {
|
||||
try {
|
||||
if (NetworkService.ethernetConnected) {
|
||||
// Match design: fixed label when on Ethernet
|
||||
return I18n.tr("common.ethernet");
|
||||
}
|
||||
// Wi‑Fi: SSID — link speed (if available)
|
||||
for (const net in NetworkService.networks) {
|
||||
if (NetworkService.networks[net].connected) {
|
||||
const w = NetworkService.activeWifiDetails || ({});
|
||||
const rate = (w.rateShort && w.rateShort.length > 0) ? w.rateShort : (w.rate || "");
|
||||
return rate && rate.length > 0 ? (net + " — " + rate) : net;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// noop
|
||||
}
|
||||
return I18n.tr("common.wifi");
|
||||
}
|
||||
icon: NetworkService.getIcon()
|
||||
tooltipText: NetworkService.getStatustxt()
|
||||
onClicked: {
|
||||
var panel = PanelService.getPanel("networkPanel", screen);
|
||||
panel?.toggle(this);
|
||||
|
||||
@@ -7,6 +7,7 @@ import qs.Commons
|
||||
import qs.Modules.MainScreen
|
||||
import qs.Modules.Panels.Settings
|
||||
import qs.Services.Networking
|
||||
import qs.Services.System
|
||||
import qs.Services.UI
|
||||
import qs.Widgets
|
||||
|
||||
@@ -20,6 +21,7 @@ SmartPanel {
|
||||
// Ethernet details UI state (mirrors Wi‑Fi info behavior)
|
||||
property bool ethernetInfoExpanded: false
|
||||
property bool ethernetDetailsGrid: (Settings.data.network.wifiDetailsViewMode === "grid")
|
||||
property int ipVersion: 4
|
||||
|
||||
// Unified panel view mode: "wifi" | "ethernet" (persisted)
|
||||
property string panelViewMode: "wifi"
|
||||
@@ -44,12 +46,23 @@ SmartPanel {
|
||||
}
|
||||
}
|
||||
|
||||
// Effectively visible tracking
|
||||
readonly property bool effectivelyVisible: root.visible && Window.window && Window.window.visible
|
||||
|
||||
onEffectivelyVisibleChanged: {
|
||||
if (effectivelyVisible) {
|
||||
SystemStatService.registerComponent("network-panel");
|
||||
NetworkService.scan();
|
||||
// Preload active Wi‑Fi details so Info shows instantly
|
||||
NetworkService.refreshActiveWifiDetails();
|
||||
// Also fetch Ethernet details if connected
|
||||
NetworkService.refreshActiveEthernetDetails();
|
||||
} else {
|
||||
SystemStatService.unregisterComponent("network-panel");
|
||||
}
|
||||
}
|
||||
|
||||
onOpened: {
|
||||
NetworkService.scan();
|
||||
// Preload active Wi‑Fi details so Info shows instantly
|
||||
NetworkService.refreshActiveWifiDetails();
|
||||
// Also fetch Ethernet details if connected
|
||||
NetworkService.refreshActiveEthernetDetails();
|
||||
// Restore last view if valid, otherwise choose what's available (prefer Wi‑Fi when both exist)
|
||||
if (Settings.data.network.networkPanelView) {
|
||||
const last = Settings.data.network.networkPanelView;
|
||||
@@ -95,7 +108,13 @@ SmartPanel {
|
||||
id: modeIcon
|
||||
icon: panelViewMode === "wifi" ? (Settings.data.network.wifiEnabled ? "wifi" : "wifi-off") : (NetworkService.hasEthernet() ? (NetworkService.ethernetConnected ? "ethernet" : "ethernet") : "ethernet-off")
|
||||
pointSize: Style.fontSizeXXL
|
||||
color: panelViewMode === "wifi" ? (Settings.data.network.wifiEnabled ? Color.mPrimary : Color.mOnSurfaceVariant) : (NetworkService.ethernetConnected ? Color.mPrimary : Color.mOnSurfaceVariant)
|
||||
color: {
|
||||
if (panelViewMode === "wifi") {
|
||||
return Settings.data.network.wifiEnabled ? Color.mPrimary : Color.mOnSurfaceVariant;
|
||||
} else {
|
||||
return NetworkService.ethernetConnected ? Color.mPrimary : Color.mOnSurfaceVariant;
|
||||
}
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
@@ -110,7 +129,7 @@ SmartPanel {
|
||||
panelViewMode = "wifi";
|
||||
}
|
||||
}
|
||||
onEntered: TooltipService.show(parent, panelViewMode === "wifi" ? I18n.tr("common.ethernet") : I18n.tr("common.wifi"))
|
||||
onEntered: TooltipService.show(parent, panelViewMode === "wifi" ? I18n.tr("common.wifi") : I18n.tr("common.ethernet"))
|
||||
onExited: TooltipService.hide()
|
||||
}
|
||||
}
|
||||
@@ -296,7 +315,7 @@ SmartPanel {
|
||||
}
|
||||
|
||||
NBusyIndicator {
|
||||
running: true
|
||||
running: visible && root.effectivelyVisible
|
||||
color: Color.mPrimary
|
||||
size: Style.baseWidgetSize
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
@@ -426,6 +445,19 @@ SmartPanel {
|
||||
delegate: NBox {
|
||||
id: ethItem
|
||||
|
||||
HoverHandler {
|
||||
id: itemHover
|
||||
}
|
||||
|
||||
function getContentColors(defaultColors = [Color.mSurface, Color.mOnSurface]) {
|
||||
if (modelData.connected) {
|
||||
// Special alpha for connected item background
|
||||
let bgAlpha = Math.min(1.15 - Color.panelBackgroundOpacity, 0.75);
|
||||
return [Qt.alpha(Color.mPrimary, bgAlpha), Color.mOnPrimary];
|
||||
}
|
||||
return defaultColors;
|
||||
}
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: Style.marginXS
|
||||
Layout.rightMargin: Style.marginXS
|
||||
@@ -433,7 +465,8 @@ SmartPanel {
|
||||
radius: Style.radiusM
|
||||
border.width: Style.borderS
|
||||
border.color: modelData.connected ? Color.mPrimary : Color.mOutline
|
||||
color: modelData.connected ? Qt.alpha(Color.mPrimary, Math.min(1.15 - Color.panelBackgroundOpacity, 0.75)) : Color.mSurface
|
||||
color: ethItem.getContentColors()[0]
|
||||
|
||||
ColumnLayout {
|
||||
id: ethItemColumn
|
||||
width: parent.width - Style.margin2M
|
||||
@@ -450,10 +483,20 @@ SmartPanel {
|
||||
// Click handling for the whole header row is provided by a sibling MouseArea
|
||||
// anchored to this row (defined right after this RowLayout).
|
||||
|
||||
NIcon {
|
||||
icon: "ethernet"
|
||||
pointSize: Style.fontSizeXXL
|
||||
color: modelData.connected ? Color.mPrimary : Color.mOnSurface
|
||||
Rectangle {
|
||||
id: ethIconBg
|
||||
Layout.preferredWidth: Style.baseWidgetSize
|
||||
Layout.preferredHeight: Style.baseWidgetSize
|
||||
radius: Style.radiusM
|
||||
color: Color.smartAlpha(Color.mSurfaceVariant)
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
|
||||
NIcon {
|
||||
anchors.centerIn: parent
|
||||
icon: NetworkService.getIcon(true)
|
||||
pointSize: Style.fontSizeXXL
|
||||
color: ethItem.getContentColors()[1]
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
@@ -461,10 +504,10 @@ SmartPanel {
|
||||
spacing: 2
|
||||
|
||||
NText {
|
||||
text: modelData.ifname
|
||||
text: modelData.connectionName || modelData.ifname
|
||||
pointSize: Style.fontSizeM
|
||||
font.weight: modelData.connected ? Style.fontWeightBold : Style.fontWeightMedium
|
||||
color: Color.mOnSurface
|
||||
color: ethItem.getContentColors()[1]
|
||||
elide: Text.ElideRight
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
@@ -472,20 +515,68 @@ SmartPanel {
|
||||
RowLayout {
|
||||
spacing: Style.marginXS
|
||||
|
||||
// Connected badge (mirrors Wi‑Fi chip)
|
||||
Rectangle {
|
||||
visible: modelData.connected
|
||||
color: Color.mPrimary
|
||||
radius: height * 0.5
|
||||
width: ethConnectedText.implicitWidth + Style.margin2S
|
||||
height: ethConnectedText.implicitHeight + (Style.margin2XXS)
|
||||
NText {
|
||||
text: {
|
||||
if (modelData.connected) {
|
||||
switch (NetworkService.networkConnectivity) {
|
||||
case "full":
|
||||
return I18n.tr("common.connected");
|
||||
case "limited":
|
||||
case "unknown":
|
||||
return I18n.tr("wifi.panel.internet-limited");
|
||||
case "portal":
|
||||
return I18n.tr("wifi.panel.action-required");
|
||||
default:
|
||||
return NetworkService.networkConnectivity;
|
||||
}
|
||||
}
|
||||
return I18n.tr("common.disconnected");
|
||||
}
|
||||
pointSize: Style.fontSizeXXS
|
||||
color: (!modelData.connected || NetworkService.networkConnectivity === "limited" || NetworkService.networkConnectivity === "portal" || NetworkService.networkConnectivity === "unknown") ? Color.mError : ethItem.getContentColors()[1]
|
||||
}
|
||||
|
||||
// Network speed indicators (visible when connected and speed > 0)
|
||||
RowLayout {
|
||||
visible: (modelData.connected && NetworkService.networkConnectivity === "full") && (SystemStatService.rxSpeed > 0 || SystemStatService.txSpeed > 0)
|
||||
spacing: 2
|
||||
Layout.leftMargin: Style.marginXS
|
||||
Layout.fillWidth: false
|
||||
|
||||
NIcon {
|
||||
visible: SystemStatService.rxSpeed > 0
|
||||
icon: "arrow-down"
|
||||
pointSize: Style.fontSizeXXS
|
||||
color: Qt.alpha(ethItem.getContentColors()[1], Style.opacityHeavy)
|
||||
}
|
||||
|
||||
NText {
|
||||
id: ethConnectedText
|
||||
anchors.centerIn: parent
|
||||
text: I18n.tr("common.connected")
|
||||
visible: SystemStatService.rxSpeed > 0
|
||||
text: SystemStatService.formatSpeed(SystemStatService.rxSpeed)
|
||||
pointSize: Style.fontSizeXXS
|
||||
color: Color.mOnPrimary
|
||||
color: Qt.alpha(ethItem.getContentColors()[1], Style.opacityHeavy)
|
||||
elide: Text.ElideNone
|
||||
}
|
||||
|
||||
Item {
|
||||
visible: SystemStatService.rxSpeed > 0 && SystemStatService.txSpeed > 0
|
||||
width: Style.marginXS
|
||||
height: 1
|
||||
}
|
||||
|
||||
NIcon {
|
||||
visible: SystemStatService.txSpeed > 0
|
||||
icon: "arrow-up"
|
||||
pointSize: Style.fontSizeXXS
|
||||
color: Qt.alpha(ethItem.getContentColors()[1], Style.opacityHeavy)
|
||||
}
|
||||
|
||||
NText {
|
||||
visible: SystemStatService.txSpeed > 0
|
||||
text: SystemStatService.formatSpeed(SystemStatService.txSpeed)
|
||||
pointSize: Style.fontSizeXXS
|
||||
color: Qt.alpha(ethItem.getContentColors()[1], Style.opacityHeavy)
|
||||
elide: Text.ElideNone
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -493,6 +584,7 @@ SmartPanel {
|
||||
|
||||
// Info button on the right
|
||||
NIconButton {
|
||||
visible: itemHover.hovered
|
||||
icon: "info"
|
||||
tooltipText: I18n.tr("common.info")
|
||||
baseSize: Style.baseWidgetSize * 0.8
|
||||
@@ -562,6 +654,8 @@ SmartPanel {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginS
|
||||
anchors.rightMargin: Style.baseWidgetSize
|
||||
flow: ethernetDetailsGrid ? GridLayout.TopToBottom : GridLayout.LeftToRight
|
||||
rows: ethernetDetailsGrid ? 3 : 6
|
||||
columns: ethernetDetailsGrid ? 2 : 1
|
||||
columnSpacing: Style.marginM
|
||||
rowSpacing: Style.marginXS
|
||||
@@ -574,13 +668,10 @@ SmartPanel {
|
||||
}
|
||||
|
||||
// --- Item 1: Interface ---
|
||||
// Grid: Row 0, Col 0 | List: Row 0
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredWidth: 1
|
||||
spacing: Style.marginXS
|
||||
Layout.row: 0
|
||||
Layout.column: 0
|
||||
NIcon {
|
||||
icon: "ethernet"
|
||||
pointSize: Style.fontSizeXS
|
||||
@@ -617,55 +708,63 @@ SmartPanel {
|
||||
const value = (NetworkService.activeEthernetDetails.ifname && NetworkService.activeEthernetDetails.ifname.length > 0) ? NetworkService.activeEthernetDetails.ifname : (NetworkService.activeEthernetIf || "");
|
||||
if (value.length > 0) {
|
||||
Quickshell.execDetached(["wl-copy", value]);
|
||||
ToastService.showNotice(I18n.tr("common.ethernet"), I18n.tr("toast.bluetooth.address-copied"), "ethernet");
|
||||
ToastService.showNotice(I18n.tr("common.ethernet"), I18n.tr("common.copied-to-clipboard"), "ethernet");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- Item 2: Internet connectivity --
|
||||
// Grid: Row 1, Col 0 | List: Row 1
|
||||
// --- Item 2: Hardware Address ---
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredWidth: 1
|
||||
Layout.row: 1
|
||||
Layout.column: 0
|
||||
spacing: Style.marginXS
|
||||
NIcon {
|
||||
// If the selected Ethernet interface is disconnected, show an explicit disconnected state
|
||||
icon: modelData.connected ? (NetworkService.internetConnectivity ? "world" : "world-off") : "world-off"
|
||||
icon: "hash"
|
||||
pointSize: Style.fontSizeXS
|
||||
color: modelData.connected ? (NetworkService.internetConnectivity ? Color.mOnSurface : Color.mError) : Color.mError
|
||||
color: Color.mOnSurface
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onEntered: TooltipService.show(parent, I18n.tr("wifi.panel.internet-status"))
|
||||
onEntered: TooltipService.show(parent, I18n.tr("bluetooth.panel.device-address"))
|
||||
onExited: TooltipService.hide()
|
||||
}
|
||||
}
|
||||
NText {
|
||||
// Show "Disconnected" when the interface itself is down
|
||||
text: modelData.connected ? (NetworkService.internetConnectivity ? I18n.tr("wifi.panel.internet-connected") : I18n.tr("wifi.panel.internet-limited")) : I18n.tr("common.disconnected")
|
||||
text: NetworkService.activeEthernetDetails.hwAddr || "-"
|
||||
pointSize: Style.fontSizeXS
|
||||
color: modelData.connected ? (NetworkService.internetConnectivity ? Color.mOnSurface : Color.mError) : Color.mError
|
||||
color: Color.mOnSurface
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
wrapMode: ethernetDetailsGrid ? Text.NoWrap : Text.WrapAtWordBoundaryOrAnywhere
|
||||
elide: ethernetDetailsGrid ? Text.ElideRight : Text.ElideNone
|
||||
maximumLineCount: ethernetDetailsGrid ? 1 : 6
|
||||
clip: true
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
enabled: (NetworkService.activeEthernetDetails.hwAddr || "").length > 0
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onEntered: TooltipService.show(parent, I18n.tr("tooltips.copy-address"))
|
||||
onExited: TooltipService.hide()
|
||||
onClicked: {
|
||||
const value = NetworkService.activeEthernetDetails.hwAddr || "";
|
||||
if (value.length > 0) {
|
||||
Quickshell.execDetached(["wl-copy", value]);
|
||||
ToastService.showNotice(I18n.tr("common.ethernet"), I18n.tr("common.copied-to-clipboard"), "ethernet");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- Iterm 3: Link speed ---
|
||||
// Grid: Row 2, Col 0 | List: Row 2
|
||||
// --- Item 3: Link speed ---
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredWidth: 1
|
||||
Layout.row: 2
|
||||
Layout.column: 0
|
||||
spacing: Style.marginXS
|
||||
NIcon {
|
||||
icon: "gauge"
|
||||
@@ -692,46 +791,10 @@ SmartPanel {
|
||||
}
|
||||
}
|
||||
|
||||
// --- Item 4: Gateway ---
|
||||
// Grid: Row 2, Col 1 | List: Row 5 (Last)
|
||||
// --- Item 4: IPv4 || IPv6 ---
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredWidth: 1
|
||||
Layout.row: ethernetDetailsGrid ? 2 : 5
|
||||
Layout.column: ethernetDetailsGrid ? 1 : 0
|
||||
spacing: Style.marginXS
|
||||
NIcon {
|
||||
icon: "router"
|
||||
pointSize: Style.fontSizeXS
|
||||
color: Color.mOnSurface
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onEntered: TooltipService.show(parent, I18n.tr("common.gateway"))
|
||||
onExited: TooltipService.hide()
|
||||
}
|
||||
}
|
||||
NText {
|
||||
text: NetworkService.activeEthernetDetails.gateway4 || "-"
|
||||
pointSize: Style.fontSizeXS
|
||||
color: Color.mOnSurface
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
wrapMode: ethernetDetailsGrid ? Text.NoWrap : Text.WrapAtWordBoundaryOrAnywhere
|
||||
elide: ethernetDetailsGrid ? Text.ElideRight : Text.ElideNone
|
||||
maximumLineCount: ethernetDetailsGrid ? 1 : 6
|
||||
clip: true
|
||||
}
|
||||
}
|
||||
|
||||
// --- Item 5: IPv4 ---
|
||||
// Grid: Row 0, Col 1 | List: Row 3
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredWidth: 1
|
||||
Layout.row: ethernetDetailsGrid ? 0 : 3
|
||||
Layout.column: ethernetDetailsGrid ? 1 : 0
|
||||
spacing: Style.marginXS
|
||||
NIcon {
|
||||
icon: "network"
|
||||
@@ -741,12 +804,16 @@ SmartPanel {
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onEntered: TooltipService.show(parent, I18n.tr("wifi.panel.ipv4"))
|
||||
onEntered: TooltipService.show(parent, root.ipVersion === 4 ? I18n.tr("wifi.panel.ipv4") : I18n.tr("wifi.panel.ipv6"))
|
||||
onExited: TooltipService.hide()
|
||||
onClicked: {
|
||||
root.ipVersion = root.ipVersion === 4 ? 6 : 4;
|
||||
TooltipService.show(parent, root.ipVersion === 4 ? I18n.tr("wifi.panel.ipv4") : I18n.tr("wifi.panel.ipv6"));
|
||||
}
|
||||
}
|
||||
}
|
||||
NText {
|
||||
text: NetworkService.activeEthernetDetails.ipv4 || "-"
|
||||
text: root.ipVersion === 4 ? (NetworkService.activeEthernetDetails.ipv4 || "-") : ((NetworkService.activeEthernetDetails.ipv6 || []).join(", ") || "-")
|
||||
pointSize: Style.fontSizeXS
|
||||
color: Color.mOnSurface
|
||||
Layout.fillWidth: true
|
||||
@@ -756,33 +823,29 @@ SmartPanel {
|
||||
maximumLineCount: ethernetDetailsGrid ? 1 : 6
|
||||
clip: true
|
||||
|
||||
// Click-to-copy Ethernet IPv4 address
|
||||
// Click-to-copy Ethernet IP address
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
// Normalize to string to avoid undefined -> bool assignment warnings
|
||||
enabled: (NetworkService.activeEthernetDetails.ipv4 || "").length > 0
|
||||
enabled: root.ipVersion === 4 ? (NetworkService.activeEthernetDetails.ipv4 || "").length > 0 : (NetworkService.activeEthernetDetails.ipv6 || []).length > 0
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onEntered: TooltipService.show(parent, I18n.tr("tooltips.copy-address"))
|
||||
onExited: TooltipService.hide()
|
||||
onClicked: {
|
||||
const value = NetworkService.activeEthernetDetails.ipv4 || "";
|
||||
const value = root.ipVersion === 4 ? (NetworkService.activeEthernetDetails.ipv4 || "") : ((NetworkService.activeEthernetDetails.ipv6 || []).join(", ") || "");
|
||||
if (value.length > 0) {
|
||||
Quickshell.execDetached(["wl-copy", value]);
|
||||
ToastService.showNotice(I18n.tr("common.ethernet"), I18n.tr("toast.bluetooth.address-copied"), "ethernet");
|
||||
ToastService.showNotice(I18n.tr("common.ethernet"), I18n.tr("common.copied-to-clipboard"), "ethernet");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- Item 6: DNS ---
|
||||
// Grid: Row 1, Col 1 | List: Row 4
|
||||
// --- Item 5: DNS ---
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredWidth: 1
|
||||
Layout.row: ethernetDetailsGrid ? 1 : 4
|
||||
Layout.column: ethernetDetailsGrid ? 1 : 0
|
||||
spacing: Style.marginXS
|
||||
NIcon {
|
||||
icon: "world"
|
||||
@@ -792,12 +855,16 @@ SmartPanel {
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onEntered: TooltipService.show(parent, I18n.tr("wifi.panel.dns"))
|
||||
onEntered: TooltipService.show(parent, root.ipVersion === 4 ? I18n.tr("wifi.panel.dns") + " (" + I18n.tr("wifi.panel.ipv4") + ")" : I18n.tr("wifi.panel.dns") + " (" + I18n.tr("wifi.panel.ipv6") + ")")
|
||||
onExited: TooltipService.hide()
|
||||
onClicked: {
|
||||
root.ipVersion = root.ipVersion === 4 ? 6 : 4;
|
||||
TooltipService.show(parent, root.ipVersion === 4 ? I18n.tr("wifi.panel.dns") + " (" + I18n.tr("wifi.panel.ipv4") + ")" : I18n.tr("wifi.panel.dns") + " (" + I18n.tr("wifi.panel.ipv6") + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
NText {
|
||||
text: NetworkService.activeEthernetDetails.dns || "-"
|
||||
text: root.ipVersion === 4 ? ((NetworkService.activeEthernetDetails.dns4 || []).join(", ") || "-") : ((NetworkService.activeEthernetDetails.dns6 || []).join(", ") || "-")
|
||||
pointSize: Style.fontSizeXS
|
||||
color: Color.mOnSurface
|
||||
Layout.fillWidth: true
|
||||
@@ -806,6 +873,74 @@ SmartPanel {
|
||||
elide: ethernetDetailsGrid ? Text.ElideRight : Text.ElideNone
|
||||
maximumLineCount: ethernetDetailsGrid ? 1 : 6
|
||||
clip: true
|
||||
|
||||
// Click-to-copy Ethernet DNS
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
enabled: root.ipVersion === 4 ? (NetworkService.activeEthernetDetails.dns4 || []).length > 0 : (NetworkService.activeEthernetDetails.dns6 || []).length > 0
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onEntered: TooltipService.show(parent, I18n.tr("tooltips.copy-address"))
|
||||
onExited: TooltipService.hide()
|
||||
onClicked: {
|
||||
const value = root.ipVersion === 4 ? ((NetworkService.activeEthernetDetails.dns4 || []).join(", ") || "") : ((NetworkService.activeEthernetDetails.dns6 || []).join(", ") || "");
|
||||
if (value.length > 0) {
|
||||
Quickshell.execDetached(["wl-copy", value]);
|
||||
ToastService.showNotice(I18n.tr("common.ethernet"), I18n.tr("common.copied-to-clipboard"), "ethernet");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- Item 6: Gateway ---
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredWidth: 1
|
||||
spacing: Style.marginXS
|
||||
NIcon {
|
||||
icon: "router"
|
||||
pointSize: Style.fontSizeXS
|
||||
color: Color.mOnSurface
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onEntered: TooltipService.show(parent, root.ipVersion === 4 ? I18n.tr("common.gateway") + " (" + I18n.tr("wifi.panel.ipv4") + ")" : I18n.tr("common.gateway") + " (" + I18n.tr("wifi.panel.ipv6") + ")")
|
||||
onExited: TooltipService.hide()
|
||||
onClicked: {
|
||||
root.ipVersion = root.ipVersion === 4 ? 6 : 4;
|
||||
TooltipService.show(parent, root.ipVersion === 4 ? I18n.tr("common.gateway") + " (" + I18n.tr("wifi.panel.ipv4") + ")" : I18n.tr("common.gateway") + " (" + I18n.tr("wifi.panel.ipv6") + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
NText {
|
||||
text: root.ipVersion === 4 ? (NetworkService.activeEthernetDetails.gateway4 || "-") : ((NetworkService.activeEthernetDetails.gateway6 || []).join(", ") || "-")
|
||||
pointSize: Style.fontSizeXS
|
||||
color: Color.mOnSurface
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
wrapMode: ethernetDetailsGrid ? Text.NoWrap : Text.WrapAtWordBoundaryOrAnywhere
|
||||
elide: ethernetDetailsGrid ? Text.ElideRight : Text.ElideNone
|
||||
maximumLineCount: ethernetDetailsGrid ? 1 : 6
|
||||
clip: true
|
||||
|
||||
// Click-to-copy Ethernet Gateway
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
enabled: root.ipVersion === 4 ? (NetworkService.activeEthernetDetails.gateway4 || "").length > 0 : (NetworkService.activeEthernetDetails.gateway6 || []).length > 0
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onEntered: TooltipService.show(parent, I18n.tr("tooltips.copy-address"))
|
||||
onExited: TooltipService.hide()
|
||||
onClicked: {
|
||||
const value = root.ipVersion === 4 ? (NetworkService.activeEthernetDetails.gateway4 || "") : ((NetworkService.activeEthernetDetails.gateway6 || []).join(", ") || "");
|
||||
if (value.length > 0) {
|
||||
Quickshell.execDetached(["wl-copy", value]);
|
||||
ToastService.showNotice(I18n.tr("common.ethernet"), I18n.tr("common.copied-to-clipboard"), "ethernet");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -370,18 +370,33 @@ Item {
|
||||
NBox {
|
||||
id: device
|
||||
|
||||
HoverHandler {
|
||||
id: itemHover
|
||||
}
|
||||
|
||||
readonly property bool canConnect: BluetoothService.canConnect(modelData)
|
||||
readonly property bool canDisconnect: BluetoothService.canDisconnect(modelData)
|
||||
readonly property bool canPair: BluetoothService.canPair(modelData)
|
||||
readonly property bool isBusy: BluetoothService.isDeviceBusy(modelData)
|
||||
readonly property bool isExpanded: root.expandedDeviceKey === BluetoothService.deviceKey(modelData)
|
||||
|
||||
function getContentColors(defaultColors = [Color.mSurface, Color.mOnSurface]) {
|
||||
if (modelData.pairing || modelData.state === BluetoothDeviceState.Connecting) {
|
||||
return [Color.mPrimary, Color.mOnPrimary];
|
||||
}
|
||||
if (modelData.connected && modelData.state !== BluetoothDeviceState.Disconnecting) {
|
||||
// Special alpha for connected item background
|
||||
let bgAlpha = Math.min(1.15 - Color.panelBackgroundOpacity, 0.75);
|
||||
return [Qt.alpha(Color.mPrimary, bgAlpha), Color.mOnPrimary];
|
||||
}
|
||||
if (modelData.blocked || modelData.state === BluetoothDeviceState.Disconnecting) {
|
||||
return [Color.mError, Color.mOnError];
|
||||
}
|
||||
return defaultColors;
|
||||
}
|
||||
|
||||
function getContentColor(defaultColor = Color.mOnSurface) {
|
||||
if (modelData.pairing || modelData.state === BluetoothDeviceState.Connecting)
|
||||
return Color.mPrimary;
|
||||
if (modelData.blocked || modelData.state === BluetoothDeviceState.Disconnecting)
|
||||
return Color.mError;
|
||||
return defaultColor;
|
||||
return getContentColors([Color.mSurface, defaultColor])[1];
|
||||
}
|
||||
|
||||
Layout.fillWidth: true
|
||||
@@ -389,7 +404,7 @@ Item {
|
||||
radius: Style.radiusM
|
||||
clip: true
|
||||
|
||||
color: (modelData.connected && modelData.state !== BluetoothDeviceState.Disconnecting) ? Qt.alpha(Color.mPrimary, Math.min(1.15 - Color.panelBackgroundOpacity, 0.75)) : Color.mSurface
|
||||
color: device.getContentColors()[0]
|
||||
|
||||
ColumnLayout {
|
||||
id: deviceColumn
|
||||
@@ -403,11 +418,20 @@ Item {
|
||||
spacing: Style.marginM
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
|
||||
NIcon {
|
||||
icon: BluetoothService.getDeviceIcon(modelData)
|
||||
pointSize: Style.fontSizeXXL
|
||||
color: modelData.connected ? Color.mPrimary : device.getContentColor(Color.mOnSurface)
|
||||
Rectangle {
|
||||
id: deviceIconBg
|
||||
Layout.preferredWidth: Style.baseWidgetSize
|
||||
Layout.preferredHeight: Style.baseWidgetSize
|
||||
radius: Style.radiusM
|
||||
color: Color.smartAlpha(Color.mSurfaceVariant)
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
|
||||
NIcon {
|
||||
anchors.centerIn: parent
|
||||
icon: BluetoothService.getDeviceIcon(modelData)
|
||||
pointSize: Style.fontSizeXXL
|
||||
color: device.getContentColors()[1]
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
@@ -419,7 +443,7 @@ Item {
|
||||
pointSize: Style.fontSizeM
|
||||
font.weight: modelData.connected ? Style.fontWeightBold : Style.fontWeightMedium
|
||||
elide: Text.ElideRight
|
||||
color: device.getContentColor(Color.mOnSurface)
|
||||
color: device.getContentColors()[1]
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
@@ -450,7 +474,7 @@ Item {
|
||||
return BatteryService.getIcon(b !== null ? b : 0, false, false, b !== null);
|
||||
}
|
||||
pointSize: Style.fontSizeXS
|
||||
color: device.getContentColor(Color.mOnSurface)
|
||||
color: device.getContentColors()[1]
|
||||
}
|
||||
NText {
|
||||
text: {
|
||||
@@ -471,7 +495,7 @@ Item {
|
||||
spacing: Style.marginS
|
||||
|
||||
NIconButton {
|
||||
visible: modelData.connected
|
||||
visible: itemHover.hovered && modelData.connected
|
||||
icon: "info"
|
||||
tooltipText: I18n.tr("common.info")
|
||||
baseSize: Style.baseWidgetSize * 0.8
|
||||
@@ -482,7 +506,7 @@ Item {
|
||||
}
|
||||
|
||||
NIconButton {
|
||||
visible: !root.showOnlyLists && (modelData.paired || modelData.trusted) && !modelData.connected && !isBusy && !modelData.blocked
|
||||
visible: itemHover.hovered && !root.showOnlyLists && (modelData.paired || modelData.trusted) && !modelData.connected && !isBusy && !modelData.blocked
|
||||
icon: "trash"
|
||||
tooltipText: I18n.tr("common.unpair")
|
||||
baseSize: Style.baseWidgetSize * 0.8
|
||||
@@ -491,7 +515,7 @@ Item {
|
||||
|
||||
NButton {
|
||||
id: button
|
||||
visible: (modelData.state !== BluetoothDeviceState.Connecting)
|
||||
visible: itemHover.hovered && (modelData.state !== BluetoothDeviceState.Connecting)
|
||||
enabled: (canConnect || canDisconnect || (root.showOnlyLists ? false : canPair)) && !isBusy
|
||||
outlined: !button.hovered
|
||||
fontSize: Style.fontSizeS
|
||||
@@ -552,6 +576,8 @@ Item {
|
||||
id: infoColumn
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginS
|
||||
flow: root.detailsGrid ? GridLayout.TopToBottom : GridLayout.LeftToRight
|
||||
rows: root.detailsGrid ? 3 : 6
|
||||
columns: root.detailsGrid ? 2 : 1
|
||||
columnSpacing: Style.marginM
|
||||
rowSpacing: Style.marginXS
|
||||
@@ -561,8 +587,6 @@ Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredWidth: 1
|
||||
spacing: Style.marginXS
|
||||
Layout.row: detailsGrid ? 0 : 0
|
||||
Layout.column: 0
|
||||
NIcon {
|
||||
icon: BluetoothService.getSignalIcon(modelData)
|
||||
pointSize: Style.fontSizeXS
|
||||
@@ -580,8 +604,6 @@ Item {
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredWidth: 1
|
||||
Layout.row: detailsGrid ? 0 : 1
|
||||
Layout.column: detailsGrid ? 1 : 0
|
||||
spacing: Style.marginXS
|
||||
NIcon {
|
||||
icon: {
|
||||
@@ -605,8 +627,6 @@ Item {
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredWidth: 1
|
||||
Layout.row: detailsGrid ? 1 : 2
|
||||
Layout.column: 0
|
||||
spacing: Style.marginXS
|
||||
NIcon {
|
||||
icon: "link"
|
||||
@@ -624,8 +644,6 @@ Item {
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredWidth: 1
|
||||
Layout.row: detailsGrid ? 1 : 3
|
||||
Layout.column: detailsGrid ? 1 : 0
|
||||
spacing: Style.marginXS
|
||||
NIcon {
|
||||
icon: "shield-check"
|
||||
@@ -643,8 +661,6 @@ Item {
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredWidth: 1
|
||||
Layout.row: detailsGrid ? 2 : 4
|
||||
Layout.column: 0
|
||||
spacing: Style.marginXS
|
||||
NIcon {
|
||||
icon: "hash"
|
||||
@@ -663,8 +679,6 @@ Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredWidth: 1
|
||||
Layout.topMargin: -Style.marginXXS
|
||||
Layout.row: detailsGrid ? 2 : 5
|
||||
Layout.column: detailsGrid ? 1 : 0
|
||||
spacing: Style.marginXS
|
||||
visible: Settings.data.network.bluetoothAutoConnect
|
||||
NIcon {
|
||||
|
||||
@@ -21,6 +21,11 @@ Item {
|
||||
|
||||
// State properties
|
||||
property string passwordSsid: ""
|
||||
property string identity: ""
|
||||
property string enterpriseEap: "peap"
|
||||
property string enterprisePhase2: "mschapv2"
|
||||
property string enterpriseAnonIdentity: ""
|
||||
property string enterpriseCaCert: ""
|
||||
property string expandedSsid: ""
|
||||
property string infoSsid: ""
|
||||
property int ipVersion: 4
|
||||
@@ -91,10 +96,20 @@ Item {
|
||||
// Actions
|
||||
function requestPassword(ssid) {
|
||||
passwordSsid = ssid;
|
||||
identity = "";
|
||||
enterpriseEap = "peap";
|
||||
enterprisePhase2 = "mschapv2";
|
||||
enterpriseAnonIdentity = "";
|
||||
enterpriseCaCert = "";
|
||||
expandedSsid = "";
|
||||
}
|
||||
function submitPassword(ssid, password) {
|
||||
NetworkService.connect(ssid, password);
|
||||
function submitPassword(ssid, password, identity = "") {
|
||||
NetworkService.connect(ssid, password, false, identity, {
|
||||
eap: enterpriseEap,
|
||||
phase2: enterprisePhase2,
|
||||
anonIdentity: enterpriseAnonIdentity,
|
||||
caCert: enterpriseCaCert
|
||||
});
|
||||
passwordSsid = "";
|
||||
}
|
||||
function cancelPassword() {
|
||||
@@ -136,16 +151,7 @@ Item {
|
||||
|
||||
NToggle {
|
||||
label: I18n.tr("common.wifi")
|
||||
icon: {
|
||||
if (!Settings.data.network.wifiEnabled) {
|
||||
return "wifi-off";
|
||||
}
|
||||
if (root.connectedNetworks.length > 0) {
|
||||
const net = root.connectedNetworks[0];
|
||||
return NetworkService.signalIcon(net.signal, true);
|
||||
}
|
||||
return "wifi";
|
||||
}
|
||||
icon: NetworkService.getIcon(false)
|
||||
checked: Settings.data.network.wifiEnabled
|
||||
onToggled: checked => NetworkService.setWifiEnabled(checked)
|
||||
enabled: ProgramCheckerService.nmcliAvailable && !Settings.data.network.airplaneModeEnabled && NetworkService.wifiAvailable
|
||||
@@ -182,6 +188,7 @@ Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: connectedCol.implicitHeight + Style.margin2M
|
||||
border.color: showOnlyLists ? Style.boxBorderColor : "transparent"
|
||||
color: showOnlyLists ? Color.mSurfaceVariant : "transparent"
|
||||
|
||||
ColumnLayout {
|
||||
id: connectedCol
|
||||
@@ -212,6 +219,7 @@ Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: savedCol.implicitHeight + Style.margin2M
|
||||
border.color: showOnlyLists ? Style.boxBorderColor : "transparent"
|
||||
color: showOnlyLists ? Color.mSurfaceVariant : "transparent"
|
||||
|
||||
ColumnLayout {
|
||||
id: savedCol
|
||||
@@ -242,6 +250,7 @@ Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: availableCol.implicitHeight + Style.margin2M
|
||||
border.color: showOnlyLists ? Style.boxBorderColor : "transparent"
|
||||
color: showOnlyLists ? Color.mSurfaceVariant : "transparent"
|
||||
|
||||
ColumnLayout {
|
||||
id: availableCol
|
||||
@@ -298,7 +307,7 @@ Item {
|
||||
}
|
||||
|
||||
NText {
|
||||
text: I18n.tr("wifi.panel.add-hidden-network")
|
||||
text: I18n.tr("wifi.panel.add-network")
|
||||
pointSize: Style.fontSizeM
|
||||
color: Color.mOnSurface
|
||||
Layout.fillWidth: true
|
||||
@@ -364,7 +373,14 @@ Item {
|
||||
|
||||
property string customSsid: ""
|
||||
property string customPassword: ""
|
||||
property string customIdentity: ""
|
||||
property string customSecurityKey: "wpa2-psk"
|
||||
property string customEnterpriseEap: "peap"
|
||||
property string customEnterprisePhase2: "mschapv2"
|
||||
property string customEnterpriseAnonIdentity: ""
|
||||
property string customEnterpriseCaCert: ""
|
||||
property bool customShowPassword: false
|
||||
property bool customIsHidden: false
|
||||
|
||||
onOpened: {
|
||||
customSsidInput.inputItem.forceActiveFocus();
|
||||
@@ -413,7 +429,7 @@ Item {
|
||||
spacing: Style.marginXS
|
||||
|
||||
NText {
|
||||
text: I18n.tr("wifi.panel.add-hidden-network")
|
||||
text: I18n.tr("wifi.panel.add-network")
|
||||
pointSize: Style.fontSizeL
|
||||
font.weight: Style.fontWeightBold
|
||||
color: Color.mOnSurface
|
||||
@@ -434,7 +450,12 @@ Item {
|
||||
onTextChanged: addNetworkPopup.customSsid = text
|
||||
onAccepted: {
|
||||
if (addNetworkPopup.customSsid.length > 0 && (addNetworkPopup.customSecurityKey === "open" || addNetworkPopup.customPassword.length > 0)) {
|
||||
NetworkService.connectManual(addNetworkPopup.customSsid, addNetworkPopup.customPassword, addNetworkPopup.customSecurityKey);
|
||||
NetworkService.connectManual(addNetworkPopup.customSsid, addNetworkPopup.customPassword, addNetworkPopup.customSecurityKey, addNetworkPopup.customIdentity, {
|
||||
eap: addNetworkPopup.customEnterpriseEap,
|
||||
phase2: addNetworkPopup.customEnterprisePhase2,
|
||||
anonIdentity: addNetworkPopup.customEnterpriseAnonIdentity,
|
||||
caCert: addNetworkPopup.customEnterpriseCaCert
|
||||
}, addNetworkPopup.customIsHidden);
|
||||
addNetworkPopup.close();
|
||||
}
|
||||
}
|
||||
@@ -449,6 +470,98 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
visible: addNetworkPopup.customSecurityKey.indexOf("-eap") !== -1
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginM
|
||||
|
||||
NComboBox {
|
||||
Layout.fillWidth: true
|
||||
label: I18n.tr("wifi.enterprise.eap-method")
|
||||
model: [
|
||||
{
|
||||
key: "peap",
|
||||
name: "PEAP"
|
||||
},
|
||||
{
|
||||
key: "ttls",
|
||||
name: "TTLS"
|
||||
}
|
||||
]
|
||||
currentKey: addNetworkPopup.customEnterpriseEap
|
||||
onSelected: key => addNetworkPopup.customEnterpriseEap = key
|
||||
}
|
||||
|
||||
NComboBox {
|
||||
Layout.fillWidth: true
|
||||
label: I18n.tr("wifi.enterprise.phase2-auth")
|
||||
model: [
|
||||
{
|
||||
key: "mschapv2",
|
||||
name: "MSCHAPv2"
|
||||
},
|
||||
{
|
||||
key: "pap",
|
||||
name: "PAP"
|
||||
},
|
||||
{
|
||||
key: "mschap",
|
||||
name: "MSCHAP"
|
||||
},
|
||||
{
|
||||
key: "chap",
|
||||
name: "CHAP"
|
||||
}
|
||||
]
|
||||
currentKey: addNetworkPopup.customEnterprisePhase2
|
||||
onSelected: key => addNetworkPopup.customEnterprisePhase2 = key
|
||||
}
|
||||
}
|
||||
|
||||
NTextInput {
|
||||
id: customAnonIdentityInput
|
||||
Layout.fillWidth: true
|
||||
inputIconName: "user-question"
|
||||
visible: addNetworkPopup.customSecurityKey.indexOf("-eap") !== -1
|
||||
placeholderText: I18n.tr("wifi.enterprise.anonymous-identity")
|
||||
label: I18n.tr("wifi.enterprise.anonymous-identity")
|
||||
text: addNetworkPopup.customEnterpriseAnonIdentity
|
||||
onTextChanged: addNetworkPopup.customEnterpriseAnonIdentity = text
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginM
|
||||
visible: addNetworkPopup.customSecurityKey.indexOf("-eap") !== -1
|
||||
|
||||
NTextInput {
|
||||
id: customCaCertInput
|
||||
Layout.fillWidth: true
|
||||
inputIconName: "certificate"
|
||||
placeholderText: I18n.tr("wifi.enterprise.ca-cert")
|
||||
label: I18n.tr("wifi.enterprise.ca-cert")
|
||||
text: addNetworkPopup.customEnterpriseCaCert
|
||||
onTextChanged: addNetworkPopup.customEnterpriseCaCert = text
|
||||
}
|
||||
|
||||
NIconButton {
|
||||
icon: "folder-open"
|
||||
Layout.alignment: Qt.AlignBottom
|
||||
onClicked: caCertPicker.openForAddNetwork()
|
||||
}
|
||||
}
|
||||
|
||||
NTextInput {
|
||||
id: customIdentityInput
|
||||
Layout.fillWidth: true
|
||||
inputIconName: "user"
|
||||
visible: addNetworkPopup.customSecurityKey.indexOf("-eap") !== -1
|
||||
placeholderText: I18n.tr("wifi.enterprise.username")
|
||||
label: I18n.tr("wifi.enterprise.username")
|
||||
text: addNetworkPopup.customIdentity
|
||||
onTextChanged: addNetworkPopup.customIdentity = text
|
||||
}
|
||||
|
||||
NTextInput {
|
||||
id: customPasswordInput
|
||||
Layout.fillWidth: true
|
||||
@@ -458,15 +571,40 @@ Item {
|
||||
label: I18n.tr("common.password")
|
||||
text: addNetworkPopup.customPassword
|
||||
onTextChanged: addNetworkPopup.customPassword = text
|
||||
inputItem.echoMode: TextInput.Password
|
||||
inputItem.echoMode: addNetworkPopup.customShowPassword ? TextInput.Normal : TextInput.Password
|
||||
onAccepted: {
|
||||
if (addNetworkPopup.customSsid.length > 0 && addNetworkPopup.customPassword.length > 0) {
|
||||
NetworkService.connectManual(addNetworkPopup.customSsid, addNetworkPopup.customPassword, addNetworkPopup.customSecurityKey);
|
||||
NetworkService.connectManual(addNetworkPopup.customSsid, addNetworkPopup.customPassword, addNetworkPopup.customSecurityKey, addNetworkPopup.customIdentity, {
|
||||
eap: addNetworkPopup.customEnterpriseEap,
|
||||
phase2: addNetworkPopup.customEnterprisePhase2,
|
||||
anonIdentity: addNetworkPopup.customEnterpriseAnonIdentity,
|
||||
caCert: addNetworkPopup.customEnterpriseCaCert
|
||||
}, addNetworkPopup.customIsHidden);
|
||||
addNetworkPopup.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginM
|
||||
|
||||
NCheckbox {
|
||||
Layout.fillWidth: true
|
||||
label: I18n.tr("wifi.panel.show-password")
|
||||
checked: addNetworkPopup.customShowPassword
|
||||
onToggled: checked => addNetworkPopup.customShowPassword = checked
|
||||
visible: addNetworkPopup.customSecurityKey !== "open"
|
||||
}
|
||||
|
||||
NCheckbox {
|
||||
Layout.fillWidth: true
|
||||
label: I18n.tr("wifi.panel.hidden-network")
|
||||
checked: addNetworkPopup.customIsHidden
|
||||
onToggled: checked => addNetworkPopup.customIsHidden = checked
|
||||
}
|
||||
}
|
||||
|
||||
// Actions
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
@@ -489,9 +627,14 @@ Item {
|
||||
text: I18n.tr("common.connect")
|
||||
backgroundColor: Color.mPrimary
|
||||
textColor: Color.mOnPrimary
|
||||
enabled: addNetworkPopup.customSsid.length > 0 && (addNetworkPopup.customSecurityKey === "open" || addNetworkPopup.customPassword.length > 0)
|
||||
enabled: addNetworkPopup.customSsid.length > 0 && (addNetworkPopup.customSecurityKey === "open" || addNetworkPopup.customPassword.length > 0) && (addNetworkPopup.customSecurityKey.indexOf("-eap") === -1 || addNetworkPopup.customIdentity.length > 0)
|
||||
onClicked: {
|
||||
NetworkService.connectManual(addNetworkPopup.customSsid, addNetworkPopup.customPassword, addNetworkPopup.customSecurityKey);
|
||||
NetworkService.connectManual(addNetworkPopup.customSsid, addNetworkPopup.customPassword, addNetworkPopup.customSecurityKey, addNetworkPopup.customIdentity, {
|
||||
eap: addNetworkPopup.customEnterpriseEap,
|
||||
phase2: addNetworkPopup.customEnterprisePhase2,
|
||||
anonIdentity: addNetworkPopup.customEnterpriseAnonIdentity,
|
||||
caCert: addNetworkPopup.customEnterpriseCaCert
|
||||
}, addNetworkPopup.customIsHidden);
|
||||
addNetworkPopup.close();
|
||||
}
|
||||
}
|
||||
@@ -507,6 +650,7 @@ Item {
|
||||
|
||||
readonly property bool isBusy: NetworkService.connectingTo === modelData.ssid || NetworkService.disconnectingFrom === modelData.ssid || NetworkService.forgettingNetwork === modelData.ssid
|
||||
readonly property bool isExpanded: root.infoSsid === modelData.ssid
|
||||
readonly property bool isEnterprise: NetworkService.isEnterprise(modelData.security)
|
||||
|
||||
function getContentColors(defaultColors = [Color.mSurface, Color.mOnSurface]) {
|
||||
if (root.passwordSsid === modelData.ssid || NetworkService.connectingTo === modelData.ssid) {
|
||||
@@ -544,15 +688,16 @@ Item {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
|
||||
NIcon {
|
||||
icon: NetworkService.signalIcon(modelData.signal, modelData.connected)
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
icon: NetworkService.getSignalInfo(modelData.signal, modelData.connected).icon
|
||||
pointSize: Style.fontSizeXXL
|
||||
color: networkItem.getContentColors()[1]
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onEntered: TooltipService.show(parent, NetworkService.getSignalStrengthLabel(modelData.signal) + " (" + modelData.signal + "%)")
|
||||
onEntered: TooltipService.show(parent, NetworkService.getSignalInfo(modelData.signal, modelData.connected).label + " (" + modelData.signal + "%)")
|
||||
onExited: TooltipService.hide()
|
||||
}
|
||||
}
|
||||
@@ -611,9 +756,10 @@ Item {
|
||||
|
||||
// Network speed indicators (visible when connected and speed > 0)
|
||||
RowLayout {
|
||||
visible: modelData.connected && (SystemStatService.rxSpeed > 0 || SystemStatService.txSpeed > 0)
|
||||
visible: (modelData.connected && NetworkService.networkConnectivity === "full") && (SystemStatService.rxSpeed > 0 || SystemStatService.txSpeed > 0)
|
||||
spacing: 2
|
||||
Layout.leftMargin: Style.marginXS
|
||||
Layout.fillWidth: false
|
||||
|
||||
NIcon {
|
||||
visible: SystemStatService.rxSpeed > 0
|
||||
@@ -627,6 +773,7 @@ Item {
|
||||
text: SystemStatService.formatSpeed(SystemStatService.rxSpeed)
|
||||
pointSize: Style.fontSizeXXS
|
||||
color: Qt.alpha(networkItem.getContentColors()[1], Style.opacityHeavy)
|
||||
elide: Text.ElideNone
|
||||
}
|
||||
|
||||
Item {
|
||||
@@ -647,6 +794,7 @@ Item {
|
||||
text: SystemStatService.formatSpeed(SystemStatService.txSpeed)
|
||||
pointSize: Style.fontSizeXXS
|
||||
color: Qt.alpha(networkItem.getContentColors()[1], Style.opacityHeavy)
|
||||
elide: Text.ElideNone
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -661,7 +809,7 @@ Item {
|
||||
|
||||
NBusyIndicator {
|
||||
visible: networkItem.isBusy
|
||||
running: visible
|
||||
running: visible && root.effectivelyVisible
|
||||
color: networkItem.getContentColors()[1]
|
||||
size: Style.baseWidgetSize * 0.5
|
||||
}
|
||||
@@ -763,6 +911,8 @@ Item {
|
||||
id: infoColumn
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginS
|
||||
flow: root.detailsGrid ? GridLayout.TopToBottom : GridLayout.LeftToRight
|
||||
rows: root.detailsGrid ? 3 : 6
|
||||
columns: root.detailsGrid ? 2 : 1
|
||||
columnSpacing: Style.marginM
|
||||
rowSpacing: Style.marginXS
|
||||
@@ -779,8 +929,6 @@ Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredWidth: 1
|
||||
spacing: Style.marginXS
|
||||
Layout.row: 0
|
||||
Layout.column: 0
|
||||
NIcon {
|
||||
icon: "network"
|
||||
pointSize: Style.fontSizeXS
|
||||
@@ -823,8 +971,6 @@ Item {
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredWidth: 1
|
||||
Layout.row: detailsGrid ? 1 : 1
|
||||
Layout.column: 0
|
||||
spacing: Style.marginXS
|
||||
NIcon {
|
||||
icon: "router"
|
||||
@@ -847,8 +993,6 @@ Item {
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredWidth: 1
|
||||
Layout.row: detailsGrid ? 2 : 2
|
||||
Layout.column: 0
|
||||
spacing: Style.marginXS
|
||||
NIcon {
|
||||
icon: "gauge"
|
||||
@@ -872,8 +1016,6 @@ Item {
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredWidth: 1
|
||||
Layout.row: detailsGrid ? 0 : 3
|
||||
Layout.column: detailsGrid ? 1 : 0
|
||||
spacing: Style.marginXS
|
||||
NIcon {
|
||||
icon: "network"
|
||||
@@ -891,20 +1033,20 @@ Item {
|
||||
}
|
||||
}
|
||||
NText {
|
||||
text: root.ipVersion === 4 ? (NetworkService.activeWifiDetails.ipv4 || "-") : (NetworkService.activeWifiDetails.ipv6 || "-")
|
||||
text: root.ipVersion === 4 ? (NetworkService.activeWifiDetails.ipv4 || "-") : ((NetworkService.activeWifiDetails.ipv6 || []).join(", ") || "-")
|
||||
pointSize: Style.fontSizeXS
|
||||
color: Color.mOnSurface
|
||||
Layout.fillWidth: true
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
enabled: root.ipVersion === 4 ? (NetworkService.activeWifiDetails.ipv4 && NetworkService.activeWifiDetails.ipv4.length > 0) : (NetworkService.activeWifiDetails.ipv6 && NetworkService.activeWifiDetails.ipv6.length > 0)
|
||||
enabled: root.ipVersion === 4 ? !!(NetworkService.activeWifiDetails.ipv4 && NetworkService.activeWifiDetails.ipv4.length > 0) : !!(NetworkService.activeWifiDetails.ipv6 && NetworkService.activeWifiDetails.ipv6.length > 0)
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onEntered: TooltipService.show(parent, I18n.tr("tooltips.copy-address"))
|
||||
onExited: TooltipService.hide()
|
||||
onClicked: {
|
||||
const value = root.ipVersion === 4 ? (NetworkService.activeWifiDetails.ipv4 || "") : (NetworkService.activeWifiDetails.ipv6 || "");
|
||||
const value = root.ipVersion === 4 ? (NetworkService.activeWifiDetails.ipv4 || "") : ((NetworkService.activeWifiDetails.ipv6 || []).join(", ") || "");
|
||||
if (value.length > 0) {
|
||||
Quickshell.execDetached(["wl-copy", value]);
|
||||
ToastService.showNotice(I18n.tr("common.wifi"), I18n.tr("toast.bluetooth.address-copied"), "wifi");
|
||||
@@ -917,8 +1059,6 @@ Item {
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredWidth: 1
|
||||
Layout.row: detailsGrid ? 1 : 4
|
||||
Layout.column: detailsGrid ? 1 : 0
|
||||
spacing: Style.marginXS
|
||||
NIcon {
|
||||
icon: "world"
|
||||
@@ -936,20 +1076,20 @@ Item {
|
||||
}
|
||||
}
|
||||
NText {
|
||||
text: root.ipVersion === 4 ? (NetworkService.activeWifiDetails.dns4 || "-") : (NetworkService.activeWifiDetails.dns6 || "-")
|
||||
text: root.ipVersion === 4 ? ((NetworkService.activeWifiDetails.dns4 || []).join(", ") || "-") : ((NetworkService.activeWifiDetails.dns6 || []).join(", ") || "-")
|
||||
pointSize: Style.fontSizeXS
|
||||
color: Color.mOnSurface
|
||||
Layout.fillWidth: true
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
enabled: root.ipVersion === 4 ? (NetworkService.activeWifiDetails.dns4 && NetworkService.activeWifiDetails.dns4.length > 0) : (NetworkService.activeWifiDetails.dns6 && NetworkService.activeWifiDetails.dns6.length > 0)
|
||||
enabled: root.ipVersion === 4 ? !!(NetworkService.activeWifiDetails.dns4 && NetworkService.activeWifiDetails.dns4.length > 0) : !!(NetworkService.activeWifiDetails.dns6 && NetworkService.activeWifiDetails.dns6.length > 0)
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onEntered: TooltipService.show(parent, I18n.tr("tooltips.copy-address"))
|
||||
onExited: TooltipService.hide()
|
||||
onClicked: {
|
||||
const value = root.ipVersion === 4 ? (NetworkService.activeWifiDetails.dns4 || "") : (NetworkService.activeWifiDetails.dns6 || "");
|
||||
const value = root.ipVersion === 4 ? ((NetworkService.activeWifiDetails.dns4 || []).join(", ") || "") : ((NetworkService.activeWifiDetails.dns6 || []).join(", ") || "");
|
||||
if (value.length > 0) {
|
||||
Quickshell.execDetached(["wl-copy", value]);
|
||||
ToastService.showNotice(I18n.tr("common.wifi"), I18n.tr("toast.bluetooth.address-copied"), "wifi");
|
||||
@@ -962,8 +1102,6 @@ Item {
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredWidth: 1
|
||||
Layout.row: detailsGrid ? 2 : 5
|
||||
Layout.column: detailsGrid ? 1 : 0
|
||||
spacing: Style.marginXS
|
||||
NIcon {
|
||||
icon: "router"
|
||||
@@ -981,20 +1119,20 @@ Item {
|
||||
}
|
||||
}
|
||||
NText {
|
||||
text: root.ipVersion === 4 ? (NetworkService.activeWifiDetails.gateway4 || "-") : (NetworkService.activeWifiDetails.gateway6 || "-")
|
||||
text: root.ipVersion === 4 ? (NetworkService.activeWifiDetails.gateway4 || "-") : ((NetworkService.activeWifiDetails.gateway6 || []).join(", ") || "-")
|
||||
pointSize: Style.fontSizeXS
|
||||
color: Color.mOnSurface
|
||||
Layout.fillWidth: true
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
enabled: root.ipVersion === 4 ? (NetworkService.activeWifiDetails.gateway4 && NetworkService.activeWifiDetails.gateway4.length > 0) : (NetworkService.activeWifiDetails.gateway6 && NetworkService.activeWifiDetails.gateway6.length > 0)
|
||||
enabled: root.ipVersion === 4 ? !!(NetworkService.activeWifiDetails.gateway4 && NetworkService.activeWifiDetails.gateway4.length > 0) : !!(NetworkService.activeWifiDetails.gateway6 && NetworkService.activeWifiDetails.gateway6.length > 0)
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onEntered: TooltipService.show(parent, I18n.tr("tooltips.copy-address"))
|
||||
onExited: TooltipService.hide()
|
||||
onClicked: {
|
||||
const value = root.ipVersion === 4 ? (NetworkService.activeWifiDetails.gateway4 || "") : (NetworkService.activeWifiDetails.gateway6 || "");
|
||||
const value = root.ipVersion === 4 ? (NetworkService.activeWifiDetails.gateway4 || "") : ((NetworkService.activeWifiDetails.gateway6 || []).join(", ") || "");
|
||||
if (value.length > 0) {
|
||||
Quickshell.execDetached(["wl-copy", value]);
|
||||
ToastService.showNotice(I18n.tr("common.wifi"), I18n.tr("toast.bluetooth.address-copied"), "wifi");
|
||||
@@ -1010,72 +1148,251 @@ Item {
|
||||
Rectangle {
|
||||
visible: root.passwordSsid === modelData.ssid && !networkItem.isBusy
|
||||
Layout.fillWidth: true
|
||||
height: passwordRow.implicitHeight + Style.margin2S
|
||||
height: passwordLayout.implicitHeight + Style.margin2S
|
||||
color: Color.mSurfaceVariant
|
||||
border.color: Color.mOutline
|
||||
border.width: Style.borderS
|
||||
radius: Style.iRadiusXS
|
||||
|
||||
RowLayout {
|
||||
id: passwordRow
|
||||
ColumnLayout {
|
||||
id: passwordLayout
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.marginS
|
||||
spacing: Style.marginM
|
||||
spacing: Style.marginS
|
||||
|
||||
Rectangle {
|
||||
// Inputs Container
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
radius: Style.iRadiusXS
|
||||
color: Color.mSurface
|
||||
border.color: pwdInput.activeFocus ? Color.mSecondary : Color.mOutline
|
||||
border.width: Style.borderS
|
||||
spacing: Style.marginS
|
||||
|
||||
TextInput {
|
||||
id: pwdInput
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.margins: Style.marginS
|
||||
font.family: Settings.data.ui.fontFixed
|
||||
font.pointSize: Style.fontSizeS
|
||||
color: Color.mOnSurface
|
||||
echoMode: TextInput.Password
|
||||
selectByMouse: true
|
||||
focus: visible
|
||||
passwordCharacter: "●"
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
forceActiveFocus();
|
||||
}
|
||||
}
|
||||
onAccepted: {
|
||||
if (text && !NetworkService.connecting) {
|
||||
root.submitPassword(modelData.ssid, text);
|
||||
}
|
||||
// Enterprise Configuration
|
||||
ColumnLayout {
|
||||
visible: networkItem.isEnterprise
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginS
|
||||
|
||||
NComboBox {
|
||||
Layout.fillWidth: true
|
||||
label: I18n.tr("wifi.enterprise.eap-method")
|
||||
model: [
|
||||
{
|
||||
key: "peap",
|
||||
name: "PEAP"
|
||||
},
|
||||
{
|
||||
key: "ttls",
|
||||
name: "TTLS"
|
||||
}
|
||||
]
|
||||
currentKey: root.enterpriseEap
|
||||
onSelected: key => root.enterpriseEap = key
|
||||
}
|
||||
|
||||
NText {
|
||||
visible: parent.text.length === 0
|
||||
NComboBox {
|
||||
Layout.fillWidth: true
|
||||
label: I18n.tr("wifi.enterprise.phase2-auth")
|
||||
model: [
|
||||
{
|
||||
key: "mschapv2",
|
||||
name: "MSCHAPv2"
|
||||
},
|
||||
{
|
||||
key: "pap",
|
||||
name: "PAP"
|
||||
},
|
||||
{
|
||||
key: "mschap",
|
||||
name: "MSCHAP"
|
||||
},
|
||||
{
|
||||
key: "chap",
|
||||
name: "CHAP"
|
||||
}
|
||||
]
|
||||
currentKey: root.enterprisePhase2
|
||||
onSelected: key => root.enterprisePhase2 = key
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginS
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: Style.baseWidgetSize * 0.9
|
||||
radius: Style.iRadiusXS
|
||||
color: Color.mSurface
|
||||
border.color: caCertInput.activeFocus ? Color.mSecondary : Color.mOutline
|
||||
border.width: Style.borderS
|
||||
|
||||
TextInput {
|
||||
id: caCertInput
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.margins: Style.marginS
|
||||
font.family: Settings.data.ui.fontFixed
|
||||
font.pointSize: Style.fontSizeS
|
||||
color: Color.mOnSurface
|
||||
selectByMouse: true
|
||||
text: root.enterpriseCaCert
|
||||
onTextChanged: root.enterpriseCaCert = text
|
||||
|
||||
NText {
|
||||
visible: parent.text.length === 0
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: I18n.tr("wifi.enterprise.ca-cert")
|
||||
color: Color.mOnSurfaceVariant
|
||||
pointSize: Style.fontSizeS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NIconButton {
|
||||
icon: "folder-open"
|
||||
baseSize: Style.baseWidgetSize * 0.8
|
||||
onClicked: caCertPicker.openForInline()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Anonymous Identity field (Enterprise only)
|
||||
Rectangle {
|
||||
visible: networkItem.isEnterprise
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: Style.baseWidgetSize * 0.9
|
||||
radius: Style.iRadiusXS
|
||||
color: Color.mSurface
|
||||
border.color: anonIdentityInput.activeFocus ? Color.mSecondary : Color.mOutline
|
||||
border.width: Style.borderS
|
||||
|
||||
TextInput {
|
||||
id: anonIdentityInput
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: I18n.tr("wifi.panel.enter-password")
|
||||
color: Color.mOnSurfaceVariant
|
||||
pointSize: Style.fontSizeS
|
||||
anchors.margins: Style.marginS
|
||||
font.family: Settings.data.ui.fontFixed
|
||||
font.pointSize: Style.fontSizeS
|
||||
color: Color.mOnSurface
|
||||
selectByMouse: true
|
||||
text: root.enterpriseAnonIdentity
|
||||
onTextChanged: root.enterpriseAnonIdentity = text
|
||||
onAccepted: identityInput.forceActiveFocus()
|
||||
|
||||
NText {
|
||||
visible: parent.text.length === 0
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: I18n.tr("wifi.enterprise.anonymous-identity")
|
||||
color: Color.mOnSurfaceVariant
|
||||
pointSize: Style.fontSizeS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Identity field (Enterprise only)
|
||||
Rectangle {
|
||||
visible: networkItem.isEnterprise
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: Style.baseWidgetSize * 0.9
|
||||
radius: Style.iRadiusXS
|
||||
color: Color.mSurface
|
||||
border.color: identityInput.activeFocus ? Color.mSecondary : Color.mOutline
|
||||
border.width: Style.borderS
|
||||
|
||||
TextInput {
|
||||
id: identityInput
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.margins: Style.marginS
|
||||
font.family: Settings.data.ui.fontFixed
|
||||
font.pointSize: Style.fontSizeS
|
||||
color: Color.mOnSurface
|
||||
selectByMouse: true
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
forceActiveFocus();
|
||||
}
|
||||
}
|
||||
onAccepted: pwdInput.forceActiveFocus()
|
||||
|
||||
NText {
|
||||
visible: parent.text.length === 0
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: I18n.tr("wifi.enterprise.username")
|
||||
color: Color.mOnSurfaceVariant
|
||||
pointSize: Style.fontSizeS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Password field
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: Style.baseWidgetSize * 0.9
|
||||
radius: Style.iRadiusXS
|
||||
color: Color.mSurface
|
||||
border.color: pwdInput.activeFocus ? Color.mSecondary : Color.mOutline
|
||||
border.width: Style.borderS
|
||||
|
||||
TextInput {
|
||||
id: pwdInput
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.margins: Style.marginS
|
||||
font.family: Settings.data.ui.fontFixed
|
||||
font.pointSize: Style.fontSizeS
|
||||
color: Color.mOnSurface
|
||||
echoMode: TextInput.Password
|
||||
selectByMouse: true
|
||||
passwordCharacter: "●"
|
||||
onVisibleChanged: {
|
||||
if (visible && !networkItem.isEnterprise) {
|
||||
forceActiveFocus();
|
||||
}
|
||||
}
|
||||
onAccepted: {
|
||||
if (text && !NetworkService.connecting) {
|
||||
if (!networkItem.isEnterprise || identityInput.text.length > 0) {
|
||||
root.submitPassword(modelData.ssid, text, identityInput.text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NText {
|
||||
visible: parent.text.length === 0
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: networkItem.isEnterprise ? I18n.tr("wifi.enterprise.password") : I18n.tr("wifi.panel.enter-password")
|
||||
color: Color.mOnSurfaceVariant
|
||||
pointSize: Style.fontSizeS
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NButton {
|
||||
text: I18n.tr("common.connect")
|
||||
fontSize: Style.fontSizeS
|
||||
enabled: pwdInput.text.length > 0 && !NetworkService.connecting
|
||||
outlined: true
|
||||
onClicked: root.submitPassword(modelData.ssid, pwdInput.text)
|
||||
}
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Style.marginS
|
||||
|
||||
NIconButton {
|
||||
icon: "close"
|
||||
baseSize: Style.baseWidgetSize * 0.8
|
||||
onClicked: root.cancelPassword()
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
NButton {
|
||||
text: I18n.tr("common.connect")
|
||||
fontSize: Style.fontSizeS
|
||||
enabled: pwdInput.text.length > 0 && (!networkItem.isEnterprise || identityInput.text.length > 0) && !NetworkService.connecting
|
||||
outlined: true
|
||||
onClicked: root.submitPassword(modelData.ssid, pwdInput.text, identityInput.text)
|
||||
}
|
||||
|
||||
NIconButton {
|
||||
icon: "close"
|
||||
baseSize: Style.baseWidgetSize * 0.8
|
||||
onClicked: root.cancelPassword()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1130,4 +1447,32 @@ Item {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NFilePicker {
|
||||
id: caCertPicker
|
||||
title: I18n.tr("wifi.enterprise.ca-cert")
|
||||
nameFilters: ["*.pem", "*.crt", "*.cer", "*.der", "*"]
|
||||
|
||||
property bool isForAddNetwork: false
|
||||
|
||||
function openForInline() {
|
||||
isForAddNetwork = false;
|
||||
openFilePicker();
|
||||
}
|
||||
|
||||
function openForAddNetwork() {
|
||||
isForAddNetwork = true;
|
||||
openFilePicker();
|
||||
}
|
||||
|
||||
onAccepted: paths => {
|
||||
if (paths.length > 0) {
|
||||
if (isForAddNetwork) {
|
||||
addNetworkPopup.customEnterpriseCaCert = paths[0];
|
||||
} else {
|
||||
root.enterpriseCaCert = paths[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,31 +47,31 @@ Singleton {
|
||||
},
|
||||
{
|
||||
key: "wep",
|
||||
name: "WEP"
|
||||
name: I18n.tr("wifi.panel.security-wep")
|
||||
},
|
||||
{
|
||||
key: "wpa-psk",
|
||||
name: "WPA"
|
||||
name: I18n.tr("wifi.panel.security-wpa")
|
||||
},
|
||||
{
|
||||
key: "wpa2-psk",
|
||||
name: "WPA2/WPA3"
|
||||
name: I18n.tr("wifi.panel.security-wpa23")
|
||||
},
|
||||
{
|
||||
key: "sae",
|
||||
name: "WPA3"
|
||||
name: I18n.tr("wifi.panel.security-wpa3")
|
||||
},
|
||||
{
|
||||
key: "wpa-eap",
|
||||
name: "WPA Enterprise"
|
||||
name: I18n.tr("wifi.panel.security-wpa-ent")
|
||||
},
|
||||
{
|
||||
key: "wpa2-eap",
|
||||
name: "WPA2 Enterprise"
|
||||
name: I18n.tr("wifi.panel.security-wpa2-ent")
|
||||
},
|
||||
{
|
||||
key: "wpa3-eap",
|
||||
name: "WPA3 Enterprise"
|
||||
name: I18n.tr("wifi.panel.security-wpa3-ent")
|
||||
}
|
||||
]
|
||||
|
||||
@@ -80,7 +80,7 @@ Singleton {
|
||||
property string activeWifiIf: ""
|
||||
property bool detailsLoading: false
|
||||
property double activeWifiDetailsTimestamp: 0
|
||||
// Cache TTL to avoid spamming nmcli/iw on rapid toggles
|
||||
// Cache TTL to avoid spamming nmcli on rapid toggles
|
||||
property int activeWifiDetailsTtlMs: 5000
|
||||
|
||||
// Persistent cache
|
||||
@@ -305,10 +305,16 @@ Singleton {
|
||||
refreshActiveEthernetDetails();
|
||||
}
|
||||
|
||||
function connect(ssid, password = "", isHidden = false) {
|
||||
function connect(ssid, password = "", isHidden = false, identity = "", enterpriseConfig = {}) {
|
||||
if (!ProgramCheckerService.nmcliAvailable || connecting) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isEnterprise(networks[ssid] ? networks[ssid].security : "")) {
|
||||
connectManual(ssid, password, "wpa-eap", identity, enterpriseConfig);
|
||||
return;
|
||||
}
|
||||
|
||||
connecting = true;
|
||||
connectingTo = ssid;
|
||||
lastError = "";
|
||||
@@ -329,7 +335,7 @@ Singleton {
|
||||
connectProcess.running = true;
|
||||
}
|
||||
|
||||
function connectManual(ssid, password, securityKey) {
|
||||
function connectManual(ssid, password, securityKey, identity = "", enterpriseConfig = {}, isHidden = false) {
|
||||
if (!ProgramCheckerService.nmcliAvailable || connecting) {
|
||||
return;
|
||||
}
|
||||
@@ -340,6 +346,12 @@ Singleton {
|
||||
manualConnectProcess.ssid = ssid;
|
||||
manualConnectProcess.password = password;
|
||||
manualConnectProcess.securityKey = securityKey;
|
||||
manualConnectProcess.identity = identity;
|
||||
manualConnectProcess.isHidden = isHidden;
|
||||
manualConnectProcess.eap = enterpriseConfig.eap || "peap";
|
||||
manualConnectProcess.phase2 = enterpriseConfig.phase2 || "mschapv2";
|
||||
manualConnectProcess.anonIdentity = enterpriseConfig.anonIdentity || "";
|
||||
manualConnectProcess.caCert = enterpriseConfig.caCert || "";
|
||||
manualConnectProcess.running = true;
|
||||
}
|
||||
|
||||
@@ -408,52 +420,83 @@ Singleton {
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
function signalIcon(signal, isConnected) {
|
||||
if (isConnected === undefined) {
|
||||
isConnected = false;
|
||||
function getSignalInfo(signal, isConnected) {
|
||||
let icon = "";
|
||||
if (isConnected) {
|
||||
if (root.networkConnectivity === "limited") {
|
||||
icon = "wifi-exclamation";
|
||||
} else if (root.networkConnectivity === "portal" || root.networkConnectivity === "unknown") {
|
||||
icon = "wifi-question";
|
||||
}
|
||||
}
|
||||
if (isConnected && !root.internetConnectivity) {
|
||||
return "world-off";
|
||||
// This is a draft actual ranges can be changed.
|
||||
const label = signal >= 80 ? I18n.tr("wifi.signal.excellent") : signal >= 60 ? I18n.tr("wifi.signal.good") : signal >= 35 ? I18n.tr("wifi.signal.fair") : signal >= 15 ? I18n.tr("wifi.signal.poor") : I18n.tr("wifi.signal.weak");
|
||||
if (!icon) {
|
||||
icon = signal >= 80 ? "wifi" : signal >= 60 ? "wifi-3" : signal >= 35 ? "wifi-2" : signal >= 15 ? "wifi-1" : "wifi-0";
|
||||
}
|
||||
if (signal >= 80) {
|
||||
return "wifi";
|
||||
}
|
||||
if (signal >= 50) {
|
||||
return "wifi-2";
|
||||
}
|
||||
if (signal >= 20) {
|
||||
return "wifi-1";
|
||||
}
|
||||
return "wifi-0";
|
||||
return {
|
||||
icon,
|
||||
label
|
||||
};
|
||||
}
|
||||
|
||||
function isSecured(security) {
|
||||
return security && security !== "--" && security.trim() !== "";
|
||||
}
|
||||
|
||||
function getSignalStrengthLabel(signal) {
|
||||
switch (true) {
|
||||
case (signal >= 80):
|
||||
return I18n.tr("wifi.signal.excellent");
|
||||
case (signal >= 50):
|
||||
return I18n.tr("wifi.signal.good");
|
||||
case (signal >= 20):
|
||||
return I18n.tr("wifi.signal.fair");
|
||||
default:
|
||||
return I18n.tr("wifi.signal.poor");
|
||||
function isEnterprise(security) {
|
||||
if (!security) {
|
||||
return false;
|
||||
}
|
||||
const s = security.toUpperCase();
|
||||
return s.indexOf("802.1X") !== -1 || s.indexOf("EAP") !== -1 || s.indexOf("ENTERPRISE") !== -1;
|
||||
}
|
||||
|
||||
function parseIpDetails(text) {
|
||||
const details = {
|
||||
connectionName: "",
|
||||
ipv4: "",
|
||||
gateway4: "",
|
||||
ipv6: "",
|
||||
gateway6: "",
|
||||
dns4: [],
|
||||
ipv6: [],
|
||||
gateway6: [],
|
||||
dns6: [],
|
||||
dns: "",
|
||||
connectionName: ""
|
||||
hwAddr: "",
|
||||
nmSpeed: ""
|
||||
};
|
||||
const addUnique = (arr, val) => {
|
||||
if (val && arr.indexOf(val) === -1) {
|
||||
arr.push(val);
|
||||
}
|
||||
};
|
||||
const handlers = {
|
||||
"GENERAL.CONNECTION": v => {
|
||||
details.connectionName = v;
|
||||
},
|
||||
"GENERAL.HWADDR": v => {
|
||||
details.hwAddr = v;
|
||||
},
|
||||
"CAPABILITIES.SPEED": v => {
|
||||
details.nmSpeed = v;
|
||||
},
|
||||
"IP4.ADDRESS": v => {
|
||||
details.ipv4 = v.split("/")[0];
|
||||
},
|
||||
"IP4.GATEWAY": v => {
|
||||
details.gateway4 = v;
|
||||
},
|
||||
"IP6.ADDRESS": v => {
|
||||
addUnique(details.ipv6, v.split("/")[0]);
|
||||
},
|
||||
"IP6.GATEWAY": v => {
|
||||
addUnique(details.gateway6, v);
|
||||
},
|
||||
"IP4.DNS": v => {
|
||||
addUnique(details.dns4, v);
|
||||
},
|
||||
"IP6.DNS": v => {
|
||||
addUnique(details.dns6, v);
|
||||
}
|
||||
};
|
||||
const lines = text.split("\n");
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
@@ -465,39 +508,100 @@ Singleton {
|
||||
if (idx === -1) {
|
||||
continue;
|
||||
}
|
||||
const key = line.substring(0, idx);
|
||||
const val = line.substring(idx + 1);
|
||||
if (key === "GENERAL.CONNECTION") {
|
||||
details.connectionName = val;
|
||||
} else if (key.indexOf("IP4.ADDRESS") === 0) {
|
||||
details.ipv4 = val.split("/")[0];
|
||||
} else if (key === "IP4.GATEWAY") {
|
||||
details.gateway4 = val;
|
||||
} else if (key.indexOf("IP6.ADDRESS") === 0) {
|
||||
details.ipv6 = val.split("/")[0];
|
||||
} else if (key === "IP6.GATEWAY") {
|
||||
details.gateway6 = val;
|
||||
} else if (key.indexOf("IP4.DNS") === 0) {
|
||||
if (val && details.dns4.indexOf(val) === -1) {
|
||||
details.dns4.push(val);
|
||||
}
|
||||
} else if (key.indexOf("IP6.DNS") === 0) {
|
||||
if (val && details.dns6.indexOf(val) === -1) {
|
||||
details.dns6.push(val);
|
||||
}
|
||||
const key = line.substring(0, idx).replace(/\[\d+\]$/, "");
|
||||
const val = line.substring(idx + 1).trim();
|
||||
if (handlers[key]) {
|
||||
handlers[key](val);
|
||||
}
|
||||
}
|
||||
details.dns4 = details.dns4.join(", ");
|
||||
details.dns6 = details.dns6.join(", ");
|
||||
details.dns = [].concat(details.dns4 ? [details.dns4] : [], details.dns6 ? [details.dns6] : []).join(", ");
|
||||
|
||||
return details;
|
||||
}
|
||||
|
||||
// Functions used in /Modules/Panels/ControlCenter/Widgets/Network.qml & /Modules/Bar/Widgets/Network.qml
|
||||
function getStatustxt() {
|
||||
if (root.connecting) {
|
||||
return root.connectingTo ? I18n.tr("common.connecting") + " " + root.connectingTo : I18n.tr("common.connecting");
|
||||
}
|
||||
|
||||
let p = [];
|
||||
|
||||
// Ethernet
|
||||
if (root.ethernetConnected) {
|
||||
const eth = root.activeEthernetDetails;
|
||||
const name = eth.connectionName || (root.ethernetInterfaces.length > 0 ? root.ethernetInterfaces[0].connectionName : "") || "";
|
||||
const speed = eth.speed || "";
|
||||
p.push(name + (speed ? " - " + speed : ""));
|
||||
}
|
||||
|
||||
// Wi-Fi
|
||||
if (root.activeWifiIf) {
|
||||
const wl = root.activeWifiDetails;
|
||||
const speed = wl.rateShort || wl.rate || "";
|
||||
const connectedNet = Object.values(root.networks).find(net => net.connected);
|
||||
const name = connectedNet ? connectedNet.ssid : (wl.connectionName || "");
|
||||
p.push(name + (speed ? " - " + speed : ""));
|
||||
}
|
||||
|
||||
if (p.length > 0) {
|
||||
return p.join(" + "); // p.length > 0 & === 1 no join used. (eg: Only Wi-Fi)
|
||||
}
|
||||
|
||||
return I18n.tr("common.disconnected"); // p.length === 0
|
||||
}
|
||||
|
||||
function getIcon(forceEthernet = false) {
|
||||
let i = "wifi-off"; // Default to prevent empty space on launch.
|
||||
// Ethernet has priority over Wi-Fi which is standard.
|
||||
if (root.ethernetConnected || forceEthernet) {
|
||||
// Ethernet connected or forced (by the NetworkPanel)
|
||||
switch (root.networkConnectivity) {
|
||||
case "limited":
|
||||
// Ethernet connected, no internet access
|
||||
i = "ethernet-exclamation";
|
||||
break;
|
||||
case "portal":
|
||||
case "unknown":
|
||||
// Ethernet connected, status unknown or behind portal
|
||||
i = "ethernet-question";
|
||||
break;
|
||||
case "full":
|
||||
// Ethernet connected and internet access
|
||||
i = "ethernet";
|
||||
break;
|
||||
default:
|
||||
// Ethernet not connected (Forced by the NetworkPanel)
|
||||
i = "ethernet-off";
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Wi-Fi
|
||||
const connectedNet = Object.values(root.networks).find(net => net.connected);
|
||||
const availableNet = Object.values(root.networks).length;
|
||||
if (connectedNet) {
|
||||
// Wi-Fi Connected
|
||||
i = root.getSignalInfo(connectedNet.signal, true).icon;
|
||||
} else if (root.connecting || availableNet > 0) {
|
||||
// Wi-Fi Connecting or Wi-Fi available but not connected
|
||||
i = "wifi-question";
|
||||
} else if (!Settings.data.network.wifiEnabled) {
|
||||
// Wi-Fi is off
|
||||
i = "wifi-off";
|
||||
}
|
||||
}
|
||||
if (Settings.data.network.airplaneModeEnabled) {
|
||||
// Airplane mode show icon if enabled
|
||||
i = "plane";
|
||||
}
|
||||
return i;
|
||||
// This function should be covering all possibilities i think it does. Tbh became fairly complicated...
|
||||
}
|
||||
|
||||
// Processes
|
||||
Process {
|
||||
id: ethernetStateProcess
|
||||
running: ProgramCheckerService.nmcliAvailable
|
||||
command: ["nmcli", "-t", "-f", "DEVICE,TYPE,STATE", "device"]
|
||||
command: ["nmcli", "-t", "-f", "DEVICE,TYPE,STATE,CONNECTION", "device"]
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
@@ -513,6 +617,7 @@ Singleton {
|
||||
var ifname = parts[0];
|
||||
var type = parts[1];
|
||||
var state = parts[2];
|
||||
var conName = parts.slice(3).join(":") || "";
|
||||
|
||||
if (type === "wifi") {
|
||||
wifiAvailable = true;
|
||||
@@ -522,7 +627,8 @@ Singleton {
|
||||
ethList.push({
|
||||
ifname: ifname,
|
||||
state: state,
|
||||
connected: isConn
|
||||
connected: isConn,
|
||||
connectionName: conName
|
||||
});
|
||||
if (isConn && !connected) {
|
||||
connected = true;
|
||||
@@ -576,7 +682,7 @@ Singleton {
|
||||
Process {
|
||||
id: ethernetDeviceListProcess
|
||||
running: false
|
||||
command: ["nmcli", "-t", "-f", "DEVICE,TYPE,STATE", "device"]
|
||||
command: ["nmcli", "-t", "-f", "DEVICE,TYPE,STATE,CONNECTION", "device"]
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
@@ -589,6 +695,7 @@ Singleton {
|
||||
const dev = parts[0];
|
||||
const type = parts[1];
|
||||
const state = parts[2];
|
||||
const conName = parts.slice(3).join(":") || "";
|
||||
if (state === "unmanaged") {
|
||||
continue;
|
||||
}
|
||||
@@ -599,7 +706,8 @@ Singleton {
|
||||
ethList.push({
|
||||
ifname: dev,
|
||||
state: state,
|
||||
connected: state === "connected"
|
||||
connected: state === "connected",
|
||||
connectionName: conName
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -642,7 +750,7 @@ Singleton {
|
||||
property string ifname: ""
|
||||
running: false
|
||||
// Speed is resolved via ethtool fallback below to avoid stderr warnings
|
||||
command: ["nmcli", "-t", "-f", "GENERAL.CONNECTION,IP4.ADDRESS,IP4.GATEWAY,IP4.DNS", "device", "show", ifname]
|
||||
command: ["nmcli", "-t", "-f", "GENERAL.CONNECTION,GENERAL.HWADDR,IP4.ADDRESS,IP4.GATEWAY,IP4.DNS,IP6.ADDRESS,IP6.GATEWAY,IP6.DNS", "device", "show", ifname]
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
@@ -659,7 +767,7 @@ Singleton {
|
||||
details.gateway6 = parsed.gateway6;
|
||||
details.dns4 = parsed.dns4;
|
||||
details.dns6 = parsed.dns6;
|
||||
details.dns = parsed.dns;
|
||||
details.hwAddr = parsed.hwAddr;
|
||||
|
||||
root.activeEthernetDetails = details;
|
||||
// If speed missing, try sysfs first, then fallback to ethtool
|
||||
@@ -703,10 +811,13 @@ Singleton {
|
||||
root.activeEthernetDetails = details;
|
||||
root.activeEthernetDetailsTimestamp = Date.now();
|
||||
root.ethernetDetailsLoading = false;
|
||||
} else {
|
||||
} else if (ProgramCheckerService.ethtoolAvailable) {
|
||||
// Fallback to ethtool if sysfs unreadable or invalid
|
||||
ethernetEthtoolProcess.ifname = ethernetSysfsSpeedProcess.ifname;
|
||||
ethernetEthtoolProcess.running = true;
|
||||
} else {
|
||||
root.activeEthernetDetailsTimestamp = Date.now();
|
||||
root.ethernetDetailsLoading = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -809,25 +920,27 @@ Singleton {
|
||||
id: wifiDeviceShowProcess
|
||||
property string ifname: ""
|
||||
running: false
|
||||
command: ["nmcli", "-t", "-f", "IP4.ADDRESS,IP4.GATEWAY,IP4.DNS,IP6.ADDRESS,IP6.GATEWAY,IP6.DNS", "device", "show", ifname]
|
||||
command: ["nmcli", "-t", "-f", "GENERAL.CONNECTION,IP4.ADDRESS,IP4.GATEWAY,IP4.DNS,IP6.ADDRESS,IP6.GATEWAY,IP6.DNS,CAPABILITIES.SPEED", "device", "show", ifname]
|
||||
// Find Signal (%) Bandwidth (MHz) Rate (Mbit/s) Freq (MHz) Chan (int)
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
const details = root.activeWifiDetails || ({});
|
||||
const parsed = root.parseIpDetails(text);
|
||||
|
||||
details.connectionName = parsed.connectionName;
|
||||
details.ipv4 = parsed.ipv4;
|
||||
details.gateway4 = parsed.gateway4;
|
||||
details.ipv6 = parsed.ipv6;
|
||||
details.gateway6 = parsed.gateway6;
|
||||
details.dns4 = parsed.dns4;
|
||||
details.dns6 = parsed.dns6;
|
||||
details.dns = parsed.dns;
|
||||
details.nmSpeed = parsed.nmSpeed;
|
||||
root.activeWifiDetails = details;
|
||||
|
||||
// Try to get link rate (best effort)
|
||||
wifiIwLinkProcess.ifname = wifiDeviceShowProcess.ifname;
|
||||
wifiIwLinkProcess.running = true;
|
||||
wifiDetailsProcess.ifname = wifiDeviceShowProcess.ifname;
|
||||
wifiDetailsProcess.running = true;
|
||||
}
|
||||
}
|
||||
stderr: StdioCollector {
|
||||
@@ -841,45 +954,43 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
// Optional: query Wi‑Fi bitrate and link info via iw if available
|
||||
// Optional: query Wi‑Fi bitrate and link info via nmcli
|
||||
Process {
|
||||
id: wifiIwLinkProcess
|
||||
id: wifiDetailsProcess
|
||||
property string ifname: ""
|
||||
running: false
|
||||
command: ["sh", "-c", "iw dev '" + ifname + "' link 2>/dev/null; iw dev '" + ifname + "' info 2>/dev/null || true"]
|
||||
command: ["nmcli", "-t", "-f", "IN-USE,SIGNAL,RATE,CHAN,FREQ,BANDWIDTH", "device", "wifi", "list", "ifname", ifname]
|
||||
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
const details = root.activeWifiDetails || ({});
|
||||
let rate = "";
|
||||
let freq = "";
|
||||
let iwChannel = "";
|
||||
let iwWidth = "";
|
||||
let channel = "";
|
||||
let width = "";
|
||||
let signal = "";
|
||||
|
||||
const lines = text.split("\n");
|
||||
for (var k = 0; k < lines.length; k++) {
|
||||
var line2 = lines[k].trim();
|
||||
var low = line2.toLowerCase();
|
||||
if (low.indexOf("tx bitrate:") === 0) {
|
||||
rate = line2.substring(11).trim();
|
||||
} else if (low.indexOf("freq:") === 0) {
|
||||
freq = line2.substring(5).trim();
|
||||
} else if (low.indexOf("channel") === 0) {
|
||||
// Parse "channel 9 (2452 MHz), width: 20 MHz, center1: 2452 MHz"
|
||||
var chanMatch = line2.match(/channel\s+(\d+)/i);
|
||||
if (chanMatch) {
|
||||
iwChannel = chanMatch[1];
|
||||
}
|
||||
var widthMatchInfo = line2.match(/width:\s+(\d+)\s+MHz/i);
|
||||
if (widthMatchInfo) {
|
||||
iwWidth = widthMatchInfo[1] + " MHz";
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = lines[i].trim();
|
||||
if (line.startsWith("*")) {
|
||||
// Found the in-use network
|
||||
// Format: *:SIGNAL:RATE:CHAN:FREQ:BANDWIDTH
|
||||
const parts = line.split(":");
|
||||
if (parts.length >= 6) {
|
||||
signal = parts[1];
|
||||
rate = parts[2];
|
||||
channel = parts[3];
|
||||
freq = parts[4].replace(" MHz", "");
|
||||
width = parts[5];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Determine band and channel from frequency
|
||||
// Determine band from frequency
|
||||
// https://en.wikipedia.org/wiki/List_of_WLAN_channels
|
||||
let band = "";
|
||||
let channel = iwChannel;
|
||||
if (freq) {
|
||||
const f = +freq;
|
||||
if (f) {
|
||||
@@ -887,27 +998,14 @@ Singleton {
|
||||
// https://en.wikipedia.org/wiki/List_of_WLAN_channels#6_GHz_(802.11ax_and_802.11be)
|
||||
case (f >= 5925 && f < 7125):
|
||||
band = "6 GHz";
|
||||
if (!channel) {
|
||||
channel = Math.round((f - 5940) / 5).toString();
|
||||
}
|
||||
break;
|
||||
// https://en.wikipedia.org/wiki/List_of_WLAN_channels#5_GHz_(802.11a/h/n/ac/ax/be)
|
||||
case (f >= 5150 && f < 5925):
|
||||
band = "5 GHz";
|
||||
if (!channel) {
|
||||
channel = Math.round((f - 5000) / 5).toString();
|
||||
}
|
||||
break;
|
||||
// https://en.wikipedia.org/wiki/List_of_WLAN_channels#2.4_GHz_(802.11b/g/n/ax/be)
|
||||
case (f >= 2400 && f < 2500):
|
||||
band = "2.4 GHz";
|
||||
if (!channel) {
|
||||
if (f === 2484) {
|
||||
channel = "14";
|
||||
} else {
|
||||
channel = Math.round((f - 2407) / 5).toString();
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
band = `${f} MHz`;
|
||||
@@ -917,16 +1015,7 @@ Singleton {
|
||||
|
||||
// Shorten verbose bitrate strings like: "360.0 MBit/s VHT-MCS 8 40MHz short GI"
|
||||
let rateShort = "";
|
||||
let width = iwWidth;
|
||||
if (rate) {
|
||||
// Extract width from bitrate if not already found in info (fallback)
|
||||
if (!width) {
|
||||
var widthMatchBitrate = rate.match(/(\d+)MHz/i);
|
||||
if (widthMatchBitrate) {
|
||||
width = widthMatchBitrate[1] + " MHz";
|
||||
}
|
||||
}
|
||||
|
||||
var parts = rate.trim().split(" ");
|
||||
// compact consecutive spaces
|
||||
var compact = [];
|
||||
@@ -959,12 +1048,20 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
// Enhance band string with channel and width: "Band / Ch Channel (Width)"
|
||||
// Enhance band string with channel and width: "Band / Channel (Width)"
|
||||
let enhancedBand = band;
|
||||
if (channel && width) {
|
||||
enhancedBand = `${band} / Ch ${channel} (${width})`;
|
||||
enhancedBand = `${band} / ${channel} (${width})`;
|
||||
} else if (channel) {
|
||||
enhancedBand = `${band} / Ch ${channel}`;
|
||||
enhancedBand = `${band} / ${channel}`;
|
||||
}
|
||||
|
||||
// Use dynamic speed if available
|
||||
if (details.nmSpeed) {
|
||||
rate = details.nmSpeed;
|
||||
// Standardize 'Mb/s' to 'Mbit/s' for UI consistency
|
||||
rate = rate.replace(/Mb\/s/i, "Mbit/s");
|
||||
rateShort = rate;
|
||||
}
|
||||
|
||||
details.rate = rate;
|
||||
@@ -972,6 +1069,7 @@ Singleton {
|
||||
details.band = enhancedBand;
|
||||
details.channel = channel;
|
||||
details.width = width;
|
||||
details.signal = signal;
|
||||
root.activeWifiDetails = details;
|
||||
root.activeWifiDetailsTimestamp = Date.now();
|
||||
root.detailsLoading = false;
|
||||
@@ -980,7 +1078,7 @@ Singleton {
|
||||
stderr: StdioCollector {
|
||||
onStreamFinished: {
|
||||
if (text && text.trim()) {
|
||||
Logger.w("Network", "iw link stderr:", text.trim());
|
||||
Logger.w("Network", "nmcli wifi details stderr:", text.trim());
|
||||
}
|
||||
root.activeWifiDetailsTimestamp = Date.now();
|
||||
root.detailsLoading = false;
|
||||
@@ -1414,42 +1512,48 @@ Singleton {
|
||||
property string ssid: ""
|
||||
property string password: ""
|
||||
property string securityKey: ""
|
||||
property string identity: ""
|
||||
property string eap: "peap"
|
||||
property string phase2: "mschapv2"
|
||||
property string anonIdentity: ""
|
||||
property string caCert: ""
|
||||
property bool isHidden: false
|
||||
running: false
|
||||
|
||||
command: {
|
||||
var script = `
|
||||
SSID="$1"
|
||||
PWD="$2"
|
||||
SEC="$3"
|
||||
const nmArgs = ["connection", "add", "type", "wifi", "con-name", ssid, "ssid", ssid, "--", "802-11-wireless.hidden", isHidden ? "yes" : "no"];
|
||||
|
||||
UUID=$(nmcli -t -f UUID,TYPE,NAME connection show | grep ":802-11-wireless:$SSID$" | head -n1 | cut -d: -f1)
|
||||
if [ -n "$UUID" ]; then
|
||||
nmcli connection delete uuid "$UUID" 2>/dev/null || true
|
||||
fi
|
||||
if (securityKey === "wpa-psk" || securityKey === "wpa2-psk") {
|
||||
nmArgs.push("wifi-sec.key-mgmt", "wpa-psk", "wifi-sec.psk", password);
|
||||
} else if (securityKey === "sae") {
|
||||
nmArgs.push("wifi-sec.key-mgmt", "sae", "wifi-sec.psk", password);
|
||||
} else if (securityKey === "wep") {
|
||||
nmArgs.push("wifi-sec.key-mgmt", "none", "wifi-sec.wep-key0", password);
|
||||
} else if (securityKey && securityKey.indexOf("-eap") !== -1) {
|
||||
nmArgs.push("wifi-sec.key-mgmt", "wpa-eap", "802-1x.eap", eap, "802-1x.phase2-auth", phase2, "802-1x.identity", identity, "802-1x.password", password);
|
||||
|
||||
case "$SEC" in
|
||||
wpa-psk|wpa2-psk)
|
||||
nmcli connection add type wifi con-name "$SSID" ssid "$SSID" -- wifi-sec.key-mgmt wpa-psk wifi-sec.psk "$PWD" 802-11-wireless.hidden yes
|
||||
;;
|
||||
sae)
|
||||
nmcli connection add type wifi con-name "$SSID" ssid "$SSID" -- wifi-sec.key-mgmt sae wifi-sec.psk "$PWD" 802-11-wireless.hidden yes
|
||||
;;
|
||||
wep)
|
||||
nmcli connection add type wifi con-name "$SSID" ssid "$SSID" -- wifi-sec.key-mgmt none wifi-sec.wep-key0 "$PWD" 802-11-wireless.hidden yes
|
||||
;;
|
||||
*-eap)
|
||||
# Enterprise not fully supported in Stage 1
|
||||
echo "Enterprise networks not supported yet"
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
nmcli connection add type wifi con-name "$SSID" ssid "$SSID" -- 802-11-wireless.hidden yes
|
||||
;;
|
||||
esac
|
||||
nmcli connection up id "$SSID"
|
||||
`;
|
||||
if (anonIdentity) {
|
||||
nmArgs.push("802-1x.anonymous-identity", anonIdentity);
|
||||
}
|
||||
|
||||
return ["sh", "-c", script, "--", ssid, password, securityKey];
|
||||
if (caCert) {
|
||||
nmArgs.push("802-1x.ca-cert", caCert);
|
||||
}
|
||||
}
|
||||
|
||||
const script = `
|
||||
SSID="$1"
|
||||
shift
|
||||
# Remove existing wifi profile with same SSID to avoid conflict
|
||||
UUID=$(nmcli -t -f UUID,TYPE,NAME connection show | grep ":802-11-wireless:$SSID$" | head -n1 | cut -d: -f1)
|
||||
if [ -n "$UUID" ]; then
|
||||
nmcli connection delete uuid "$UUID" 2>/dev/null || true
|
||||
fi
|
||||
nmcli "$@"
|
||||
nmcli connection up id "$SSID"
|
||||
`;
|
||||
|
||||
return ["sh", "-c", script, "--", ssid].concat(nmArgs);
|
||||
}
|
||||
environment: ({
|
||||
"LC_ALL": "C"
|
||||
@@ -1547,39 +1651,39 @@ Singleton {
|
||||
// Try multiple common profile name patterns
|
||||
command: {
|
||||
var script = `
|
||||
ssid="$1"
|
||||
deleted=false
|
||||
ssid="$1"
|
||||
deleted=false
|
||||
|
||||
# Try to find a wifi connection with this SSID and delete it
|
||||
UUID=$(nmcli -t -f UUID,TYPE,NAME connection show | grep ":802-11-wireless:$ssid$" | head -n1 | cut -d: -f1)
|
||||
if [ -n "$UUID" ]; then
|
||||
if nmcli connection delete uuid "$UUID" 2>/dev/null; then
|
||||
echo "Deleted profile: $ssid ($UUID)"
|
||||
deleted=true
|
||||
fi
|
||||
fi
|
||||
|
||||
# Fallback: try common patterns if UUID lookup failed
|
||||
if [ "$deleted" = "false" ]; then
|
||||
# Try "Auto $ssid" pattern
|
||||
if nmcli connection delete id "Auto $ssid" 2>/dev/null; then
|
||||
echo "Deleted profile: Auto $ssid"
|
||||
deleted=true
|
||||
fi
|
||||
|
||||
# Try "$ssid 1", "$ssid 2", etc. patterns
|
||||
for i in 1 2 3; do
|
||||
if nmcli connection delete id "$ssid $i" 2>/dev/null; then
|
||||
echo "Deleted profile: $ssid $i"
|
||||
deleted=true
|
||||
# Try to find a wifi connection with this SSID and delete it
|
||||
UUID=$(nmcli -t -f UUID,TYPE,NAME connection show | grep ":802-11-wireless:$ssid$" | head -n1 | cut -d: -f1)
|
||||
if [ -n "$UUID" ]; then
|
||||
if nmcli connection delete uuid "$UUID" 2>/dev/null; then
|
||||
echo "Deleted profile: $ssid ($UUID)"
|
||||
deleted=true
|
||||
fi
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
if [ "$deleted" = "false" ]; then
|
||||
echo "No profiles found for SSID: $ssid"
|
||||
fi
|
||||
`;
|
||||
# Fallback: try common patterns if UUID lookup failed
|
||||
if [ "$deleted" = "false" ]; then
|
||||
# Try "Auto $ssid" pattern
|
||||
if nmcli connection delete id "Auto $ssid" 2>/dev/null; then
|
||||
echo "Deleted profile: Auto $ssid"
|
||||
deleted=true
|
||||
fi
|
||||
|
||||
# Try "$ssid 1", "$ssid 2", etc. patterns
|
||||
for i in 1 2 3; do
|
||||
if nmcli connection delete id "$ssid $i" 2>/dev/null; then
|
||||
echo "Deleted profile: $ssid $i"
|
||||
deleted=true
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
if [ "$deleted" = "false" ]; then
|
||||
echo "No profiles found for SSID: $ssid"
|
||||
fi
|
||||
`;
|
||||
|
||||
return ["sh", "-c", script, "--", ssid];
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ Singleton {
|
||||
property bool gnomeCalendarAvailable: false
|
||||
property bool pythonAvailable: false
|
||||
property bool wtypeAvailable: false
|
||||
property bool ethtoolAvailable: false
|
||||
|
||||
// Programs to check - maps property names to commands
|
||||
readonly property var programsToCheck: ({
|
||||
@@ -25,7 +26,8 @@ Singleton {
|
||||
"wlsunsetAvailable": ["sh", "-c", "command -v wlsunset"],
|
||||
"gnomeCalendarAvailable": ["sh", "-c", "command -v gnome-calendar"],
|
||||
"wtypeAvailable": ["sh", "-c", "command -v wtype"],
|
||||
"pythonAvailable": ["sh", "-c", "command -v python3"]
|
||||
"pythonAvailable": ["sh", "-c", "command -v python3"],
|
||||
"ethtoolAvailable": ["sh", "-c", "command -v ethtool"]
|
||||
})
|
||||
|
||||
// Discord client auto-detection
|
||||
|
||||
Reference in New Issue
Block a user