mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2025-12-07 05:55:37 -05:00
networking: improve wifi experience and bugs
This commit is contained in:
@@ -28,14 +28,25 @@ Item {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
var networks = [...WifiService.wifiNetworks];
|
// Explicitly reference both arrays to ensure reactivity
|
||||||
|
var allNetworks = WifiService.wifiNetworks;
|
||||||
|
var savedNetworks = WifiService.savedWifiNetworks;
|
||||||
|
var currentSSID = WifiService.currentWifiSSID;
|
||||||
|
var signalStrength = WifiService.wifiSignalStrength;
|
||||||
|
var refreshTrigger = forceRefresh; // Force recalculation
|
||||||
|
|
||||||
// Update connected status and signal strength based on current WiFi SSID
|
var networks = [...allNetworks];
|
||||||
|
|
||||||
|
// Update connected status, saved status and signal strength based on current state
|
||||||
networks.forEach(function(network) {
|
networks.forEach(function(network) {
|
||||||
network.connected = (network.ssid === WifiService.currentWifiSSID);
|
network.connected = (network.ssid === currentSSID);
|
||||||
|
// Update saved status based on savedWifiNetworks
|
||||||
|
network.saved = savedNetworks.some(function(saved) {
|
||||||
|
return saved.ssid === network.ssid;
|
||||||
|
});
|
||||||
// Use current connection's signal strength for connected network
|
// Use current connection's signal strength for connected network
|
||||||
if (network.connected && WifiService.wifiSignalStrength) {
|
if (network.connected && signalStrength) {
|
||||||
network.signalStrength = WifiService.wifiSignalStrength;
|
network.signalStrength = signalStrength;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -51,6 +62,16 @@ Item {
|
|||||||
return networks;
|
return networks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Force refresh of sortedWifiNetworks when networks are updated
|
||||||
|
property int forceRefresh: 0
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: WifiService
|
||||||
|
function onNetworksUpdated() {
|
||||||
|
forceRefresh++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Auto-enable WiFi auto-refresh when network tab is visible
|
// Auto-enable WiFi auto-refresh when network tab is visible
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
WifiService.autoRefreshEnabled = true;
|
WifiService.autoRefreshEnabled = true;
|
||||||
@@ -166,6 +187,29 @@ Item {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Loading spinner for preference changes
|
||||||
|
DankIcon {
|
||||||
|
id: wifiLoadingSpinner
|
||||||
|
name: "refresh"
|
||||||
|
size: Theme.iconSize - 4
|
||||||
|
color: Theme.primary
|
||||||
|
anchors.right: wifiToggle.left
|
||||||
|
anchors.rightMargin: Theme.spacingS
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
visible: NetworkService.changingPreference && NetworkService.targetPreference === "wifi"
|
||||||
|
z: 10
|
||||||
|
|
||||||
|
RotationAnimation {
|
||||||
|
target: wifiLoadingSpinner
|
||||||
|
property: "rotation"
|
||||||
|
running: wifiLoadingSpinner.visible
|
||||||
|
from: 0
|
||||||
|
to: 360
|
||||||
|
duration: 1000
|
||||||
|
loops: Animation.Infinite
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// WiFi toggle switch
|
// WiFi toggle switch
|
||||||
DankToggle {
|
DankToggle {
|
||||||
id: wifiToggle
|
id: wifiToggle
|
||||||
@@ -219,111 +263,6 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connection status indicator
|
|
||||||
Rectangle {
|
|
||||||
width: parent.width
|
|
||||||
height: 32
|
|
||||||
radius: Theme.cornerRadius
|
|
||||||
color: {
|
|
||||||
if (WifiService.connectionStatus === "connecting")
|
|
||||||
return Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.12);
|
|
||||||
else if (WifiService.connectionStatus === "failed")
|
|
||||||
return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12);
|
|
||||||
else if (WifiService.connectionStatus === "connected")
|
|
||||||
return Qt.rgba(Theme.success.r, Theme.success.g, Theme.success.b, 0.12);
|
|
||||||
return "transparent";
|
|
||||||
}
|
|
||||||
border.color: {
|
|
||||||
if (WifiService.connectionStatus === "connecting")
|
|
||||||
return Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.3);
|
|
||||||
else if (WifiService.connectionStatus === "failed")
|
|
||||||
return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.3);
|
|
||||||
else if (WifiService.connectionStatus === "connected")
|
|
||||||
return Qt.rgba(Theme.success.r, Theme.success.g, Theme.success.b, 0.3);
|
|
||||||
return "transparent";
|
|
||||||
}
|
|
||||||
border.width: WifiService.connectionStatus !== "" ? 1 : 0
|
|
||||||
visible: WifiService.connectionStatus !== ""
|
|
||||||
|
|
||||||
Row {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
spacing: Theme.spacingS
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
id: connectionIcon
|
|
||||||
|
|
||||||
name: {
|
|
||||||
if (WifiService.connectionStatus === "connecting")
|
|
||||||
return "sync";
|
|
||||||
if (WifiService.connectionStatus === "failed")
|
|
||||||
return "error";
|
|
||||||
if (WifiService.connectionStatus === "connected")
|
|
||||||
return "check_circle";
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
size: Theme.iconSize - 6
|
|
||||||
color: {
|
|
||||||
if (WifiService.connectionStatus === "connecting")
|
|
||||||
return Theme.warning;
|
|
||||||
if (WifiService.connectionStatus === "failed")
|
|
||||||
return Theme.error;
|
|
||||||
if (WifiService.connectionStatus === "connected")
|
|
||||||
return Theme.success;
|
|
||||||
return Theme.surfaceText;
|
|
||||||
}
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
rotation: WifiService.connectionStatus === "connecting" ? connectionIcon.rotation : 0
|
|
||||||
|
|
||||||
RotationAnimation {
|
|
||||||
target: connectionIcon
|
|
||||||
property: "rotation"
|
|
||||||
running: WifiService.connectionStatus === "connecting"
|
|
||||||
from: 0
|
|
||||||
to: 360
|
|
||||||
duration: 1000
|
|
||||||
loops: Animation.Infinite
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on rotation {
|
|
||||||
RotationAnimation {
|
|
||||||
duration: 200
|
|
||||||
easing.type: Easing.OutQuad
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Text {
|
|
||||||
text: {
|
|
||||||
if (WifiService.connectionStatus === "connecting")
|
|
||||||
return "Connecting to " + WifiService.connectingSSID;
|
|
||||||
if (WifiService.connectionStatus === "failed")
|
|
||||||
return "Failed to connect to " + WifiService.connectingSSID;
|
|
||||||
if (WifiService.connectionStatus === "connected")
|
|
||||||
return "Connected to " + WifiService.connectingSSID;
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
color: {
|
|
||||||
if (WifiService.connectionStatus === "connecting")
|
|
||||||
return Theme.warning;
|
|
||||||
if (WifiService.connectionStatus === "failed")
|
|
||||||
return Theme.error;
|
|
||||||
if (WifiService.connectionStatus === "connected")
|
|
||||||
return Theme.success;
|
|
||||||
return Theme.surfaceText;
|
|
||||||
}
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
elide: Text.ElideRight
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
easing.type: Theme.standardEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -410,6 +349,29 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Loading spinner for preference changes
|
||||||
|
DankIcon {
|
||||||
|
id: ethernetLoadingSpinner
|
||||||
|
name: "refresh"
|
||||||
|
size: Theme.iconSize - 4
|
||||||
|
color: Theme.primary
|
||||||
|
anchors.right: ethernetToggle.left
|
||||||
|
anchors.rightMargin: Theme.spacingS
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
visible: NetworkService.changingPreference && NetworkService.targetPreference === "ethernet"
|
||||||
|
z: 10
|
||||||
|
|
||||||
|
RotationAnimation {
|
||||||
|
target: ethernetLoadingSpinner
|
||||||
|
property: "rotation"
|
||||||
|
running: ethernetLoadingSpinner.visible
|
||||||
|
from: 0
|
||||||
|
to: 360
|
||||||
|
duration: 1000
|
||||||
|
loops: Animation.Infinite
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Ethernet toggle switch (matching WiFi style)
|
// Ethernet toggle switch (matching WiFi style)
|
||||||
DankToggle {
|
DankToggle {
|
||||||
id: ethernetToggle
|
id: ethernetToggle
|
||||||
@@ -604,6 +566,7 @@ Item {
|
|||||||
Item {
|
Item {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Theme.spacingXS
|
anchors.margins: Theme.spacingXS
|
||||||
|
anchors.rightMargin: Theme.spacingM // Extra right margin for scrollbar
|
||||||
|
|
||||||
// Signal strength icon
|
// Signal strength icon
|
||||||
DankIcon {
|
DankIcon {
|
||||||
@@ -638,12 +601,22 @@ Item {
|
|||||||
text: {
|
text: {
|
||||||
if (modelData.connected)
|
if (modelData.connected)
|
||||||
return "Connected";
|
return "Connected";
|
||||||
|
if (WifiService.connectionStatus === "connecting" && WifiService.connectingSSID === modelData.ssid)
|
||||||
|
return "Connecting...";
|
||||||
|
if (WifiService.connectionStatus === "invalid_password" && WifiService.connectingSSID === modelData.ssid)
|
||||||
|
return "Invalid password";
|
||||||
if (modelData.saved)
|
if (modelData.saved)
|
||||||
return "Saved" + (modelData.secured ? " • Secured" : " • Open");
|
return "Saved" + (modelData.secured ? " • Secured" : " • Open");
|
||||||
return modelData.secured ? "Secured" : "Open";
|
return modelData.secured ? "Secured" : "Open";
|
||||||
}
|
}
|
||||||
font.pixelSize: Theme.fontSizeSmall - 1
|
font.pixelSize: Theme.fontSizeSmall - 1
|
||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
color: {
|
||||||
|
if (WifiService.connectionStatus === "connecting" && WifiService.connectingSSID === modelData.ssid)
|
||||||
|
return Theme.primary;
|
||||||
|
if (WifiService.connectionStatus === "invalid_password" && WifiService.connectingSSID === modelData.ssid)
|
||||||
|
return Theme.error;
|
||||||
|
return Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7);
|
||||||
|
}
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -664,28 +637,37 @@ Item {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
// Forget button (for saved networks)
|
// Context menu button
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
id: wifiMenuButton
|
||||||
width: 24
|
width: 24
|
||||||
height: 24
|
height: 24
|
||||||
radius: 12
|
radius: 12
|
||||||
color: forgetArea2.containsMouse ? Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12) : "transparent"
|
color: wifiMenuButtonArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : "transparent"
|
||||||
visible: modelData.saved || modelData.connected
|
|
||||||
|
|
||||||
DankIcon {
|
DankIcon {
|
||||||
anchors.centerIn: parent
|
name: "more_vert"
|
||||||
name: "delete"
|
|
||||||
size: Theme.iconSize - 8
|
size: Theme.iconSize - 8
|
||||||
color: forgetArea2.containsMouse ? Theme.error : Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
|
opacity: 0.6
|
||||||
|
anchors.centerIn: parent
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: forgetArea2
|
id: wifiMenuButtonArea
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
WifiService.forgetWifiNetwork(modelData.ssid);
|
wifiContextMenuWindow.networkData = modelData;
|
||||||
|
let localPos = wifiMenuButtonArea.mapToItem(networkTab, wifiMenuButtonArea.width / 2, wifiMenuButtonArea.height);
|
||||||
|
wifiContextMenuWindow.show(localPos.x, localPos.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -695,6 +677,7 @@ Item {
|
|||||||
MouseArea {
|
MouseArea {
|
||||||
id: networkArea2
|
id: networkArea2
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
anchors.rightMargin: 32 // Exclude menu button area
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: {
|
onClicked: {
|
||||||
@@ -841,4 +824,279 @@ Item {
|
|||||||
wifiMonitorTimer.stop();
|
wifiMonitorTimer.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WiFi Context Menu Window
|
||||||
|
Rectangle {
|
||||||
|
id: wifiContextMenuWindow
|
||||||
|
|
||||||
|
property var networkData: null
|
||||||
|
property bool menuVisible: false
|
||||||
|
|
||||||
|
function show(x, y) {
|
||||||
|
const menuWidth = 160;
|
||||||
|
const menuHeight = wifiMenuColumn.implicitHeight + Theme.spacingS * 2;
|
||||||
|
let finalX = x - menuWidth / 2;
|
||||||
|
let finalY = y;
|
||||||
|
finalX = Math.max(0, Math.min(finalX, networkTab.width - menuWidth));
|
||||||
|
finalY = Math.max(0, Math.min(finalY, networkTab.height - menuHeight));
|
||||||
|
wifiContextMenuWindow.x = finalX;
|
||||||
|
wifiContextMenuWindow.y = finalY;
|
||||||
|
wifiContextMenuWindow.visible = true;
|
||||||
|
wifiContextMenuWindow.menuVisible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hide() {
|
||||||
|
wifiContextMenuWindow.menuVisible = false;
|
||||||
|
Qt.callLater(() => {
|
||||||
|
wifiContextMenuWindow.visible = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
visible: false
|
||||||
|
width: 160
|
||||||
|
height: wifiMenuColumn.implicitHeight + Theme.spacingS * 2
|
||||||
|
radius: Theme.cornerRadiusLarge
|
||||||
|
color: Theme.popupBackground()
|
||||||
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||||
|
border.width: 1
|
||||||
|
z: 1000
|
||||||
|
opacity: menuVisible ? 1 : 0
|
||||||
|
scale: menuVisible ? 1 : 0.85
|
||||||
|
|
||||||
|
// Drop shadow
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.topMargin: 4
|
||||||
|
anchors.leftMargin: 2
|
||||||
|
anchors.rightMargin: -2
|
||||||
|
anchors.bottomMargin: -4
|
||||||
|
radius: parent.radius
|
||||||
|
color: Qt.rgba(0, 0, 0, 0.15)
|
||||||
|
z: parent.z - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: wifiMenuColumn
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Theme.spacingS
|
||||||
|
spacing: 1
|
||||||
|
|
||||||
|
// Connect/Disconnect option
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width
|
||||||
|
height: 32
|
||||||
|
radius: Theme.cornerRadiusSmall
|
||||||
|
color: connectWifiArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: Theme.spacingS
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: wifiContextMenuWindow.networkData && wifiContextMenuWindow.networkData.connected ? "wifi_off" : "wifi"
|
||||||
|
size: Theme.iconSize - 2
|
||||||
|
color: Theme.surfaceText
|
||||||
|
opacity: 0.7
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: wifiContextMenuWindow.networkData && wifiContextMenuWindow.networkData.connected ? "Disconnect" : "Connect"
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Normal
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: connectWifiArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
if (wifiContextMenuWindow.networkData) {
|
||||||
|
if (wifiContextMenuWindow.networkData.connected) {
|
||||||
|
// Disconnect from current network
|
||||||
|
WifiService.disconnectWifi();
|
||||||
|
} else {
|
||||||
|
// Connect to selected network
|
||||||
|
if (wifiContextMenuWindow.networkData.saved) {
|
||||||
|
WifiService.connectToWifi(wifiContextMenuWindow.networkData.ssid);
|
||||||
|
} else if (wifiContextMenuWindow.networkData.secured) {
|
||||||
|
// Show password dialog for secured networks
|
||||||
|
wifiPasswordDialog.wifiPasswordSSID = wifiContextMenuWindow.networkData.ssid;
|
||||||
|
wifiPasswordDialog.wifiPasswordInput = "";
|
||||||
|
wifiPasswordDialog.wifiPasswordDialogVisible = true;
|
||||||
|
} else {
|
||||||
|
WifiService.connectToWifi(wifiContextMenuWindow.networkData.ssid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wifiContextMenuWindow.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Separator
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width - Theme.spacingS * 2
|
||||||
|
height: 5
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: parent.width
|
||||||
|
height: 1
|
||||||
|
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forget Network option (only for saved networks)
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width
|
||||||
|
height: 32
|
||||||
|
radius: Theme.cornerRadiusSmall
|
||||||
|
color: forgetWifiArea.containsMouse ? Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12) : "transparent"
|
||||||
|
visible: wifiContextMenuWindow.networkData && (wifiContextMenuWindow.networkData.saved || wifiContextMenuWindow.networkData.connected)
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: Theme.spacingS
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "delete"
|
||||||
|
size: Theme.iconSize - 2
|
||||||
|
color: forgetWifiArea.containsMouse ? Theme.error : Theme.surfaceText
|
||||||
|
opacity: 0.7
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "Forget Network"
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: forgetWifiArea.containsMouse ? Theme.error : Theme.surfaceText
|
||||||
|
font.weight: Font.Normal
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: forgetWifiArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
if (wifiContextMenuWindow.networkData) {
|
||||||
|
WifiService.forgetWifiNetwork(wifiContextMenuWindow.networkData.ssid);
|
||||||
|
}
|
||||||
|
wifiContextMenuWindow.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Network Info option
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width
|
||||||
|
height: 32
|
||||||
|
radius: Theme.cornerRadiusSmall
|
||||||
|
color: infoWifiArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: Theme.spacingS
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "info"
|
||||||
|
size: Theme.iconSize - 2
|
||||||
|
color: Theme.surfaceText
|
||||||
|
opacity: 0.7
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "Network Info"
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Normal
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: infoWifiArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
if (wifiContextMenuWindow.networkData) {
|
||||||
|
networkInfoDialog.showNetworkInfo(wifiContextMenuWindow.networkData.ssid, wifiContextMenuWindow.networkData);
|
||||||
|
}
|
||||||
|
wifiContextMenuWindow.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.mediumDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on scale {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.mediumDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Background MouseArea to close the context menu
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
visible: wifiContextMenuWindow.visible
|
||||||
|
onClicked: {
|
||||||
|
wifiContextMenuWindow.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
x: wifiContextMenuWindow.x
|
||||||
|
y: wifiContextMenuWindow.y
|
||||||
|
width: wifiContextMenuWindow.width
|
||||||
|
height: wifiContextMenuWindow.height
|
||||||
|
onClicked: {
|
||||||
|
// Prevent clicks on menu from closing it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
208
Modules/NetworkInfoDialog.qml
Normal file
208
Modules/NetworkInfoDialog.qml
Normal file
@@ -0,0 +1,208 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import Quickshell
|
||||||
|
import Quickshell.Wayland
|
||||||
|
import Quickshell.Widgets
|
||||||
|
import Quickshell.Io
|
||||||
|
import qs.Common
|
||||||
|
import qs.Services
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
PanelWindow {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property bool networkInfoDialogVisible: false
|
||||||
|
property string networkSSID: ""
|
||||||
|
property var networkData: null
|
||||||
|
property string networkDetails: ""
|
||||||
|
|
||||||
|
visible: networkInfoDialogVisible
|
||||||
|
WlrLayershell.layer: WlrLayershell.Overlay
|
||||||
|
WlrLayershell.exclusiveZone: -1
|
||||||
|
WlrLayershell.keyboardFocus: networkInfoDialogVisible ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
function showNetworkInfo(ssid, data) {
|
||||||
|
networkSSID = ssid;
|
||||||
|
networkData = data;
|
||||||
|
networkInfoDialogVisible = true;
|
||||||
|
WifiService.fetchNetworkInfo(ssid);
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideDialog() {
|
||||||
|
networkInfoDialogVisible = false;
|
||||||
|
networkSSID = "";
|
||||||
|
networkData = null;
|
||||||
|
networkDetails = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors {
|
||||||
|
top: true
|
||||||
|
left: true
|
||||||
|
right: true
|
||||||
|
bottom: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: Qt.rgba(0, 0, 0, 0.5)
|
||||||
|
opacity: networkInfoDialogVisible ? 1 : 0
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: {
|
||||||
|
root.hideDialog();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.mediumDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: Math.min(600, parent.width - Theme.spacingL * 2)
|
||||||
|
height: Math.min(500, parent.height - Theme.spacingL * 2)
|
||||||
|
anchors.centerIn: parent
|
||||||
|
color: Theme.surfaceContainer
|
||||||
|
radius: Theme.cornerRadiusLarge
|
||||||
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||||
|
border.width: 1
|
||||||
|
opacity: networkInfoDialogVisible ? 1 : 0
|
||||||
|
scale: networkInfoDialogVisible ? 1 : 0.9
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Theme.spacingL
|
||||||
|
spacing: Theme.spacingL
|
||||||
|
|
||||||
|
// Header
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: parent.width - 40
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "Network Information"
|
||||||
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Medium
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: "Details for \"" + networkSSID + "\""
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
||||||
|
width: parent.width
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankActionButton {
|
||||||
|
iconName: "close"
|
||||||
|
iconSize: Theme.iconSize - 4
|
||||||
|
iconColor: Theme.surfaceText
|
||||||
|
hoverColor: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12)
|
||||||
|
onClicked: {
|
||||||
|
root.hideDialog();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Network Details
|
||||||
|
ScrollView {
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height - 140
|
||||||
|
clip: true
|
||||||
|
ScrollBar.vertical.policy: ScrollBar.AsNeeded
|
||||||
|
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||||
|
|
||||||
|
Flickable {
|
||||||
|
contentWidth: parent.width
|
||||||
|
contentHeight: detailsRect.height
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: detailsRect
|
||||||
|
width: parent.width
|
||||||
|
height: Math.max(parent.parent.height, detailsText.contentHeight + Theme.spacingM * 2)
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08)
|
||||||
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||||
|
border.width: 1
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: detailsText
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Theme.spacingM
|
||||||
|
text: WifiService.networkInfoDetails.replace(/\\n/g, '\n') || "No information available"
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
lineHeight: 1.5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close Button
|
||||||
|
Item {
|
||||||
|
width: parent.width
|
||||||
|
height: 40
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
width: Math.max(70, closeText.contentWidth + Theme.spacingM * 2)
|
||||||
|
height: 36
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: closeArea.containsMouse ? Qt.darker(Theme.primary, 1.1) : Theme.primary
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: closeText
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: "Close"
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.background
|
||||||
|
font.weight: Font.Medium
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: closeArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
root.hideDialog();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.mediumDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on scale {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.mediumDuration
|
||||||
|
easing.type: Theme.emphasizedEasing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -72,6 +72,14 @@ PanelWindow {
|
|||||||
opacity: wifiPasswordDialogVisible ? 1 : 0
|
opacity: wifiPasswordDialogVisible ? 1 : 0
|
||||||
scale: wifiPasswordDialogVisible ? 1 : 0.9
|
scale: wifiPasswordDialogVisible ? 1 : 0.9
|
||||||
|
|
||||||
|
// Prevent clicks inside dialog from closing it
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: {
|
||||||
|
// Do nothing - prevent propagation to background
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Theme.spacingL
|
anchors.margins: Theme.spacingL
|
||||||
@@ -131,6 +139,7 @@ PanelWindow {
|
|||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
textColor: Theme.surfaceText
|
textColor: Theme.surfaceText
|
||||||
|
text: wifiPasswordInput
|
||||||
echoMode: showPasswordCheckbox.checked ? TextInput.Normal : TextInput.Password
|
echoMode: showPasswordCheckbox.checked ? TextInput.Normal : TextInput.Password
|
||||||
enabled: wifiPasswordDialogVisible
|
enabled: wifiPasswordDialogVisible
|
||||||
placeholderText: "Enter password"
|
placeholderText: "Enter password"
|
||||||
@@ -142,6 +151,10 @@ PanelWindow {
|
|||||||
}
|
}
|
||||||
onAccepted: {
|
onAccepted: {
|
||||||
WifiService.connectToWifiWithPassword(wifiPasswordSSID, wifiPasswordInput);
|
WifiService.connectToWifiWithPassword(wifiPasswordSSID, wifiPasswordInput);
|
||||||
|
// Close dialog immediately after pressing Enter
|
||||||
|
passwordInput.enabled = false;
|
||||||
|
wifiPasswordDialogVisible = false;
|
||||||
|
wifiPasswordInput = "";
|
||||||
}
|
}
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
if (wifiPasswordDialogVisible)
|
if (wifiPasswordDialogVisible)
|
||||||
@@ -267,6 +280,10 @@ PanelWindow {
|
|||||||
enabled: parent.enabled
|
enabled: parent.enabled
|
||||||
onClicked: {
|
onClicked: {
|
||||||
WifiService.connectToWifiWithPassword(wifiPasswordSSID, wifiPasswordInput);
|
WifiService.connectToWifiWithPassword(wifiPasswordSSID, wifiPasswordInput);
|
||||||
|
// Close dialog immediately after clicking connect
|
||||||
|
passwordInput.enabled = false;
|
||||||
|
wifiPasswordDialogVisible = false;
|
||||||
|
wifiPasswordInput = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -304,4 +321,17 @@ PanelWindow {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Auto-reopen dialog on invalid password
|
||||||
|
Connections {
|
||||||
|
target: WifiService
|
||||||
|
function onPasswordDialogShouldReopenChanged() {
|
||||||
|
if (WifiService.passwordDialogShouldReopen && WifiService.connectingSSID !== "") {
|
||||||
|
wifiPasswordSSID = WifiService.connectingSSID;
|
||||||
|
wifiPasswordInput = "";
|
||||||
|
wifiPasswordDialogVisible = true;
|
||||||
|
WifiService.passwordDialogShouldReopen = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,35 +75,7 @@ Singleton {
|
|||||||
console.log("User prefers Ethernet, setting status to ethernet")
|
console.log("User prefers Ethernet, setting status to ethernet")
|
||||||
} else {
|
} else {
|
||||||
// Auto mode - check which interface has the default route
|
// Auto mode - check which interface has the default route
|
||||||
let priorityChecker = Qt.createQmlObject(`
|
defaultRouteChecker.running = true
|
||||||
import Quickshell.Io
|
|
||||||
Process {
|
|
||||||
command: ["sh", "-c", "ip route show default | head -1 | cut -d\\\" \\\" -f5"]
|
|
||||||
running: true
|
|
||||||
stdout: SplitParser {
|
|
||||||
splitMarker: "\\n"
|
|
||||||
onRead: function(data) {
|
|
||||||
let defaultInterface = data.trim()
|
|
||||||
console.log("Default route interface:", defaultInterface)
|
|
||||||
// Check if the interface is wifi or ethernet
|
|
||||||
if (defaultInterface.startsWith("wl") || defaultInterface.includes("wifi")) {
|
|
||||||
root.networkStatus = "wifi"
|
|
||||||
console.log("WiFi interface has default route, setting status to wifi")
|
|
||||||
// Trigger WiFi SSID update
|
|
||||||
if (root.wifiEnabled) {
|
|
||||||
WifiService.updateCurrentWifiInfo()
|
|
||||||
}
|
|
||||||
} else if (defaultInterface.startsWith("en") || defaultInterface.includes("eth")) {
|
|
||||||
root.networkStatus = "ethernet"
|
|
||||||
console.log("Ethernet interface has default route, setting status to ethernet")
|
|
||||||
} else {
|
|
||||||
root.networkStatus = "disconnected"
|
|
||||||
console.log("Unknown interface type:", defaultInterface)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`, root)
|
|
||||||
}
|
}
|
||||||
} else if (hasWifi) {
|
} else if (hasWifi) {
|
||||||
root.networkStatus = "wifi"
|
root.networkStatus = "wifi"
|
||||||
@@ -157,6 +129,35 @@ Singleton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: defaultRouteChecker
|
||||||
|
command: ["sh", "-c", "ip route show default | head -1 | cut -d' ' -f5"]
|
||||||
|
running: false
|
||||||
|
|
||||||
|
stdout: SplitParser {
|
||||||
|
splitMarker: "\n"
|
||||||
|
onRead: function(data) {
|
||||||
|
let defaultInterface = data.trim()
|
||||||
|
console.log("Default route interface:", defaultInterface)
|
||||||
|
// Check if the interface is wifi or ethernet
|
||||||
|
if (defaultInterface.startsWith("wl") || defaultInterface.includes("wifi")) {
|
||||||
|
root.networkStatus = "wifi"
|
||||||
|
console.log("WiFi interface has default route, setting status to wifi")
|
||||||
|
// Trigger WiFi SSID update
|
||||||
|
if (root.wifiEnabled) {
|
||||||
|
WifiService.updateCurrentWifiInfo()
|
||||||
|
}
|
||||||
|
} else if (defaultInterface.startsWith("en") || defaultInterface.includes("eth")) {
|
||||||
|
root.networkStatus = "ethernet"
|
||||||
|
console.log("Ethernet interface has default route, setting status to ethernet")
|
||||||
|
} else {
|
||||||
|
root.networkStatus = "disconnected"
|
||||||
|
console.log("Unknown interface type:", defaultInterface)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Process {
|
Process {
|
||||||
id: wifiRadioChecker
|
id: wifiRadioChecker
|
||||||
command: ["nmcli", "radio", "wifi"]
|
command: ["nmcli", "radio", "wifi"]
|
||||||
@@ -186,13 +187,23 @@ Singleton {
|
|||||||
console.log("Ethernet IP:", root.ethernetIP)
|
console.log("Ethernet IP:", root.ethernetIP)
|
||||||
|
|
||||||
// Get the ethernet interface name
|
// Get the ethernet interface name
|
||||||
let ethInterfaceProcess = Qt.createQmlObject(`
|
ethernetInterfaceChecker.running = true
|
||||||
import Quickshell.Io
|
} else {
|
||||||
|
console.log("No ethernet IP found")
|
||||||
|
root.ethernetIP = ""
|
||||||
|
root.ethernetInterface = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Process {
|
Process {
|
||||||
|
id: ethernetInterfaceChecker
|
||||||
command: ["sh", "-c", "nmcli -t -f DEVICE,TYPE device | grep ethernet | grep connected | cut -d: -f1 | head -1"]
|
command: ["sh", "-c", "nmcli -t -f DEVICE,TYPE device | grep ethernet | grep connected | cut -d: -f1 | head -1"]
|
||||||
running: true
|
running: false
|
||||||
|
|
||||||
stdout: SplitParser {
|
stdout: SplitParser {
|
||||||
splitMarker: "\\n"
|
splitMarker: "\n"
|
||||||
onRead: function(interfaceData) {
|
onRead: function(interfaceData) {
|
||||||
if (interfaceData.trim()) {
|
if (interfaceData.trim()) {
|
||||||
root.ethernetInterface = interfaceData.trim()
|
root.ethernetInterface = interfaceData.trim()
|
||||||
@@ -204,15 +215,6 @@ Singleton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`, root)
|
|
||||||
} else {
|
|
||||||
console.log("No ethernet IP found")
|
|
||||||
root.ethernetIP = ""
|
|
||||||
root.ethernetInterface = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Process {
|
Process {
|
||||||
id: wifiIPChecker
|
id: wifiIPChecker
|
||||||
@@ -236,37 +238,30 @@ Singleton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleNetworkConnection(type) {
|
// Static processes for network operations
|
||||||
if (type === "ethernet") {
|
|
||||||
// Toggle ethernet connection
|
|
||||||
if (root.networkStatus === "ethernet") {
|
|
||||||
// Disconnect ethernet
|
|
||||||
console.log("Disconnecting ethernet...")
|
|
||||||
let disconnectProcess = Qt.createQmlObject(`
|
|
||||||
import Quickshell.Io
|
|
||||||
Process {
|
Process {
|
||||||
|
id: ethernetDisconnector
|
||||||
command: ["sh", "-c", "nmcli device disconnect $(nmcli -t -f DEVICE,TYPE device | grep ethernet | cut -d: -f1 | head -1)"]
|
command: ["sh", "-c", "nmcli device disconnect $(nmcli -t -f DEVICE,TYPE device | grep ethernet | cut -d: -f1 | head -1)"]
|
||||||
running: true
|
running: false
|
||||||
|
|
||||||
onExited: function(exitCode) {
|
onExited: function(exitCode) {
|
||||||
console.log("Ethernet disconnect result:", exitCode)
|
console.log("Ethernet disconnect result:", exitCode)
|
||||||
delayedRefreshNetworkStatus()
|
delayedRefreshNetworkStatus()
|
||||||
}
|
}
|
||||||
|
|
||||||
stderr: SplitParser {
|
stderr: SplitParser {
|
||||||
splitMarker: "\\n"
|
splitMarker: "\n"
|
||||||
onRead: function(data) {
|
onRead: function(data) {
|
||||||
console.log("Ethernet disconnect stderr:", data)
|
console.log("Ethernet disconnect stderr:", data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`, root)
|
|
||||||
} else {
|
|
||||||
// Connect ethernet and set higher priority
|
|
||||||
console.log("Connecting ethernet...")
|
|
||||||
let connectProcess = Qt.createQmlObject(`
|
|
||||||
import Quickshell.Io
|
|
||||||
Process {
|
Process {
|
||||||
command: ["sh", "-c", "ETH_DEV=$(nmcli -t -f DEVICE,TYPE device | grep ethernet | cut -d: -f1 | head -1); if [ -n \\"$ETH_DEV\\" ]; then nmcli device connect \\"$ETH_DEV\\"; ETH_CONN=$(nmcli -t -f NAME,DEVICE connection show --active | grep \\"$ETH_DEV\\" | cut -d: -f1); if [ -n \\"$ETH_CONN\\" ]; then nmcli connection modify \\"$ETH_CONN\\" connection.autoconnect-priority 100; nmcli connection down \\"$ETH_CONN\\"; nmcli connection up \\"$ETH_CONN\\"; fi; else echo \\"No ethernet device found\\"; exit 1; fi"]
|
id: ethernetConnector
|
||||||
running: true
|
command: ["sh", "-c", "ETH_DEV=$(nmcli -t -f DEVICE,TYPE device | grep ethernet | cut -d: -f1 | head -1); if [ -n \"$ETH_DEV\" ]; then nmcli device connect \"$ETH_DEV\"; ETH_CONN=$(nmcli -t -f NAME,DEVICE connection show --active | grep \"$ETH_DEV\" | cut -d: -f1); if [ -n \"$ETH_CONN\" ]; then nmcli connection modify \"$ETH_CONN\" connection.autoconnect-priority 100; nmcli connection down \"$ETH_CONN\"; nmcli connection up \"$ETH_CONN\"; fi; else echo \"No ethernet device found\"; exit 1; fi"]
|
||||||
|
running: false
|
||||||
|
|
||||||
onExited: function(exitCode) {
|
onExited: function(exitCode) {
|
||||||
console.log("Ethernet connect result:", exitCode)
|
console.log("Ethernet connect result:", exitCode)
|
||||||
if (exitCode === 0) {
|
if (exitCode === 0) {
|
||||||
@@ -276,36 +271,121 @@ Singleton {
|
|||||||
}
|
}
|
||||||
delayedRefreshNetworkStatus()
|
delayedRefreshNetworkStatus()
|
||||||
}
|
}
|
||||||
|
|
||||||
stderr: SplitParser {
|
stderr: SplitParser {
|
||||||
splitMarker: "\\n"
|
splitMarker: "\n"
|
||||||
onRead: function(data) {
|
onRead: function(data) {
|
||||||
console.log("Ethernet connect stderr:", data)
|
console.log("Ethernet connect stderr:", data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`, root)
|
|
||||||
}
|
|
||||||
} else if (type === "wifi") {
|
|
||||||
// Connect to WiFi if disconnected
|
|
||||||
if (root.networkStatus !== "wifi" && root.wifiEnabled) {
|
|
||||||
console.log("Connecting to WiFi device...")
|
|
||||||
let connectProcess = Qt.createQmlObject(`
|
|
||||||
import Quickshell.Io
|
|
||||||
Process {
|
Process {
|
||||||
command: ["sh", "-c", "WIFI_DEV=$(nmcli -t -f DEVICE,TYPE device | grep wifi | cut -d: -f1 | head -1); if [ -n \\"$WIFI_DEV\\" ]; then nmcli device connect \\"$WIFI_DEV\\"; else echo \\"No WiFi device found\\"; exit 1; fi"]
|
id: wifiDeviceConnector
|
||||||
running: true
|
command: ["sh", "-c", "WIFI_DEV=$(nmcli -t -f DEVICE,TYPE device | grep wifi | cut -d: -f1 | head -1); if [ -n \"$WIFI_DEV\" ]; then nmcli device connect \"$WIFI_DEV\"; else echo \"No WiFi device found\"; exit 1; fi"]
|
||||||
|
running: false
|
||||||
|
|
||||||
onExited: function(exitCode) {
|
onExited: function(exitCode) {
|
||||||
console.log("WiFi device connect result:", exitCode)
|
console.log("WiFi device connect result:", exitCode)
|
||||||
delayedRefreshNetworkStatus()
|
delayedRefreshNetworkStatus()
|
||||||
}
|
}
|
||||||
|
|
||||||
stderr: SplitParser {
|
stderr: SplitParser {
|
||||||
splitMarker: "\\n"
|
splitMarker: "\n"
|
||||||
onRead: function(data) {
|
onRead: function(data) {
|
||||||
console.log("WiFi device connect stderr:", data)
|
console.log("WiFi device connect stderr:", data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`, root)
|
|
||||||
|
Process {
|
||||||
|
id: wifiSwitcher
|
||||||
|
command: ["sh", "-c", "ETH_DEV=$(nmcli -t -f DEVICE,TYPE device | grep ethernet | cut -d: -f1 | head -1); WIFI_DEV=$(nmcli -t -f DEVICE,TYPE device | grep wifi | cut -d: -f1 | head -1); [ -n \"$ETH_DEV\" ] && nmcli device disconnect \"$ETH_DEV\" 2>/dev/null; [ -n \"$WIFI_DEV\" ] && nmcli device connect \"$WIFI_DEV\" 2>/dev/null || true"]
|
||||||
|
running: false
|
||||||
|
|
||||||
|
onExited: function(exitCode) {
|
||||||
|
console.log("Switch to wifi result:", exitCode)
|
||||||
|
delayedRefreshNetworkStatus()
|
||||||
|
}
|
||||||
|
|
||||||
|
stderr: SplitParser {
|
||||||
|
splitMarker: "\n"
|
||||||
|
onRead: function(data) {
|
||||||
|
console.log("Switch to wifi stderr:", data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: ethernetSwitcher
|
||||||
|
command: ["sh", "-c", "WIFI_DEV=$(nmcli -t -f DEVICE,TYPE device | grep wifi | cut -d: -f1 | head -1); ETH_DEV=$(nmcli -t -f DEVICE,TYPE device | grep ethernet | cut -d: -f1 | head -1); [ -n \"$WIFI_DEV\" ] && nmcli device disconnect \"$WIFI_DEV\" 2>/dev/null; [ -n \"$ETH_DEV\" ] && nmcli device connect \"$ETH_DEV\" 2>/dev/null || true"]
|
||||||
|
running: false
|
||||||
|
|
||||||
|
onExited: function(exitCode) {
|
||||||
|
console.log("Switch to ethernet result:", exitCode)
|
||||||
|
delayedRefreshNetworkStatus()
|
||||||
|
}
|
||||||
|
|
||||||
|
stderr: SplitParser {
|
||||||
|
splitMarker: "\n"
|
||||||
|
onRead: function(data) {
|
||||||
|
console.log("Switch to ethernet stderr:", data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: wifiRadioToggler
|
||||||
|
command: ["nmcli", "radio", "wifi", root.wifiEnabled ? "off" : "on"]
|
||||||
|
running: false
|
||||||
|
|
||||||
|
onExited: {
|
||||||
|
root.wifiToggling = false
|
||||||
|
networkStatusChecker.running = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: wifiPriorityChanger
|
||||||
|
command: ["sh", "-c", "nmcli -t -f NAME,TYPE connection show | grep 802-11-wireless | cut -d: -f1 | while read conn; do nmcli connection modify \"$conn\" ipv4.route-metric 50; done; nmcli -t -f NAME,TYPE connection show | grep 802-3-ethernet | cut -d: -f1 | while read conn; do nmcli connection modify \"$conn\" ipv4.route-metric 200; done; nmcli -t -f NAME,TYPE connection show --active | grep -E \"(802-11-wireless|802-3-ethernet)\" | cut -d: -f1 | while read conn; do nmcli connection down \"$conn\" && nmcli connection up \"$conn\"; done"]
|
||||||
|
running: false
|
||||||
|
|
||||||
|
onExited: function(exitCode) {
|
||||||
|
console.log("WiFi route metric set to 50, ethernet to 200, connections restarted, exit code:", exitCode)
|
||||||
|
// Don't reset changingPreference here - let network status check handle it
|
||||||
|
delayedRefreshNetworkStatus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: ethernetPriorityChanger
|
||||||
|
command: ["sh", "-c", "nmcli -t -f NAME,TYPE connection show | grep 802-3-ethernet | cut -d: -f1 | while read conn; do nmcli connection modify \"$conn\" ipv4.route-metric 50; done; nmcli -t -f NAME,TYPE connection show | grep 802-11-wireless | cut -d: -f1 | while read conn; do nmcli connection modify \"$conn\" ipv4.route-metric 200; done; nmcli -t -f NAME,TYPE connection show --active | grep -E \"(802-11-wireless|802-3-ethernet)\" | cut -d: -f1 | while read conn; do nmcli connection down \"$conn\" && nmcli connection up \"$conn\"; done"]
|
||||||
|
running: false
|
||||||
|
|
||||||
|
onExited: function(exitCode) {
|
||||||
|
console.log("Ethernet route metric set to 50, WiFi to 200, connections restarted, exit code:", exitCode)
|
||||||
|
// Don't reset changingPreference here - let network status check handle it
|
||||||
|
delayedRefreshNetworkStatus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleNetworkConnection(type) {
|
||||||
|
if (type === "ethernet") {
|
||||||
|
// Toggle ethernet connection
|
||||||
|
if (root.networkStatus === "ethernet") {
|
||||||
|
// Disconnect ethernet
|
||||||
|
console.log("Disconnecting ethernet...")
|
||||||
|
ethernetDisconnector.running = true
|
||||||
|
} else {
|
||||||
|
// Connect ethernet and set higher priority
|
||||||
|
console.log("Connecting ethernet...")
|
||||||
|
ethernetConnector.running = true
|
||||||
|
}
|
||||||
|
} else if (type === "wifi") {
|
||||||
|
// Connect to WiFi if disconnected
|
||||||
|
if (root.networkStatus !== "wifi" && root.wifiEnabled) {
|
||||||
|
console.log("Connecting to WiFi device...")
|
||||||
|
wifiDeviceConnector.running = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -313,63 +393,21 @@ Singleton {
|
|||||||
function switchToWifi() {
|
function switchToWifi() {
|
||||||
console.log("Switching to WiFi")
|
console.log("Switching to WiFi")
|
||||||
// Disconnect ethernet first, then try to connect to a known WiFi network
|
// Disconnect ethernet first, then try to connect to a known WiFi network
|
||||||
let switchProcess = Qt.createQmlObject(`
|
wifiSwitcher.running = true
|
||||||
import Quickshell.Io
|
|
||||||
Process {
|
|
||||||
command: ["sh", "-c", "ETH_DEV=$(nmcli -t -f DEVICE,TYPE device | grep ethernet | cut -d: -f1 | head -1); WIFI_DEV=$(nmcli -t -f DEVICE,TYPE device | grep wifi | cut -d: -f1 | head -1); [ -n \\"$ETH_DEV\\" ] && nmcli device disconnect \\"$ETH_DEV\\" 2>/dev/null; [ -n \\"$WIFI_DEV\\" ] && nmcli device connect \\"$WIFI_DEV\\" 2>/dev/null || true"]
|
|
||||||
running: true
|
|
||||||
onExited: function(exitCode) {
|
|
||||||
console.log("Switch to wifi result:", exitCode)
|
|
||||||
delayedRefreshNetworkStatus()
|
|
||||||
}
|
|
||||||
stderr: SplitParser {
|
|
||||||
splitMarker: "\\n"
|
|
||||||
onRead: function(data) {
|
|
||||||
console.log("Switch to wifi stderr:", data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`, root)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function switchToEthernet() {
|
function switchToEthernet() {
|
||||||
console.log("Switching to Ethernet")
|
console.log("Switching to Ethernet")
|
||||||
// Disconnect WiFi first, then connect ethernet
|
// Disconnect WiFi first, then connect ethernet
|
||||||
let switchProcess = Qt.createQmlObject(`
|
ethernetSwitcher.running = true
|
||||||
import Quickshell.Io
|
|
||||||
Process {
|
|
||||||
command: ["sh", "-c", "WIFI_DEV=$(nmcli -t -f DEVICE,TYPE device | grep wifi | cut -d: -f1 | head -1); ETH_DEV=$(nmcli -t -f DEVICE,TYPE device | grep ethernet | cut -d: -f1 | head -1); [ -n \\"$WIFI_DEV\\" ] && nmcli device disconnect \\"$WIFI_DEV\\" 2>/dev/null; [ -n \\"$ETH_DEV\\" ] && nmcli device connect \\"$ETH_DEV\\" 2>/dev/null || true"]
|
|
||||||
running: true
|
|
||||||
onExited: function(exitCode) {
|
|
||||||
console.log("Switch to ethernet result:", exitCode)
|
|
||||||
delayedRefreshNetworkStatus()
|
|
||||||
}
|
|
||||||
stderr: SplitParser {
|
|
||||||
splitMarker: "\\n"
|
|
||||||
onRead: function(data) {
|
|
||||||
console.log("Switch to ethernet stderr:", data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`, root)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleWifiRadio() {
|
function toggleWifiRadio() {
|
||||||
if (root.wifiToggling) return
|
if (root.wifiToggling) return
|
||||||
|
|
||||||
root.wifiToggling = true
|
root.wifiToggling = true
|
||||||
let action = root.wifiEnabled ? "off" : "on"
|
wifiRadioToggler.command = ["nmcli", "radio", "wifi", root.wifiEnabled ? "off" : "on"]
|
||||||
let toggleProcess = Qt.createQmlObject(`
|
wifiRadioToggler.running = true
|
||||||
import Quickshell.Io
|
|
||||||
Process {
|
|
||||||
command: ["nmcli", "radio", "wifi", "${action}"]
|
|
||||||
running: true
|
|
||||||
onExited: {
|
|
||||||
root.wifiToggling = false
|
|
||||||
networkStatusChecker.running = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`, root)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function refreshNetworkStatus() {
|
function refreshNetworkStatus() {
|
||||||
@@ -391,32 +429,10 @@ Singleton {
|
|||||||
|
|
||||||
if (preference === "wifi") {
|
if (preference === "wifi") {
|
||||||
// Set WiFi to low route metric (high priority), ethernet to high route metric (low priority)
|
// Set WiFi to low route metric (high priority), ethernet to high route metric (low priority)
|
||||||
let wifiPriorityProcess = Qt.createQmlObject(`
|
wifiPriorityChanger.running = true
|
||||||
import Quickshell.Io
|
|
||||||
Process {
|
|
||||||
command: ["sh", "-c", "nmcli -t -f NAME,TYPE connection show | grep 802-11-wireless | cut -d: -f1 | while read conn; do nmcli connection modify \\\\\\"$conn\\\\\\" ipv4.route-metric 50; done; nmcli -t -f NAME,TYPE connection show | grep 802-3-ethernet | cut -d: -f1 | while read conn; do nmcli connection modify \\\\\\"$conn\\\\\\" ipv4.route-metric 200; done; nmcli -t -f NAME,TYPE connection show --active | grep -E \\\\\\"(802-11-wireless|802-3-ethernet)\\\\\\" | cut -d: -f1 | while read conn; do nmcli connection down \\\\\\"$conn\\\\\\" && nmcli connection up \\\\\\"$conn\\\\\\"; done"]
|
|
||||||
running: true
|
|
||||||
onExited: function(exitCode) {
|
|
||||||
console.log("WiFi route metric set to 50, ethernet to 200, connections restarted, exit code:", exitCode)
|
|
||||||
// Don't reset changingPreference here - let network status check handle it
|
|
||||||
delayedRefreshNetworkStatus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`, root)
|
|
||||||
} else if (preference === "ethernet") {
|
} else if (preference === "ethernet") {
|
||||||
// Set ethernet to low route metric (high priority), WiFi to high route metric (low priority)
|
// Set ethernet to low route metric (high priority), WiFi to high route metric (low priority)
|
||||||
let ethernetPriorityProcess = Qt.createQmlObject(`
|
ethernetPriorityChanger.running = true
|
||||||
import Quickshell.Io
|
|
||||||
Process {
|
|
||||||
command: ["sh", "-c", "nmcli -t -f NAME,TYPE connection show | grep 802-3-ethernet | cut -d: -f1 | while read conn; do nmcli connection modify \\\\\\"$conn\\\\\\" ipv4.route-metric 50; done; nmcli -t -f NAME,TYPE connection show | grep 802-11-wireless | cut -d: -f1 | while read conn; do nmcli connection modify \\\\\\"$conn\\\\\\" ipv4.route-metric 200; done; nmcli -t -f NAME,TYPE connection show --active | grep -E \\\\\\"(802-11-wireless|802-3-ethernet)\\\\\\" | cut -d: -f1 | while read conn; do nmcli connection down \\\\\\"$conn\\\\\\" && nmcli connection up \\\\\\"$conn\\\\\\"; done"]
|
|
||||||
running: true
|
|
||||||
onExited: function(exitCode) {
|
|
||||||
console.log("Ethernet route metric set to 50, WiFi to 200, connections restarted, exit code:", exitCode)
|
|
||||||
// Don't reset changingPreference here - let network status check handle it
|
|
||||||
delayedRefreshNetworkStatus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`, root)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -119,25 +119,13 @@ Singleton {
|
|||||||
updateProcessList();
|
updateProcessList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
property int killPid: 0
|
||||||
|
|
||||||
function killProcess(pid) {
|
function killProcess(pid) {
|
||||||
if (pid > 0) {
|
if (pid > 0) {
|
||||||
const killCmd = ["bash", "-c", "kill " + pid];
|
root.killPid = pid
|
||||||
const killProcess = Qt.createQmlObject(`
|
processKiller.command = ["bash", "-c", "kill " + pid]
|
||||||
import QtQuick
|
processKiller.running = true
|
||||||
import Quickshell.Io
|
|
||||||
Process {
|
|
||||||
command: ${JSON.stringify(killCmd)}
|
|
||||||
running: true
|
|
||||||
onExited: (exitCode) => {
|
|
||||||
if (exitCode === 0) {
|
|
||||||
console.log("Process killed successfully:", ${pid})
|
|
||||||
} else {
|
|
||||||
console.warn("Failed to kill process:", ${pid}, "exit code:", exitCode)
|
|
||||||
}
|
|
||||||
destroy()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`, root);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -438,6 +426,21 @@ Singleton {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Process {
|
||||||
|
id: processKiller
|
||||||
|
command: ["bash", "-c", "kill " + root.killPid]
|
||||||
|
running: false
|
||||||
|
|
||||||
|
onExited: (exitCode) => {
|
||||||
|
if (exitCode === 0) {
|
||||||
|
console.log("Process killed successfully:", root.killPid)
|
||||||
|
} else {
|
||||||
|
console.warn("Failed to kill process:", root.killPid, "exit code:", exitCode)
|
||||||
|
}
|
||||||
|
root.killPid = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: processTimer
|
id: processTimer
|
||||||
|
|
||||||
|
|||||||
@@ -13,11 +13,20 @@ Singleton {
|
|||||||
property var wifiNetworks: []
|
property var wifiNetworks: []
|
||||||
property var savedWifiNetworks: []
|
property var savedWifiNetworks: []
|
||||||
property bool isScanning: false
|
property bool isScanning: false
|
||||||
property string connectionStatus: "" // "cosnnecting", "connected", "failed", ""
|
property string connectionStatus: "" // "connecting", "connected", "failed", "invalid_password", ""
|
||||||
property string connectingSSID: ""
|
property string connectingSSID: ""
|
||||||
|
property string lastConnectionError: ""
|
||||||
|
property bool passwordDialogShouldReopen: false
|
||||||
// Auto-refresh timer for when control center is open
|
// Auto-refresh timer for when control center is open
|
||||||
property bool autoRefreshEnabled: false
|
property bool autoRefreshEnabled: false
|
||||||
|
|
||||||
|
signal networksUpdated()
|
||||||
|
|
||||||
|
// Network info properties
|
||||||
|
property string networkInfoSSID: ""
|
||||||
|
property string networkInfoDetails: ""
|
||||||
|
property bool networkInfoLoading: false
|
||||||
|
|
||||||
function scanWifi() {
|
function scanWifi() {
|
||||||
if (root.isScanning)
|
if (root.isScanning)
|
||||||
return ;
|
return ;
|
||||||
@@ -33,107 +42,49 @@ Singleton {
|
|||||||
console.log("Connecting to WiFi:", ssid);
|
console.log("Connecting to WiFi:", ssid);
|
||||||
root.connectionStatus = "connecting";
|
root.connectionStatus = "connecting";
|
||||||
root.connectingSSID = ssid;
|
root.connectingSSID = ssid;
|
||||||
let connectProcess = Qt.createQmlObject(`
|
ToastService.showInfo("Connecting to " + ssid + "...");
|
||||||
import Quickshell.Io
|
wifiConnector.running = true;
|
||||||
Process {
|
|
||||||
command: ["bash", "-c", "nmcli dev wifi connect \\"' + ssid + '\\" || nmcli connection up \\"' + ssid + '\\"; if [ $? -eq 0 ]; then nmcli connection modify \\"' + ssid + '\\" connection.autoconnect-priority 50; nmcli connection down \\"' + ssid + '\\"; nmcli connection up \\"' + ssid + '\\"; fi"]
|
|
||||||
running: true
|
|
||||||
onExited: (exitCode) => {
|
|
||||||
console.log("WiFi connection result:", exitCode)
|
|
||||||
if (exitCode === 0) {
|
|
||||||
root.connectionStatus = "connected"
|
|
||||||
console.log("Connected to WiFi successfully")
|
|
||||||
// Set user preference to WiFi when manually connecting
|
|
||||||
NetworkService.setNetworkPreference("wifi")
|
|
||||||
// Force network status refresh after successful connection
|
|
||||||
NetworkService.delayedRefreshNetworkStatus()
|
|
||||||
} else {
|
|
||||||
root.connectionStatus = "failed"
|
|
||||||
console.log("WiFi connection failed")
|
|
||||||
}
|
|
||||||
scanWifi()
|
|
||||||
|
|
||||||
statusResetTimer.start()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stderr: SplitParser {
|
property string wifiPassword: ""
|
||||||
splitMarker: "\\n"
|
|
||||||
onRead: (data) => {
|
|
||||||
console.log("WiFi connection stderr:", data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`, root);
|
|
||||||
}
|
|
||||||
|
|
||||||
function connectToWifiWithPassword(ssid, password) {
|
function connectToWifiWithPassword(ssid, password) {
|
||||||
console.log("Connecting to WiFi with password:", ssid);
|
console.log("Connecting to WiFi with password:", ssid);
|
||||||
root.connectionStatus = "connecting";
|
root.connectionStatus = "connecting";
|
||||||
root.connectingSSID = ssid;
|
root.connectingSSID = ssid;
|
||||||
let connectProcess = Qt.createQmlObject(`
|
root.wifiPassword = password;
|
||||||
import Quickshell.Io
|
root.lastConnectionError = "";
|
||||||
Process {
|
root.passwordDialogShouldReopen = false;
|
||||||
command: ["bash", "-c", "nmcli dev wifi connect \\"' + ssid + '\\" password \\"' + password + '\\"; if [ $? -eq 0 ]; then nmcli connection modify \\"' + ssid + '\\" connection.autoconnect-priority 50; nmcli connection down \\"' + ssid + '\\"; nmcli connection up \\"' + ssid + '\\"; fi"]
|
ToastService.showInfo("Connecting to " + ssid + "...");
|
||||||
running: true
|
wifiPasswordConnector.running = true;
|
||||||
onExited: (exitCode) => {
|
|
||||||
console.log("WiFi connection with password result:", exitCode)
|
|
||||||
if (exitCode === 0) {
|
|
||||||
root.connectionStatus = "connected"
|
|
||||||
console.log("Connected to WiFi with password successfully")
|
|
||||||
// Set user preference to WiFi when manually connecting
|
|
||||||
NetworkService.setNetworkPreference("wifi")
|
|
||||||
// Force network status refresh after successful connection
|
|
||||||
NetworkService.delayedRefreshNetworkStatus()
|
|
||||||
} else {
|
|
||||||
root.connectionStatus = "failed"
|
|
||||||
console.log("WiFi connection with password failed")
|
|
||||||
}
|
|
||||||
scanWifi()
|
|
||||||
|
|
||||||
statusResetTimer.start()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stderr: SplitParser {
|
function disconnectWifi() {
|
||||||
splitMarker: "\\n"
|
console.log("Disconnecting from current WiFi network");
|
||||||
onRead: (data) => {
|
wifiDisconnector.running = true;
|
||||||
console.log("WiFi connection with password stderr:", data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`, root);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
property string forgetSSID: ""
|
||||||
|
|
||||||
function forgetWifiNetwork(ssid) {
|
function forgetWifiNetwork(ssid) {
|
||||||
console.log("Forgetting WiFi network:", ssid);
|
console.log("Forgetting WiFi network:", ssid);
|
||||||
let forgetProcess = Qt.createQmlObject(`
|
root.forgetSSID = ssid;
|
||||||
import Quickshell.Io
|
wifiForget.running = true;
|
||||||
Process {
|
|
||||||
command: ["bash", "-c", "nmcli connection delete \\"' + ssid + '\\" || nmcli connection delete id \\"' + ssid + '\\""]
|
|
||||||
running: true
|
|
||||||
onExited: (exitCode) => {
|
|
||||||
console.log("WiFi forget result:", exitCode)
|
|
||||||
if (exitCode === 0) {
|
|
||||||
console.log("Successfully forgot WiFi network:", "' + ssid + '")
|
|
||||||
} else {
|
|
||||||
console.log("Failed to forget WiFi network:", "' + ssid + '")
|
|
||||||
}
|
|
||||||
scanWifi()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stderr: SplitParser {
|
function fetchNetworkInfo(ssid) {
|
||||||
splitMarker: "\\n"
|
console.log("Fetching network info for:", ssid);
|
||||||
onRead: (data) => {
|
root.networkInfoSSID = ssid;
|
||||||
console.log("WiFi forget stderr:", data)
|
root.networkInfoLoading = true;
|
||||||
}
|
root.networkInfoDetails = "Loading network information...";
|
||||||
}
|
wifiInfoFetcher.running = true;
|
||||||
}
|
|
||||||
`, root);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateCurrentWifiInfo() {
|
function updateCurrentWifiInfo() {
|
||||||
currentWifiInfo.running = true;
|
currentWifiInfo.running = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Process {
|
Process {
|
||||||
id: currentWifiInfo
|
id: currentWifiInfo
|
||||||
|
|
||||||
@@ -245,6 +196,7 @@ Singleton {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: fallbackTimer
|
id: fallbackTimer
|
||||||
|
|
||||||
@@ -273,4 +225,304 @@ Singleton {
|
|||||||
onTriggered: root.scanWifi()
|
onTriggered: root.scanWifi()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WiFi Connection Process
|
||||||
|
Process {
|
||||||
|
id: wifiConnector
|
||||||
|
command: ["bash", "-c", "timeout 30 nmcli dev wifi connect \"" + root.connectingSSID + "\" || nmcli connection up \"" + root.connectingSSID + "\"; exit_code=$?; echo \"nmcli exit code: $exit_code\" >&2; if [ $exit_code -eq 0 ]; then nmcli connection modify \"" + root.connectingSSID + "\" connection.autoconnect-priority 50; sleep 2; if nmcli -t -f ACTIVE,SSID dev wifi | grep -q \"^yes:" + root.connectingSSID + "\"; then echo \"Connection verified\" >&2; exit 0; else echo \"Connection failed verification\" >&2; exit 4; fi; else exit $exit_code; fi"]
|
||||||
|
running: false
|
||||||
|
|
||||||
|
stderr: StdioCollector {
|
||||||
|
onStreamFinished: {
|
||||||
|
console.log("WiFi connection debug output:", text.trim())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onExited: (exitCode) => {
|
||||||
|
console.log("WiFi connection result:", exitCode)
|
||||||
|
if (exitCode === 0) {
|
||||||
|
root.connectionStatus = "connected"
|
||||||
|
root.passwordDialogShouldReopen = false
|
||||||
|
console.log("Connected to WiFi successfully")
|
||||||
|
ToastService.showInfo("Connected to " + root.connectingSSID)
|
||||||
|
NetworkService.setNetworkPreference("wifi")
|
||||||
|
NetworkService.delayedRefreshNetworkStatus()
|
||||||
|
|
||||||
|
// Immediately update savedWifiNetworks to include the new connection
|
||||||
|
if (!root.savedWifiNetworks.some((saved) => saved.ssid === root.connectingSSID)) {
|
||||||
|
let updatedSaved = [...root.savedWifiNetworks];
|
||||||
|
updatedSaved.push({"ssid": root.connectingSSID, "saved": true});
|
||||||
|
root.savedWifiNetworks = updatedSaved;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update wifiNetworks to reflect the change
|
||||||
|
let updatedNetworks = [...root.wifiNetworks];
|
||||||
|
for (let i = 0; i < updatedNetworks.length; i++) {
|
||||||
|
if (updatedNetworks[i].ssid === root.connectingSSID) {
|
||||||
|
updatedNetworks[i].saved = true;
|
||||||
|
updatedNetworks[i].connected = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
root.wifiNetworks = updatedNetworks;
|
||||||
|
} else if (exitCode === 4) {
|
||||||
|
// Connection failed - likely needs password for saved network
|
||||||
|
root.connectionStatus = "invalid_password"
|
||||||
|
root.passwordDialogShouldReopen = true
|
||||||
|
console.log("Saved network connection failed - password required")
|
||||||
|
ToastService.showError("Authentication failed for " + root.connectingSSID)
|
||||||
|
} else {
|
||||||
|
root.connectionStatus = "failed"
|
||||||
|
console.log("WiFi connection failed")
|
||||||
|
ToastService.showError("Failed to connect to " + root.connectingSSID)
|
||||||
|
}
|
||||||
|
scanWifi()
|
||||||
|
statusResetTimer.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// WiFi Connection with Password Process
|
||||||
|
Process {
|
||||||
|
id: wifiPasswordConnector
|
||||||
|
command: ["bash", "-c", "nmcli connection delete \"" + root.connectingSSID + "\" 2>/dev/null || true; timeout 30 nmcli dev wifi connect \"" + root.connectingSSID + "\" password \"" + root.wifiPassword + "\"; exit_code=$?; echo \"nmcli exit code: $exit_code\" >&2; if [ $exit_code -eq 0 ]; then nmcli connection modify \"" + root.connectingSSID + "\" connection.autoconnect-priority 50; sleep 2; if nmcli -t -f ACTIVE,SSID dev wifi | grep -q \"^yes:" + root.connectingSSID + "\"; then echo \"Connection verified\" >&2; exit 0; else echo \"Connection failed verification\" >&2; exit 4; fi; else exit $exit_code; fi"]
|
||||||
|
running: false
|
||||||
|
|
||||||
|
stdout: StdioCollector {
|
||||||
|
onStreamFinished: {
|
||||||
|
if (text.trim()) {
|
||||||
|
console.log("WiFi connection stdout:", text.trim())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stderr: StdioCollector {
|
||||||
|
onStreamFinished: {
|
||||||
|
root.lastConnectionError = text.trim()
|
||||||
|
console.log("WiFi connection debug output:", text.trim())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onExited: (exitCode) => {
|
||||||
|
console.log("WiFi connection with password result:", exitCode)
|
||||||
|
console.log("Error output:", root.lastConnectionError)
|
||||||
|
|
||||||
|
if (exitCode === 0) {
|
||||||
|
root.connectionStatus = "connected"
|
||||||
|
root.passwordDialogShouldReopen = false
|
||||||
|
console.log("Connected to WiFi with password successfully")
|
||||||
|
ToastService.showInfo("Connected to " + root.connectingSSID)
|
||||||
|
NetworkService.setNetworkPreference("wifi")
|
||||||
|
NetworkService.delayedRefreshNetworkStatus()
|
||||||
|
|
||||||
|
// Immediately update savedWifiNetworks to include the new connection
|
||||||
|
if (!root.savedWifiNetworks.some((saved) => saved.ssid === root.connectingSSID)) {
|
||||||
|
let updatedSaved = [...root.savedWifiNetworks];
|
||||||
|
updatedSaved.push({"ssid": root.connectingSSID, "saved": true});
|
||||||
|
root.savedWifiNetworks = updatedSaved;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update wifiNetworks to reflect the change
|
||||||
|
let updatedNetworks = [...root.wifiNetworks];
|
||||||
|
for (let i = 0; i < updatedNetworks.length; i++) {
|
||||||
|
if (updatedNetworks[i].ssid === root.connectingSSID) {
|
||||||
|
updatedNetworks[i].saved = true;
|
||||||
|
updatedNetworks[i].connected = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
root.wifiNetworks = updatedNetworks;
|
||||||
|
} else if (exitCode === 4) {
|
||||||
|
// Connection activation failed - likely invalid credentials
|
||||||
|
if (root.lastConnectionError.includes("Secrets were required") ||
|
||||||
|
root.lastConnectionError.includes("authentication") ||
|
||||||
|
root.lastConnectionError.includes("AUTH_TIMED_OUT")) {
|
||||||
|
root.connectionStatus = "invalid_password"
|
||||||
|
root.passwordDialogShouldReopen = true
|
||||||
|
console.log("Invalid password detected")
|
||||||
|
ToastService.showError("Invalid password for " + root.connectingSSID)
|
||||||
|
} else {
|
||||||
|
root.connectionStatus = "failed"
|
||||||
|
console.log("Connection failed - not password related")
|
||||||
|
ToastService.showError("Failed to connect to " + root.connectingSSID)
|
||||||
|
}
|
||||||
|
} else if (exitCode === 3 || exitCode === 124) {
|
||||||
|
root.connectionStatus = "failed"
|
||||||
|
console.log("Connection timed out")
|
||||||
|
ToastService.showError("Connection to " + root.connectingSSID + " timed out")
|
||||||
|
} else {
|
||||||
|
root.connectionStatus = "failed"
|
||||||
|
console.log("WiFi connection with password failed")
|
||||||
|
ToastService.showError("Failed to connect to " + root.connectingSSID)
|
||||||
|
}
|
||||||
|
root.wifiPassword = "" // Clear password
|
||||||
|
scanWifi()
|
||||||
|
statusResetTimer.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// WiFi Disconnect Process
|
||||||
|
Process {
|
||||||
|
id: wifiDisconnector
|
||||||
|
command: ["bash", "-c", "WIFI_DEV=$(nmcli -t -f DEVICE,TYPE device | grep wifi | cut -d: -f1 | head -1); [ -n \"$WIFI_DEV\" ] && nmcli device disconnect \"$WIFI_DEV\""]
|
||||||
|
running: false
|
||||||
|
|
||||||
|
onExited: (exitCode) => {
|
||||||
|
console.log("WiFi disconnect result:", exitCode)
|
||||||
|
if (exitCode === 0) {
|
||||||
|
console.log("Successfully disconnected from WiFi")
|
||||||
|
ToastService.showInfo("Disconnected from WiFi")
|
||||||
|
root.currentWifiSSID = ""
|
||||||
|
root.connectionStatus = ""
|
||||||
|
NetworkService.refreshNetworkStatus()
|
||||||
|
} else {
|
||||||
|
console.log("Failed to disconnect from WiFi")
|
||||||
|
ToastService.showError("Failed to disconnect from WiFi")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stderr: SplitParser {
|
||||||
|
splitMarker: "\\n"
|
||||||
|
onRead: (data) => {
|
||||||
|
console.log("WiFi disconnect stderr:", data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WiFi Forget Network Process
|
||||||
|
Process {
|
||||||
|
id: wifiForget
|
||||||
|
command: ["bash", "-c", "nmcli connection delete \"" + root.forgetSSID + "\" || nmcli connection delete id \"" + root.forgetSSID + "\""]
|
||||||
|
running: false
|
||||||
|
|
||||||
|
onExited: (exitCode) => {
|
||||||
|
console.log("WiFi forget result:", exitCode)
|
||||||
|
if (exitCode === 0) {
|
||||||
|
console.log("Successfully forgot WiFi network:", root.forgetSSID)
|
||||||
|
ToastService.showInfo("Forgot network \"" + root.forgetSSID + "\"")
|
||||||
|
|
||||||
|
// If we forgot the currently connected network, clear connection status
|
||||||
|
if (root.forgetSSID === root.currentWifiSSID) {
|
||||||
|
root.currentWifiSSID = "";
|
||||||
|
root.connectionStatus = "";
|
||||||
|
NetworkService.refreshNetworkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update savedWifiNetworks to remove the forgotten network
|
||||||
|
root.savedWifiNetworks = root.savedWifiNetworks.filter((saved) => {
|
||||||
|
return saved.ssid !== root.forgetSSID;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update wifiNetworks - create new array with updated objects
|
||||||
|
let updatedNetworks = [];
|
||||||
|
for (let i = 0; i < root.wifiNetworks.length; i++) {
|
||||||
|
let network = root.wifiNetworks[i];
|
||||||
|
if (network.ssid === root.forgetSSID) {
|
||||||
|
let updatedNetwork = Object.assign({}, network);
|
||||||
|
updatedNetwork.saved = false;
|
||||||
|
updatedNetwork.connected = false;
|
||||||
|
updatedNetworks.push(updatedNetwork);
|
||||||
|
} else {
|
||||||
|
updatedNetworks.push(network);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
root.wifiNetworks = updatedNetworks;
|
||||||
|
root.networksUpdated();
|
||||||
|
} else {
|
||||||
|
console.log("Failed to forget WiFi network:", root.forgetSSID)
|
||||||
|
ToastService.showError("Failed to forget network \"" + root.forgetSSID + "\"")
|
||||||
|
}
|
||||||
|
root.forgetSSID = "" // Clear SSID
|
||||||
|
}
|
||||||
|
|
||||||
|
stderr: SplitParser {
|
||||||
|
splitMarker: "\\n"
|
||||||
|
onRead: (data) => {
|
||||||
|
console.log("WiFi forget stderr:", data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WiFi Network Info Fetcher Process - Using detailed nmcli output
|
||||||
|
Process {
|
||||||
|
id: wifiInfoFetcher
|
||||||
|
command: ["nmcli", "-t", "-f", "SSID,SIGNAL,SECURITY,FREQ,RATE,MODE,CHAN,WPA-FLAGS,RSN-FLAGS", "dev", "wifi", "list"]
|
||||||
|
running: false
|
||||||
|
|
||||||
|
stdout: StdioCollector {
|
||||||
|
onStreamFinished: {
|
||||||
|
let details = "";
|
||||||
|
if (text.trim()) {
|
||||||
|
let lines = text.trim().split('\n');
|
||||||
|
for (let line of lines) {
|
||||||
|
let parts = line.split(':');
|
||||||
|
if (parts.length >= 9 && parts[0] === root.networkInfoSSID) {
|
||||||
|
let ssid = parts[0] || "Unknown";
|
||||||
|
let signal = parts[1] || "0";
|
||||||
|
let security = parts[2] || "Open";
|
||||||
|
let freq = parts[3] || "Unknown";
|
||||||
|
let rate = parts[4] || "Unknown";
|
||||||
|
let mode = parts[5] || "Unknown";
|
||||||
|
let channel = parts[6] || "Unknown";
|
||||||
|
let wpaFlags = parts[7] || "";
|
||||||
|
let rsnFlags = parts[8] || "";
|
||||||
|
|
||||||
|
// Determine band from frequency
|
||||||
|
let band = "Unknown";
|
||||||
|
let freqNum = parseInt(freq);
|
||||||
|
if (freqNum >= 2400 && freqNum <= 2500) {
|
||||||
|
band = "2.4 GHz";
|
||||||
|
} else if (freqNum >= 5000 && freqNum <= 6000) {
|
||||||
|
band = "5 GHz";
|
||||||
|
} else if (freqNum >= 6000) {
|
||||||
|
band = "6 GHz";
|
||||||
|
}
|
||||||
|
|
||||||
|
details = "Network Name: " + ssid + "\\n";
|
||||||
|
details += "Signal Strength: " + signal + "%\\n";
|
||||||
|
details += "Security: " + (security === "" ? "Open" : security) + "\\n";
|
||||||
|
details += "Frequency: " + freq + " MHz\\n";
|
||||||
|
details += "Band: " + band + "\\n";
|
||||||
|
details += "Channel: " + channel + "\\n";
|
||||||
|
details += "Mode: " + mode + "\\n";
|
||||||
|
details += "Max Rate: " + rate + " Mbit/s\\n";
|
||||||
|
|
||||||
|
if (wpaFlags !== "") {
|
||||||
|
details += "WPA Flags: " + wpaFlags + "\\n";
|
||||||
|
}
|
||||||
|
if (rsnFlags !== "") {
|
||||||
|
details += "RSN Flags: " + rsnFlags + "\\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (details === "") {
|
||||||
|
details = "Network information not found or network not available.";
|
||||||
|
}
|
||||||
|
|
||||||
|
root.networkInfoDetails = details;
|
||||||
|
root.networkInfoLoading = false;
|
||||||
|
console.log("Network info fetched for:", root.networkInfoSSID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onExited: (exitCode) => {
|
||||||
|
root.networkInfoLoading = false;
|
||||||
|
if (exitCode !== 0) {
|
||||||
|
console.log("Failed to fetch network info, exit code:", exitCode);
|
||||||
|
root.networkInfoDetails = "Failed to fetch network information";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stderr: SplitParser {
|
||||||
|
splitMarker: "\\n"
|
||||||
|
onRead: (data) => {
|
||||||
|
console.log("WiFi info stderr:", data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user