Very early KDE 6 release.

This commit is contained in:
wackyideas 2024-08-09 03:20:25 +02:00
parent 7cc4ccabbc
commit 686046d4f7
6272 changed files with 140920 additions and 529657 deletions

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
SPDX-FileCopyrightText: 2023 Kai Uwe Broulik <kde@broulik.de>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
-->
<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
<kcfgfile name=""/>
<group name="General">
<entry name="currentDetailsTab" type="String">
<label>Last selected details tab (speed, details)</label>
<default>speed</default>
</entry>
</group>
</kcfg>

View file

@ -0,0 +1,40 @@
/*
SPDX-FileCopyrightText: 2013-2017 Jan Grulich <jgrulich@redhat.com>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
import QtQuick 2.2
import org.kde.kirigami 2.20 as Kirigami
import org.kde.plasma.plasmoid 2.0
MouseArea {
id: root
required property bool airplaneModeAvailable
required property string iconName
hoverEnabled: true
acceptedButtons: airplaneModeAvailable ? Qt.LeftButton | Qt.MiddleButton : Qt.LeftButton
property bool wasExpanded
onPressed: wasExpanded = mainWindow.expanded
onClicked: mouse => {
if (airplaneModeAvailable && mouse.button === Qt.MiddleButton) {
mainWindow.planeModeSwitchAction.trigger();
} else {
mainWindow.expanded = !wasExpanded;
}
}
Kirigami.Icon {
id: connectionIcon
anchors.fill: parent
source: root.iconName
active: parent.containsMouse
}
}

View file

@ -0,0 +1,450 @@
/*
SPDX-FileCopyrightText: 2013-2017 Jan Grulich <jgrulich@redhat.com>
SPDX-FileCopyrightText: 2020 Nate Graham <nate@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
import QtQuick 2.15
import QtQuick.Layouts 1.15
import QtQuick.Controls 2.15
import org.kde.coreaddons 1.0 as KCoreAddons
import org.kde.kcmutils as KCMUtils
import org.kde.kirigami 2.20 as Kirigami
import org.kde.plasma.components 3.0 as PlasmaComponents3
import org.kde.plasma.extras 2.0 as PlasmaExtras
import org.kde.plasma.networkmanagement as PlasmaNM
import org.kde.plasma.plasmoid 2.0
ExpandableListItem {
id: connectionItem
property bool activating: ConnectionState === PlasmaNM.Enums.Activating
property bool deactivated: ConnectionState === PlasmaNM.Enums.Deactivated
property bool passwordIsStatic: (SecurityType === PlasmaNM.Enums.StaticWep || SecurityType == PlasmaNM.Enums.WpaPsk ||
SecurityType === PlasmaNM.Enums.Wpa2Psk || SecurityType == PlasmaNM.Enums.SAE)
property bool predictableWirelessPassword: !Uuid && Type === PlasmaNM.Enums.Wireless && passwordIsStatic
property bool showSpeed: mainWindow.expanded &&
ConnectionState === PlasmaNM.Enums.Activated &&
(Type === PlasmaNM.Enums.Wired ||
Type === PlasmaNM.Enums.Wireless ||
Type === PlasmaNM.Enums.Gsm ||
Type === PlasmaNM.Enums.Cdma)
property real rxSpeed: 0
property real txSpeed: 0
icon: model.ConnectionIcon
title: model.ItemUniqueName
subtitle: itemText()
isBusy: false
//isBusy: mainWindow.expanded && model.ConnectionState === PlasmaNM.Enums.Activating
isDefault: ConnectionState === PlasmaNM.Enums.Activated
//defaultActionButtonAction:
showDefaultActionButtonWhenBusy: false
Keys.onPressed: event => {
if (!connectionItem.expanded) {
event.accepted = false;
return;
}
/*if ((customExpandedViewContent === detailsComponent) && showSpeed) {
if (event.key === Qt.Key_Right) {
customExpandedViewContentItem.detailsTabBar.currentIndex = 1;
event.accepted = true;
} else if (event.key === Qt.Key_Left) {
customExpandedViewContentItem.detailsTabBar.currentIndex = 0;
event.accepted = true;
}
}*/
}
Connections {
target: connectionItem.mouseArea
function onPressed(mouse) {
contextMenu.show(this, mouse.x, mouse.y);
}
}
PlasmaExtras.Menu {
id: contextMenu
property string text
function show(item, x, y) {
visualParent = connectionItem
open(x, y)
}
PlasmaExtras.MenuItem {
readonly property bool isDeactivated: model.ConnectionState === PlasmaNM.Enums.Deactivated
enabled: {
if (!connectionItem.expanded) {
return true;
}
if (connectionItem.customExpandedViewContent === passwordDialogComponent) {
return connectionItem.customExpandedViewContentItem?.passwordField.acceptableInput ?? false;
}
return true;
}
//icon.name: isDeactivated ? "network-connect" : "network-disconnect"
text: isDeactivated ? i18n("Connect") : i18n("Disconnect")
onClicked: changeState()
}
PlasmaExtras.MenuItem {
text: i18n("Speed")
icon: "preferences-system-performance"
onClicked: {
const speedGraphComponent = Qt.createComponent("SpeedGraphPage.qml");
if (speedGraphComponent.status === Component.Error) {
console.warn("Cannot create speed graph component:", speedGraphComponent.errorString());
return;
}
mainWindow.expanded = true; // just in case.
stack.push(speedGraphComponent, {
downloadSpeed: Qt.binding(() => rxSpeed),
uploadSpeed: Qt.binding(() => txSpeed),
connectionTitle: Qt.binding(() => model.ItemUniqueName)
});
}
}
PlasmaExtras.MenuItem {
//text: i18n("Copy")
text: i18n("Show Network's QR Code")
icon: "view-barcode-qr"
visible: Uuid && Type === PlasmaNM.Enums.Wireless && passwordIsStatic
onClicked: handler.requestWifiCode(ConnectionPath, Ssid, SecurityType);
}
PlasmaExtras.MenuItem {
text: i18n("Configure…")
icon: "configure"
onClicked: KCMUtils.KCMLauncher.openSystemSettings(mainWindow.kcm, ["--args", "Uuid=" + Uuid])
}
}
contextualActions: [
Action {
id: stateChangeButton
readonly property bool isDeactivated: model.ConnectionState === PlasmaNM.Enums.Deactivated
enabled: {
if (!connectionItem.expanded) {
return true;
}
if (connectionItem.customExpandedViewContent === passwordDialogComponent) {
return connectionItem.customExpandedViewContentItem?.passwordField.acceptableInput ?? false;
}
return true;
}
//icon.name: isDeactivated ? "network-connect" : "network-disconnect"
text: isDeactivated ? i18n("Connect") : i18n("Disconnect")
onTriggered: changeState()
},
Action {
text: i18n("Details")
//icon.name: "configure"
onTriggered: {
const showDetailscomponent = Qt.createComponent("NetworkDetailsPage.qml");
if (showDetailscomponent.status === Component.Error) {
console.warn("Cannot create details page component:", showDetailscomponent.errorString());
return;
}
mainWindow.expanded = true; // just in case.
stack.push(showDetailscomponent, {
details: Qt.binding(() => ConnectionDetails),
connectionTitle: Qt.binding(() => model.ItemUniqueName)
});
}
}/*,
Action {
enabled: Uuid && Type === PlasmaNM.Enums.Wireless && passwordIsStatic
text: i18n("Show Network's QR Code")
icon.name: "view-barcode-qr"
onTriggered: handler.requestWifiCode(ConnectionPath, Ssid, SecurityType);
},
Action {
text: i18n("Configure…")
icon.name: "configure"
onTriggered: KCMUtils.KCMLauncher.openSystemSettings(mainWindow.kcm, ["--args", "Uuid=" + Uuid])
}*/
]
/*Component {
id: buttonsComponent
Column {
Button {
text: i18n("Details")
onClicked: {
const showDetailscomponent = Qt.createComponent("NetworkDetailsPage.qml");
if (showDetailscomponent.status === Component.Error) {
console.warn("Cannot create QR code component:", showDetailscomponent.errorString());
return;
}
mainWindow.expanded = true; // just in case.
stack.push(showDetailscomponent, {
details: ConnectionDetails
});
}
}
}
}*/
//customExpandedViewContent: detailsComponent
Accessible.description: `${model.AccessibleDescription} ${subtitle}`
/*Component {
id: detailsComponent
Column {
visible: false
spacing: Kirigami.Units.smallSpacing
property Item detailsTabBar: detailsTabBar
PlasmaComponents3.TabBar {
id: detailsTabBar
anchors {
left: parent.left
right: parent.right
}
height: visible ? implicitHeight : 0
implicitHeight: contentHeight
position: PlasmaComponents3.TabBar.Header
visible: false //showSpeed
onCurrentIndexChanged: {
// Only if there are the two tabs.
if (showSpeed) {
Plasmoid.configuration.currentDetailsTab = ["speed", "details"][currentIndex];
}
}
onVisibleChanged: {
if (!visible) {
currentIndex = 1;
}
}
PlasmaComponents3.TabButton {
id: speedTabButton
text: i18n("Speed")
}
PlasmaComponents3.TabButton {
id: detailsTabButton
text: i18n("Details")
}
Component.onCompleted: {
if (!showSpeed || Plasmoid.configuration.currentDetailsTab === "details") {
currentIndex = 1;
}
}
}
DetailsText {
id: detailsTextColumn
width: parent.width
visible: detailsTabBar.currentIndex === 1
activeFocusOnTab: details.length > 0
details: ConnectionDetails
Accessible.description: details.join(" ")
Loader {
anchors.fill: parent
active: parent.activeFocus
asynchronous: true
z: -1
sourceComponent: PlasmaExtras.Highlight {
hovered: true
}
}
}
FocusScope {
anchors {
left: parent.left
right: parent.right
}
height: trafficMonitorGraph.implicitHeight
visible: false// detailsTabBar.currentIndex === 0
activeFocusOnTab: true
Accessible.description: i18nc("@info:tooltip", "Current download speed is %1 kibibytes per second; current upload speed is %2 kibibytes per second", Math.round(rxSpeed / 1024), Math.round(txSpeed / 1024))
Loader {
anchors.fill: parent
active: parent.activeFocus
asynchronous: true
z: -1
sourceComponent: PlasmaExtras.Highlight {
hovered: true
}
}
TrafficMonitor {
id: trafficMonitorGraph
width: parent.width
downloadSpeed: rxSpeed
uploadSpeed: txSpeed
}
}
}
}*/
Component {
id: passwordDialogComponent
ColumnLayout {
property alias password: passwordField.text
property alias passwordField: passwordField
PasswordField {
id: passwordField
Layout.fillWidth: true
Layout.leftMargin: Kirigami.Units.iconSizes.small
Layout.rightMargin: Kirigami.Units.iconSizes.small
securityType: SecurityType
onAccepted: {
stateChangeButton.trigger()
//connectionItem.customExpandedViewContent = detailsComponent
}
Component.onCompleted: {
passwordField.forceActiveFocus()
setDelayModelUpdates(true)
}
}
}
}
Timer {
id: timer
repeat: true
interval: 2000
running: showSpeed
triggeredOnStart: true
// property int can overflow with the amount of bytes.
property double prevRxBytes: 0
property double prevTxBytes: 0
onTriggered: {
rxSpeed = prevRxBytes === 0 ? 0 : (RxBytes - prevRxBytes) * 1000 / interval
txSpeed = prevTxBytes === 0 ? 0 : (TxBytes - prevTxBytes) * 1000 / interval
prevRxBytes = RxBytes
prevTxBytes = TxBytes
}
}
function changeState() {
if (Uuid || !predictableWirelessPassword || connectionItem.customExpandedViewContent == passwordDialogComponent) {
if (ConnectionState == PlasmaNM.Enums.Deactivated) {
if (!predictableWirelessPassword && !Uuid) {
handler.addAndActivateConnection(DevicePath, SpecificPath)
} else if (connectionItem.customExpandedViewContent == passwordDialogComponent) {
const item = connectionItem.customExpandedViewContentItem;
if (item && item.password !== "") {
handler.addAndActivateConnection(DevicePath, SpecificPath, item.password)
//connectionItem.customExpandedViewContent = detailsComponent
connectionItem.collapse()
} else {
connectionItem.expand()
}
} else {
handler.activateConnection(ConnectionPath, DevicePath, SpecificPath)
}
} else {
handler.deactivateConnection(ConnectionPath, DevicePath)
}
} else if (predictableWirelessPassword) {
setDelayModelUpdates(true)
connectionItem.customExpandedViewContent = passwordDialogComponent
connectionItem.expand()
}
}
/* This generates the formatted text under the connection name
in the popup where the connections can be "Connect"ed and
"Disconnect"ed. */
function itemText() {
if (ConnectionState === PlasmaNM.Enums.Activating) {
if (Type === PlasmaNM.Enums.Vpn) {
return VpnState
} else {
return DeviceState
}
} else if (ConnectionState === PlasmaNM.Enums.Deactivating) {
if (Type === PlasmaNM.Enums.Vpn) {
return VpnState
} else {
return DeviceState
}
} else if (Uuid && ConnectionState === PlasmaNM.Enums.Deactivated) {
return LastUsed
} else if (ConnectionState === PlasmaNM.Enums.Activated) {
if (showSpeed) {
return i18n("Connected, ⬇ %1/s, ⬆ %2/s",
KCoreAddons.Format.formatByteSize(rxSpeed),
KCoreAddons.Format.formatByteSize(txSpeed))
} else {
return i18n("Connected")
}
}
return ""
}
function setDelayModelUpdates(delay: bool) {
appletProxyModel.setData(appletProxyModel.index(index, 0), delay, PlasmaNM.NetworkModel.DelayModelUpdatesRole);
}
onShowSpeedChanged: {
connectionModel.setDeviceStatisticsRefreshRateMs(DevicePath, showSpeed ? 2000 : 0)
}
onActivatingChanged: {
if (ConnectionState === PlasmaNM.Enums.Activating) {
ListView.view.positionViewAtBeginning()
}
}
onDeactivatedChanged: {
/* Separator is part of section, which is visible only when available connections exist. Need to determine
if there is a connection in use, to show Separator. Otherwise need to hide it from the top of the list.
Connections in use are always on top, only need to check the first one. */
if (appletProxyModel.data(appletProxyModel.index(0, 0), PlasmaNM.NetworkModel.SectionRole) !== "Available connections") {
if (connectionView.showSeparator != true) {
connectionView.showSeparator = true
}
return
}
connectionView.showSeparator = false
return
}
onItemCollapsed: {
//connectionItem.customExpandedViewContent = detailsComponent;
setDelayModelUpdates(false);
}
Component.onDestruction: {
setDelayModelUpdates(false);
}
}

