feat: add password overlay for network popout + redesign
C++ / fmt (pull_request) Successful in 4s
JS/TS / fmt (pull_request) Successful in 11s
JS/TS / lint (pull_request) Successful in 24s
Python / fmt (pull_request) Successful in 30s
Python / lint (pull_request) Successful in 34s
Python / test (pull_request) Successful in 53s
C++ / build (pull_request) Successful in 1m52s
Rust / fmt (pull_request) Successful in 1m14s
Rust / build (pull_request) Successful in 1m57s
Rust / clippy (pull_request) Successful in 2m0s
Python / buildcheck (pull_request) Successful in 2m37s
C++ / clang-tidy (pull_request) Successful in 3m43s

This commit is contained in:
2026-07-09 17:33:13 +02:00
parent f9eeb793db
commit a64eadd14a
13 changed files with 665 additions and 123 deletions
+222 -87
View File
@@ -3,6 +3,7 @@ pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import Quickshell
import qs.Modules.SysTray.Popouts.Network
import qs.Components
import qs.Config
import qs.Helpers
@@ -13,7 +14,6 @@ CustomClippingRect {
required property var wrapper
anchors.horizontalCenter: parent.horizontalCenter
color: DynamicColors.tPalette.m3surfaceContainer
implicitHeight: networkPopContent.height + networks.implicitHeight + networkPopContent.anchors.margins + networks.anchors.margins * 2
implicitWidth: 500 + 8 * 2
radius: (20 - Appearance.padding.small) * Appearance.rounding.scale
@@ -28,6 +28,7 @@ CustomClippingRect {
anchors.margins: Appearance.padding.large
anchors.right: parent.right
anchors.top: parent.top
spacing: Appearance.spacing.extraSmall / 2
CustomText {
Layout.preferredHeight: visible ? implicitHeight : 0
@@ -39,28 +40,17 @@ CustomClippingRect {
Toggle {
Layout.preferredHeight: visible ? implicitHeight : 0
checked: Network.wifiEnabled
label: qsTr("WiFi enabled")
first: true
last: connected.count === 0
subtext: qsTr("Toggle WiFi on or off")
text: qsTr("Set WiFi")
toggle.onToggled: Network.setWifi(checked)
}
}
ColumnLayout {
id: networks
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.margins: Appearance.padding.normal
anchors.right: parent.right
anchors.top: networkPopContent.bottom
CustomText {
Layout.leftMargin: Appearance.padding.normal
text: qsTr("Connected")
visible: Network.connectedNetworks.length > 0
onToggled: Network.setWifi(checked)
}
Repeater {
id: connected
visible: Network.connectedNetworks.length > 0
model: ScriptModel {
@@ -70,20 +60,40 @@ CustomClippingRect {
}
NetworkItem {
first: false
known: true
repeater: connected
}
}
}
ColumnLayout {
id: networks
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.margins: Appearance.padding.large
anchors.right: parent.right
anchors.top: networkPopContent.bottom
spacing: Appearance.spacing.extraSmall / 2
Spacer {
repeater: known
visible: Network.connectedNetworks.length > 0
}
CustomText {
Layout.leftMargin: Appearance.padding.normal
text: qsTr("Known networks")
Layout.preferredHeight: visible ? implicitHeight : 0
color: DynamicColors.palette.m3onSurfaceVariant
font.pointSize: Appearance.font.size.small
text: Network.knownNetworks.length > 1 ? qsTr("%1 known networks available").arg(Network.knownNetworks.length) : qsTr("1 known network available") // qmllint disable missing-property
visible: Network.knownNetworks.length > 0
}
Repeater {
id: known
visible: Network.knownNetworks.length > 0
model: ScriptModel {
@@ -93,28 +103,27 @@ CustomClippingRect {
}
NetworkItem {
known: true
repeater: known
}
}
Spacer {
repeater: unknown
visible: Network.knownNetworks.length > 0
}
CustomText {
Layout.leftMargin: Appearance.padding.normal
text: qsTr("Networks")
}
CustomText {
Layout.leftMargin: Appearance.padding.normal
Layout.preferredHeight: visible ? implicitHeight : 0
color: DynamicColors.palette.m3onSurfaceVariant
font.pointSize: Appearance.font.size.extraSmall
text: qsTr("%1 networks available").arg(Network.networks.length) // qmllint disable missing-property
font.pointSize: Appearance.font.size.small
text: Network.unknownNetworks.length > 1 ? qsTr("%1 networks available").arg(Network.unknownNetworks.length) : qsTr("1 network available") // qmllint disable missing-property
visible: Network.unknownNetworks.length > 0
}
Repeater {
id: networkRepeater
id: unknown
model: ScriptModel {
values: [...Network.unknownNetworks].sort((a, b) => {
@@ -123,101 +132,227 @@ CustomClippingRect {
}
NetworkItem {
known: false
repeater: unknown
}
}
}
component NetworkItem: CustomRect {
id: knownNetworkItem
NetworkOverlay {
anchors.fill: parent
}
component ConnectedRect: CustomRect {
id: bg
property bool first
property bool last
bottomLeftRadius: last ? Appearance.rounding.large : Appearance.rounding.extraSmall
bottomRightRadius: last ? Appearance.rounding.large : Appearance.rounding.extraSmall
color: DynamicColors.tPalette.m3surfaceContainer
topLeftRadius: first ? Appearance.rounding.large : Appearance.rounding.extraSmall
topRightRadius: first ? Appearance.rounding.large : Appearance.rounding.extraSmall
}
component NetworkItem: ConnectedRect {
id: networkItem
readonly property bool connecting: Network.connectingNetwork === modelData
property int horizontalPadding: Appearance.padding.largeIncreased
required property int index
readonly property bool isSecure: Network.isSecure(modelData.security)
required property bool known
required property var modelData
required property Repeater repeater
Layout.fillWidth: true
Layout.preferredHeight: visible ? knownNetworkRow.implicitHeight + Appearance.padding.smaller * 2 : 0
Layout.rightMargin: Appearance.padding.extraSmall
Layout.preferredHeight: visible ? rowLayout.implicitHeight + Appearance.padding.smaller * 2 : 0
first: index === 0
last: index === (repeater.count - 1)
radius: Appearance.rounding.small
RowLayout {
id: knownNetworkRow
id: rowLayout
anchors.fill: parent
anchors.leftMargin: Appearance.padding.larger
anchors.rightMargin: Appearance.padding.normal
opacity: 0
scale: 0.7
spacing: Appearance.spacing.small
anchors.leftMargin: networkItem.horizontalPadding
anchors.rightMargin: networkItem.horizontalPadding
Behavior on opacity {
Anim {
type: Anim.DefaultEffects
}
}
Behavior on scale {
Anim {
}
}
Column {
id: column
Component.onCompleted: {
opacity = 1;
scale = 1;
}
MaterialIcon {
color: knownNetworkItem.modelData.active ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurfaceVariant
text: Icons.getNetworkIcon(knownNetworkItem.modelData.signalStrength * 100, Network.isSecure(knownNetworkItem.modelData.security))
}
CustomText {
Layout.fillWidth: true
Layout.leftMargin: Appearance.spacing.extraSmall
Layout.rightMargin: Appearance.spacing.extraSmall
color: knownNetworkItem.modelData.active ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurface
elide: Text.ElideRight
text: knownNetworkItem.modelData.name
CustomText {
Layout.fillWidth: true
Layout.leftMargin: Appearance.spacing.extraSmall
Layout.rightMargin: Appearance.spacing.extraSmall
color: networkItem.modelData.active ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurface
elide: Text.ElideRight
text: networkItem.modelData.name
}
CustomText {
id: subtext
anchors.left: parent.left
anchors.right: parent.right
color: DynamicColors.palette.m3outline
elide: Text.ElideRight
font: {
const f = Qt.font(subtext.font);
f.pointSize = Appearance.font.size.small;
return f;
}
text: qsTr("%1 strength").arg((networkItem.modelData.signalStrength * 100) + "%")
}
}
CustomRect {
color: Qt.alpha(DynamicColors.palette.m3primary, knownNetworkItem.modelData.active ? 1 : 0)
implicitHeight: knownWirelessConnectIcon.implicitHeight + Appearance.padding.extraSmall
implicitWidth: implicitHeight
radius: Appearance.rounding.full
Item {
Layout.alignment: Qt.AlignRight
implicitHeight: icon.height
implicitWidth: icon.width
// CircularIndicator {
// anchors.fill: parent
// running: knownNetworkItem.loading
// }
MaterialIcon {
id: icon
color: networkItem.modelData.active ? DynamicColors.palette.m3primary : DynamicColors.palette.m3onSurfaceVariant
font.pointSize: Appearance.font.size.large
opacity: networkItem.connecting ? 0 : 1
text: Icons.getNetworkIcon(networkItem.modelData.signalStrength * 100, networkItem.isSecure)
Behavior on opacity {
Anim {
}
}
}
Loader {
active: opacity > 0
anchors.fill: parent
opacity: networkItem.connecting ? 1 : 0
Behavior on opacity {
Anim {
}
}
sourceComponent: CircularIndicator {
bgColor: "transparent"
running: true
}
}
}
}
StateLayer {
color: knownNetworkItem.modelData.active ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface
color: networkItem.modelData.active ? DynamicColors.palette.m3onPrimary : DynamicColors.palette.m3onSurface
onClicked: {
NetworkPassword.requestOpen();
if (networkItem.isSecure && !networkItem.known) {
PopupManager.requestOpen(Qt.resolvedUrl("Network/PSKOverlay.qml"), {
network: networkItem.modelData
});
} else {
Network.requestConnect(networkItem.modelData);
}
}
}
IconButton {
anchors.right: rowLayout.right
anchors.rightMargin: rowLayout.anchors.rightMargin + icon.width
anchors.verticalCenter: parent.verticalCenter
font.pointSize: Appearance.font.size.large
icon: "cancel"
implicitHeight: parent.height - Appearance.padding.larger * 2
type: IconButton.Text
visible: networkItem.known
onClicked: networkItem.modelData.forget()
}
}
component Spacer: Item {
id: spacer
Layout.preferredHeight: networkRepeater.count > 0 ? Appearance.spacing.extraSmall : 0
required property Repeater repeater
Layout.preferredHeight: repeater.count > 0 ? Appearance.spacing.extraSmall : 0
}
component Toggle: RowLayout {
property alias checked: toggle.checked
required property string label
property alias toggle: toggle
component Toggle: CustomSwitch {
id: toggleItem
readonly property alias bg: bg
property alias first: bg.first
property alias last: bg.last
property string subtext
Layout.fillWidth: true
Layout.rightMargin: Appearance.padding.extraSmall
spacing: Appearance.spacing.small
cLayer: 2
horizontalPadding: Appearance.padding.largeIncreased
implicitHeight: Math.max(implicitContentHeight, implicitIndicatorHeight) + verticalPadding * 2
implicitWidth: implicitContentWidth + implicitIndicatorWidth + horizontalPadding * 2
indicator.anchors.right: right
indicator.anchors.rightMargin: toggleItem.horizontalPadding
indicator.anchors.verticalCenter: verticalCenter
verticalPadding: Appearance.padding.normal
CustomText {
Layout.fillWidth: true
text: parent.label
background: ConnectedRect {
id: bg
StateLayer {
id: stateLayer
manualPressOverride: toggleItem.pressed
}
}
contentItem: Item {
anchors.left: parent.left
anchors.leftMargin: toggleItem.horizontalPadding
anchors.right: toggleItem.indicator.left
anchors.rightMargin: Appearance.spacing.normal
implicitHeight: column.implicitHeight
implicitWidth: column.implicitWidth
Column {
id: column
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: 0
CustomText {
id: label
anchors.left: parent.left
anchors.right: parent.right
elide: Text.ElideRight
font: {
const f = Qt.font(label.font);
f.pointSize = Appearance.font.size.smaller;
return f;
}
text: toggleItem.text
}
CustomText {
id: subtext
anchors.left: parent.left
anchors.right: parent.right
color: DynamicColors.palette.m3outline
elide: Text.ElideRight
font: {
const f = Qt.font(subtext.font);
f.pointSize = Appearance.font.size.small;
return f;
}
text: toggleItem.subtext
visible: toggleItem.subtext
}
}
}
CustomSwitch {
id: toggle
}
onPressed: stateLayer.press(stateLayer.mouseX, stateLayer.mouseY)
}
}