diff options
Diffstat (limited to 'modules/system/quickshell/Wifi.qml')
| -rw-r--r-- | modules/system/quickshell/Wifi.qml | 397 |
1 files changed, 397 insertions, 0 deletions
diff --git a/modules/system/quickshell/Wifi.qml b/modules/system/quickshell/Wifi.qml new file mode 100644 index 0000000..0ce57a0 --- /dev/null +++ b/modules/system/quickshell/Wifi.qml @@ -0,0 +1,397 @@ +import QtQuick +import QtQuick.Layouts +import Quickshell +import Quickshell.Networking +import Quickshell.Widgets + +Item { + id: root + + Scope { + id: internal + + readonly property var device: { + if (!Networking.devices) return null + for (const d of Networking.devices.values || []) { + if (d && d.scannerEnabled !== undefined) return d + } + return null + } + + readonly property var allNetworks: device?.networks ? device.networks.values : [] + + property bool hasKnown: false + property bool hasOther: false + property var activeNetwork: null + + function updateState() { + let known = false + let other = false + let active = null + + for (const n of internal.allNetworks) { + if (n?.connected) active = n + if (n?.known) known = true + if (n && !n.known && n.name) other = true + } + + internal.hasKnown = known + internal.hasOther = other + internal.activeNetwork = active + } + + Connections { + target: internal.device ? internal.device.networks : null + function onValuesChanged() { internal.updateState() } + } + } + + function _getWifiIcon(strength) { + if (!Networking.wifiEnabled) return "network-wireless-offline-symbolic" + if (!internal.activeNetwork && !internal.device?.enabled) return "network-wireless-offline-symbolic" + + const s = strength ?? 0 + if (s >= 0.75) return "network-wireless-signal-excellent-symbolic" + if (s >= 0.50) return "network-wireless-signal-good-symbolic" + if (s >= 0.25) return "network-wireless-signal-ok-symbolic" + if (s > 0) return "network-wireless-signal-weak-symbolic" + return "network-wireless-signal-none-symbolic" + } + + function _onNetworkClick(net) { + if (!net) return + if (net.connected) { net.disconnect(); return } + if (net.stateChanging) return + if (net.known) { + net.connect() + } else if ((net.security ?? 0) === 0) { + net.connect() + } else { + pskPrompt.network = net + pskPrompt.open(net.name ?? "") + } + } + + width: childrenRect.width + height: parent.height + + Component.onCompleted: internal.updateState() + + Row { + anchors { + verticalCenter: parent.verticalCenter + } + spacing: 4 + Image { + width: 20 + height: 20 + source: Quickshell.iconPath(root._getWifiIcon(internal.activeNetwork?.signalStrength ?? 0)) + sourceSize: Qt.size(width, height) + smooth: true + mipmap: true + opacity: !Networking.wifiEnabled ? 0.35 : 1.0 + } + } + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.LeftButton | Qt.RightButton + onClicked: mouse => { + if (mouse.button === Qt.LeftButton) { + GlobalState.toggle("Wifi") + } else if (mouse.button === Qt.RightButton) { + Networking.wifiEnabled = !Networking.wifiEnabled + } + } + } + + PopupWindow { + id: popup + visible: GlobalState.activePopup === "Wifi" + grabFocus: true + implicitWidth: card.width + implicitHeight: card.height + + anchor { + window: barWindow + item: root + edges: Edges.Bottom + gravity: Edges.Bottom + margins.top: Theme.popupGap + } + + color: Theme.transparent + + onVisibleChanged: { + if (visible) { + anchor.updateAnchor() + if (internal.device) internal.device.scannerEnabled = true + } else if (internal.device?.scannerEnabled) { + internal.device.scannerEnabled = false + } + } + + PopupCard { + id: card + margins: 16 + + // Wi-Fi { Toggle } + RowLayout { + Layout.fillWidth: true + Layout.topMargin: 4 + Layout.bottomMargin: 4 + Layout.leftMargin: 8 + Layout.rightMargin: 8 + spacing: 8 + + Text { + text: "Wi-Fi" + color: Theme.text + font { + family: Theme.mainFont + pixelSize: 14 + weight: Font.DemiBold + } + } + + Item { Layout.fillWidth: true } + + Toggle { + checked: Networking.wifiEnabled + enabled: Networking.wifiHardwareEnabled + onToggled: Networking.wifiEnabled = !Networking.wifiEnabled + } + } + + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: 1 + color: Theme.border + Layout.topMargin: 4 + Layout.bottomMargin: 4 + } + + // Known Network Header + Text { + visible: internal.hasKnown + text: "Known Network" + color: Theme.textMuted + font { + family: Theme.mainFont + pixelSize: 12 + weight: Font.DemiBold + } + Layout.leftMargin: 8 + Layout.topMargin: 2 + Layout.bottomMargin: 2 + } + + // Known networks + ColumnLayout { + Layout.fillWidth: true + spacing: 2 + visible: internal.hasKnown + + Repeater { + model: internal.device?.networks ?? null + + delegate: Squircle { + id: knownItem + required property var modelData + readonly property bool isConnected: knownItem.modelData?.connected ?? false + visible: knownItem.modelData?.known ?? false + Layout.fillWidth: true + Layout.preferredHeight: visible ? 36 : 0 + fillColor: knownArea.containsMouse ? Theme.surface : Theme.transparent + cornerRadius: 6 + + Connections { + target: knownItem.modelData + function onKnownChanged() { internal.updateState() } + function onConnectedChanged() { internal.updateState() } + function onNameChanged() { internal.updateState() } + function onSignalStrengthChanged() { internal.updateState() } + } + + MouseArea { + id: knownArea + anchors.fill: parent + hoverEnabled: true + enabled: !(knownItem.modelData?.stateChanging ?? false) + onClicked: root._onNetworkClick(knownItem.modelData) + } + + RowLayout { + anchors { + fill: parent + leftMargin: 8 + rightMargin: 8 + } + spacing: 12 + + IconCircle { + size: 24 + source: root._getWifiIcon(knownItem.modelData?.signalStrength ?? 0) + active: knownItem.isConnected + } + + Text { + Layout.fillWidth: true + text: knownItem.modelData?.name ?? "" + color: Theme.text + font { + family: Theme.mainFont + pixelSize: 13 + } + elide: Text.ElideRight + } + + IconImage { + visible: (knownItem.modelData?.security ?? 0) !== 0 + Layout.preferredWidth: 14 + Layout.preferredHeight: 14 + source: Quickshell.iconPath("changes-prevent-symbolic") + } + } + } + } + } + + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: 1 + color: Theme.border + Layout.topMargin: 4 + Layout.bottomMargin: 4 + } + + // Other Networks Header + RowLayout { + Layout.fillWidth: true + Layout.leftMargin: 8 + Layout.rightMargin: 8 + Layout.topMargin: 2 + Layout.bottomMargin: 2 + + Text { + text: "Other Networks" + color: Theme.textMuted + font { + family: Theme.mainFont + pixelSize: 12 + weight: Font.DemiBold + } + Layout.fillWidth: true + } + + Image { + id: refreshIcon + width: 14 + height: 14 + source: Quickshell.iconPath("view-refresh-symbolic") + sourceSize: Qt.size(width, height) + opacity: refreshMouse.containsMouse ? 1.0 : 0.6 + + RotationAnimation on rotation { + running: internal.device?.scannerEnabled ?? false + from: 0 + to: 360 + duration: 1000 + loops: Animation.Infinite + } + + MouseArea { + id: refreshMouse + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + if (internal.device) { + internal.device.scannerEnabled = !internal.device.scannerEnabled + } + } + } + } + } + + // Other networks list + ColumnLayout { + Layout.fillWidth: true + spacing: 2 + visible: internal.hasOther + + Repeater { + model: internal.device?.networks ?? null + + delegate: Squircle { + id: otherItem + required property var modelData + visible: !(otherItem.modelData?.known ?? true) && (otherItem.modelData?.name ?? "") !== "" + Layout.fillWidth: true + Layout.preferredHeight: visible ? 36 : 0 + fillColor: otherArea.containsMouse ? Theme.surface : Theme.transparent + cornerRadius: 6 + + Connections { + target: otherItem.modelData + function onKnownChanged() { internal.updateState() } + function onNameChanged() { internal.updateState() } + function onSignalStrengthChanged() { internal.updateState() } + } + + MouseArea { + id: otherArea + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + enabled: !(otherItem.modelData?.stateChanging ?? false) + onClicked: root._onNetworkClick(otherItem.modelData) + } + + RowLayout { + anchors { + fill: parent + leftMargin: 8 + rightMargin: 8 + } + spacing: 12 + + IconCircle { + size: 24 + source: root._getWifiIcon(otherItem.modelData?.signalStrength ?? 0) + active: false + } + + Text { + Layout.fillWidth: true + text: otherItem.modelData?.name ?? "" + color: Theme.text + opacity: 0.8 + font { + family: Theme.mainFont + pixelSize: 13 + } + elide: Text.ElideRight + } + + IconImage { + visible: (otherItem.modelData?.security ?? 0) !== 0 + Layout.preferredWidth: 14 + Layout.preferredHeight: 14 + source: Quickshell.iconPath("changes-prevent-symbolic") + } + } + } + } + } + } + } + + WifiPasswordPrompt { + id: pskPrompt + property var network: null + onSubmitted: (text, remember) => { + if (network) network.connectWithPsk(text) + } + } +} |