View file

@ -0,0 +1,136 @@
/*
SPDX-FileCopyrightText: 2013-2017 Jan Grulich <jgrulich@redhat.com>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
import QtQuick 2.15
import QtQuick.Layouts 1.2
import org.kde.plasma.components 3.0 as PlasmaComponents3
import org.kde.kirigami 2.20 as Kirigami
import org.kde.plasma.extras 2.0 as PlasmaExtras
import org.kde.plasma.networkmanagement as PlasmaNM
import org.kde.networkmanager as NMQt
ColumnLayout {
id: connectionListPage
required property PlasmaNM.NetworkStatus nmStatus
property alias model: connectionView.model
property alias count: connectionView.count
spacing: Kirigami.Units.smallSpacing * 2
Keys.forwardTo: [connectionView]
Kirigami.InlineMessage {
id: connectivityMessage
Layout.fillWidth: true
Layout.leftMargin: connectionView.leftMargin
Layout.rightMargin: connectionView.rightMargin
Layout.topMargin: Kirigami.Units.smallSpacing * 2
Layout.preferredHeight: contentItem.implicitHeight + topPadding + bottomPadding
type: Kirigami.MessageType.Information
icon.name: "dialog-password"
text: i18n("You need to log in to this network")
visible: connectionListPage.nmStatus.connectivity === NMQt.NetworkManager.Portal
actions: Kirigami.Action {
text: i18nc("@action:button", "Log in")
onTriggered: {
Qt.openUrlExternally(connectionListPage.nmStatus.networkCheckUrl);
}
}
}
PlasmaComponents3.ScrollView {
id: scrollView
Layout.fillWidth: true
Layout.fillHeight: true
contentWidth: availableWidth - contentItem.leftMargin - contentItem.rightMargin
PlasmaComponents3.ScrollBar.horizontal.policy: PlasmaComponents3.ScrollBar.AlwaysOff
contentItem: ListView {
id: connectionView
property int currentVisibleButtonIndex: -1
property bool showSeparator: false
Keys.onDownPressed: event => {
connectionView.incrementCurrentIndex();
connectionView.currentItem.forceActiveFocus();
}
Keys.onUpPressed: event => {
if (connectionView.currentIndex === 0) {
connectionView.currentIndex = -1;
toolbar.searchTextField.forceActiveFocus();
toolbar.searchTextField.selectAll();
} else {
event.accepted = false;
}
}
// We use the spacing around the connectivity message, if shown.
//topMargin: connectivityMessage.visible ? 0 : Kirigami.Units.smallSpacing
bottomMargin: Kirigami.Units.smallSpacing * 2
//leftMargin: Kirigami.Units.smallSpacing * 2
//rightMargin: Kirigami.Units.smallSpacing * 2
spacing: Kirigami.Units.smallSpacing
model: appletProxyModel
currentIndex: -1
boundsBehavior: Flickable.StopAtBounds
section.property: showSeparator ? "Section" : ""
section.delegate: ListItem {
separator: true
}
highlight: PlasmaExtras.Highlight { }
highlightMoveDuration: 0//Kirigami.Units.shortDuration
highlightResizeDuration: 0//Kirigami.Units.shortDuration
delegate: ConnectionItem {
width: connectionView.width// - Kirigami.Units.smallSpacing * 4
}
// Placeholder message
Loader {
anchors.centerIn: parent
width: parent.width - (Kirigami.Units.largeSpacing * 4)
active: connectionView.count === 0
asynchronous: true
visible: status === Loader.Ready
sourceComponent: PlasmaExtras.PlaceholderMessage {
iconName: {
if (toolbar.displayplaneModeMessage) {
return "network-flightmode-on"
}
if (toolbar.displayWifiMessage) {
return "network-wireless-off"
}
if (toolbar.displayWwanMessage) {
return "network-mobile-off"
}
return "edit-none"
}
text: {
if (toolbar.displayplaneModeMessage) {
return i18n("Airplane mode is enabled")
}
if (toolbar.displayWifiMessage) {
if (toolbar.displayWwanMessage) {
return i18n("Wireless and mobile networks are deactivated")
}
return i18n("Wireless is deactivated")
}
if (toolbar.displayWwanMessage) {
return i18n("Mobile network is deactivated")
}
if (toolbar.searchTextField.text.length > 0) {
return i18n("No matches")
}
return i18n("No available connections")
}
}
}
}
}
}

View file

@ -0,0 +1,96 @@
/*
SPDX-FileCopyrightText: 2013-2017 Jan Grulich <jgrulich@redhat.com>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
import QtQuick 2.2
import QtQuick.Layouts 1.15
import org.kde.kquickcontrolsaddons 2.0 as KQuickControlsAddons
import org.kde.kirigami 2.20 as Kirigami
import org.kde.plasma.extras 2.0 as PlasmaExtras
import org.kde.plasma.components 3.0 as PlasmaComponents3
MouseArea {
height: detailsGrid.implicitHeight
property string connectionTitle: ""
property var details: []
acceptedButtons: Qt.RightButton
onPressed: mouse => {
const item = detailsGrid.childAt(mouse.x, mouse.y);
if (!item || !item.isContent) {
return;
}
contextMenu.show(this, item.text, mouse.x, mouse.y);
}
KQuickControlsAddons.Clipboard {
id: clipboard
}
PlasmaExtras.Menu {
id: contextMenu
property string text
function show(item, text, x, y) {
contextMenu.text = text
visualParent = item
open(x, y)
}
PlasmaExtras.MenuItem {
text: i18n("Copy")
icon: "edit-copy"
enabled: contextMenu.text !== ""
onClicked: clipboard.content = contextMenu.text
}
}
PlasmaComponents3.Label {
id: titleLabel
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.rightMargin: Kirigami.Units.smallSpacing
anchors.leftMargin: Kirigami.Units.smallSpacing
anchors.topMargin: -Kirigami.Units.smallSpacing*2
//horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignTop
text: connectionTitle + " " + i18n("Details")
}
GridLayout {
id: detailsGrid
width: parent.width
columns: 2
rowSpacing: Kirigami.Units.smallSpacing / 4
anchors.top: titleLabel.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.rightMargin: Kirigami.Units.smallSpacing
anchors.leftMargin: Kirigami.Units.smallSpacing
anchors.topMargin: Kirigami.Units.smallSpacing
Repeater {
id: repeater
model: details.length
PlasmaComponents3.Label {
Layout.fillWidth: true
readonly property bool isContent: index % 2
elide: isContent ? Text.ElideRight : Text.ElideNone
font: Kirigami.Theme.smallFont
horizontalAlignment: isContent ? Text.AlignRight : Text.AlignLeft
text: isContent ? details[index] : `${details[index]}:`
textFormat: Text.PlainText
opacity: isContent ? 1 : 0.6
}
}
}
}

View file

@ -0,0 +1,647 @@
/*
SPDX-FileCopyrightText: 2020 Nate Graham <nate@kde.org>
SPDX-License-Identifier: LGPL-2.0-or-later
*/
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import QtQuick.Templates as T
import org.kde.plasma.core as PlasmaCore
import org.kde.ksvg as KSvg
import org.kde.plasma.components as PlasmaComponents3
import org.kde.plasma.extras as PlasmaExtras
import org.kde.kirigami as Kirigami
import QtQuick.Controls 2.15 as QQC2
/**
* A list item that expands when clicked to show additional actions and/or a
* custom view.
* The list item has a standardized appearance, with an icon on the left badged
* with an optional emblem, a title and optional subtitle to the right, an
* optional default action button, and a button to expand and collapse the list
* item.
*
* When expanded, the list item shows a list of contextually-appropriate actions
* if contextualActions has been defined.
* If customExpandedViewContent has been defined, it will show a custom view.
* If both have been defined, it shows both, with the actions above the custom
* view.
*
* It is not valid to define neither; define one or both.
*
* Note: this component should only be used for lists where the maximum number
* of items is very low, ideally less than 10. For longer lists, consider using
* a different paradigm.
*
*
* Example usage:
*
* @code
* import QtQuick
* import QtQuick.Controls as QQC2
* import org.kde.kirigami as Kirigami
* import org.kde.plasma.extras as PlasmaExtras
* import org.kde.plasma.components as PlasmaComponents
*
* PlasmaComponents.ScrollView {
* ListView {
* anchors.fill: parent
* focus: true
* currentIndex: -1
* clip: true
* model: myModel
* highlight: PlasmaExtras.Highlight {}
* highlightMoveDuration: Kirigami.Units.shortDuration
* highlightResizeDuration: Kirigami.Units.shortDuration
* delegate: PlasmaExtras.ExpandableListItem {
* icon: model.iconName
* iconEmblem: model.isPaused ? "emblem-pause" : ""
* title: model.name
* subtitle: model.subtitle
* isDefault: model.isDefault
* defaultActionButtonAction: QQC2.Action {
* icon.name: model.isPaused ? "media-playback-start" : "media-playback-pause"
* text: model.isPaused ? "Resume" : "Pause"
* onTriggered: {
* if (model.isPaused) {
* model.resume(model.name);
* } else {
* model.pause(model.name);
* }
* }
* }
* contextualActions: [
* QQC2.Action {
* icon.name: "configure"
* text: "Configure…"
* onTriggered: model.configure(model.name);
* }
* ]
* }
* }
* }
* @endcode
*/
Item {
id: listItem
property alias mouseArea: listMouseArea
/**
* icon: var
* The name of the icon used in the list item.
* @sa Kirigami.Icon::source
*
* Required.
*/
property alias icon: listItemIcon.source
/**
* iconEmblem: var
* The name of the emblem to badge the icon with.
* @sa Kirigami.Icon::source
*
* Optional, defaults to nothing, in which case there is no emblem.
*/
property alias iconEmblem: iconEmblem.source
/*
* title: string
* The name or title for this list item.
*
* Optional; if not defined, there will be no title and the subtitle will be
* vertically centered in the list item.
*/
property alias title: listItemTitle.text
/*
* subtitle: string
* The subtitle for this list item, displayed under the title.
*
* Optional; if not defined, there will be no subtitle and the title will be
* vertically centered in the list item.
*/
property alias subtitle: listItemSubtitle.text
/*
* subtitleCanWrap: bool
* Whether to allow the subtitle to become a multi-line string instead of
* eliding when the text is very long.
*
* Optional, defaults to false.
*/
property bool subtitleCanWrap: false
/*
* subtitleColor: color
* The color of the subtitle text
*
* Optional; if not defined, the subtitle will use the default text color
*/
property alias subtitleColor: listItemSubtitle.color
/*
* allowStyledText: bool
* Whether to allow the title, subtitle, and tooltip to contain styled text.
* For performance and security reasons, keep this off unless needed.
*
* Optional, defaults to false.
*/
property bool allowStyledText: false
/*
* defaultActionButtonAction: T.Action
* The Action to execute when the default button is clicked.
*
* Optional; if not defined, no default action button will be displayed.
*/
property alias defaultActionButtonAction: defaultActionButton.action
/*
* defaultActionButtonVisible: bool
* When/whether to show to default action button. Useful for making it
* conditionally appear or disappear.
*
* Optional; defaults to true
*/
property bool defaultActionButtonVisible: true
/*
* showDefaultActionButtonWhenBusy : bool
* Whether to continue showing the default action button while the busy
* indicator is visible. Useful for cancelable actions that could take a few
* seconds and show a busy indicator while processing.
*
* Optional; defaults to false
*/
property bool showDefaultActionButtonWhenBusy: false
/*
* contextualActions: list<T.Action>
* A list of standard QQC2.Action objects that describes additional actions
* that can be performed on this list item. For example:
*
* @code
* contextualActions: [
* Action {
* text: "Do something"
* icon.name: "document-edit"
* onTriggered: doSomething()
* },
* Action {
* text: "Do something else"
* icon.name: "draw-polygon"
* onTriggered: doSomethingElse()
* },
* Action {
* text: "Do something completely different"
* icon.name: "games-highscores"
* onTriggered: doSomethingCompletelyDifferent()
* }
* ]
* @endcode
*
* Optional; if not defined, no contextual actions will be displayed and
* you should instead assign a custom view to customExpandedViewContent,
* which will be shown when the user expands the list item.
*/
property list<T.Action> contextualActions
readonly property list<T.Action> __enabledContextualActions: contextualActions.filter(action => action?.enabled ?? false)
/*
* A custom view to display when the user expands the list item.
*
* This component must define width and height properties. Width should be
* equal to the width of the list item itself, while height: will depend
* on the component itself.
*
* Optional; if not defined, no custom view actions will be displayed and
* you should instead define contextualActions, and then actions will
* be shown when the user expands the list item.
*/
property Component customExpandedViewContent
/*
* The actual instance of the custom view content, if loaded.
* @since 5.72
*/
property alias customExpandedViewContentItem: customContentLoader.item
/*
* isBusy: bool
* Whether or not to display a busy indicator on the list item. Set to true
* while the item should be non-interactive because things are processing.
*
* Optional; defaults to false.
*/
property bool isBusy: false
/*
* isDefault: bool
* Whether or not this list item should be considered the "default" or
* "Current" item in the list. When set to true, and the list itself has
* more than one item in it, the list item's title and subtitle will be
* drawn in a bold style.
*
* Optional; defaults to false.
*/
property bool isDefault: false
/**
* expanded: bool
* Whether the expanded view is visible.
*
* @since 5.98
*/
readonly property alias expanded: expandedView.expanded
/*
* hasExpandableContent: bool (read-only)
* Whether or not this expandable list item is actually expandable. True if
* this item has either a custom view or else at least one enabled action.
* Otherwise false.
*/
readonly property bool hasExpandableContent: customExpandedViewContent !== null || __enabledContextualActions.length > 0
/*
* expand()
* Show the expanded view, growing the list item to its taller size.
*/
function expand() {
if (!listItem.hasExpandableContent) {
return;
}
expandedView.expanded = true
listItem.itemExpanded()
}
/*
* collapse()
* Hide the expanded view and collapse the list item to its shorter size.
*/
function collapse() {
if (!listItem.hasExpandableContent) {
return;
}
expandedView.expanded = false
listItem.itemCollapsed()
}
/*
* toggleExpanded()
* Expand or collapse the list item depending on its current state.
*/
function toggleExpanded() {
if (!listItem.hasExpandableContent) {
return;
}
expandedView.expanded ? listItem.collapse() : listItem.expand()
}
signal itemExpanded()
signal itemCollapsed()
width: parent ? parent.width : undefined // Assume that we will be used as a delegate, not placed in a layout
height: mainLayout.height
/* Behavior on height {
enabled: false //listItem.ListView.view.highlightResizeDuration > 0
SmoothedAnimation { // to match the highlight
id: heightAnimation
duration: listItem.ListView.view.highlightResizeDuration || -1
velocity: listItem.ListView.view.highlightResizeVelocity
easing.type: Easing.InOutCubic
}
}
clip: heightAnimation.running || expandedItemOpacityFade.running*/
onEnabledChanged: if (!listItem.enabled) { collapse() }
Keys.onPressed: event => {
if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
if (defaultActionButtonAction) {
defaultActionButtonAction.trigger()
} else {
toggleExpanded();
}
event.accepted = true;
} else if (event.key === Qt.Key_Escape) {
if (expandedView.expanded) {
collapse();
event.accepted = true;
}
// if not active, we'll let the Escape event pass through, so it can close the applet, etc.
} else if (event.key === Qt.Key_Space) {
toggleExpanded();
event.accepted = true;
}
}
/*KeyNavigation.tab: defaultActionButtonVisible ? defaultActionButton : expandToggleButton
KeyNavigation.right: defaultActionButtonVisible ? defaultActionButton : expandToggleButton
KeyNavigation.down: expandToggleButton.KeyNavigation.down*/
Keys.onDownPressed: event => {
if (!actionsListLoader.item || ListView.view.currentIndex < 0) {
ListView.view.incrementCurrentIndex();
ListView.view.currentItem.forceActiveFocus(Qt.TabFocusReason);
event.accepted = true;
return;
}
event.accepted = false; // Forward to KeyNavigation.down
}
Keys.onUpPressed: event => {
if (ListView.view.currentIndex === 0) {
event.accepted = false;
} else {
ListView.view.decrementCurrentIndex();
ListView.view.currentItem.forceActiveFocus(Qt.BacktabFocusReason);
}
}
Accessible.role: Accessible.Button
Accessible.name: title
Accessible.description: subtitle
// Handle left clicks and taps; don't accept stylus input or else it steals
// events from the buttons on the list item
TapHandler {
enabled: listItem.hasExpandableContent
acceptedPointerTypes: PointerDevice.Generic | PointerDevice.Finger
onSingleTapped: {
listItem.ListView.view.currentIndex = index
listItem.toggleExpanded()
}
}
MouseArea {
id: listMouseArea
anchors.fill: parent
// This MouseArea used to intercept RightButton to open a context
// menu, but that has been removed, and now it's only used for hover
acceptedButtons: Qt.RightButton
hoverEnabled: true
// using onPositionChanged instead of onContainsMouseChanged so this doesn't trigger when the list reflows
onPositionChanged: {
// don't change currentIndex if it would make listview scroll
// see https://bugs.kde.org/show_bug.cgi?id=387797
// this is a workaround till https://bugreports.qt.io/browse/QTBUG-114574 gets fixed
// which would allow a proper solution
if (parent.y - listItem.ListView.view.contentY >= 0 && parent.y - listItem.ListView.view.contentY + parent.height + 1 /* border */ < listItem.ListView.view.height) {
listItem.ListView.view.currentIndex = (containsMouse ? index : -1)
}
}
onExited: if (listItem.ListView.view.currentIndex === index) {
listItem.ListView.view.currentIndex = -1;
}
ColumnLayout {
id: mainLayout
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
spacing: 0
RowLayout {
id: mainRowLayout
Layout.fillWidth: true
Layout.margins: Kirigami.Units.smallSpacing
// Otherwise it becomes taller when the button appears
Layout.minimumHeight: defaultActionButton.height
// Icon and optional emblem
Kirigami.Icon {
id: listItemIcon
implicitWidth: Kirigami.Units.iconSizes.medium
implicitHeight: Kirigami.Units.iconSizes.medium
Kirigami.Icon {
id: iconEmblem
visible: valid
anchors.right: parent.right
anchors.bottom: parent.bottom
implicitWidth: Kirigami.Units.iconSizes.small
implicitHeight: Kirigami.Units.iconSizes.small
}
}
// Title and subtitle
ColumnLayout {
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
spacing: 0
Kirigami.Heading {
id: listItemTitle
visible: text.length > 0
Layout.fillWidth: true
level: 5
textFormat: listItem.allowStyledText ? Text.StyledText : Text.PlainText
elide: Text.ElideRight
maximumLineCount: 1
// Even if it's the default item, only make it bold when
// there's more than one item in the list, or else there's
// only one item and it's bold, which is a little bit weird
font.weight: listItem.isDefault && listItem.ListView.view.count > 1
? Font.Bold
: Font.Normal
}
PlasmaComponents3.Label {
id: listItemSubtitle
visible: text.length > 0
font: Kirigami.Theme.smallFont
// Otherwise colored text can be hard to see
opacity: color === Kirigami.Theme.textColor ? 0.7 : 1.0
Layout.fillWidth: true
textFormat: listItem.allowStyledText ? Text.StyledText : Text.PlainText
elide: Text.ElideRight
maximumLineCount: subtitleCanWrap ? 9999 : 1
wrapMode: subtitleCanWrap ? Text.WordWrap : Text.NoWrap
}
}
// Busy indicator
PlasmaComponents3.BusyIndicator {
id: busyIndicator
visible: listItem.isBusy
// Otherwise it makes the list item taller when it appears
Layout.maximumHeight: defaultActionButton.implicitHeight
Layout.maximumWidth: Layout.maximumHeight
}
// Default action button
PlasmaComponents3.ToolButton {
id: defaultActionButton
visible: defaultActionButtonAction
&& listItem.defaultActionButtonVisible
&& (!busyIndicator.visible || listItem.showDefaultActionButtonWhenBusy)
/*KeyNavigation.tab: expandToggleButton
KeyNavigation.right: expandToggleButton
KeyNavigation.down: expandToggleButton.KeyNavigation.down*/
Keys.onUpPressed: event => listItem.Keys.upPressed(event)
Accessible.name: action !== null ? action.text : ""
}
// Expand/collapse button
/*PlasmaComponents3.ToolButton {
id: expandToggleButton
visible: listItem.hasExpandableContent
display: PlasmaComponents3.AbstractButton.IconOnly
text: expandedView.expanded ? i18ndc("libplasma6", "@action:button", "Collapse") : i18ndc("libplasma6", "@action:button", "Expand")
icon.name: expandedView.expanded ? "collapse" : "expand"
Keys.onUpPressed: event => listItem.Keys.upPressed(event)
onClicked: listItem.toggleExpanded()
PlasmaComponents3.ToolTip {
text: parent.text
}
}*/
}
// Expanded view with actions and/or custom content in it
Item {
id: expandedView
property bool expanded: false
Layout.preferredHeight: expanded ?
expandedViewLayout.implicitHeight + expandedViewLayout.anchors.topMargin + expandedViewLayout.anchors.bottomMargin : 0
Layout.fillWidth: true
opacity: expanded ? 1 : 0
/* Behavior on opacity {
enabled: false //listItem.ListView.view.highlightResizeDuration > 0
SmoothedAnimation { // to match the highlight
id: expandedItemOpacityFade
duration: listItem.ListView.view.highlightResizeDuration || -1
// velocity is divided by the default speed, as we're in the range 0-1
velocity: listItem.ListView.view.highlightResizeVelocity / 200
easing.type: Easing.InOutCubic
}
}*/
visible: opacity > 0
ColumnLayout {
id: expandedViewLayout
anchors.fill: parent
anchors.margins: Kirigami.Units.smallSpacing
spacing: Kirigami.Units.smallSpacing
// Actions list
Loader {
id: actionsListLoader
visible: status === Loader.Ready
active: expandedView.visible && listItem.__enabledContextualActions.length > 0
Layout.fillWidth: true
sourceComponent: Item {
height: buttonFlow.implicitHeight /2
width: actionsListLoader.width // basically, parent.width but null-proof
Flow {
id: buttonFlow
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
//anchors.leftMargin: Kirigami.Units.iconSizes.small
//anchors.rightMargin: Kirigami.Units.iconSizes.small
spacing: Kirigami.Units.smallSpacing
layoutDirection: Qt.RightToLeft
Repeater {
id: actionRepeater
model: listItem.__enabledContextualActions
delegate: QQC2.Button {
id: buttonDelegate
required property int index
required property T.Action modelData
//Layout.fillWidth: true
//Layout.alignment: Qt.AlignRight
text: modelData.text
icon.name: modelData.icon.name
//KeyNavigation.up: index > 0 ? actionRepeater.itemAt(index - 1) : expandToggleButton
Keys.onDownPressed: event => {
if (index === actionRepeater.count - 1) {
event.accepted = true;
listItem.ListView.view.incrementCurrentIndex();
listItem.ListView.view.currentItem.forceActiveFocus(Qt.TabFocusReason);
} else {
event.accepted = false; // Forward to KeyNavigation.down
}
}
onClicked: {
modelData.trigger()
collapse()
}
}
}
}
}
}
// Separator between the two items when both are shown
KSvg.SvgItem {
Layout.fillWidth: true
imagePath: "widgets/line"
elementId: "horizontal-line"
visible: actionsListLoader.visible && customContentLoader.visible
}
// Custom content item, if any
Loader {
id: customContentLoader
visible: status === Loader.Ready
Layout.fillWidth: true
active: expandedView.visible
asynchronous: true
sourceComponent: listItem.customExpandedViewContent
}
}
}
}
}
}

