controlcenter: refactored wifi/ethernet panels

This commit is contained in:
ATMDA 2025-11-12 16:36:42 -05:00
parent e21a1519b0
commit 893a91a95a
9 changed files with 436 additions and 435 deletions

View file

@ -0,0 +1,32 @@
import qs.components
import qs.services
import qs.config
import QtQuick
import QtQuick.Layouts
ColumnLayout {
id: root
required property string icon
required property string title
spacing: Appearance.spacing.normal
Layout.alignment: Qt.AlignHCenter
MaterialIcon {
Layout.alignment: Qt.AlignHCenter
animate: true
text: root.icon
font.pointSize: Appearance.font.size.extraLarge * 3
font.bold: true
}
StyledText {
Layout.alignment: Qt.AlignHCenter
animate: true
text: root.title
font.pointSize: Appearance.font.size.large
font.bold: true
}
}

View file

@ -0,0 +1,60 @@
import qs.components
import qs.components.effects
import qs.services
import qs.config
import QtQuick
import QtQuick.Layouts
ColumnLayout {
id: root
required property var deviceDetails
spacing: Appearance.spacing.small / 2
StyledText {
text: qsTr("IP Address")
}
StyledText {
text: root.deviceDetails?.ipAddress || qsTr("Not available")
color: Colours.palette.m3outline
font.pointSize: Appearance.font.size.small
}
StyledText {
Layout.topMargin: Appearance.spacing.normal
text: qsTr("Subnet Mask")
}
StyledText {
text: root.deviceDetails?.subnet || qsTr("Not available")
color: Colours.palette.m3outline
font.pointSize: Appearance.font.size.small
}
StyledText {
Layout.topMargin: Appearance.spacing.normal
text: qsTr("Gateway")
}
StyledText {
text: root.deviceDetails?.gateway || qsTr("Not available")
color: Colours.palette.m3outline
font.pointSize: Appearance.font.size.small
}
StyledText {
Layout.topMargin: Appearance.spacing.normal
text: qsTr("DNS Servers")
}
StyledText {
text: (root.deviceDetails && root.deviceDetails.dns && root.deviceDetails.dns.length > 0) ? root.deviceDetails.dns.join(", ") : qsTr("Not available")
color: Colours.palette.m3outline
font.pointSize: Appearance.font.size.small
wrapMode: Text.Wrap
Layout.maximumWidth: parent.width
}
}

View file

@ -0,0 +1,27 @@
import qs.components
import qs.services
import qs.config
import QtQuick
import QtQuick.Layouts
ColumnLayout {
id: root
required property string label
required property string value
property bool showTopMargin: false
spacing: Appearance.spacing.small / 2
StyledText {
Layout.topMargin: root.showTopMargin ? Appearance.spacing.normal : 0
text: root.label
}
StyledText {
text: root.value
color: Colours.palette.m3outline
font.pointSize: Appearance.font.size.small
}
}

View file

@ -0,0 +1,31 @@
import qs.components
import qs.components.effects
import qs.services
import qs.config
import QtQuick
import QtQuick.Layouts
StyledRect {
id: root
default property alias content: contentColumn.data
property real contentSpacing: Appearance.spacing.larger
Layout.fillWidth: true
implicitHeight: contentColumn.implicitHeight + Appearance.padding.large * 2
radius: Appearance.rounding.normal
color: Colours.tPalette.m3surfaceContainer
ColumnLayout {
id: contentColumn
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
anchors.margins: Appearance.padding.large
spacing: root.contentSpacing
}
}

View file

@ -0,0 +1,28 @@
import qs.components
import qs.services
import qs.config
import QtQuick
import QtQuick.Layouts
ColumnLayout {
id: root
required property string title
property string description: ""
spacing: 0
StyledText {
Layout.topMargin: Appearance.spacing.large
text: root.title
font.pointSize: Appearance.font.size.larger
font.weight: 500
}
StyledText {
visible: root.description !== ""
text: root.description
color: Colours.palette.m3outline
}
}

