pragma ComponentBehavior: Bound import QtQuick import QtQuick.Layouts import ZShell.Config import qs.Components import qs.Helpers.Network import qs.Services import qs.Modules.Settings.Common PageBase { id: root flickable.bottomMargin: Tokens.padding.extraExtraLarge * 2 // Extra scrolling space at the bottom isSubPage: true title: Tr.tr("All networks") ColumnLayout { anchors.horizontalCenter: parent.horizontalCenter anchors.top: parent.top // spacing: Tokens.spacing.extraSmall / 2 width: root.cappedWidth Timer { interval: 300 // GlobalConfig.nexus.networkRescanInterval repeat: true running: root.visible && Nmcli.wifiEnabled triggeredOnStart: true onTriggered: Nmcli.rescanWifi() } ConnectedRect { Layout.fillWidth: true first: true implicitHeight: headerLayout.implicitHeight + Tokens.padding.medium * 2 RowLayout { id: headerLayout anchors.left: parent.left anchors.leftMargin: Tokens.padding.largeIncreased anchors.right: parent.right anchors.rightMargin: Tokens.padding.large anchors.verticalCenter: parent.verticalCenter anchors.verticalCenterOffset: 1 // spacing: Tokens.spacing.extraSmall / 2 CustomText { font: Tokens.font.title.small text: Tr.trCtx("Filters", "network filter heading") } Item { Layout.fillWidth: true } FilterButton { id: savedFilter function internalFilter(ap: Nmcli.AccessPoint): bool { return Nmcli.hasSavedProfile(ap.ssid); } bottomLeftRadius: pressed ? pressedRadius : implicitHeight / 2 text: Tr.trCtx("Saved", "network filter") topLeftRadius: pressed ? pressedRadius : implicitHeight / 2 Behavior on bottomLeftRadius { Anim { type: Anim.DefaultEffects } } Behavior on topLeftRadius { Anim { type: Anim.DefaultEffects } } } FilterButton { id: secureFilter function internalFilter(ap: Nmcli.AccessPoint): bool { return ap.security !== "none"; } text: Tr.trCtx("Secured", "network filter") } FilterButton { id: highFreqFilter function internalFilter(ap: Nmcli.AccessPoint): bool { return ap.frequency >= 4900 && ap.frequency <= 5900; } text: Tr.trCtx("5 GHz", "network filter") } FilterButton { id: lowFreqFilter function internalFilter(ap: Nmcli.AccessPoint): bool { return ap.frequency >= 2400 && ap.frequency <= 2500; } bottomRightRadius: pressed ? pressedRadius : implicitHeight / 2 text: Tr.trCtx("2.4 GHz", "network filter") topRightRadius: pressed ? pressedRadius : implicitHeight / 2 Behavior on bottomRightRadius { Anim { type: Anim.DefaultEffects } } Behavior on topRightRadius { Anim { type: Anim.DefaultEffects } } } } } NetworkList { id: networkList function networkFilter(ap: Nmcli.AccessPoint): bool { return savedFilter.filter(ap) && secureFilter.filter(ap) && highFreqFilter.filter(ap) && lowFreqFilter.filter(ap); } enableFilter: true last: true // nState: root.nState } } component FilterButton: IconTextButton { id: filter property int filterState // 0 = default, 1 = on, 2 = negate function filter(ap: Nmcli.AccessPoint): bool { if (filterState === 0) return true; if (filterState === 1) return internalFilter(ap); return !internalFilter(ap); } function internalFilter(ap: Nmcli.AccessPoint): bool { return true; } defaultRadius: filterState > 0 ? implicitHeight / 2 : Tokens.rounding.small icon: ["check_indeterminate_small", "check", "close"][filterState] // inactiveColour: [Colours.palette.m3secondaryContainer, Colours.palette.m3secondary, Colours.palette.m3tertiary][filterState] // inactiveOnColour: [Colours.palette.m3onSecondaryContainer, Colours.palette.m3onSecondary, Colours.palette.m3onTertiary][filterState] pressedRadius: Tokens.rounding.extraSmall // spacing: Tokens.spacing.extraSmall onClicked: filterState = (filterState + 1) % 3 } component NetworkList: ItemList { id: netList property bool enableFilter property int limit: 0 // 0 = show all // required property NexusState nState signal networkSelected(ap: Nmcli.AccessPoint) function networkFilter(ap: Nmcli.AccessPoint): bool { return true; } extraHeight: Nmcli.scanning ? Tokens.rounding.extraSmall : 0 // Inline so it isn't affected by anim list.anchors.top: scanningIndicator.bottom placeholderIcon: Nmcli.wifiEnabled ? "wifi_find" : "signal_wifi_off" placeholderText: Nmcli.wifiEnabled ? Tr.tr("No networks found") : Tr.tr("Wi-Fi disabled") showList: Nmcli.wifiEnabled delegate: StateLayer { id: network property bool currentSelected required property int index required property var modelData property real textOpacity: disabled ? 0.5 : 1 anchors.fill: undefined anchors.left: netList.list.contentItem.left anchors.right: netList.list.contentItem.right bottomLeftRadius: netList?.last && index === netList.list.count - 1 ? Tokens.rounding.extraLarge : radius bottomRightRadius: netList?.last && index === netList.list.count - 1 ? Tokens.rounding.extraLarge : radius // disabled: currentSelected || Nmcli.connectingSsid() === modelData.ssid implicitHeight: networkLayout.implicitHeight + networkLayout.anchors.margins * 2 radius: Tokens.rounding.extraSmall Behavior on textOpacity { Anim { type: Anim.DefaultEffects } } onClicked: { if (!modelData.active) { NetworkConnection.handleConnect(modelData); currentSelected = true; netList.networkSelected(modelData); } else { // Active network: open its detail/settings sub-page. netList.nState.selectedNetworkSsid = modelData.ssid; netList.nState.networkDetailsFromSaved = false; netList.nState.openSubPage(3); } } Connections { function onActiveChanged(): void { if (network.modelData.active) network.currentSelected = false; } target: network.modelData } Connections { function onNetworkSelected(ap: Nmcli.AccessPoint): void { if (ap !== network.modelData) network.currentSelected = false; } target: netList } RowLayout { id: networkLayout anchors.fill: parent anchors.leftMargin: Tokens.padding.extraLarge anchors.margins: Tokens.padding.large anchors.rightMargin: Tokens.padding.extraLarge // spacing: Tokens.spacing.extraSmall MaterialIcon { color: network.modelData.active ? Colours.palette.m3primary : Colours.palette.m3onSurfaceVariant font.pointSize: Tokens.font.size.normal opacity: network.textOpacity text: Icons.getNetworkIcon(network.modelData.strength) } ColumnLayout { Layout.fillWidth: true opacity: network.textOpacity // spacing: 0 CustomText { Layout.fillWidth: true elide: Text.ElideRight font: Tokens.font.body.small text: network.modelData.ssid } CustomText { Layout.fillWidth: true color: Colours.palette.m3outline elide: Text.ElideRight font: Tokens.font.label.small text: { const net = network.modelData; let status = ""; if (net.active) status = Tr.trCtx("Connected", "network connected"); else if (Nmcli.hasSavedProfile(net.ssid)) status = Tr.trCtx("Saved", "network saved"); if (status) // TRANSLATORS: %1 = security type, %2 = connection status return Tr.trCtx("Security: %1 • %2", "network security and status").arg(net.security).arg(status); return Tr.trCtx("Security: %1", "network security").arg(net.security); } } } // Anim { // // sourceComp: Nmcli.connectingSsid() === network.modelData.ssid ? loadingComp : iconComp // // Component { // id: iconComp // // MaterialIcon { // color: network.modelData.active ? Colours.palette.m3primary : Colours.palette.m3onSurfaceVariant // fontStyle: Tokens.font.icon.medium // opacity: network.textOpacity // text: network.modelData.active ? "settings" : "lock" // } // } // // Component { // id: loadingComp // // LoadingIndicator { // implicitSize: Math.round(Tokens.font.icon.medium.pointSize * 1.3) // } // } // } } } // model: ScriptModel { // values: { // const connecting = Nmcli.connectingSsid(); // // Lower rank sorts higher in the list // const rank = n => n.active ? 0 : n.ssid === connecting ? 1 : Nmcli.hasSavedProfile(n.ssid) ? 2 : 3; // const sorted = [...Nmcli.networks].sort((a, b) => rank(a) - rank(b) || b.strength - a.strength); // if (netList.limit > 0 && sorted.length > netList.limit) // sorted.length = netList.limit; // return netList.enableFilter ? sorted.filter(netList.networkFilter) : sorted; // } // } // StyledProgressBar { // id: scanningIndicator // // anchors.left: parent.left // anchors.margins: 1 // anchors.right: parent.right // implicitHeight: Nmcli.scanning ? Tokens.rounding.extraSmall : 0 // indeterminate: true // // Behavior on implicitHeight { // Anim { // type: Anim.DefaultEffects // } // } // } } }