View file

@ -0,0 +1,64 @@
/*
SPDX-FileCopyrightText: 2010 Marco Martin <notmart@gmail.com>
SPDX-FileCopyrightText: 2016 Jan Grulich <jgrulich@redhat.com>
SPDX-FileCopyrightText: 2020 George Vogiatzis <gvgeo@protonmail.com>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
import QtQuick 2.1
import org.kde.kirigami 2.20 as Kirigami
import org.kde.ksvg 1.0 as KSvg
/**
* Ignores the theme's listItem margins, and uses custom highlight(pressed) area.
* Could break some themes but the majority look fine.
* Also includes a separator to be used in sections.
*/
MouseArea {
id: listItem
property bool checked: false
property bool separator: false
property rect highlightRect: Qt.rect(0, 0, width, height)
width: parent.width
// Sections have spacing above but not below. Will use 2 of them below.
height: separator ? separatorLine.height + Kirigami.Units.smallSpacing * 3 : parent.height
hoverEnabled: true
KSvg.SvgItem {
id: separatorLine
anchors {
horizontalCenter: parent.horizontalCenter
top: parent.top
topMargin: Kirigami.Units.smallSpacing
}
imagePath: "widgets/line"
elementId: "horizontal-line"
width: parent.width - Kirigami.Units.iconSizes.small * 2
visible: separator
}
KSvg.FrameSvgItem {
id: background
imagePath: "widgets/listitem"
prefix: "normal"
anchors.fill: parent
visible: separator ? false : true
}
KSvg.FrameSvgItem {
id: pressed
imagePath: "widgets/listitem"
prefix: "pressed"
opacity: checked ? 1 : 0
Behavior on opacity { NumberAnimation { duration: Kirigami.Units.shortDuration } }
x: highlightRect.x
y: highlightRect.y
height: highlightRect.height
width: highlightRect.width
}
}