View file

@ -0,0 +1,29 @@
import qs.components
import qs.components.controls
import qs.services
import qs.config
import QtQuick
import QtQuick.Layouts
RowLayout {
id: root
required property string label
property alias checked: toggle.checked
property alias toggle: toggle
Layout.fillWidth: true
spacing: Appearance.spacing.normal
StyledText {
Layout.fillWidth: true
text: root.label
}
StyledSwitch {
id: toggle
cLayer: 2
}
}

View file

@ -43,229 +43,73 @@ Item {
anchors.right: parent.right anchors.right: parent.right
spacing: Appearance.spacing.normal spacing: Appearance.spacing.normal
MaterialIcon { ConnectionHeader {
Layout.alignment: Qt.AlignHCenter icon: "cable"
animate: true title: root.device?.interface ?? qsTr("Unknown")
text: "cable"
font.pointSize: Appearance.font.size.extraLarge * 3
font.bold: true
} }
StyledText { SectionHeader {
Layout.alignment: Qt.AlignHCenter title: qsTr("Connection status")
animate: true description: qsTr("Connection settings for this device")
text: root.device?.interface ?? qsTr("Unknown")
font.pointSize: Appearance.font.size.large
font.bold: true
} }
StyledText { SectionContainer {
Layout.topMargin: Appearance.spacing.large ToggleRow {
text: qsTr("Connection status") label: qsTr("Connected")
font.pointSize: Appearance.font.size.larger checked: root.device?.connected ?? false
font.weight: 500 toggle.onToggled: {
} if (checked) {
// Use connection name if available, otherwise use interface
StyledText { Network.connectEthernet(root.device?.connection || "", root.device?.interface || "");
text: qsTr("Connection settings for this device") } else {
color: Colours.palette.m3outline if (root.device?.connection) {
} Network.disconnectEthernet(root.device.connection);
StyledRect {
Layout.fillWidth: true
implicitHeight: deviceStatus.implicitHeight + Appearance.padding.large * 2
radius: Appearance.rounding.normal
color: Colours.tPalette.m3surfaceContainer
ColumnLayout {
id: deviceStatus
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
anchors.margins: Appearance.padding.large
spacing: Appearance.spacing.larger
Toggle {
label: qsTr("Connected")
checked: root.device?.connected ?? false
toggle.onToggled: {
if (checked) {
// Use connection name if available, otherwise use interface
Network.connectEthernet(root.device?.connection || "", root.device?.interface || "");
} else {
if (root.device?.connection) {
Network.disconnectEthernet(root.device.connection);
}
} }
} }
} }
} }
} }
StyledText { SectionHeader {
Layout.topMargin: Appearance.spacing.large title: qsTr("Device properties")
text: qsTr("Device properties") description: qsTr("Additional information")
font.pointSize: Appearance.font.size.larger
font.weight: 500
} }
StyledText { SectionContainer {
text: qsTr("Additional information") contentSpacing: Appearance.spacing.small / 2
color: Colours.palette.m3outline
}
StyledRect { PropertyRow {
Layout.fillWidth: true label: qsTr("Interface")
implicitHeight: deviceProps.implicitHeight + Appearance.padding.large * 2 value: root.device?.interface ?? qsTr("Unknown")
}
radius: Appearance.rounding.normal PropertyRow {
color: Colours.tPalette.m3surfaceContainer showTopMargin: true
label: qsTr("Connection")
value: root.device?.connection || qsTr("Not connected")
}
ColumnLayout { PropertyRow {
id: deviceProps showTopMargin: true
label: qsTr("State")
anchors.left: parent.left value: root.device?.state ?? qsTr("Unknown")
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
anchors.margins: Appearance.padding.large
spacing: Appearance.spacing.small / 2
StyledText {
text: qsTr("Interface")
}
StyledText {
text: root.device?.interface ?? qsTr("Unknown")
color: Colours.palette.m3outline
font.pointSize: Appearance.font.size.small
}
StyledText {
Layout.topMargin: Appearance.spacing.normal
text: qsTr("Connection")
}
StyledText {
text: root.device?.connection || qsTr("Not connected")
color: Colours.palette.m3outline
font.pointSize: Appearance.font.size.small
}
StyledText {
Layout.topMargin: Appearance.spacing.normal
text: qsTr("State")
}
StyledText {
text: root.device?.state ?? qsTr("Unknown")
color: Colours.palette.m3outline
font.pointSize: Appearance.font.size.small
}
} }
} }
StyledText { SectionHeader {
Layout.topMargin: Appearance.spacing.large title: qsTr("Connection information")
text: qsTr("Connection information") description: qsTr("Network connection details")
font.pointSize: Appearance.font.size.larger
font.weight: 500
} }
StyledText { SectionContainer {
text: qsTr("Network connection details") ConnectionInfoSection {
color: Colours.palette.m3outline deviceDetails: Network.ethernetDeviceDetails
}
StyledRect {
Layout.fillWidth: true
implicitHeight: connectionInfo.implicitHeight + Appearance.padding.large * 2
radius: Appearance.rounding.normal
color: Colours.tPalette.m3surfaceContainer
ColumnLayout {
id: connectionInfo
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
anchors.margins: Appearance.padding.large
spacing: Appearance.spacing.small / 2
StyledText {
text: qsTr("IP Address")
}
StyledText {
text: Network.ethernetDeviceDetails?.ipAddress || qsTr("Not available")
color: Colours.palette.m3outline
font.pointSize: Appearance.font.size.small
}
StyledText {
Layout.topMargin: Appearance.spacing.normal
text: qsTr("Subnet Mask")
}
StyledText {
text: Network.ethernetDeviceDetails?.subnet || qsTr("Not available")
color: Colours.palette.m3outline
font.pointSize: Appearance.font.size.small
}
StyledText {
Layout.topMargin: Appearance.spacing.normal
text: qsTr("Gateway")
}
StyledText {
text: Network.ethernetDeviceDetails?.gateway || qsTr("Not available")
color: Colours.palette.m3outline
font.pointSize: Appearance.font.size.small
}
StyledText {
Layout.topMargin: Appearance.spacing.normal
text: qsTr("DNS Servers")
}
StyledText {
text: (Network.ethernetDeviceDetails && Network.ethernetDeviceDetails.dns && Network.ethernetDeviceDetails.dns.length > 0) ? Network.ethernetDeviceDetails.dns.join(", ") : qsTr("Not available")
color: Colours.palette.m3outline
font.pointSize: Appearance.font.size.small
wrapMode: Text.Wrap
Layout.maximumWidth: parent.width
}
} }
} }
} }
} }
component Toggle: RowLayout {
required property string label
property alias checked: toggle.checked
property alias toggle: toggle
Layout.fillWidth: true
spacing: Appearance.spacing.normal
StyledText {
Layout.fillWidth: true
text: parent.label
}
StyledSwitch {
id: toggle
cLayer: 2
}
}
} }

