mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-06-15 15:45:20 -04:00
refactor(settings): split network settings into tabs (#2633)
This commit is contained in:
@@ -956,7 +956,7 @@ Item {
|
|||||||
|
|
||||||
function tabs(): string {
|
function tabs(): string {
|
||||||
if (!PopoutService.settingsModal)
|
if (!PopoutService.settingsModal)
|
||||||
return "wallpaper\ntheme\ntypography\ntime_weather\nsounds\ndankbar\ndankbar_settings\ndankbar_appearance\ndankbar_widgets\nframe\nworkspaces\ncompositor\nmedia_player\nnotifications\nosd\nrunning_apps\nupdater\ndock\nlauncher\nkeybinds\ndisplays\nnetwork\nprinters\nlock_screen\npower_sleep\nplugins\nabout";
|
return "wallpaper\ntheme\ntypography\ntime_weather\nsounds\ndankbar\ndankbar_settings\ndankbar_appearance\ndankbar_widgets\nframe\nworkspaces\ncompositor\nmedia_player\nnotifications\nosd\nrunning_apps\nupdater\ndock\nlauncher\nkeybinds\ndisplays\nnetwork\nnetwork_status\nnetwork_ethernet\nnetwork_wifi\nnetwork_vpn\nprinters\nlock_screen\npower_sleep\nplugins\nabout";
|
||||||
var modal = PopoutService.settingsModal;
|
var modal = PopoutService.settingsModal;
|
||||||
var ids = [];
|
var ids = [];
|
||||||
var structure = modal.sidebar?.categoryStructure ?? [];
|
var structure = modal.sidebar?.categoryStructure ?? [];
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import qs.Common
|
import qs.Common
|
||||||
import qs.Modules.Settings
|
import qs.Modules.Settings
|
||||||
|
import qs.Services
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
|
|
||||||
FocusScope {
|
FocusScope {
|
||||||
@@ -232,7 +233,52 @@ FocusScope {
|
|||||||
visible: active
|
visible: active
|
||||||
focus: active
|
focus: active
|
||||||
|
|
||||||
sourceComponent: NetworkTab {}
|
sourceComponent: NetworkStatusTab {}
|
||||||
|
|
||||||
|
onActiveChanged: {
|
||||||
|
if (active && item)
|
||||||
|
Qt.callLater(() => item.forceActiveFocus());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: networkEthernetLoader
|
||||||
|
anchors.fill: parent
|
||||||
|
active: root.currentIndex === 39
|
||||||
|
visible: active
|
||||||
|
focus: active
|
||||||
|
|
||||||
|
sourceComponent: NetworkEthernetTab {}
|
||||||
|
|
||||||
|
onActiveChanged: {
|
||||||
|
if (active && item)
|
||||||
|
Qt.callLater(() => item.forceActiveFocus());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: networkWifiLoader
|
||||||
|
anchors.fill: parent
|
||||||
|
active: root.currentIndex === 40
|
||||||
|
visible: active
|
||||||
|
focus: active
|
||||||
|
|
||||||
|
sourceComponent: NetworkWifiTab {}
|
||||||
|
|
||||||
|
onActiveChanged: {
|
||||||
|
if (active && item)
|
||||||
|
Qt.callLater(() => item.forceActiveFocus());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: networkVpnLoader
|
||||||
|
anchors.fill: parent
|
||||||
|
active: root.currentIndex === 41
|
||||||
|
visible: active
|
||||||
|
focus: active
|
||||||
|
|
||||||
|
sourceComponent: NetworkVpnTab {}
|
||||||
|
|
||||||
onActiveChanged: {
|
onActiveChanged: {
|
||||||
if (active && item)
|
if (active && item)
|
||||||
|
|||||||
@@ -53,20 +53,21 @@ FloatingWindow {
|
|||||||
visible = !visible;
|
visible = !visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setTabIndex(tabIndex: int) {
|
||||||
|
if (tabIndex < 0)
|
||||||
|
return;
|
||||||
|
currentTabIndex = tabIndex;
|
||||||
|
sidebar.autoExpandForTab(tabIndex);
|
||||||
|
}
|
||||||
|
|
||||||
function showWithTab(tabIndex: int) {
|
function showWithTab(tabIndex: int) {
|
||||||
if (tabIndex >= 0) {
|
setTabIndex(tabIndex);
|
||||||
currentTabIndex = tabIndex;
|
|
||||||
sidebar.autoExpandForTab(tabIndex);
|
|
||||||
}
|
|
||||||
visible = true;
|
visible = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function showWithTabName(tabName: string) {
|
function showWithTabName(tabName: string) {
|
||||||
var idx = sidebar.resolveTabIndex(tabName);
|
var idx = sidebar.resolveTabIndex(tabName);
|
||||||
if (idx >= 0) {
|
setTabIndex(idx);
|
||||||
currentTabIndex = idx;
|
|
||||||
sidebar.autoExpandForTab(idx);
|
|
||||||
}
|
|
||||||
visible = true;
|
visible = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -238,8 +238,33 @@ Rectangle {
|
|||||||
"id": "network",
|
"id": "network",
|
||||||
"text": I18n.tr("Network"),
|
"text": I18n.tr("Network"),
|
||||||
"icon": "wifi",
|
"icon": "wifi",
|
||||||
"tabIndex": 7,
|
"dmsOnly": true,
|
||||||
"dmsOnly": true
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "network_status",
|
||||||
|
"text": I18n.tr("Status"),
|
||||||
|
"icon": "lan",
|
||||||
|
"tabIndex": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "network_ethernet",
|
||||||
|
"text": I18n.tr("Ethernet"),
|
||||||
|
"icon": "settings_ethernet",
|
||||||
|
"tabIndex": 39
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "network_wifi",
|
||||||
|
"text": I18n.tr("WiFi"),
|
||||||
|
"icon": "wifi",
|
||||||
|
"tabIndex": 40
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "network_vpn",
|
||||||
|
"text": I18n.tr("VPN"),
|
||||||
|
"icon": "vpn_key",
|
||||||
|
"tabIndex": 41
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "applications",
|
"id": "applications",
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ Rectangle {
|
|||||||
iconColor: Theme.surfaceVariantText
|
iconColor: Theme.surfaceVariantText
|
||||||
onClicked: {
|
onClicked: {
|
||||||
PopoutService.closeControlCenter();
|
PopoutService.closeControlCenter();
|
||||||
PopoutService.openSettingsWithTab("network");
|
PopoutService.openSettingsWithTab(currentPreferenceIndex === 0 ? "network_ethernet" : "network_wifi");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,462 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import qs.Common
|
||||||
|
import qs.Modules.Settings.Widgets
|
||||||
|
import qs.Services
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: networkEthernetTab
|
||||||
|
|
||||||
|
LayoutMirroring.enabled: I18n.isRtl
|
||||||
|
LayoutMirroring.childrenInherit: true
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
NetworkService.addRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onDestruction: {
|
||||||
|
NetworkService.removeRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
DankFlickable {
|
||||||
|
anchors.fill: parent
|
||||||
|
clip: true
|
||||||
|
contentHeight: mainColumn.height + Theme.spacingXL
|
||||||
|
contentWidth: width
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: mainColumn
|
||||||
|
|
||||||
|
topPadding: 4
|
||||||
|
width: Math.min(600, parent.width - Theme.spacingL * 2)
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
spacing: Theme.spacingL
|
||||||
|
|
||||||
|
SettingsCard {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property string expandedEthDevice: ""
|
||||||
|
|
||||||
|
title: I18n.tr("Ethernet")
|
||||||
|
iconName: "settings_ethernet"
|
||||||
|
settingKey: "networkEthernet"
|
||||||
|
tags: ["ethernet", "wired", "network", "adapters", "connection"]
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: ethernetSection
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: {
|
||||||
|
const devices = NetworkService.ethernetDevices;
|
||||||
|
const connected = devices.filter(d => d.connected).length;
|
||||||
|
if (devices.length === 0)
|
||||||
|
return I18n.tr("No adapters");
|
||||||
|
if (connected === 0)
|
||||||
|
return devices.length === 1 ? I18n.tr("%1 adapter, none connected").arg(devices.length) : I18n.tr("%1 adapters, none connected").arg(devices.length);
|
||||||
|
return I18n.tr("%1 connected").arg(connected);
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: NetworkService.ethernetConnected ? Theme.primary : Theme.surfaceVariantText
|
||||||
|
width: parent.width
|
||||||
|
horizontalAlignment: Text.AlignLeft
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width
|
||||||
|
height: 1
|
||||||
|
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: parent.width
|
||||||
|
spacing: 4
|
||||||
|
visible: NetworkService.ethernetDevices.length > 0
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Adapters")
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
font.weight: Font.Medium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
width: parent.width
|
||||||
|
horizontalAlignment: Text.AlignLeft
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: NetworkService.ethernetDevices
|
||||||
|
|
||||||
|
delegate: Rectangle {
|
||||||
|
id: ethDeviceDelegate
|
||||||
|
required property var modelData
|
||||||
|
required property int index
|
||||||
|
|
||||||
|
readonly property bool isConnected: modelData.connected || false
|
||||||
|
readonly property bool isExpanded: root.expandedEthDevice === modelData.name
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
height: isExpanded ? 56 + ethExpandedContent.height : 56
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: ethDeviceMouseArea.containsMouse ? Theme.primaryHoverLight : Theme.surfaceLight
|
||||||
|
border.width: isConnected ? 2 : 0
|
||||||
|
border.color: Theme.primary
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
Behavior on height {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 150
|
||||||
|
easing.type: Easing.OutQuad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.fill: parent
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: parent.width
|
||||||
|
height: 56
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: Theme.spacingM
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.right: ethDeviceActions.left
|
||||||
|
anchors.rightMargin: Theme.spacingS
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "lan"
|
||||||
|
size: 20
|
||||||
|
color: isConnected ? Theme.primary : Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: 2
|
||||||
|
width: parent.width - 20 - Theme.spacingS
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: modelData.name || I18n.tr("Unknown")
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: isConnected ? Theme.primary : Theme.surfaceText
|
||||||
|
font.weight: isConnected ? Font.Medium : Font.Normal
|
||||||
|
elide: Text.ElideRight
|
||||||
|
width: parent.width
|
||||||
|
horizontalAlignment: Text.AlignLeft
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.left: parent.left
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: {
|
||||||
|
switch (modelData.state) {
|
||||||
|
case "activated":
|
||||||
|
return I18n.tr("Connected");
|
||||||
|
case "disconnected":
|
||||||
|
return I18n.tr("Disconnected");
|
||||||
|
case "unavailable":
|
||||||
|
return I18n.tr("Unavailable");
|
||||||
|
default:
|
||||||
|
return modelData.state || I18n.tr("Unknown");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: isConnected ? Theme.primary : Theme.surfaceVariantText
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: "•"
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
visible: (modelData.ip || "").length > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: modelData.ip || ""
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
visible: (modelData.ip || "").length > 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: ethDeviceActions
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: Theme.spacingS
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 28
|
||||||
|
height: 28
|
||||||
|
radius: 14
|
||||||
|
color: ethExpandBtn.containsMouse ? Theme.surfacePressed : "transparent"
|
||||||
|
visible: isConnected
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
name: isExpanded ? "expand_less" : "expand_more"
|
||||||
|
size: 18
|
||||||
|
color: Theme.surfaceText
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: ethExpandBtn
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
if (isExpanded) {
|
||||||
|
root.expandedEthDevice = "";
|
||||||
|
} else {
|
||||||
|
root.expandedEthDevice = modelData.name;
|
||||||
|
NetworkService.fetchWiredNetworkInfo(NetworkService.ethernetConnectionUuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 28
|
||||||
|
height: 28
|
||||||
|
radius: 14
|
||||||
|
color: ethDisconnectBtn.containsMouse ? Theme.errorHover : "transparent"
|
||||||
|
visible: isConnected
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
name: "link_off"
|
||||||
|
size: 18
|
||||||
|
color: ethDisconnectBtn.containsMouse ? Theme.error : Theme.surfaceVariantText
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: ethDisconnectBtn
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: NetworkService.disconnectEthernetDevice(modelData.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: ethDeviceMouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.rightMargin: ethDeviceActions.width + Theme.spacingM
|
||||||
|
hoverEnabled: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: ethExpandedContent
|
||||||
|
width: parent.width
|
||||||
|
visible: isExpanded
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width - Theme.spacingM * 2
|
||||||
|
height: 1
|
||||||
|
x: Theme.spacingM
|
||||||
|
color: Theme.outlineLight
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: parent.width
|
||||||
|
height: ethDetailsColumn.implicitHeight + Theme.spacingM * 2
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: ethDetailsColumn
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Theme.spacingM
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
Flow {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: {
|
||||||
|
const fields = [];
|
||||||
|
const dev = modelData;
|
||||||
|
if (!dev)
|
||||||
|
return fields;
|
||||||
|
|
||||||
|
if (dev.ip)
|
||||||
|
fields.push({
|
||||||
|
label: I18n.tr("IP"),
|
||||||
|
value: dev.ip
|
||||||
|
});
|
||||||
|
if (dev.speed && dev.speed > 0)
|
||||||
|
fields.push({
|
||||||
|
label: I18n.tr("Speed"),
|
||||||
|
value: dev.speed + " Mbps"
|
||||||
|
});
|
||||||
|
if (dev.hwAddress)
|
||||||
|
fields.push({
|
||||||
|
label: I18n.tr("MAC"),
|
||||||
|
value: dev.hwAddress
|
||||||
|
});
|
||||||
|
if (dev.driver)
|
||||||
|
fields.push({
|
||||||
|
label: I18n.tr("Driver"),
|
||||||
|
value: dev.driver
|
||||||
|
});
|
||||||
|
fields.push({
|
||||||
|
label: I18n.tr("State"),
|
||||||
|
value: dev.state || I18n.tr("Unknown")
|
||||||
|
});
|
||||||
|
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: Rectangle {
|
||||||
|
required property var modelData
|
||||||
|
required property int index
|
||||||
|
|
||||||
|
width: ethFieldContent.width + Theme.spacingM * 2
|
||||||
|
height: 32
|
||||||
|
radius: Theme.cornerRadius - 2
|
||||||
|
color: Theme.surfaceContainerHigh
|
||||||
|
border.width: 1
|
||||||
|
border.color: Theme.outlineLight
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: ethFieldContent
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: modelData.label + ":"
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: modelData.value
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Medium
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: parent.width
|
||||||
|
height: NetworkService.networkWiredInfoLoading ? 40 : 0
|
||||||
|
visible: NetworkService.networkWiredInfoLoading
|
||||||
|
|
||||||
|
DankSpinner {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
size: 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
visible: NetworkService.wiredConnections.length > 0
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width
|
||||||
|
height: 1
|
||||||
|
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Saved Configurations")
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
font.weight: Font.Medium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
width: parent.width
|
||||||
|
horizontalAlignment: Text.AlignLeft
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: NetworkService.wiredConnections
|
||||||
|
|
||||||
|
delegate: Rectangle {
|
||||||
|
required property var modelData
|
||||||
|
required property int index
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
height: 48
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: wiredMouseArea.containsMouse ? Theme.primaryHoverLight : Theme.surfaceLight
|
||||||
|
border.width: modelData.isActive ? 2 : 0
|
||||||
|
border.color: Theme.primary
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: Theme.spacingM
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "lan"
|
||||||
|
size: 20
|
||||||
|
color: modelData.isActive ? Theme.primary : Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: 2
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: modelData.id || I18n.tr("Unknown")
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: modelData.isActive ? Theme.primary : Theme.surfaceText
|
||||||
|
font.weight: modelData.isActive ? Font.Medium : Font.Normal
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: modelData.isActive ? I18n.tr("Active") : ""
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.primary
|
||||||
|
visible: modelData.isActive
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: wiredMouseArea
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
if (!modelData.isActive) {
|
||||||
|
NetworkService.connectToSpecificWiredConfig(modelData.uuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,202 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import qs.Common
|
||||||
|
import qs.Modules.Settings.Widgets
|
||||||
|
import qs.Services
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: networkStatusTab
|
||||||
|
|
||||||
|
LayoutMirroring.enabled: I18n.isRtl
|
||||||
|
LayoutMirroring.childrenInherit: true
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
NetworkService.addRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onDestruction: {
|
||||||
|
NetworkService.removeRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
DankFlickable {
|
||||||
|
anchors.fill: parent
|
||||||
|
clip: true
|
||||||
|
contentHeight: mainColumn.height + Theme.spacingXL
|
||||||
|
contentWidth: width
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: mainColumn
|
||||||
|
|
||||||
|
topPadding: 4
|
||||||
|
width: Math.min(600, parent.width - Theme.spacingL * 2)
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
spacing: Theme.spacingL
|
||||||
|
|
||||||
|
SettingsCard {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
title: I18n.tr("Network Status")
|
||||||
|
iconName: "lan"
|
||||||
|
settingKey: "networkStatus"
|
||||||
|
tags: ["status", "network", "connectivity", "internet"]
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: overviewSection
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Overview of your network connections")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
width: parent.width
|
||||||
|
horizontalAlignment: Text.AlignLeft
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width
|
||||||
|
height: 1
|
||||||
|
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||||
|
}
|
||||||
|
|
||||||
|
Grid {
|
||||||
|
columns: 2
|
||||||
|
columnSpacing: Theme.spacingL
|
||||||
|
rowSpacing: Theme.spacingS
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Backend")
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
}
|
||||||
|
StyledText {
|
||||||
|
text: NetworkService.backend || I18n.tr("Unknown")
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Medium
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Status")
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
}
|
||||||
|
Row {
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 8
|
||||||
|
height: 8
|
||||||
|
radius: 4
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
color: {
|
||||||
|
switch (NetworkService.networkStatus) {
|
||||||
|
case "ethernet":
|
||||||
|
case "wifi":
|
||||||
|
return Theme.success;
|
||||||
|
case "disconnected":
|
||||||
|
return Theme.error;
|
||||||
|
default:
|
||||||
|
return Theme.warning;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: {
|
||||||
|
switch (NetworkService.networkStatus) {
|
||||||
|
case "ethernet":
|
||||||
|
return I18n.tr("Ethernet");
|
||||||
|
case "wifi":
|
||||||
|
return I18n.tr("WiFi");
|
||||||
|
case "disconnected":
|
||||||
|
return I18n.tr("Disconnected");
|
||||||
|
default:
|
||||||
|
return NetworkService.networkStatus || I18n.tr("Unknown");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Medium
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Primary")
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
visible: NetworkService.primaryConnection.length > 0
|
||||||
|
}
|
||||||
|
StyledText {
|
||||||
|
text: NetworkService.primaryConnection || "-"
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
elide: Text.ElideRight
|
||||||
|
visible: NetworkService.primaryConnection.length > 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
visible: NetworkService.backend === "networkmanager" && NetworkService.ethernetConnected && NetworkService.wifiConnected
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Preference")
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: parent.width - preferenceLabel.width - preferenceButtons.width - Theme.spacingM * 2
|
||||||
|
height: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
DankButtonGroup {
|
||||||
|
id: preferenceButtons
|
||||||
|
model: [I18n.tr("Auto"), I18n.tr("Ethernet"), I18n.tr("WiFi")]
|
||||||
|
currentIndex: {
|
||||||
|
switch (NetworkService.userPreference) {
|
||||||
|
case "ethernet":
|
||||||
|
return 1;
|
||||||
|
case "wifi":
|
||||||
|
return 2;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onSelectionChanged: (index, selected) => {
|
||||||
|
if (!selected)
|
||||||
|
return;
|
||||||
|
switch (index) {
|
||||||
|
case 0:
|
||||||
|
NetworkService.setNetworkPreference("auto");
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
NetworkService.setNetworkPreference("ethernet");
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
NetworkService.setNetworkPreference("wifi");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: preferenceLabel
|
||||||
|
visible: false
|
||||||
|
text: I18n.tr("Preference")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,516 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell
|
||||||
|
import qs.Common
|
||||||
|
import qs.Modules.Settings.Widgets
|
||||||
|
import qs.Modals.Common
|
||||||
|
import qs.Modals.FileBrowser
|
||||||
|
import qs.Services
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: networkVpnTab
|
||||||
|
|
||||||
|
LayoutMirroring.enabled: I18n.isRtl
|
||||||
|
LayoutMirroring.childrenInherit: true
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
NetworkService.addRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onDestruction: {
|
||||||
|
NetworkService.removeRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
DankFlickable {
|
||||||
|
anchors.fill: parent
|
||||||
|
clip: true
|
||||||
|
contentHeight: mainColumn.height + Theme.spacingXL
|
||||||
|
contentWidth: width
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: mainColumn
|
||||||
|
|
||||||
|
topPadding: 4
|
||||||
|
width: Math.min(600, parent.width - Theme.spacingL * 2)
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
spacing: Theme.spacingL
|
||||||
|
|
||||||
|
SettingsCard {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property string expandedVpnUuid: ""
|
||||||
|
|
||||||
|
title: I18n.tr("VPN")
|
||||||
|
iconName: "vpn_key"
|
||||||
|
settingKey: "networkVpn"
|
||||||
|
tags: ["vpn", "network", "profiles", "import", "openvpn", "wireguard"]
|
||||||
|
|
||||||
|
function openVpnFileBrowser() {
|
||||||
|
vpnFileBrowserLoader.active = true;
|
||||||
|
if (vpnFileBrowserLoader.item)
|
||||||
|
vpnFileBrowserLoader.item.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
property var vpnFileBrowserLoader: LazyLoader {
|
||||||
|
active: false
|
||||||
|
|
||||||
|
FileBrowserModal {
|
||||||
|
browserTitle: I18n.tr("Import VPN")
|
||||||
|
browserIcon: "vpn_key"
|
||||||
|
browserType: "vpn"
|
||||||
|
fileExtensions: VPNService.getFileFilter()
|
||||||
|
|
||||||
|
onFileSelected: path => {
|
||||||
|
VPNService.importVpn(path.replace("file://", ""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
property var deleteVpnConfirm: ConfirmModal {}
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: vpnSection
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Unavailable")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
width: parent.width
|
||||||
|
horizontalAlignment: Text.AlignLeft
|
||||||
|
visible: !DMSNetworkService.vpnAvailable
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
visible: DMSNetworkService.vpnAvailable
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: {
|
||||||
|
if (!DMSNetworkService.connected)
|
||||||
|
return I18n.tr("Disconnected");
|
||||||
|
const names = DMSNetworkService.activeNames || [];
|
||||||
|
if (names.length <= 1)
|
||||||
|
return names[0] || I18n.tr("Connected");
|
||||||
|
return names[0] + " +" + (names.length - 1);
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: DMSNetworkService.connected ? Theme.primary : Theme.surfaceVariantText
|
||||||
|
width: parent.width - vpnHeaderControls.width - Theme.spacingM
|
||||||
|
horizontalAlignment: Text.AlignLeft
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: vpnHeaderControls
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
height: 28
|
||||||
|
radius: 14
|
||||||
|
width: importVpnRow.width + Theme.spacingM * 2
|
||||||
|
color: importVpnArea.containsMouse ? Theme.primaryHoverLight : Theme.surfaceLight
|
||||||
|
opacity: VPNService.importing ? 0.5 : 1.0
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: importVpnRow
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: VPNService.importing ? "sync" : "add"
|
||||||
|
size: Theme.fontSizeSmall
|
||||||
|
color: Theme.primary
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Import")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.primary
|
||||||
|
font.weight: Font.Medium
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: importVpnArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: VPNService.importing ? Qt.BusyCursor : Qt.PointingHandCursor
|
||||||
|
enabled: !VPNService.importing
|
||||||
|
onClicked: root.openVpnFileBrowser()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
height: 28
|
||||||
|
radius: 14
|
||||||
|
width: disconnectAllRow.width + Theme.spacingM * 2
|
||||||
|
color: disconnectAllArea.containsMouse ? Theme.errorHover : Theme.surfaceLight
|
||||||
|
visible: DMSNetworkService.connected
|
||||||
|
opacity: DMSNetworkService.isBusy ? 0.5 : 1.0
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: disconnectAllRow
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "link_off"
|
||||||
|
size: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Disconnect")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Medium
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: disconnectAllArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: DMSNetworkService.isBusy ? Qt.BusyCursor : Qt.PointingHandCursor
|
||||||
|
enabled: !DMSNetworkService.isBusy
|
||||||
|
onClicked: DMSNetworkService.disconnectAllActive()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width
|
||||||
|
height: 1
|
||||||
|
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||||
|
visible: DMSNetworkService.vpnAvailable
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: parent.width
|
||||||
|
height: 100
|
||||||
|
visible: DMSNetworkService.vpnAvailable && DMSNetworkService.profiles.length === 0
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "vpn_key_off"
|
||||||
|
size: 36
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("No VPN profiles")
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Click Import to add a .ovpn or .conf")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: parent.width
|
||||||
|
spacing: 4
|
||||||
|
visible: DMSNetworkService.vpnAvailable && DMSNetworkService.profiles.length > 0
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: DMSNetworkService.profiles
|
||||||
|
|
||||||
|
delegate: Rectangle {
|
||||||
|
id: vpnProfileRow
|
||||||
|
required property var modelData
|
||||||
|
required property int index
|
||||||
|
|
||||||
|
readonly property bool isActive: DMSNetworkService.isActiveUuid(modelData.uuid)
|
||||||
|
readonly property bool isTransient: !!modelData.transient
|
||||||
|
readonly property bool canExpand: modelData.canExpand !== false
|
||||||
|
readonly property bool canDelete: modelData.canDelete !== false
|
||||||
|
readonly property bool isExpanded: root.expandedVpnUuid === modelData.uuid
|
||||||
|
readonly property var configData: (!isTransient && isExpanded) ? VPNService.editConfig : null
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
height: isExpanded ? 56 + vpnExpandedContent.height : 56
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: vpnRowArea.containsMouse ? Theme.primaryHoverLight : (isActive ? Theme.primaryPressed : Theme.surfaceLight)
|
||||||
|
border.width: isActive ? 2 : 0
|
||||||
|
border.color: Theme.primary
|
||||||
|
opacity: DMSNetworkService.isBusy ? 0.6 : 1.0
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
Behavior on height {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 150
|
||||||
|
easing.type: Easing.OutQuad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: vpnRowArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: DMSNetworkService.isBusy ? Qt.BusyCursor : Qt.PointingHandCursor
|
||||||
|
enabled: !DMSNetworkService.isBusy
|
||||||
|
onClicked: DMSNetworkService.toggle(modelData.uuid)
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Theme.spacingS
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
height: 56 - Theme.spacingS * 2
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: isActive ? "vpn_lock" : "vpn_key_off"
|
||||||
|
size: 20
|
||||||
|
color: isActive ? Theme.primary : Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
spacing: 2
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
width: parent.width - 20 - ((canExpand ? 28 : 0) + (canDelete ? 28 : 0)) - Theme.spacingS * 4
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: modelData.name
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: isActive ? Theme.primary : Theme.surfaceText
|
||||||
|
elide: Text.ElideRight
|
||||||
|
width: parent.width
|
||||||
|
horizontalAlignment: Text.AlignLeft
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: VPNService.getVpnTypeFromProfile(modelData)
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
anchors.left: parent.left
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: Theme.spacingXS
|
||||||
|
height: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 28
|
||||||
|
height: 28
|
||||||
|
radius: 14
|
||||||
|
color: vpnExpandBtn.containsMouse ? Theme.surfacePressed : "transparent"
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
visible: canExpand
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
name: isExpanded ? "expand_less" : "expand_more"
|
||||||
|
size: 18
|
||||||
|
color: Theme.surfaceText
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: vpnExpandBtn
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
if (isExpanded) {
|
||||||
|
root.expandedVpnUuid = "";
|
||||||
|
} else {
|
||||||
|
root.expandedVpnUuid = modelData.uuid;
|
||||||
|
VPNService.getConfig(modelData.uuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 28
|
||||||
|
height: 28
|
||||||
|
radius: 14
|
||||||
|
color: vpnDeleteBtn.containsMouse ? Theme.errorHover : "transparent"
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
visible: canDelete
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
name: "delete"
|
||||||
|
size: 18
|
||||||
|
color: vpnDeleteBtn.containsMouse ? Theme.error : Theme.surfaceVariantText
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: vpnDeleteBtn
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
deleteVpnConfirm.showWithOptions({
|
||||||
|
title: I18n.tr("Delete VPN"),
|
||||||
|
message: I18n.tr("Delete \"%1\"?").arg(modelData.name),
|
||||||
|
confirmText: I18n.tr("Delete"),
|
||||||
|
confirmColor: Theme.error,
|
||||||
|
onConfirm: () => VPNService.deleteVpn(modelData.uuid)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: vpnExpandedContent
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
visible: !isTransient && isExpanded
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width
|
||||||
|
height: 1
|
||||||
|
color: Theme.outlineLight
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: parent.width
|
||||||
|
height: VPNService.configLoading ? 40 : 0
|
||||||
|
visible: VPNService.configLoading
|
||||||
|
|
||||||
|
DankSpinner {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
size: 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Flow {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
visible: !VPNService.configLoading && configData
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: {
|
||||||
|
if (!configData)
|
||||||
|
return [];
|
||||||
|
const fields = [];
|
||||||
|
const data = configData.data || {};
|
||||||
|
|
||||||
|
if (data.remote)
|
||||||
|
fields.push({
|
||||||
|
label: I18n.tr("Server"),
|
||||||
|
value: data.remote
|
||||||
|
});
|
||||||
|
if (configData.username || data.username)
|
||||||
|
fields.push({
|
||||||
|
label: I18n.tr("Username"),
|
||||||
|
value: configData.username || data.username
|
||||||
|
});
|
||||||
|
if (data.cipher)
|
||||||
|
fields.push({
|
||||||
|
label: I18n.tr("Cipher"),
|
||||||
|
value: data.cipher
|
||||||
|
});
|
||||||
|
if (data.auth)
|
||||||
|
fields.push({
|
||||||
|
label: I18n.tr("Auth"),
|
||||||
|
value: data.auth
|
||||||
|
});
|
||||||
|
if (data["proto-tcp"] === "yes" || data["proto-tcp"] === "no")
|
||||||
|
fields.push({
|
||||||
|
label: I18n.tr("Protocol"),
|
||||||
|
value: data["proto-tcp"] === "yes" ? "TCP" : "UDP"
|
||||||
|
});
|
||||||
|
if (data["tunnel-mtu"])
|
||||||
|
fields.push({
|
||||||
|
label: I18n.tr("MTU"),
|
||||||
|
value: data["tunnel-mtu"]
|
||||||
|
});
|
||||||
|
if (data["connection-type"])
|
||||||
|
fields.push({
|
||||||
|
label: I18n.tr("Auth Type"),
|
||||||
|
value: data["connection-type"]
|
||||||
|
});
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: Rectangle {
|
||||||
|
required property var modelData
|
||||||
|
required property int index
|
||||||
|
|
||||||
|
width: vpnFieldContent.width + Theme.spacingM * 2
|
||||||
|
height: 32
|
||||||
|
radius: Theme.cornerRadius - 2
|
||||||
|
color: Theme.surfaceContainerHigh
|
||||||
|
border.width: 1
|
||||||
|
border.color: Theme.outlineLight
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: vpnFieldContent
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: modelData.label + ":"
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: modelData.value
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Medium
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankToggle {
|
||||||
|
width: parent.width
|
||||||
|
text: I18n.tr("Autoconnect")
|
||||||
|
checked: configData ? (configData.autoconnect || false) : false
|
||||||
|
visible: !VPNService.configLoading && configData !== null
|
||||||
|
onToggled: checked => {
|
||||||
|
VPNService.updateConfig(modelData.uuid, {
|
||||||
|
autoconnect: checked
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: 1
|
||||||
|
height: Theme.spacingXS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,761 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
|
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import qs.Common
|
||||||
|
import qs.Modules.Settings.Widgets
|
||||||
|
import qs.Modals.Common
|
||||||
|
import qs.Services
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: networkWifiTab
|
||||||
|
|
||||||
|
LayoutMirroring.enabled: I18n.isRtl
|
||||||
|
LayoutMirroring.childrenInherit: true
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
NetworkService.addRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onDestruction: {
|
||||||
|
NetworkService.removeRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
DankFlickable {
|
||||||
|
anchors.fill: parent
|
||||||
|
clip: true
|
||||||
|
contentHeight: mainColumn.height + Theme.spacingXL
|
||||||
|
contentWidth: width
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: mainColumn
|
||||||
|
|
||||||
|
topPadding: 4
|
||||||
|
width: Math.min(600, parent.width - Theme.spacingL * 2)
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
spacing: Theme.spacingL
|
||||||
|
|
||||||
|
SettingsCard {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property string expandedWifiSsid: ""
|
||||||
|
property int maxPinnedWifiNetworks: 3
|
||||||
|
|
||||||
|
function normalizePinList(value) {
|
||||||
|
if (Array.isArray(value))
|
||||||
|
return value.filter(v => v);
|
||||||
|
if (typeof value === "string" && value.length > 0)
|
||||||
|
return [value];
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPinnedWifiNetworks() {
|
||||||
|
const pins = SettingsData.wifiNetworkPins || {};
|
||||||
|
return normalizePinList(pins["preferredWifi"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleWifiPin(ssid) {
|
||||||
|
const pins = JSON.parse(JSON.stringify(SettingsData.wifiNetworkPins || {}));
|
||||||
|
let pinnedList = normalizePinList(pins["preferredWifi"]);
|
||||||
|
const pinIndex = pinnedList.indexOf(ssid);
|
||||||
|
|
||||||
|
if (pinIndex !== -1) {
|
||||||
|
pinnedList.splice(pinIndex, 1);
|
||||||
|
} else {
|
||||||
|
pinnedList.unshift(ssid);
|
||||||
|
if (pinnedList.length > maxPinnedWifiNetworks)
|
||||||
|
pinnedList = pinnedList.slice(0, maxPinnedWifiNetworks);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pinnedList.length > 0)
|
||||||
|
pins["preferredWifi"] = pinnedList;
|
||||||
|
else
|
||||||
|
delete pins["preferredWifi"];
|
||||||
|
|
||||||
|
SettingsData.set("wifiNetworkPins", pins);
|
||||||
|
}
|
||||||
|
|
||||||
|
property var forgetNetworkConfirm: ConfirmModal {}
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
title: I18n.tr("WiFi")
|
||||||
|
iconName: "wifi"
|
||||||
|
settingKey: "networkWifi"
|
||||||
|
tags: ["wifi", "wi-fi", "wireless", "network", "ssid", "adapter", "radio"]
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: wifiSection
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: {
|
||||||
|
if (NetworkService.wifiToggling)
|
||||||
|
return I18n.tr("Toggling...");
|
||||||
|
if (!NetworkService.wifiEnabled)
|
||||||
|
return I18n.tr("Disabled");
|
||||||
|
if (NetworkService.wifiConnected)
|
||||||
|
return NetworkService.currentWifiSSID;
|
||||||
|
return I18n.tr("Not connected");
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: NetworkService.wifiConnected ? Theme.primary : Theme.surfaceVariantText
|
||||||
|
width: parent.width - wifiControls.width - Theme.spacingM
|
||||||
|
horizontalAlignment: Text.AlignLeft
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: wifiControls
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankActionButton {
|
||||||
|
iconName: "wifi_find"
|
||||||
|
buttonSize: 32
|
||||||
|
visible: NetworkService.backend === "networkmanager" && NetworkService.wifiEnabled && !NetworkService.wifiToggling
|
||||||
|
onClicked: PopoutService.showHiddenNetworkModal()
|
||||||
|
}
|
||||||
|
|
||||||
|
DankActionButton {
|
||||||
|
iconName: "refresh"
|
||||||
|
buttonSize: 32
|
||||||
|
visible: NetworkService.wifiEnabled && !NetworkService.wifiToggling && !NetworkService.isScanning
|
||||||
|
onClicked: NetworkService.scanWifi()
|
||||||
|
}
|
||||||
|
|
||||||
|
DankToggle {
|
||||||
|
checked: NetworkService.wifiEnabled
|
||||||
|
enabled: !NetworkService.wifiToggling
|
||||||
|
onToggled: NetworkService.toggleWifiRadio()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
visible: NetworkService.wifiEnabled && (NetworkService.wifiDevices?.length ?? 0) > 1
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("WiFi Device")
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: parent.width - wifiDeviceLabel.width - wifiDeviceDropdown.width - Theme.spacingM * 2
|
||||||
|
height: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
DankDropdown {
|
||||||
|
id: wifiDeviceDropdown
|
||||||
|
dropdownWidth: 150
|
||||||
|
popupWidth: 180
|
||||||
|
currentValue: NetworkService.wifiDeviceOverride || I18n.tr("Auto")
|
||||||
|
options: {
|
||||||
|
const devices = NetworkService.wifiDevices;
|
||||||
|
if (!devices || devices.length === 0)
|
||||||
|
return [I18n.tr("Auto")];
|
||||||
|
return [I18n.tr("Auto")].concat(devices.map(d => d.name));
|
||||||
|
}
|
||||||
|
onValueChanged: value => {
|
||||||
|
const deviceName = value === I18n.tr("Auto") ? "" : value;
|
||||||
|
NetworkService.setWifiDeviceOverride(deviceName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: wifiDeviceLabel
|
||||||
|
visible: false
|
||||||
|
text: I18n.tr("WiFi Device")
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width
|
||||||
|
height: 1
|
||||||
|
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
|
||||||
|
visible: NetworkService.wifiEnabled
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
visible: NetworkService.wifiEnabled && !NetworkService.wifiToggling
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
visible: NetworkService.wifiInterface.length > 0
|
||||||
|
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
height: 24
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Interface:")
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
width: 100
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
StyledText {
|
||||||
|
text: NetworkService.wifiInterface || "-"
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
height: 24
|
||||||
|
visible: NetworkService.wifiIP.length > 0
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("IP Address:")
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
width: 100
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
StyledText {
|
||||||
|
text: NetworkService.wifiIP || "-"
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
height: 24
|
||||||
|
visible: NetworkService.wifiConnected
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Signal:")
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
width: 100
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: {
|
||||||
|
const s = NetworkService.wifiSignalStrength;
|
||||||
|
if (s >= 50)
|
||||||
|
return "wifi";
|
||||||
|
if (s >= 25)
|
||||||
|
return "wifi_2_bar";
|
||||||
|
return "wifi_1_bar";
|
||||||
|
}
|
||||||
|
size: 18
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: NetworkService.wifiSignalStrength + "%"
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: parent.width
|
||||||
|
height: Theme.spacingS
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Available Networks")
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
font.weight: Font.Medium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: 1
|
||||||
|
height: 1
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: NetworkService.wifiNetworks?.length ?? 0
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: parent.width
|
||||||
|
height: 80
|
||||||
|
visible: NetworkService.isScanning && (NetworkService.wifiNetworks?.length ?? 0) === 0
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
id: scanningIcon
|
||||||
|
name: "wifi_find"
|
||||||
|
size: 32
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
|
SequentialAnimation {
|
||||||
|
running: NetworkService.isScanning
|
||||||
|
loops: Animation.Infinite
|
||||||
|
OpacityAnimator {
|
||||||
|
target: scanningIcon
|
||||||
|
to: 0.3
|
||||||
|
duration: 400
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
OpacityAnimator {
|
||||||
|
target: scanningIcon
|
||||||
|
to: 1.0
|
||||||
|
duration: 400
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
onRunningChanged: if (!running)
|
||||||
|
scanningIcon.opacity = 1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Scanning...")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
width: parent.width
|
||||||
|
spacing: 4
|
||||||
|
visible: (NetworkService.wifiNetworks?.length ?? 0) > 0
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: {
|
||||||
|
const ssid = NetworkService.currentWifiSSID;
|
||||||
|
const networks = NetworkService.wifiNetworks || [];
|
||||||
|
const pinnedList = root.getPinnedWifiNetworks();
|
||||||
|
|
||||||
|
let sorted = [...networks];
|
||||||
|
sorted.sort((a, b) => {
|
||||||
|
const aPinnedIndex = pinnedList.indexOf(a.ssid);
|
||||||
|
const bPinnedIndex = pinnedList.indexOf(b.ssid);
|
||||||
|
if (aPinnedIndex !== -1 || bPinnedIndex !== -1) {
|
||||||
|
if (aPinnedIndex === -1)
|
||||||
|
return 1;
|
||||||
|
if (bPinnedIndex === -1)
|
||||||
|
return -1;
|
||||||
|
return aPinnedIndex - bPinnedIndex;
|
||||||
|
}
|
||||||
|
if (a.ssid === ssid)
|
||||||
|
return -1;
|
||||||
|
if (b.ssid === ssid)
|
||||||
|
return 1;
|
||||||
|
return b.signal - a.signal;
|
||||||
|
});
|
||||||
|
return sorted;
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: Rectangle {
|
||||||
|
id: wifiNetworkDelegate
|
||||||
|
required property var modelData
|
||||||
|
required property int index
|
||||||
|
|
||||||
|
readonly property bool isConnected: modelData.ssid === NetworkService.currentWifiSSID
|
||||||
|
readonly property bool isPinned: root.getPinnedWifiNetworks().includes(modelData.ssid)
|
||||||
|
readonly property bool isExpanded: root.expandedWifiSsid === modelData.ssid
|
||||||
|
|
||||||
|
width: parent.width
|
||||||
|
height: isExpanded ? 56 + wifiExpandedContent.height : 56
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: wifiNetworkMouseArea.containsMouse ? Theme.primaryHoverLight : Theme.surfaceLight
|
||||||
|
border.width: isConnected ? 2 : 0
|
||||||
|
border.color: Theme.primary
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
Behavior on height {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: 150
|
||||||
|
easing.type: Easing.OutQuad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.fill: parent
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: parent.width
|
||||||
|
height: 56
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: Theme.spacingM
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.right: wifiNetworkActions.left
|
||||||
|
anchors.rightMargin: Theme.spacingS
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: {
|
||||||
|
const s = modelData.signal || 0;
|
||||||
|
if (s >= 50)
|
||||||
|
return "wifi";
|
||||||
|
if (s >= 25)
|
||||||
|
return "wifi_2_bar";
|
||||||
|
return "wifi_1_bar";
|
||||||
|
}
|
||||||
|
size: 20
|
||||||
|
color: isConnected ? Theme.primary : Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: 2
|
||||||
|
width: parent.width - 20 - Theme.spacingS
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.left: parent.left
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: modelData.ssid || I18n.tr("Unknown")
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: isConnected ? Theme.primary : Theme.surfaceText
|
||||||
|
font.weight: isConnected ? Font.Medium : Font.Normal
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "push_pin"
|
||||||
|
size: 14
|
||||||
|
color: Theme.primary
|
||||||
|
visible: isPinned
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "visibility_off"
|
||||||
|
size: 14
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
visible: modelData.hidden || false
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.left: parent.left
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: isConnected ? I18n.tr("Connected") : (modelData.secured ? I18n.tr("Secured") : I18n.tr("Open"))
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: isConnected ? Theme.primary : Theme.surfaceVariantText
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: "•"
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
visible: modelData.saved
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Saved")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.primary
|
||||||
|
visible: modelData.saved
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: "•"
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
visible: modelData.hidden || false
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Hidden")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
visible: modelData.hidden || false
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: "•"
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: modelData.signal + "%"
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: wifiNetworkActions
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: Theme.spacingS
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 28
|
||||||
|
height: 28
|
||||||
|
radius: 14
|
||||||
|
color: wifiExpandBtn.containsMouse ? Theme.surfacePressed : "transparent"
|
||||||
|
visible: isConnected || modelData.saved
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
name: isExpanded ? "expand_less" : "expand_more"
|
||||||
|
size: 18
|
||||||
|
color: Theme.surfaceText
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: wifiExpandBtn
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
if (isExpanded) {
|
||||||
|
root.expandedWifiSsid = "";
|
||||||
|
} else {
|
||||||
|
root.expandedWifiSsid = modelData.ssid;
|
||||||
|
NetworkService.fetchNetworkInfo(modelData.ssid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankActionButton {
|
||||||
|
iconName: "qr_code"
|
||||||
|
buttonSize: 28
|
||||||
|
visible: modelData.secured && modelData.saved
|
||||||
|
onClicked: {
|
||||||
|
PopoutService.showWifiQRCodeModal(modelData.ssid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankActionButton {
|
||||||
|
iconName: isPinned ? "push_pin" : "push_pin"
|
||||||
|
buttonSize: 28
|
||||||
|
iconColor: isPinned ? Theme.primary : Theme.surfaceVariantText
|
||||||
|
onClicked: {
|
||||||
|
root.toggleWifiPin(modelData.ssid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DankActionButton {
|
||||||
|
iconName: "delete"
|
||||||
|
buttonSize: 28
|
||||||
|
iconColor: Theme.error
|
||||||
|
visible: modelData.saved || isConnected
|
||||||
|
onClicked: {
|
||||||
|
forgetNetworkConfirm.showWithOptions({
|
||||||
|
title: I18n.tr("Forget Network"),
|
||||||
|
message: I18n.tr("Forget \"%1\"?").arg(modelData.ssid),
|
||||||
|
confirmText: I18n.tr("Forget"),
|
||||||
|
confirmColor: Theme.error,
|
||||||
|
onConfirm: () => NetworkService.forgetWifiNetwork(modelData.ssid)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: wifiNetworkMouseArea
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.rightMargin: wifiNetworkActions.width + Theme.spacingM
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
if (isConnected) {
|
||||||
|
NetworkService.disconnectWifi();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (modelData.secured && !modelData.saved && (DMSService.apiVersion < 7 || modelData.enterprise)) {
|
||||||
|
PopoutService.showWifiPasswordModal(modelData.ssid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
NetworkService.connectToWifi(modelData.ssid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: wifiExpandedContent
|
||||||
|
width: parent.width
|
||||||
|
visible: isExpanded
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width - Theme.spacingM * 2
|
||||||
|
height: 1
|
||||||
|
x: Theme.spacingM
|
||||||
|
color: Theme.outlineLight
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: parent.width
|
||||||
|
height: wifiDetailsColumn.implicitHeight + Theme.spacingM * 2
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: wifiDetailsColumn
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Theme.spacingM
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: parent.width
|
||||||
|
height: NetworkService.networkInfoLoading ? 40 : 0
|
||||||
|
visible: NetworkService.networkInfoLoading
|
||||||
|
|
||||||
|
DankSpinner {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
size: 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Flow {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
visible: !NetworkService.networkInfoLoading
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: {
|
||||||
|
const fields = [];
|
||||||
|
const net = modelData;
|
||||||
|
if (!net)
|
||||||
|
return fields;
|
||||||
|
|
||||||
|
fields.push({
|
||||||
|
label: I18n.tr("Signal"),
|
||||||
|
value: net.signal + "%"
|
||||||
|
});
|
||||||
|
if (net.frequency)
|
||||||
|
fields.push({
|
||||||
|
label: I18n.tr("Frequency"),
|
||||||
|
value: (net.frequency / 1000).toFixed(1) + " GHz"
|
||||||
|
});
|
||||||
|
if (net.channel)
|
||||||
|
fields.push({
|
||||||
|
label: I18n.tr("Channel"),
|
||||||
|
value: String(net.channel)
|
||||||
|
});
|
||||||
|
if (net.rate)
|
||||||
|
fields.push({
|
||||||
|
label: I18n.tr("Rate"),
|
||||||
|
value: net.rate + " Mbps"
|
||||||
|
});
|
||||||
|
if (net.mode)
|
||||||
|
fields.push({
|
||||||
|
label: I18n.tr("Mode"),
|
||||||
|
value: net.mode
|
||||||
|
});
|
||||||
|
if (net.bssid)
|
||||||
|
fields.push({
|
||||||
|
label: I18n.tr("BSSID"),
|
||||||
|
value: net.bssid
|
||||||
|
});
|
||||||
|
fields.push({
|
||||||
|
label: I18n.tr("Security"),
|
||||||
|
value: net.secured ? (net.enterprise ? I18n.tr("Enterprise") : I18n.tr("WPA/WPA2")) : I18n.tr("Open")
|
||||||
|
});
|
||||||
|
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: Rectangle {
|
||||||
|
required property var modelData
|
||||||
|
required property int index
|
||||||
|
|
||||||
|
width: wifiFieldContent.width + Theme.spacingM * 2
|
||||||
|
height: 32
|
||||||
|
radius: Theme.cornerRadius - 2
|
||||||
|
color: Theme.surfaceContainerHigh
|
||||||
|
border.width: 1
|
||||||
|
border.color: Theme.outlineLight
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: wifiFieldContent
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: modelData.label + ":"
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: modelData.value
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceText
|
||||||
|
font.weight: Font.Medium
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
visible: (modelData.saved || isConnected) && DMSService.apiVersion > 13
|
||||||
|
|
||||||
|
DankToggle {
|
||||||
|
id: autoconnectToggle
|
||||||
|
text: I18n.tr("Autoconnect")
|
||||||
|
checked: modelData.autoconnect || false
|
||||||
|
onToggled: checked => {
|
||||||
|
NetworkService.setWifiAutoconnect(modelData.ssid, checked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -392,8 +392,7 @@ Singleton {
|
|||||||
function toggleSettingsWithTab(tabName: string) {
|
function toggleSettingsWithTab(tabName: string) {
|
||||||
if (settingsModal) {
|
if (settingsModal) {
|
||||||
var idx = settingsModal.resolveTabIndex(tabName);
|
var idx = settingsModal.resolveTabIndex(tabName);
|
||||||
if (idx >= 0)
|
settingsModal.setTabIndex(idx);
|
||||||
settingsModal.currentTabIndex = idx;
|
|
||||||
settingsModal.toggle();
|
settingsModal.toggle();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -433,8 +432,7 @@ Singleton {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var idx = settingsModal.resolveTabIndex(tabName);
|
var idx = settingsModal.resolveTabIndex(tabName);
|
||||||
if (idx >= 0)
|
settingsModal.setTabIndex(idx);
|
||||||
settingsModal.currentTabIndex = idx;
|
|
||||||
toplevel.activate();
|
toplevel.activate();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -466,12 +464,11 @@ Singleton {
|
|||||||
if (_settingsWantsToggle) {
|
if (_settingsWantsToggle) {
|
||||||
_settingsWantsToggle = false;
|
_settingsWantsToggle = false;
|
||||||
if (_settingsPendingTabIndex >= 0) {
|
if (_settingsPendingTabIndex >= 0) {
|
||||||
settingsModal.currentTabIndex = _settingsPendingTabIndex;
|
settingsModal?.setTabIndex(_settingsPendingTabIndex);
|
||||||
_settingsPendingTabIndex = -1;
|
_settingsPendingTabIndex = -1;
|
||||||
} else if (_settingsPendingTab) {
|
} else if (_settingsPendingTab) {
|
||||||
var idx = settingsModal?.resolveTabIndex(_settingsPendingTab) ?? -1;
|
var idx = settingsModal?.resolveTabIndex(_settingsPendingTab) ?? -1;
|
||||||
if (idx >= 0)
|
settingsModal?.setTabIndex(idx);
|
||||||
settingsModal.currentTabIndex = idx;
|
|
||||||
_settingsPendingTab = "";
|
_settingsPendingTab = "";
|
||||||
}
|
}
|
||||||
settingsModal?.toggle();
|
settingsModal?.toggle();
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ Rectangle {
|
|||||||
iconColor: Theme.surfaceVariantText
|
iconColor: Theme.surfaceVariantText
|
||||||
onClicked: {
|
onClicked: {
|
||||||
PopoutService.closeControlCenter();
|
PopoutService.closeControlCenter();
|
||||||
PopoutService.openSettingsWithTab("network");
|
PopoutService.openSettingsWithTab("network_vpn");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,7 +102,10 @@ TAB_INDEX_MAP = {
|
|||||||
"DockTab.qml": 5,
|
"DockTab.qml": 5,
|
||||||
"DankBarAppearanceTab.qml": 6,
|
"DankBarAppearanceTab.qml": 6,
|
||||||
"WorkspaceAppearanceCard.qml": 6,
|
"WorkspaceAppearanceCard.qml": 6,
|
||||||
"NetworkTab.qml": 7,
|
"NetworkStatusTab.qml": 7,
|
||||||
|
"NetworkEthernetTab.qml": 39,
|
||||||
|
"NetworkWifiTab.qml": 40,
|
||||||
|
"NetworkVpnTab.qml": 41,
|
||||||
"PrinterTab.qml": 8,
|
"PrinterTab.qml": 8,
|
||||||
"LauncherTab.qml": 9,
|
"LauncherTab.qml": 9,
|
||||||
"ThemeColorsTab.qml": 10,
|
"ThemeColorsTab.qml": 10,
|
||||||
@@ -172,6 +175,9 @@ TAB_CATEGORY_MAP = {
|
|||||||
36: "Autostart",
|
36: "Autostart",
|
||||||
37: "Personalization",
|
37: "Personalization",
|
||||||
38: "Applications",
|
38: "Applications",
|
||||||
|
39: "Network",
|
||||||
|
40: "Network",
|
||||||
|
41: "Network",
|
||||||
}
|
}
|
||||||
|
|
||||||
SEARCHABLE_COMPONENTS = [
|
SEARCHABLE_COMPONENTS = [
|
||||||
|
|||||||
@@ -2118,6 +2118,25 @@
|
|||||||
"icon": "wifi",
|
"icon": "wifi",
|
||||||
"conditionKey": "dmsConnected"
|
"conditionKey": "dmsConnected"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"section": "networkStatus",
|
||||||
|
"label": "Network Status",
|
||||||
|
"tabIndex": 7,
|
||||||
|
"category": "Network",
|
||||||
|
"keywords": [
|
||||||
|
"connection",
|
||||||
|
"connectivity",
|
||||||
|
"ethernet",
|
||||||
|
"internet",
|
||||||
|
"network",
|
||||||
|
"online",
|
||||||
|
"status",
|
||||||
|
"wi-fi",
|
||||||
|
"wifi",
|
||||||
|
"wireless"
|
||||||
|
],
|
||||||
|
"icon": "lan"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"section": "_tab_8",
|
"section": "_tab_8",
|
||||||
"label": "Printers",
|
"label": "Printers",
|
||||||
@@ -7302,7 +7321,8 @@
|
|||||||
"screen",
|
"screen",
|
||||||
"widgets"
|
"widgets"
|
||||||
],
|
],
|
||||||
"icon": "widgets"
|
"icon": "widgets",
|
||||||
|
"conditionKey": "dmsConnected"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"section": "_tab_27",
|
"section": "_tab_27",
|
||||||
@@ -8901,5 +8921,95 @@
|
|||||||
"icon": "select_window",
|
"icon": "select_window",
|
||||||
"description": "Define compositor rules for window behavior",
|
"description": "Define compositor rules for window behavior",
|
||||||
"conditionKey": "windowRulesCapable"
|
"conditionKey": "windowRulesCapable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"section": "_tab_39",
|
||||||
|
"label": "Ethernet",
|
||||||
|
"tabIndex": 39,
|
||||||
|
"category": "Network",
|
||||||
|
"keywords": [
|
||||||
|
"connectivity",
|
||||||
|
"ethernet",
|
||||||
|
"network",
|
||||||
|
"online"
|
||||||
|
],
|
||||||
|
"icon": "settings_ethernet"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"section": "networkEthernet",
|
||||||
|
"label": "Ethernet",
|
||||||
|
"tabIndex": 39,
|
||||||
|
"category": "Network",
|
||||||
|
"keywords": [
|
||||||
|
"adapters",
|
||||||
|
"connection",
|
||||||
|
"connectivity",
|
||||||
|
"ethernet",
|
||||||
|
"network",
|
||||||
|
"online",
|
||||||
|
"wired"
|
||||||
|
],
|
||||||
|
"icon": "settings_ethernet"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"section": "_tab_40",
|
||||||
|
"label": "WiFi",
|
||||||
|
"tabIndex": 40,
|
||||||
|
"category": "Network",
|
||||||
|
"keywords": [
|
||||||
|
"connectivity",
|
||||||
|
"network",
|
||||||
|
"online",
|
||||||
|
"wifi"
|
||||||
|
],
|
||||||
|
"icon": "wifi"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"section": "networkWifi",
|
||||||
|
"label": "WiFi",
|
||||||
|
"tabIndex": 40,
|
||||||
|
"category": "Network",
|
||||||
|
"keywords": [
|
||||||
|
"adapter",
|
||||||
|
"connectivity",
|
||||||
|
"network",
|
||||||
|
"online",
|
||||||
|
"radio",
|
||||||
|
"ssid",
|
||||||
|
"wi-fi",
|
||||||
|
"wifi",
|
||||||
|
"wireless"
|
||||||
|
],
|
||||||
|
"icon": "wifi"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"section": "_tab_41",
|
||||||
|
"label": "VPN",
|
||||||
|
"tabIndex": 41,
|
||||||
|
"category": "Network",
|
||||||
|
"keywords": [
|
||||||
|
"connectivity",
|
||||||
|
"network",
|
||||||
|
"online",
|
||||||
|
"vpn"
|
||||||
|
],
|
||||||
|
"icon": "vpn_key"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"section": "networkVpn",
|
||||||
|
"label": "VPN",
|
||||||
|
"tabIndex": 41,
|
||||||
|
"category": "Network",
|
||||||
|
"keywords": [
|
||||||
|
"connectivity",
|
||||||
|
"import",
|
||||||
|
"network",
|
||||||
|
"online",
|
||||||
|
"openvpn",
|
||||||
|
"profiles",
|
||||||
|
"vpn",
|
||||||
|
"wireguard"
|
||||||
|
],
|
||||||
|
"icon": "vpn_key"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user