View file

@ -0,0 +1,25 @@
/*
SPDX-FileCopyrightText: 2023 Kai Uwe Broulik <kde@broulik.de>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
import QtQuick 2.15
import QtQuick.Layouts 1.2
import org.kde.plasma.components 3.0 as PlasmaComponents3
import org.kde.kirigami 2.20 as Kirigami
import org.kde.plasma.extras 2.0 as PlasmaExtras
DetailsText {
id: detailsTextColumn
/*width: parent.width
* visible: detailsTabBar.currentIndex === 1
*
* activeFocusOnTab: details.length > 0*/
//details: page.details //ConnectionDetails
Accessible.description: details.join(" ")
}

View file

@ -0,0 +1,20 @@
/*
SPDX-FileCopyrightText: 2013-2017 Jan Grulich <jgrulich@redhat.com>
SPDX-FileCopyrightText: 2023 ivan tkachenko <me@ratijas.tk>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
import QtQuick
import org.kde.plasma.networkmanagement as PlasmaNM
import org.kde.plasma.extras as PlasmaExtras
PlasmaExtras.PasswordField {
property int/*PlasmaNM.Enums.SecurityType*/ securityType
validator: RegularExpressionValidator {
regularExpression: (securityType === PlasmaNM.Enums.StaticWep)
? /^(?:.{5}|[0-9a-fA-F]{10}|.{13}|[0-9a-fA-F]{26})$/
: /^(?:.{8,64})$/
}
}