View file

@ -54,272 +54,109 @@ Item {
anchors.right: parent.right anchors.right: parent.right
spacing: Appearance.spacing.normal spacing: Appearance.spacing.normal
MaterialIcon { ConnectionHeader {
Layout.alignment: Qt.AlignHCenter icon: root.network?.isSecure ? "lock" : "wifi"
animate: true title: root.network?.ssid ?? qsTr("Unknown")
text: root.network?.isSecure ? "lock" : "wifi"
font.pointSize: Appearance.font.size.extraLarge * 3
font.bold: true
} }
StyledText { SectionHeader {
Layout.alignment: Qt.AlignHCenter title: qsTr("Connection status")
animate: true description: qsTr("Connection settings for this network")
text: root.network?.ssid ?? qsTr("Unknown")
font.pointSize: Appearance.font.size.large
font.bold: true
} }
StyledText { SectionContainer {
Layout.topMargin: Appearance.spacing.large ToggleRow {
text: qsTr("Connection status") label: qsTr("Connected")
font.pointSize: Appearance.font.size.larger checked: root.network?.active ?? false
font.weight: 500 toggle.onToggled: {
} if (checked) {
// If already connected to a different network, disconnect first
StyledText { if (Network.active && Network.active.ssid !== root.network.ssid) {
text: qsTr("Connection settings for this network")
color: Colours.palette.m3outline
}
StyledRect {
Layout.fillWidth: true
implicitHeight: networkStatus.implicitHeight + Appearance.padding.large * 2
radius: Appearance.rounding.normal
color: Colours.tPalette.m3surfaceContainer
ColumnLayout {
id: networkStatus
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
anchors.margins: Appearance.padding.large
spacing: Appearance.spacing.larger
Toggle {
label: qsTr("Connected")
checked: root.network?.active ?? false
toggle.onToggled: {
if (checked) {
// If already connected to a different network, disconnect first
if (Network.active && Network.active.ssid !== root.network.ssid) {
Network.disconnectFromNetwork();
// Wait a moment before connecting to new network
Qt.callLater(() => {
connectToNetwork();
});
} else {
connectToNetwork();
}
} else {
Network.disconnectFromNetwork(); Network.disconnectFromNetwork();
} // Wait a moment before connecting to new network
} Qt.callLater(() => {
connectToNetwork();
function connectToNetwork(): void { });
if (root.network.isSecure) {
// Try connecting without password first (in case it's saved)
Network.connectToNetworkWithPasswordCheck(
root.network.ssid,
root.network.isSecure,
() => {
// Callback: connection failed, show password dialog
root.session.network.showPasswordDialog = true;
root.session.network.pendingNetwork = root.network;
}
);
} else { } else {
Network.connectToNetwork(root.network.ssid, ""); connectToNetwork();
} }
} else {
Network.disconnectFromNetwork();
}
}
function connectToNetwork(): void {
if (root.network.isSecure) {
// Try connecting without password first (in case it's saved)
Network.connectToNetworkWithPasswordCheck(
root.network.ssid,
root.network.isSecure,
() => {
// Callback: connection failed, show password dialog
root.session.network.showPasswordDialog = true;
root.session.network.pendingNetwork = root.network;
}
);
} else {
Network.connectToNetwork(root.network.ssid, "");
} }
} }
} }
} }
StyledText { SectionHeader {
Layout.topMargin: Appearance.spacing.large title: qsTr("Network properties")
text: qsTr("Network properties") description: qsTr("Additional information")
font.pointSize: Appearance.font.size.larger
font.weight: 500
} }
StyledText { SectionContainer {
text: qsTr("Additional information") contentSpacing: Appearance.spacing.small / 2
color: Colours.palette.m3outline
}
StyledRect { PropertyRow {
Layout.fillWidth: true label: qsTr("SSID")
implicitHeight: networkProps.implicitHeight + Appearance.padding.large * 2 value: root.network?.ssid ?? qsTr("Unknown")
}
radius: Appearance.rounding.normal PropertyRow {
color: Colours.tPalette.m3surfaceContainer showTopMargin: true
label: qsTr("BSSID")
value: root.network?.bssid ?? qsTr("Unknown")
}
ColumnLayout { PropertyRow {
id: networkProps showTopMargin: true
label: qsTr("Signal strength")
value: root.network ? qsTr("%1%").arg(root.network.strength) : qsTr("N/A")
}
anchors.left: parent.left PropertyRow {
anchors.right: parent.right showTopMargin: true
anchors.verticalCenter: parent.verticalCenter label: qsTr("Frequency")
anchors.margins: Appearance.padding.large value: root.network ? qsTr("%1 MHz").arg(root.network.frequency) : qsTr("N/A")
}
spacing: Appearance.spacing.small / 2 PropertyRow {
showTopMargin: true
StyledText { label: qsTr("Security")
text: qsTr("SSID") value: root.network ? (root.network.isSecure ? root.network.security : qsTr("Open")) : qsTr("N/A")
}
StyledText {
text: root.network?.ssid ?? qsTr("Unknown")
color: Colours.palette.m3outline
font.pointSize: Appearance.font.size.small
}
StyledText {
Layout.topMargin: Appearance.spacing.normal
text: qsTr("BSSID")
}
StyledText {
text: root.network?.bssid ?? qsTr("Unknown")
color: Colours.palette.m3outline
font.pointSize: Appearance.font.size.small
}
StyledText {
Layout.topMargin: Appearance.spacing.normal
text: qsTr("Signal strength")
}
StyledText {
text: root.network ? qsTr("%1%").arg(root.network.strength) : qsTr("N/A")
color: Colours.palette.m3outline
font.pointSize: Appearance.font.size.small
}
StyledText {
Layout.topMargin: Appearance.spacing.normal
text: qsTr("Frequency")
}
StyledText {
text: root.network ? qsTr("%1 MHz").arg(root.network.frequency) : qsTr("N/A")
color: Colours.palette.m3outline
font.pointSize: Appearance.font.size.small
}
StyledText {
Layout.topMargin: Appearance.spacing.normal
text: qsTr("Security")
}
StyledText {
text: root.network ? (root.network.isSecure ? root.network.security : qsTr("Open")) : qsTr("N/A")
color: Colours.palette.m3outline
font.pointSize: Appearance.font.size.small
}
} }
} }
StyledText { SectionHeader {
Layout.topMargin: Appearance.spacing.large title: qsTr("Connection information")
text: qsTr("Connection information") description: qsTr("Network connection details")
font.pointSize: Appearance.font.size.larger
font.weight: 500
} }
StyledText { SectionContainer {
text: qsTr("Network connection details") ConnectionInfoSection {
color: Colours.palette.m3outline deviceDetails: Network.wirelessDeviceDetails
}
StyledRect {
Layout.fillWidth: true
implicitHeight: connectionInfo.implicitHeight + Appearance.padding.large * 2
radius: Appearance.rounding.normal
color: Colours.tPalette.m3surfaceContainer
ColumnLayout {
id: connectionInfo
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
anchors.margins: Appearance.padding.large
spacing: Appearance.spacing.small / 2
StyledText {
text: qsTr("IP Address")
}
StyledText {
text: Network.wirelessDeviceDetails?.ipAddress || qsTr("Not available")
color: Colours.palette.m3outline
font.pointSize: Appearance.font.size.small
}
StyledText {
Layout.topMargin: Appearance.spacing.normal
text: qsTr("Subnet Mask")
}
StyledText {
text: Network.wirelessDeviceDetails?.subnet || qsTr("Not available")
color: Colours.palette.m3outline
font.pointSize: Appearance.font.size.small
}
StyledText {
Layout.topMargin: Appearance.spacing.normal
text: qsTr("Gateway")
}
StyledText {
text: Network.wirelessDeviceDetails?.gateway || qsTr("Not available")
color: Colours.palette.m3outline
font.pointSize: Appearance.font.size.small
}
StyledText {
Layout.topMargin: Appearance.spacing.normal
text: qsTr("DNS Servers")
}
StyledText {
text: (Network.wirelessDeviceDetails && Network.wirelessDeviceDetails.dns && Network.wirelessDeviceDetails.dns.length > 0) ? Network.wirelessDeviceDetails.dns.join(", ") : qsTr("Not available")
color: Colours.palette.m3outline
font.pointSize: Appearance.font.size.small
wrapMode: Text.Wrap
Layout.maximumWidth: parent.width
}
} }
} }
} }
} }
component Toggle: RowLayout {
required property string label
property alias checked: toggle.checked
property alias toggle: toggle
Layout.fillWidth: true
spacing: Appearance.spacing.normal
StyledText {
Layout.fillWidth: true
text: parent.label
}
StyledSwitch {
id: toggle
cLayer: 2
}
}
} }

