import QtQuick import QtQuick.Controls import Quickshell import Quickshell.Io import Quickshell.Widgets import qs.Common import qs.Services import qs.Widgets Rectangle { id: wifiContextMenuWindow property var networkData: null property bool menuVisible: false property var parentItem property var wifiPasswordModalRef property var networkInfoModalRef function show(x, y) { const menuWidth = 160; wifiContextMenuWindow.visible = true; Qt.callLater(() => { const menuHeight = wifiMenuColumn.implicitHeight + Theme.spacingS * 2; let finalX = x - menuWidth / 2; let finalY = y + 4; finalX = Math.max(Theme.spacingS, Math.min(finalX, parentItem.width - menuWidth - Theme.spacingS)); finalY = Math.max(Theme.spacingS, Math.min(finalY, parentItem.height - menuHeight - Theme.spacingS)); if (finalY + menuHeight > parentItem.height - Theme.spacingS) { finalY = y - menuHeight - 4; finalY = Math.max(Theme.spacingS, finalY); } wifiContextMenuWindow.x = finalX; wifiContextMenuWindow.y = finalY; 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 Component.onCompleted: { menuVisible = false; visible = false; } // 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) { WifiService.disconnectWifi(); } else { if (wifiContextMenuWindow.networkData.saved) { WifiService.connectToWifi(wifiContextMenuWindow.networkData.ssid); } else if (wifiContextMenuWindow.networkData.secured) { if (wifiPasswordModalRef) { wifiPasswordModalRef.wifiPasswordSSID = wifiContextMenuWindow.networkData.ssid; wifiPasswordModalRef.wifiPasswordInput = ""; wifiPasswordModalRef.wifiPasswordModalVisible = 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 && networkInfoModalRef) { networkInfoModalRef.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 } } }