View file

@ -0,0 +1,162 @@
/*
SPDX-FileCopyrightText: 2013-2017 Jan Grulich <jgrulich@redhat.com>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
import QtQuick 2.15
import QtQuick.Controls 2.15 as QQC2
import QtQuick.Layouts 1.2
import org.kde.plasma.components 3.0 as PlasmaComponents3
import org.kde.plasma.extras 2.0 as PlasmaExtras
import org.kde.plasma.networkmanagement as PlasmaNM
import org.kde.plasma.plasmoid 2.0
PlasmaExtras.Representation {
id: full
required property PlasmaNM.Handler nmHandler
required property PlasmaNM.NetworkStatus nmStatus
collapseMarginsHint: true
Component {
id: networkModelComponent
PlasmaNM.NetworkModel {}
}
property PlasmaNM.NetworkModel connectionModel: null
PlasmaNM.AppletProxyModel {
id: appletProxyModel
sourceModel: full.connectionModel
}
header: PlasmaExtras.PlasmoidHeading {
focus: true
contentItem: RowLayout {
Layout.fillWidth: true
Toolbar {
id: toolbar
Layout.fillWidth: true
hasConnections: connectionListPage.count > 0
visible: stack.depth === 1
}
Loader {
sourceComponent: stack.currentItem?.headerItems
visible: !!item
}
}
}
Connections {
target: full.nmHandler
function onWifiCodeReceived(data, ssid) {
if (data.length === 0) {
console.error("Cannot create QR code component: Unsupported connection");
return;
}
const showQRComponent = Qt.createComponent("ShareNetworkQrCodePage.qml");
if (showQRComponent.status === Component.Error) {
console.warn("Cannot create QR code component:", showQRComponent.errorString());
return;
}
mainWindow.expanded = true; // just in case.
stack.push(showQRComponent, {
content: data,
ssid
});
}
}
Keys.forwardTo: [stack.currentItem]
Keys.onPressed: event => {
if (event.modifiers & Qt.ControlModifier && event.key == Qt.Key_F) {
toolbar.searchTextField.forceActiveFocus();
toolbar.searchTextField.selectAll();
event.accepted = true;
} else if (event.key === Qt.Key_Back || (event.modifiers & Qt.AltModifier && event.key == Qt.Key_Left)) {
if (stack.depth > 1) {
stack.pop();
event.accepted = true;
}
} else {
event.accepted = false;
}
}
QQC2.StackView {
id: stack
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: backButton.top
initialItem: ConnectionListPage {
id: connectionListPage
model: appletProxyModel
nmStatus: full.nmStatus
}
popEnter: Transition {}
popExit: Transition {}
pushEnter: Transition {}
pushExit: Transition {}
replaceEnter: Transition {}
replaceExit: Transition {}
}
QQC2.Button {
id: backButton
//anchors.top: stack.bottom
anchors.left: parent.left
anchors.bottom: parent.bottom
//Layout.alignment: Qt.AlignRight | Qt.AlignBottom
text: "Back"
visible: stack.depth > 1
onClicked: {
stack.pop()
}
}
/*PlasmaExtras.Heading {
Layout.fillWidth: true
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
horizontalAlignment: Qt.AlignHCenter
text: i18nc("@action:button", "Return to Network Connections")
visible: stack.depth > 1
color: "#0066cc" //heading_ma.containsPress ? "#90e7ff" : (heading_ma.containsMouse ? "#b6ffff" : Kirigami.Theme.textColor)
font.underline: heading_ma.containsMouse
level: 5
MouseArea {
id: heading_ma
anchors.fill: parent
hoverEnabled: true
onClicked: {
stack.pop()
}
cursorShape: Qt.PointingHandCursor
z: 5
}
}*/
Connections {
target: mainWindow
function onExpandedChanged(expanded) {
if (expanded) {
handler.requestScan();
if (!full.connectionModel) {
full.connectionModel = networkModelComponent.createObject(full);
}
} else {
if (full.connectionModel) {
full.connectionModel.destroy();
full.connectionModel = null;
}
}
}
}
}

View file

@ -0,0 +1,40 @@
/*
SPDX-FileCopyrightText: 2023 Kai Uwe Broulik <kde@broulik.de>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
import QtQuick 2.15
import QtQuick.Layouts 1.2
import org.kde.plasma.components 3.0 as PlasmaComponents3
import org.kde.kirigami 2.20 as Kirigami
import org.kde.prison 1.0 as Prison
ColumnLayout {
id: page
property string ssid
property alias content: barcode.content
spacing: Kirigami.Units.smallSpacing
PlasmaComponents3.Label {
Layout.topMargin: page.spacing
Layout.alignment: Qt.AlignHCenter
Layout.minimumWidth: barcode.height
Layout.maximumWidth: barcode.width - page.spacing * 2
wrapMode: Text.WordWrap
horizontalAlignment: Text.AlignHCenter
textFormat: Text.PlainText
text: i18n("Scan this QR code with another device to connect to the \"%1\" network.", page.ssid)
}
Prison.Barcode {
id: barcode
Layout.fillWidth: true
Layout.fillHeight: true
barcodeType: Prison.Barcode.QRCode
}
}

View file

@ -0,0 +1,18 @@
/*
SPDX-FileCopyrightText: 2023 Kai Uwe Broulik <kde@broulik.de>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
import QtQuick 2.15
import QtQuick.Layouts 1.2
import org.kde.plasma.components 3.0 as PlasmaComponents3
import org.kde.kirigami 2.20 as Kirigami
import org.kde.plasma.extras 2.0 as PlasmaExtras
TrafficMonitor {
id: trafficMonitorGraph
Accessible.description: i18nc("@info:tooltip", "Current download speed is %1 kibibytes per second; current upload speed is %2 kibibytes per second", Math.round(rxSpeed / 1024), Math.round(txSpeed / 1024))
//width: parent.width
}

View file

@ -0,0 +1,115 @@
/*
SPDX-FileCopyrightText: 2016 Marco Martin <mart@kde.org>
SPDX-FileCopyrightText: 2016 The Qt Company Ltd.
SPDX-License-Identifier: LGPL-2.0-or-later
*/
import QtQuick
import QtQuick.Templates as T
import org.kde.ksvg as KSvg
//NOTE: importing PlasmaCore is necessary in order to make KSvg load the current Plasma Theme
import org.kde.plasma.core as PlasmaCore
import org.kde.plasma.components 3.0 as PlasmaComponents3
import org.kde.kirigami as Kirigami
T.ToolTip {
id: control
x: parent ? Math.round((parent.width - implicitWidth) / 2) : 0
y: -implicitHeight - 3
visible: parent instanceof T.AbstractButton && (Kirigami.Settings.tabletMode ? parent.pressed : parent.hovered) && text.length > 0
delay: Kirigami.Settings.tabletMode ? Qt.styleHints.mousePressAndHoldInterval : Kirigami.Units.toolTipDelay
// Never time out while being hovered; it's annoying
timeout: -1
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset, contentHeight + topPadding + bottomPadding)
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset, contentWidth + leftPadding + rightPadding)
margins: Kirigami.Units.smallSpacing
topPadding: backgroundItem.margins.top
leftPadding: backgroundItem.margins.left
rightPadding: backgroundItem.margins.right
bottomPadding: backgroundItem.margins.bottom
enter: Transition {
NumberAnimation {
property: "opacity"
from: 0.0
to: 1.0
duration: Kirigami.Units.longDuration
easing.type: Easing.OutCubic
}
}
exit: Transition {
NumberAnimation {
property: "opacity"
from: 1.0
to: 0.0
duration: Kirigami.Units.longDuration
easing.type: Easing.OutCubic
}
}
closePolicy: T.Popup.CloseOnEscape | T.Popup.CloseOnPressOutsideParent | T.Popup.CloseOnReleaseOutsideParent
contentItem: Item {
implicitWidth: Math.min(label.maxTextLength, label.contentWidth)
implicitHeight: label.implicitHeight
PlasmaComponents3.Label {
id: label
// This value is basically arbitrary. It just looks nice.
readonly property double maxTextLength: Kirigami.Units.iconSizes.small * 14
// Strip out ampersands right before non-whitespace characters, i.e.
// those used to determine the alt key shortcut
text: control.text.replace(/&(?=\S)/g, "")
wrapMode: Text.WordWrap
font: control.font
Kirigami.Theme.colorSet: Kirigami.Theme.Tooltip
Kirigami.Theme.inherit: false
// ensure that long text actually gets wrapped
onLineLaidOut: (line) => {
if (line.implicitWidth > maxTextLength) {
line.width = maxTextLength
}
}
}
}
background: Item {
implicitHeight: Kirigami.Units.iconSizes.small + backgroundItem.margins.top + backgroundItem.margins.bottom
implicitWidth: Kirigami.Units.iconSizes.small + backgroundItem.margins.left + backgroundItem.margins.right
KSvg.FrameSvgItem {
anchors {
fill: parent
topMargin: -margins.top
leftMargin: -margins.left
rightMargin: -margins.right
bottomMargin: -margins.bottom
}
imagePath: "solid/widgets/tooltip"
prefix: "shadow"
Kirigami.Theme.colorSet: Kirigami.Theme.Tooltip
Kirigami.Theme.inherit: false
}
KSvg.FrameSvgItem {
id: backgroundItem
anchors.fill: parent
// Because the transparent one doesn't match the appearance of all
// other ones
imagePath: "solid/widgets/tooltip"
Kirigami.Theme.colorSet: Kirigami.Theme.Tooltip
Kirigami.Theme.inherit: false
}
}
}