113
plan.plan.md Normal file
View file

@ -0,0 +1,113 @@
# Refactoring Plan: Control Center Panes
## Overview
After analyzing the last 30 commits, I've identified significant code duplication and opportunities for modularization in the control center panels. This plan focuses on extracting common patterns into reusable components.
## Key Refactoring Opportunities
### 1. Details Component Consolidation
**Files affected:** `modules/controlcenter/network/Details.qml`, `modules/controlcenter/ethernet/EthernetDetails.qml`
**Issue:** Both files share identical structure:
- Header with icon and title
- Connection status section
- Properties section
- Connection information section (IP, subnet, gateway, DNS)
**Solution:** Create `components/ConnectionDetails.qml` that accepts:
- Device/network object
- Icon name
- Title property path
- Details source (wirelessDeviceDetails vs ethernetDeviceDetails)
**Impact:** Reduces ~200 lines of duplication.
### 2. ToggleButton Component Extraction
**Files affected:** `modules/controlcenter/network/NetworkList.qml`, `modules/controlcenter/ethernet/EthernetList.qml`
**Issue:** Both files define identical `ToggleButton` component (lines 228-301 in NetworkList, 170-243 in EthernetList).
**Solution:** Move to `components/controls/ToggleButton.qml` and import in both files.
**Impact:** Eliminates ~70 lines of duplication.
### 3. Switch/SpinBox Row Components
**Files affected:** `modules/controlcenter/appearance/AppearancePane.qml`, `modules/controlcenter/taskbar/TaskbarPane.qml`
**Issue:** Repeated patterns for:
- Switch rows (label + StyledSwitch)
- SpinBox rows (label + CustomSpinBox)
- Same layout, spacing, and styling
**Solution:** Create:
- `components/controls/SwitchRow.qml` - label + switch with config save callback
- `components/controls/SpinBoxRow.qml` - label + spinbox with config save callback
**Impact:** Reduces ~30-40 lines per row instance (20+ instances total).
### 4. Font List Delegate Consolidation
**Files affected:** `modules/controlcenter/appearance/AppearancePane.qml`
**Issue:** Three nearly identical font list implementations (Material, Mono, Sans) with only the property binding differing.
**Solution:** Create `components/FontList.qml` that accepts:
- Current font property
- Save callback function
- Title text
**Impact:** Reduces ~150 lines of duplication.
### 5. List Item Selection Pattern
**Files affected:** Multiple list delegates across panes
**Issue:** Repeated pattern for selected item highlighting:
- Color with alpha based on selection
- Border width/color based on selection
- StateLayer click handler
**Solution:** Create `components/SelectableListItem.qml` wrapper that handles selection styling.
**Impact:** Reduces ~10-15 lines per list delegate.
## Implementation Order
1. **ConnectionDetails consolidation** (medium impact)
2. **FontList consolidation** (low-medium impact)
3. **SelectableListItem pattern** (nice-to-have, lower priority)
## Files to Create
- `components/controls/SelectableListItem.qml`
- `components/ConnectionDetails.qml`
- `components/FontList.qml`
## Completed Items
- ✅ `components/controls/CollapsibleSection.qml` - DONE
- ✅ `components/controls/SwitchRow.qml` - DONE
- ✅ `components/controls/SpinBoxRow.qml` - DONE
- ✅ `components/controls/ToggleButton.qml` - DONE
## Estimated Impact
- **Lines removed:** ~400-500 lines of duplicated code (from remaining items)
- **Maintainability:** Significantly improved - changes to common patterns only need to be made once
- **Readability:** Panes become more declarative and easier to understand
- **Testability:** Reusable components can be tested independently
## Completed Refactoring
- **Lines removed so far:** ~1300+ lines of duplicated code
- **Components created:** CollapsibleSection, SwitchRow, SpinBoxRow, ToggleButton