From 79762e41931e695f8132028e80eb4dff53e3e0f1 Mon Sep 17 00:00:00 2001
From: end-4 <97237370+end-4@users.noreply.github.com>
Date: Sat, 22 Nov 2025 00:19:53 +0100
Subject: [PATCH] waffle: action center: wifi menu (without auth)
---
.../icons/fluent/lock-closed-filled.svg | 1 +
.../ii/assets/icons/fluent/lock-closed.svg | 1 +
.../assets/icons/fluent/lock-open-filled.svg | 1 +
.../ii/assets/icons/fluent/lock-open.svg | 1 +
.../quickshell/ii/modules/common/Config.qml | 2 +-
.../sidebarRight/wifiNetworks/WifiDialog.qml | 13 +-
.../toggles/ActionCenterToggleButton.qml | 2 +-
.../ActionCenterTogglesDelegateChooser.qml | 18 ++-
.../actionCenter/wifi/WWifiNetworkItem.qml | 114 +++++++++++++++++
.../waffle/actionCenter/wifi/WifiControl.qml | 121 ++++++++++++++++++
.../ii/modules/waffle/looks/Looks.qml | 12 +-
.../ii/modules/waffle/looks/WButton.qml | 38 ++++--
.../ii/modules/waffle/looks/WChoiceButton.qml | 12 +-
.../ii/modules/waffle/looks/WIcons.qml | 18 ++-
.../quickshell/ii/services/Network.qml | 7 +
15 files changed, 313 insertions(+), 48 deletions(-)
create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/lock-closed-filled.svg
create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/lock-closed.svg
create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/lock-open-filled.svg
create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/lock-open.svg
create mode 100644 dots/.config/quickshell/ii/modules/waffle/actionCenter/wifi/WWifiNetworkItem.qml
create mode 100644 dots/.config/quickshell/ii/modules/waffle/actionCenter/wifi/WifiControl.qml
diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/lock-closed-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/lock-closed-filled.svg
new file mode 100644
index 000000000..31b51d02a
--- /dev/null
+++ b/dots/.config/quickshell/ii/assets/icons/fluent/lock-closed-filled.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/lock-closed.svg b/dots/.config/quickshell/ii/assets/icons/fluent/lock-closed.svg
new file mode 100644
index 000000000..93966ef08
--- /dev/null
+++ b/dots/.config/quickshell/ii/assets/icons/fluent/lock-closed.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/lock-open-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/lock-open-filled.svg
new file mode 100644
index 000000000..f7ebddd49
--- /dev/null
+++ b/dots/.config/quickshell/ii/assets/icons/fluent/lock-open-filled.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/lock-open.svg b/dots/.config/quickshell/ii/assets/icons/fluent/lock-open.svg
new file mode 100644
index 000000000..a1d5a9592
--- /dev/null
+++ b/dots/.config/quickshell/ii/assets/icons/fluent/lock-open.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/dots/.config/quickshell/ii/modules/common/Config.qml b/dots/.config/quickshell/ii/modules/common/Config.qml
index 83dc5a87b..66b76a2ca 100644
--- a/dots/.config/quickshell/ii/modules/common/Config.qml
+++ b/dots/.config/quickshell/ii/modules/common/Config.qml
@@ -581,7 +581,7 @@ Singleton {
property bool leftAlignApps: false
}
property JsonObject actionCenter: JsonObject {
- property list toggles: [ "network", "bluetooth", "easyEffects", "powerProfile", "idleInhibitor", "nightLight", "darkMode", "antiFlashbang", "cloudflareWarp", "mic", "audio", "musicRecognition", "notifications", "onScreenKeyboard", "gameMode", "screenSnip", "colorPicker" ]
+ property list toggles: [ "network", "bluetooth", "easyEffects", "powerProfile", "idleInhibitor", "nightLight", "darkMode", "antiFlashbang", "cloudflareWarp", "mic", "musicRecognition", "notifications", "onScreenKeyboard", "gameMode", "screenSnip", "colorPicker" ]
}
}
}
diff --git a/dots/.config/quickshell/ii/modules/ii/sidebarRight/wifiNetworks/WifiDialog.qml b/dots/.config/quickshell/ii/modules/ii/sidebarRight/wifiNetworks/WifiDialog.qml
index 36683c353..f1de76a58 100644
--- a/dots/.config/quickshell/ii/modules/ii/sidebarRight/wifiNetworks/WifiDialog.qml
+++ b/dots/.config/quickshell/ii/modules/ii/sidebarRight/wifiNetworks/WifiDialog.qml
@@ -37,21 +37,12 @@ WindowDialog {
spacing: 0
model: ScriptModel {
- values: [...Network.wifiNetworks].sort((a, b) => {
- if (a.active && !b.active)
- return -1;
- if (!a.active && b.active)
- return 1;
- return b.strength - a.strength;
- })
+ values: Network.friendlyWifiNetworks
}
delegate: WifiNetworkItem {
required property WifiAccessPoint modelData
wifiNetwork: modelData
- anchors {
- left: parent?.left
- right: parent?.right
- }
+ width: ListView.view.width
}
}
WindowDialogSeparator {}
diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/toggles/ActionCenterToggleButton.qml b/dots/.config/quickshell/ii/modules/waffle/actionCenter/toggles/ActionCenterToggleButton.qml
index 3f2dc224b..41da6f7cd 100644
--- a/dots/.config/quickshell/ii/modules/waffle/actionCenter/toggles/ActionCenterToggleButton.qml
+++ b/dots/.config/quickshell/ii/modules/waffle/actionCenter/toggles/ActionCenterToggleButton.qml
@@ -27,7 +27,7 @@ ColumnLayout {
property var mainAction: toggleModel?.mainAction ?? null
property var altAction: toggleModel?.hasMenu ? (() => root.openMenu()) : (toggleModel?.altAction ?? null)
property bool hasMenu: toggleModel?.hasMenu ?? false
- property Item menu
+ property Component menu
property color colBackground: toggled ? Looks.colors.accent : Looks.colors.bg2
property color colBackgroundHovered: toggled ? Looks.colors.accentHover : Looks.colors.bg2Hover
diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/toggles/ActionCenterTogglesDelegateChooser.qml b/dots/.config/quickshell/ii/modules/waffle/actionCenter/toggles/ActionCenterTogglesDelegateChooser.qml
index e74ddaf0c..7f7c2b47b 100644
--- a/dots/.config/quickshell/ii/modules/waffle/actionCenter/toggles/ActionCenterTogglesDelegateChooser.qml
+++ b/dots/.config/quickshell/ii/modules/waffle/actionCenter/toggles/ActionCenterTogglesDelegateChooser.qml
@@ -1,13 +1,15 @@
pragma ComponentBehavior: Bound
+
+import QtQuick
+import QtQuick.Layouts
+import Quickshell
import qs
import qs.services
import qs.modules.common
import qs.modules.common.models.quickToggles
import qs.modules.common.widgets
import qs.modules.waffle.looks
-import QtQuick
-import QtQuick.Layouts
-import Quickshell
+import qs.modules.waffle.actionCenter.wifi
DelegateChooser {
id: root
@@ -21,13 +23,6 @@ DelegateChooser {
icon: "flash-off"
}
}
- DelegateChoice {
- roleValue: "audio"
- ActionCenterToggleButton {
- toggleModel: AudioToggle {}
- icon: "speaker-2"
- }
- }
DelegateChoice {
roleValue: "bluetooth"
ActionCenterToggleButton {
@@ -98,6 +93,9 @@ DelegateChooser {
toggleModel: NetworkToggle {}
name: toggleModel.statusText
icon: WIcons.internetIcon
+ menu: Component {
+ WifiControl {}
+ }
}
}
DelegateChoice {
diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/wifi/WWifiNetworkItem.qml b/dots/.config/quickshell/ii/modules/waffle/actionCenter/wifi/WWifiNetworkItem.qml
new file mode 100644
index 000000000..0c1b93c8c
--- /dev/null
+++ b/dots/.config/quickshell/ii/modules/waffle/actionCenter/wifi/WWifiNetworkItem.qml
@@ -0,0 +1,114 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import Quickshell
+import qs
+import qs.services
+import qs.services.network
+import qs.modules.common
+import qs.modules.common.functions
+import qs.modules.common.widgets
+import qs.modules.waffle.looks
+import qs.modules.waffle.actionCenter
+
+WChoiceButton {
+ id: root
+ required property WifiAccessPoint wifiNetwork
+
+ property bool expanded: false
+ checked: expanded
+ clip: true
+
+ horizontalPadding: 12
+ verticalPadding: 6
+ animateChoiceHighlight: false
+
+ Behavior on implicitHeight {
+ animation: Looks.transition.resize.createObject(this)
+ }
+
+ onClicked: expanded = !expanded
+
+ contentItem: RowLayout {
+ id: contentItem
+ spacing: 12
+
+ FluentIcon { // Duotone hack
+ Layout.bottomMargin: 2
+ Layout.alignment: Qt.AlignTop
+ property int strength: root.wifiNetwork?.strength ?? 0
+ icon: "wifi-1"
+ implicitSize: 30
+ color: Looks.colors.inactiveIcon
+
+ FluentIcon { // Signal
+ property int strength: root.wifiNetwork?.strength ?? 0
+ icon: WIcons.wifiIconForStrength(strength)
+ implicitSize: 30
+
+ FluentIcon { // Security
+ anchors {
+ right: parent.right
+ bottom: parent.bottom
+ }
+ visible: root?.wifiNetwork?.isSecure ?? false
+ icon: "lock-closed"
+ filled: true
+ implicitSize: 14
+ }
+ }
+ }
+
+ ColumnLayout {
+ Layout.topMargin: statusText.visible ? 4 : 7
+ Layout.bottomMargin: 4
+ Layout.alignment: Qt.AlignTop
+ Layout.fillWidth: true
+ spacing: 1
+
+ Behavior on Layout.topMargin {
+ animation: Looks.transition.move.createObject(this)
+ }
+
+ WText { // Network name
+ Layout.fillWidth: true
+ elide: Text.ElideRight
+ font.pixelSize: Looks.font.pixelSize.large
+ text: root.wifiNetwork?.ssid ?? Translation.tr("Unknown")
+ }
+ WText { // Status
+ id: statusText
+ Layout.fillWidth: true
+ elide: Text.ElideRight
+ text: root.wifiNetwork?.active ? Translation.tr("Connected") : root.wifiNetwork?.isSecure ? Translation.tr("Secured") : Translation.tr("Not secured")
+ font.pixelSize: Looks.font.pixelSize.large
+ color: Looks.colors.subfg
+ visible: root.wifiNetwork?.active || root.expanded
+ Behavior on opacity {
+ animation: Looks.transition.opacity.createObject(this)
+ }
+ }
+
+ WButton {
+ Layout.alignment: Qt.AlignRight
+ horizontalAlignment: Text.AlignHCenter
+ visible: root.expanded
+ checked: !(root.wifiNetwork?.active ?? false)
+ colBackgroundHover: Looks.colors.bg2Hover
+ colBackgroundActive: Looks.colors.bg2Active
+ inset: 0
+ implicitHeight: 30
+ implicitWidth: 148
+ text: root.wifiNetwork?.active ? Translation.tr("Disconnect") : Translation.tr("Connect")
+
+ onClicked: {
+ if (root.wifiNetwork?.active) {
+ Network.disconnectWifiNetwork();
+ } else {
+ Network.connectToWifiNetwork(root.wifiNetwork);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/wifi/WifiControl.qml b/dots/.config/quickshell/ii/modules/waffle/actionCenter/wifi/WifiControl.qml
new file mode 100644
index 000000000..ce6a2e6a7
--- /dev/null
+++ b/dots/.config/quickshell/ii/modules/waffle/actionCenter/wifi/WifiControl.qml
@@ -0,0 +1,121 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+import Qt5Compat.GraphicalEffects
+import Quickshell
+import qs
+import qs.services
+import qs.services.network
+import qs.modules.common
+import qs.modules.common.functions
+import qs.modules.common.widgets
+import qs.modules.waffle.looks
+import qs.modules.waffle.actionCenter
+
+Item {
+ id: root
+ implicitWidth: 360
+ implicitHeight: 352
+
+ Component.onCompleted: {
+ Network.rescanWifi();
+ }
+
+ PageColumn {
+ anchors.fill: parent
+
+ BodyRectangle {
+ implicitHeight: 400
+ implicitWidth: 50
+
+ ColumnLayout {
+ anchors.fill: parent
+ anchors.margins: 4
+ spacing: 4
+
+ ColumnLayout {
+ implicitHeight: headerRow.implicitHeight
+ Layout.fillWidth: true
+ spacing: 0
+ HeaderRow {
+ id: headerRow
+ Layout.fillWidth: true
+ title: qsTr("Wi-Fi")
+ }
+ FadeLoader {
+ Layout.leftMargin: -4
+ Layout.rightMargin: -4
+ Layout.fillWidth: true
+ shown: Network.wifiScanning
+ sourceComponent: StyledIndeterminateProgressBar {
+ id: progressBar
+ implicitHeight: 3
+ background: null
+ layer.enabled: true
+ layer.effect: OpacityMask {
+ maskSource: Rectangle {
+ width: progressBar.width
+ height: progressBar.height
+ radius: progressBar.height / 2
+ }
+ }
+ }
+ }
+ }
+
+ StyledListView {
+ id: listView
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ animateAppearance: false
+
+ contentHeight: contentLayout.implicitHeight
+ contentWidth: width
+ clip: true
+ spacing: 4
+
+ model: ScriptModel {
+ values: Network.friendlyWifiNetworks
+ }
+ delegate: WWifiNetworkItem {
+ required property WifiAccessPoint modelData
+ wifiNetwork: modelData
+ width: ListView.view.width
+ }
+ }
+ }
+ }
+
+ Separator {}
+
+ FooterRectangle {
+ WButton {
+ id: moreSettingsButton
+ anchors {
+ verticalCenter: parent.verticalCenter
+ left: parent.left
+ }
+ inset: 0
+ implicitHeight: 40
+ implicitWidth: contentItem.implicitWidth + 30
+ color: "transparent"
+
+ onClicked: {
+ Quickshell.execDetached(["qs", "-p", Quickshell.shellPath(""), "ipc", "call", "sidebarLeft", "toggle"]);
+ Quickshell.execDetached(["bash", "-c", Config.options.apps.network]);
+ }
+
+ contentItem: Item {
+ anchors.centerIn: parent
+ implicitWidth: buttonText.implicitWidth
+ WText {
+ id: buttonText
+ anchors.centerIn: parent
+ text: qsTr("More Internet settings")
+ color: moreSettingsButton.pressed ? Looks.colors.fg : Looks.colors.fg1
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml b/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml
index e62657636..5ef0031f8 100644
--- a/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml
+++ b/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml
@@ -39,8 +39,10 @@ Singleton {
property color bg2Hover: root.dark ? "#383838" : "#FDFDFD"
property color bg2Active: root.dark ? "#333333" : "#FDFDFD"
property color bg2Border: root.dark ? "#464646" : "#EEEEEE"
+ property color subfg: root.dark ? "#CED1D7" : "#5C5C5C"
property color fg: root.dark ? "#FFFFFF" : "#000000"
property color fg1: root.dark ? "#D1D1D1" : "#626262"
+ property color inactiveIcon: root.dark ? "#494949" : "#C4C4C4"
property color controlBg: root.dark ? "#9B9B9B" : "#868686"
property color controlFg: root.dark ? "#454545" : "#FFFFFF"
property color danger: "#C42B1C"
@@ -103,13 +105,21 @@ Singleton {
}
property Component opacity: Component {
- NumberAnimation{
+ NumberAnimation {
duration: 120
easing.type: Easing.BezierSpline
easing.bezierCurve: transition.easing.bezierCurve.easeIn
}
}
+ property Component resize: Component { // TODO: better curve needed
+ NumberAnimation {
+ duration: 200
+ easing.type: Easing.BezierSpline
+ easing.bezierCurve: transition.easing.bezierCurve.easeIn
+ }
+ }
+
property Component enter: Component {
NumberAnimation {
duration: 250
diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WButton.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WButton.qml
index d158c5949..76cf00e49 100644
--- a/dots/.config/quickshell/ii/modules/waffle/looks/WButton.qml
+++ b/dots/.config/quickshell/ii/modules/waffle/looks/WButton.qml
@@ -16,6 +16,7 @@ Button {
property color colBackgroundToggledHover: Looks.colors.accentHover
property color colBackgroundToggledActive: Looks.colors.accentActive
property color colForeground: Looks.colors.fg
+ property color colForegroundToggled: Looks.colors.accentFg
property alias backgroundOpacity: backgroundRect.opacity
property color color: {
if (root.checked) {
@@ -35,25 +36,32 @@ Button {
return root.colBackground;
}
}
+ property color fgColor: root.checked ? root.colForegroundToggled : root.colForeground
+ property alias horizontalAlignment: buttonText.horizontalAlignment
+ font {
+ family: Looks.font.family.ui
+ pixelSize: Looks.font.pixelSize.large
+ weight: Looks.font.weight.regular
+ }
// Hover stuff
- signal hoverTimedOut()
+ signal hoverTimedOut
property bool shouldShowTooltip: false
property Timer hoverTimer: Timer {
id: hoverTimer
running: root.hovered
interval: 400
onTriggered: {
- root.hoverTimedOut()
+ root.hoverTimedOut();
}
}
onHoverTimedOut: {
- root.shouldShowTooltip = true
+ root.shouldShowTooltip = true;
}
onHoveredChanged: {
if (!root.hovered) {
- root.shouldShowTooltip = false
- root.hoverTimer.stop()
+ root.shouldShowTooltip = false;
+ root.hoverTimer.stop();
}
}
@@ -92,10 +100,13 @@ Button {
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.RightButton | Qt.MiddleButton
- onClicked: (event) => {
- if (event.button === Qt.LeftButton) root.clicked();
- if (event.button === Qt.RightButton) root.altAction();
- if (event.button === Qt.MiddleButton) root.middleClickAction();
+ onClicked: event => {
+ if (event.button === Qt.LeftButton)
+ root.clicked();
+ if (event.button === Qt.RightButton)
+ root.altAction();
+ if (event.button === Qt.MiddleButton)
+ root.middleClickAction();
}
}
@@ -122,18 +133,17 @@ Button {
Layout.fillWidth: false
Layout.alignment: Qt.AlignVCenter
icon: root.icon.name
- color: root.colForeground
+ color: root.fgColor
visible: root.icon.name !== ""
}
WText {
+ id: buttonText
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
text: root.text
horizontalAlignment: Text.AlignLeft
- font {
- pixelSize: Looks.font.pixelSize.large
- }
- color: root.colForeground
+ font: root.font
+ color: root.fgColor
}
}
}
diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WChoiceButton.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WChoiceButton.qml
index 60de3d816..cd0d66d59 100644
--- a/dots/.config/quickshell/ii/modules/waffle/looks/WChoiceButton.qml
+++ b/dots/.config/quickshell/ii/modules/waffle/looks/WChoiceButton.qml
@@ -11,6 +11,8 @@ import qs.modules.waffle.looks
WButton {
id: root
+ property bool animateChoiceHighlight: true
+
Layout.fillWidth: true
implicitWidth: contentItem.implicitWidth
horizontalPadding: 10
@@ -18,7 +20,7 @@ WButton {
inset: 0
buttonSpacing: 8
- property color color: {
+ color: {
if (root.checked) {
if (root.down) {
return root.colBackgroundHover;
@@ -36,6 +38,7 @@ WButton {
return root.colBackground;
}
}
+ fgColor: colForeground
background: Rectangle {
id: backgroundRect
@@ -54,11 +57,14 @@ WButton {
implicitHeight: 3
radius: width / 2
color: Looks.colors.accent
+ property bool forceZeroHeight: true
+ height: forceZeroHeight ? 0 : Math.max(16, root.background.height - 18 * 2)
Component.onCompleted: {
- implicitHeight = 16;
+ forceZeroHeight = false;
}
- Behavior on implicitHeight {
+ Behavior on height {
+ enabled: root.animateChoiceHighlight
animation: Looks.transition.opacity.createObject(this)
}
}
diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WIcons.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WIcons.qml
index 42836fbc7..6a5814bbc 100644
--- a/dots/.config/quickshell/ii/modules/waffle/looks/WIcons.qml
+++ b/dots/.config/quickshell/ii/modules/waffle/looks/WIcons.qml
@@ -7,18 +7,22 @@ import qs.services
Singleton {
id: root
+ function wifiIconForStrength(strength) {
+ if (strength > 75)
+ return "wifi-1";
+ if (strength > 50)
+ return "wifi-2";
+ if (strength > 25)
+ return "wifi-3";
+ return "wifi-4";
+ }
+
property string internetIcon: {
if (Network.ethernet)
return "ethernet";
if (Network.wifiEnabled) {
const strength = Network.networkStrength;
- if (strength > 75)
- return "wifi-1";
- if (strength > 50)
- return "wifi-2";
- if (strength > 25)
- return "wifi-3";
- return "wifi-4";
+ return wifiIconForStrength(strength);
}
if (Network.wifiStatus === "connecting")
return "wifi-4";
diff --git a/dots/.config/quickshell/ii/services/Network.qml b/dots/.config/quickshell/ii/services/Network.qml
index 7d16a9450..69bd5337d 100644
--- a/dots/.config/quickshell/ii/services/Network.qml
+++ b/dots/.config/quickshell/ii/services/Network.qml
@@ -23,6 +23,13 @@ Singleton {
property WifiAccessPoint wifiConnectTarget
readonly property list wifiNetworks: []
readonly property WifiAccessPoint active: wifiNetworks.find(n => n.active) ?? null
+ readonly property list friendlyWifiNetworks: [...wifiNetworks].sort((a, b) => {
+ if (a.active && !b.active)
+ return -1;
+ if (!a.active && b.active)
+ return 1;
+ return b.strength - a.strength;
+ })
property string wifiStatus: "disconnected"
property string networkName: ""