View file

@ -0,0 +1,226 @@
/*
SPDX-FileCopyrightText: 2013-2017 Jan Grulich <jgrulich@redhat.com>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
import QtQuick 2.2
import QtQuick.Layouts 1.2
import org.kde.plasma.components 3.0 as PlasmaComponents3
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.kirigami 2.20 as Kirigami
import org.kde.plasma.extras 2.0 as PlasmaExtras
import org.kde.plasma.networkmanagement as PlasmaNM
import org.kde.kcmutils as KCMUtils
import QtQuick.Controls 2.15 as QQC2
ColumnLayout {
id: toolbar
readonly property var displayWifiMessage: !wifiSwitchButton.checked && wifiSwitchButton.visible
readonly property var displayWwanMessage: !wwanSwitchButton.checked && wwanSwitchButton.visible
readonly property var displayplaneModeMessage: planeModeSwitchButton.checked && planeModeSwitchButton.visible
property bool hasConnections
property alias searchTextField: searchTextField
PlasmaNM.EnabledConnections {
id: enabledConnections
// When user interactively toggles a checkbox, a binding may be
// preserved, but the state gets out of sync until next relevant
// notify signal is dispatched. So, we refresh the bindings here.
onWirelessEnabledChanged: wifiSwitchButton.checked = Qt.binding(() =>
wifiSwitchButton.administrativelyEnabled && enabledConnections.wirelessEnabled
);
onWwanEnabledChanged: wwanSwitchButton.checked = Qt.binding(() =>
wwanSwitchButton.administrativelyEnabled && enabledConnections.wwanEnabled
);
}
spacing: Kirigami.Units.smallSpacing * 3
RowLayout {
// Add margin before switches for consistency with other applets
Layout.leftMargin: Kirigami.Units.smallSpacing / 2
spacing: parent.spacing
// Only show when switches are visible (and avoid parent spacing otherwise)
visible: availableDevices.wirelessDeviceAvailable || availableDevices.modemDeviceAvailable
QQC2.CheckBox {
id: wifiSwitchButton
// can't overload Item::enabled, because it's being used for other things, like Edit Mode on a desktop
readonly property bool administrativelyEnabled:
!PlasmaNM.Configuration.airplaneModeEnabled
&& availableDevices.wirelessDeviceAvailable
&& enabledConnections.wirelessHwEnabled
checked: administrativelyEnabled && enabledConnections.wirelessEnabled
enabled: administrativelyEnabled
icon.name: administrativelyEnabled ? ( timer.running ? "network-wireless-acquiring" : "network-wireless-on" ) : "network-wireless-off"
visible: availableDevices.wirelessDeviceAvailable
KeyNavigation.right: wwanSwitchButton.visible ? wwanSwitchButton : wwanSwitchButton.KeyNavigation.right
onToggled: handler.enableWireless(checked);
text: i18n("Wi-Fi")
PlasmaComponents3.BusyIndicator {
parent: wifiSwitchButton
anchors {
fill: wifiSwitchButton.contentItem
leftMargin: wifiSwitchButton.indicator.width + wifiSwitchButton.spacing
}
z: 1
visible: false
// Scanning may be too fast to notice. Prolong the animation up to at least `humanMoment`.
running: handler.scanning || timer.running
Timer {
id: timer
interval: Kirigami.Units.humanMoment
}
Connections {
target: handler
function onScanningChanged() {
if (handler.scanning) {
timer.restart();
}
}
}
}
}
QQC2.CheckBox {
id: wwanSwitchButton
// can't overload Item::enabled, because it's being used for other things, like Edit Mode on a desktop
readonly property bool administrativelyEnabled:
!PlasmaNM.Configuration.airplaneModeEnabled
&& availableDevices.modemDeviceAvailable
&& enabledConnections.wwanHwEnabled
checked: administrativelyEnabled && enabledConnections.wwanEnabled
enabled: administrativelyEnabled
icon.name: administrativelyEnabled ? "network-mobile-on" : "network-mobile-off"
visible: availableDevices.modemDeviceAvailable
KeyNavigation.left: wifiSwitchButton
KeyNavigation.right: planeModeSwitchButton.visible ? planeModeSwitchButton : planeModeSwitchButton.KeyNavigation.right
onToggled: handler.enableWwan(checked);
text: i18n("Mobile network")
}
QQC2.CheckBox {
id: planeModeSwitchButton
property bool initialized: false
checked: PlasmaNM.Configuration.airplaneModeEnabled
icon.name: PlasmaNM.Configuration.airplaneModeEnabled ? "network-flightmode-on" : "network-flightmode-off"
visible: availableDevices.modemDeviceAvailable || availableDevices.wirelessDeviceAvailable
KeyNavigation.left: wwanSwitchButton.visible ? wwanSwitchButton : wwanSwitchButton.KeyNavigation.left
KeyNavigation.right: hotspotButton.visible ? hotspotButton : hotspotButton.KeyNavigation.right
text: i18n("Airplane mode")
onToggled: {
handler.enableAirplaneMode(checked);
PlasmaNM.Configuration.airplaneModeEnabled = checked;
}
}
}
PlasmaComponents3.ToolButton {
id: hotspotButton
visible: false//handler.hotspotSupported
checkable: true
text: i18n("Hotspot")
icon.name: "network-wireless-on"
KeyNavigation.left: planeModeSwitchButton.visible ? planeModeSwitchButton : planeModeSwitchButton.KeyNavigation.left
KeyNavigation.right: searchTextField
onClicked: {
if (PlasmaNM.Configuration.hotspotConnectionPath) {
checked = false
handler.stopHotspot()
} else {
checked = true
handler.createHotspot()
}
}
PlasmaComponents3.ToolTip {
id: tooltip
}
Connections {
target: handler
function onHotspotCreated() {
hotspotButton.checked = true
tooltip.text = i18n("Disable Hotspot")
}
function onHotspotDisabled() {
hotspotButton.checked = false
tooltip.text = i18n("Create Hotspot")
}
}
Component.onCompleted: {
checked = PlasmaNM.Configuration.hotspotConnectionPath
tooltip.text = PlasmaNM.Configuration.hotspotConnectionPath ? i18n("Disable Hotspot") : i18n("Create Hotspot")
}
}
PlasmaExtras.SearchField {
id: searchTextField
Layout.fillWidth: true
visible: false
enabled: toolbar.hasConnections || text.length > 0
// This uses expanded to ensure the binding gets reevaluated
// when the plasmoid is shown again and that way ensure we are
// always in the correct state on show.
focus: mainWindow.expanded && !Kirigami.InputMethod.willShowOnActive
KeyNavigation.left: hotspotButton.visible ? hotspotButton : hotspotButton.KeyNavigation.left
KeyNavigation.right: openEditorButton
onTextChanged: {
appletProxyModel.setFilterFixedString(text)
}
}
PlasmaComponents3.ToolButton {
id: openEditorButton
visible: mainWindow.kcmAuthorized && !(plasmoid.containmentDisplayHints & PlasmaCore.Types.ContainmentDrawsPlasmoidHeading)
icon.name: "configure"
PlasmaComponents3.ToolTip {
text: i18n("Configure network connections…")
}
onClicked: {
KCMUtils.KCMLauncher.openSystemSettings(mainWindow.kcm)
}
}
}

View file

@ -0,0 +1,154 @@
/*
SPDX-FileCopyrightText: 2013-2017 Jan Grulich <jgrulich@redhat.com>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
import QtQuick
import QtQuick.Layouts
import org.kde.coreaddons as KCoreAddons
import org.kde.quickcharts as QuickCharts
import org.kde.quickcharts.controls as QuickChartsControls
import org.kde.plasma.components as PlasmaComponents3
import org.kde.kirigami as Kirigami
ColumnLayout {
property string connectionTitle: ""
property alias downloadSpeed: download.value
property alias uploadSpeed: upload.value
spacing: Kirigami.Units.smallSpacing
PlasmaComponents3.Label {
id: titleLabel
//Layout.fillWidth: true
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.rightMargin: Kirigami.Units.smallSpacing
anchors.leftMargin: Kirigami.Units.smallSpacing
anchors.topMargin: -Kirigami.Units.smallSpacing*2
//horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignTop
text: connectionTitle + " " + i18n("Speed")
}
Item {
Layout.leftMargin: Kirigami.Units.smallSpacing
Layout.fillWidth: true
Layout.fillHeight: true
implicitHeight: plotter.height + metricsLabel.implicitHeight
QuickChartsControls.AxisLabels {
id: verticalAxisLabels
anchors {
left: parent.left
top: plotter.top
bottom: plotter.bottom
}
width: metricsLabel.implicitWidth
constrainToBounds: false
direction: QuickChartsControls.AxisLabels.VerticalBottomTop
delegate: PlasmaComponents3.Label {
text: KCoreAddons.Format.formatByteSize(QuickChartsControls.AxisLabels.label) + i18n("/s")
font: metricsLabel.font
}
source: QuickCharts.ChartAxisSource {
chart: plotter
axis: QuickCharts.ChartAxisSource.YAxis
itemCount: 5
}
}
QuickChartsControls.GridLines {
anchors.fill: plotter
direction: QuickChartsControls.GridLines.Vertical
minor.visible: false
major.count: 3
major.lineWidth: 1
// Same calculation as Kirigami Separator
major.color: Kirigami.ColorUtils.linearInterpolation(Kirigami.Theme.backgroundColor, Kirigami.Theme.textColor, 0.4)
}
QuickCharts.LineChart {
id: plotter
anchors {
left: verticalAxisLabels.right
leftMargin: Kirigami.Units.smallSpacing
right: parent.right
top: parent.top
// Align plotter lines with labels.
topMargin: Math.round(metricsLabel.implicitHeight / 2) + Kirigami.Units.smallSpacing
}
height: Kirigami.Units.iconSizes.small * 8
interpolate: true
direction: QuickCharts.XYChart.ZeroAtEnd
yRange {
minimum: 100 * 1024
increment: 100 * 1024
}
valueSources: [
QuickCharts.HistoryProxySource {
source: QuickCharts.SingleValueSource {
id: upload
}
maximumHistory: 40
fillMode: QuickCharts.HistoryProxySource.FillFromStart
},
QuickCharts.HistoryProxySource {
source: QuickCharts.SingleValueSource {
id: download
}
maximumHistory: 40
fillMode: QuickCharts.HistoryProxySource.FillFromStart
}
]
nameSource: QuickCharts.ArraySource {
array: [i18n("Upload"), i18n("Download")]
}
colorSource: QuickCharts.ArraySource {
// Array.reverse() mutates the array but colors.colors is read-only.
array: [colors.colors[1], colors.colors[0]]
}
fillColorSource: QuickCharts.ArraySource {
array: plotter.colorSource.array.map(color => Qt.lighter(color, 1.5))
}
QuickCharts.ColorGradientSource {
id: colors
baseColor: Kirigami.Theme.highlightColor
itemCount: 2
}
}
// Note: TextMetrics might be using a different renderType by default,
// so we need a Label instance anyway.
PlasmaComponents3.Label {
id: metricsLabel
visible: false
font: Kirigami.Theme.smallFont
// Measure 888.8 KiB/s
text: KCoreAddons.Format.formatByteSize(910131) + i18n("/s")
}
}
QuickChartsControls.Legend {
chart: plotter
Layout.leftMargin: Kirigami.Units.smallSpacing
Layout.bottomMargin: Kirigami.Units.mediumSpacing*2
Layout.fillWidth: true
spacing: Kirigami.Units.largeSpacing
delegate: RowLayout {
spacing: Kirigami.Units.smallSpacing
QuickChartsControls.LegendLayout.maximumWidth: implicitWidth
Rectangle {
color: model.color
width: Kirigami.Units.smallSpacing
height: legendLabel.height
}
PlasmaComponents3.Label {
id: legendLabel
font: Kirigami.Theme.smallFont
text: model.name
}
}
}
}

View file

@ -0,0 +1,192 @@
/*
SPDX-FileCopyrightText: 2013-2017 Jan Grulich <jgrulich@redhat.com>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
import QtQuick 2.2
import org.kde.plasma.plasmoid 2.0
import org.kde.kirigami 2.20 as Kirigami
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.networkmanagement as PlasmaNM
import org.kde.networkmanager as NMQt
import QtQuick.Layouts 1.1
import org.kde.kcmutils as KCMUtils
import org.kde.config as KConfig
PlasmoidItem {
id: mainWindow
readonly property string kcm: "kcm_networkmanagement"
readonly property bool kcmAuthorized: KConfig.KAuthorized.authorizeControlModule("kcm_networkmanagement")
readonly property bool delayModelUpdates: fullRepresentationItem !== null
&& fullRepresentationItem.connectionModel !== null
&& fullRepresentationItem.connectionModel.delayModelUpdates
readonly property bool airplaneModeAvailable: availableDevices.modemDeviceAvailable || availableDevices.wirelessDeviceAvailable
readonly property bool inPanel: (Plasmoid.location === PlasmaCore.Types.TopEdge
|| Plasmoid.location === PlasmaCore.Types.RightEdge
|| Plasmoid.location === PlasmaCore.Types.BottomEdge
|| Plasmoid.location === PlasmaCore.Types.LeftEdge)
property alias planeModeSwitchAction: planeAction
Plasmoid.title: "Open Network and Sharing Center"
toolTipMainText: i18n("Networks")
toolTipSubText: {
const activeConnections = networkStatus.activeConnections;
if (!airplaneModeAvailable) {
return activeConnections;
}
if (PlasmaNM.Configuration.airplaneModeEnabled) {
return i18nc("@info:tooltip", "Middle-click to turn off Airplane Mode");
} else {
const hint = i18nc("@info:tooltip", "Middle-click to turn on Airplane Mode");
return activeConnections ? `${activeConnections}\n${hint}` : hint;
}
}
Plasmoid.busy: connectionIconProvider.connecting
Plasmoid.icon: inPanel ? connectionIconProvider.connectionIcon + "-symbolic" : connectionIconProvider.connectionTooltipIcon
switchWidth: Kirigami.Units.iconSizes.small * 10
switchHeight: Kirigami.Units.iconSizes.small * 10
// Only exists because the default CompactRepresentation doesn't expose
// a middle-click action.
// TODO remove once it gains that feature.
compactRepresentation: CompactRepresentation {
airplaneModeAvailable: mainWindow.airplaneModeAvailable
iconName: Plasmoid.icon
}
fullRepresentation: PopupDialog {
id: dialogItem
nmHandler: handler
nmStatus: networkStatus
readonly property int flyoutIntendedWidth: Kirigami.Units.iconSizes.small * 18
Layout.minimumWidth: Kirigami.Units.iconSizes.medium * 10
Layout.minimumHeight: Kirigami.Units.iconSizes.small * 20
anchors.fill: parent
focus: true
}
Connections {
target: handler
function onHotspotCreated() {
hotspotAction.checked = true
}
function onHotspotDisabled() {
hotspotAction.checked = false
}
}
Plasmoid.contextualActions: [
PlasmaCore.Action {
text: i18n("Enable Wi-Fi")
icon.name: "network-wireless-on"
priority: PlasmaCore.Action.LowPriority
checkable: true
checked: enabledConnections.wirelessEnabled
visible: enabledConnections.wirelessHwEnabled
&& availableDevices.wirelessDeviceAvailable
&& !PlasmaNM.Configuration.airplaneModeEnabled
onTriggered: checked => {handler.enableWireless(checked)}
},
PlasmaCore.Action {
text: i18n("Enable Mobile Network")
icon.name: "network-mobile-on"
priority: PlasmaCore.Action.LowPriority
checkable: true
checked: enabledConnections.wwanEnabled
visible: enabledConnections.wwanHwEnabled
&& availableDevices.modemDeviceAvailable
&& !PlasmaNM.Configuration.airplaneModeEnabled
onTriggered: checked => {handler.enableWwan(checked)}
},
PlasmaCore.Action {
id: planeAction
text: i18n("Enable Airplane Mode")
icon.name: "network-flightmode-on"
priority: PlasmaCore.Action.LowPriority
checkable: true
checked: PlasmaNM.Configuration.airplaneModeEnabled
visible: mainWindow.airplaneModeAvailable
onTriggered: checked => {
handler.enableAirplaneMode(checked)
PlasmaNM.Configuration.airplaneModeEnabled = checked
}
},
PlasmaCore.Action {
id: hotspotAction
text: PlasmaNM.Configuration.hotspotConnectionPath ? i18n("Disable Hotspot") : i18n("Create Hotspot")
priority: PlasmaCore.Action.LowPriority
checkable: true
//checked: PlasmaNM.Configuration.airplaneModeEnabled
visible: handler.hotspotSupported
onTriggered: checked => {
if (PlasmaNM.Configuration.hotspotConnectionPath) {
checked = false;
handler.stopHotspot();
} else {
checked = true;
handler.createHotspot();
}
}
Component.onCompleted: {
checked = PlasmaNM.Configuration.hotspotConnectionPath
}
},
PlasmaCore.Action {
text: i18n("Open Network Login Page…")
icon.name: "network-flightmode-on"
priority: PlasmaCore.Action.LowPriority
visible: networkStatus.connectivity === NMQt.NetworkManager.Portal
onTriggered: Qt.openUrlExternally("http://networkcheck.kde.org")
}
]
PlasmaCore.Action {
id: configureAction
text: i18n("&Configure Network Connections…")
icon.name: "configure"
visible: kcmAuthorized
shortcut: "alt+d, s"
onTriggered: KCMUtils.KCMLauncher.openSystemSettings(kcm)
}
Component.onCompleted: {
plasmoid.setInternalAction("configure", configureAction);
}
PlasmaNM.EnabledConnections {
id: enabledConnections
}
PlasmaNM.AvailableDevices {
id: availableDevices
}
PlasmaNM.NetworkStatus {
id: networkStatus
}
PlasmaNM.ConnectionIcon {
id: connectionIconProvider
connectivity: networkStatus.connectivity
}
PlasmaNM.Handler {
id: handler
}
Timer {
id: scanTimer
interval: 10200
repeat: true
running: mainWindow.expanded && !PlasmaNM.Configuration.airplaneModeEnabled && !mainWindow.delayModelUpdates
onTriggered: handler.requestScan()
}
}

View file

@ -0,0 +1,166 @@
{
"KPackageStructure": "Plasma/Applet",
"KPlugin": {
"Authors": [
{
"Email": "jgrulich@redhat.com,ltinkl@redhat.com",
"Name": "Jan Grulich,Lukáš Tinkl",
"Name[ar]": "Jan Grulich,Lukáš Tinkl",
"Name[az]": "Jan Grulich,Lukáš Tinkl",
"Name[bg]": "Jan Grulich,Lukáš Tinkl",
"Name[ca@valencia]": "Jan Grulich,Lukáš Tinkl",
"Name[ca]": "Jan Grulich,Lukáš Tinkl",
"Name[cs]": "Jan Grulich,Lukáš Tinkl",
"Name[de]": "Jan Grulich, Lukáš Tinkl",
"Name[el]": "Jan Grulich,Lukáš Tinkl",
"Name[en_GB]": "Jan Grulich,Lukáš Tinkl",
"Name[eo]": "Jan Grulich,Lukáš Tinkl",
"Name[es]": "Jan Grulich,Lukáš Tinkl",
"Name[eu]": "Jan Grulich,Lukáš Tinkl",
"Name[fi]": "Jan Grulich,Lukáš Tinkl",
"Name[fr]": "Jan Grulich, Lukáš Tinkl",
"Name[gl]": "Jan Grulich,Lukáš Tinkl",
"Name[he]": "יאן גרוליך, לוקאש טינקל",
"Name[hu]": "Jan Grulich,Lukáš Tinkl",
"Name[ia]": "Jan Grulich,Lukáš Tinkl",
"Name[is]": "Jan Grulich,Lukáš Tinkl",
"Name[it]": "Jan Grulich,Lukáš Tinkl",
"Name[ja]": "Jan Grulich,Lukáš Tinkl",
"Name[ka]": "Jan Grulich,Lukáš Tinkl",
"Name[ko]": "Jan Grulich,Lukáš Tinkl",
"Name[lt]": "Jan Grulich,Lukáš Tinkl",
"Name[lv]": "Jan Grulich,Lukáš Tinkl",
"Name[nl]": "Jan Grulich,Lukáš Tinkl",
"Name[nn]": "Jan Grulich, Lukáš Tinkl",
"Name[pl]": "Jan Grulich,Lukáš Tinkl",
"Name[pt]": "Jan Grulich,Lukáš Tinkl",
"Name[pt_BR]": "Jan Grulich,Lukáš Tinkl",
"Name[ro]": "Jan Grulich,Lukáš Tinkl",
"Name[ru]": "Jan Grulich,Lukáš Tinkl",
"Name[sk]": "Jan Grulich,Lukáš Tinkl",
"Name[sl]": "Jan Grulich,Lukáš Tinkl",
"Name[sv]": "Jan Grulich,Lukáš Tinkl",
"Name[ta]": "ஜான் குரூலிச், லூக்காஸ் டிங்க்குள்",
"Name[tr]": "Jan Grulich,Lukáš Tinkl",
"Name[uk]": "Jan Grulich, Lukáš Tinkl",
"Name[x-test]": "xxJan Grulich,Lukáš Tinklxx",
"Name[zh_CN]": "Jan Grulich,Lukáš Tinkl",
"Name[zh_TW]": "Jan Grulich,Lukáš Tinkl"
}
],
"BugReportUrl": "https://bugs.kde.org/enter_bug.cgi?product=plasma-nm&component=applet",
"Category": "System Information",
"Description": "Network status and control",
"Description[ar]": "حالة الشّبكة والتّحكّم بها",
"Description[az]": "Şəbəkə statusu və idarəsi",
"Description[bg]": "Състояние и контрол на мрежата",
"Description[ca@valencia]": "Control i estat de la xarxa",
"Description[ca]": "Control i estat de la xarxa",
"Description[cs]": "Stav a ovládání sítě",
"Description[de]": "Netzwerkstatus und -steuerung",
"Description[el]": "Κατάσταση δικτύου και έλεγχος",
"Description[en_GB]": "Network status and control",
"Description[eo]": "Reto statuso kaj kontrolo",
"Description[es]": "Estado y control de redes",
"Description[eu]": "Sareen egoera eta agintea",
"Description[fi]": "Verkon tila ja hallinta",
"Description[fr]": "État et contrôle du réseau",
"Description[gl]": "Monitorización e control da rede.",
"Description[he]": "מצב ושליטה על הרשת",
"Description[hu]": "Hálózatállapot és vezérlés",
"Description[ia]": "Stato e controlo de rete",
"Description[is]": "Netstaða og umsjón",
"Description[it]": "Stato e controllo della rete",
"Description[ja]": "ネットワークの状態と管理",
"Description[ka]": "ქსელის მდგომარეობა და მართვა",
"Description[ko]": "네트워크 상태 및 제어",
"Description[lt]": "Tinklo būsena ir valdymas",
"Description[lv]": "Tīkla statuss un pārvaldība",
"Description[nl]": "Netwerkstatus en besturing",
"Description[nn]": "Nettverksstatus og -styring",
"Description[pa]": "ਨੈੱਟਵਰਕ ਹਾਲਤ ਅਤੇ ਕੰਟਰੋਲ",
"Description[pl]": "Stan i obsługa sieci",
"Description[pt]": "Estado e controlo da rede",
"Description[pt_BR]": "Status e controle da rede",
"Description[ro]": "Starea și controlul rețelei",
"Description[ru]": "Состояние и управление сетью",
"Description[sk]": "Správa a ovládanie siete",
"Description[sl]": "Stanje omrežja in nadzor",
"Description[sv]": "Nätverksstatus och kontroll",
"Description[ta]": "பிணைய நிலை மற்றும் கட்டுப்பாடு",
"Description[tr]": "Ağ denetim ve durum izleme",
"Description[uk]": "Стеження за станом і керування мережею",
"Description[x-test]": "xxNetwork status and controlxx",
"Description[zh_CN]": "网络状态和控制",
"Description[zh_TW]": "網路狀態與控制",
"EnabledByDefault": true,
"FormFactors": [
"desktop"
],
"Icon": "preferences-system-network",
"Id": "org.kde.plasma.networkmanagement",
"License": "GPL",
"Name": "Networks",
"Name[ar]": "الشّبكات",
"Name[ast]": "Redes",
"Name[az]": "Şəbəkələr",
"Name[bg]": "Мрежи",
"Name[bs]": "Mreže",
"Name[ca@valencia]": "Xarxes",
"Name[ca]": "Xarxes",
"Name[cs]": "Sítě",
"Name[da]": "Netværk",
"Name[de]": "Netzwerke",
"Name[el]": "Δίκτυα",
"Name[en_GB]": "Networks",
"Name[eo]": "Retoj",
"Name[es]": "Redes",
"Name[et]": "Võrgud",
"Name[eu]": "Sareak",
"Name[fi]": "Verkot",
"Name[fr]": "Réseaux",
"Name[gl]": "Redes",
"Name[he]": "רשתות",
"Name[hsb]": "Syć",
"Name[hu]": "Hálózatok",
"Name[ia]": "Retes",
"Name[id]": "Jaringan",
"Name[is]": "Net",
"Name[it]": "Reti",
"Name[ja]": "ネットワーク",
"Name[ka]": "ქსელები",
"Name[ko]": "네트워크",
"Name[lt]": "Tinklai",
"Name[lv]": "Tīkli",
"Name[ml]": "ശൃംഖലകൾ",
"Name[nb]": "Nettverk",
"Name[nds]": "Nettwarken",
"Name[nl]": "Netwerken",
"Name[nn]": "Nettverk",
"Name[pa]": "ਨੈੱਟਵਰਕ",
"Name[pl]": "Sieci",
"Name[pt]": "Redes",
"Name[pt_BR]": "Redes",
"Name[ro]": "Rețele",
"Name[ru]": "Сети",
"Name[sk]": "Siete",
"Name[sl]": "Omrežja",
"Name[sr@ijekavian]": "Мреже",
"Name[sr@ijekavianlatin]": "Mreže",
"Name[sr@latin]": "Mreže",
"Name[sr]": "Мреже",
"Name[sv]": "Nätverk",
"Name[ta]": "பிணையங்கள்",
"Name[tg]": "Шабакаҳо",
"Name[tr]": "Ağlar",
"Name[uk]": "Мережі",
"Name[x-test]": "xxNetworksxx",
"Name[zh_CN]": "网络",
"Name[zh_TW]": "網路",
"Website": "https://projects.kde.org/projects/kde/workspace/plasma-nm"
},
"X-KDE-Keywords": "network,internet,ethernet,wireless,wifi,wlan,vpn",
"X-Plasma-API-Minimum-Version": "6.0",
"X-Plasma-DBusActivationService": "org.freedesktop.NetworkManager",
"X-Plasma-NotificationAreaCategory": "Hardware"
}