From 5d7151e79e04d8a6073ecb4ea4a14c5c9bdcfc52 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sun, 9 Nov 2025 17:54:16 -0500 Subject: controlcenter: network and audio panels --- modules/controlcenter/Session.qml | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'modules/controlcenter/Session.qml') diff --git a/modules/controlcenter/Session.qml b/modules/controlcenter/Session.qml index a143470..b716b20 100644 --- a/modules/controlcenter/Session.qml +++ b/modules/controlcenter/Session.qml @@ -11,6 +11,7 @@ QtObject { property bool navExpanded: false readonly property Bt bt: Bt {} + readonly property Network network: Network {} onActiveChanged: activeIndex = panes.indexOf(active) onActiveIndexChanged: active = panes[activeIndex] @@ -22,4 +23,10 @@ QtObject { property bool fabMenuOpen property bool editingDeviceName } + + component Network: QtObject { + property var active + property bool showPasswordDialog: false + property var pendingNetwork + } } -- cgit v1.2.3-freya From 8d7bb827ebd8284706577518e2e58d6a61972889 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sun, 9 Nov 2025 22:48:38 -0500 Subject: controlcenter: appearance pane --- modules/controlcenter/NavRail.qml | 5 + modules/controlcenter/Panes.qml | 8 + modules/controlcenter/Session.qml | 2 +- .../controlcenter/appearance/AppearancePane.qml | 520 +++++++++++++++++++++ 4 files changed, 534 insertions(+), 1 deletion(-) create mode 100644 modules/controlcenter/appearance/AppearancePane.qml (limited to 'modules/controlcenter/Session.qml') diff --git a/modules/controlcenter/NavRail.qml b/modules/controlcenter/NavRail.qml index 70438a6..96bbb65 100644 --- a/modules/controlcenter/NavRail.qml +++ b/modules/controlcenter/NavRail.qml @@ -171,6 +171,11 @@ Item { icon: "volume_up" label: "audio" } + + NavItem { + icon: "palette" + label: "appearance" + } } component NavItem: Item { diff --git a/modules/controlcenter/Panes.qml b/modules/controlcenter/Panes.qml index cc4a4cb..5b1039c 100644 --- a/modules/controlcenter/Panes.qml +++ b/modules/controlcenter/Panes.qml @@ -3,6 +3,7 @@ pragma ComponentBehavior: Bound import "bluetooth" import "network" import "audio" +import "appearance" import qs.components import qs.services import qs.config @@ -44,6 +45,13 @@ ClippingRectangle { } } + Pane { + index: 3 + sourceComponent: AppearancePane { + session: root.session + } + } + Behavior on y { Anim {} } diff --git a/modules/controlcenter/Session.qml b/modules/controlcenter/Session.qml index b716b20..4ac09a4 100644 --- a/modules/controlcenter/Session.qml +++ b/modules/controlcenter/Session.qml @@ -2,7 +2,7 @@ import Quickshell.Bluetooth import QtQuick QtObject { - readonly property list panes: ["network", "bluetooth", "audio"] + readonly property list panes: ["network", "bluetooth", "audio", "appearance"] required property var root property bool floating: false diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml new file mode 100644 index 0000000..ab00345 --- /dev/null +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -0,0 +1,520 @@ +pragma ComponentBehavior: Bound + +import ".." +import "../../launcher/services" +import qs.components +import qs.components.controls +import qs.components.effects +import qs.components.containers +import qs.components.images +import qs.services +import qs.config +import Caelestia.Models +import Quickshell +import QtQuick +import QtQuick.Layouts + +RowLayout { + id: root + + required property Session session + + anchors.fill: parent + + spacing: 0 + + Item { + Layout.preferredWidth: Math.floor(parent.width * 0.4) + Layout.minimumWidth: 420 + Layout.fillHeight: true + + ColumnLayout { + anchors.fill: parent + anchors.margins: Appearance.padding.large + Appearance.padding.normal + anchors.leftMargin: Appearance.padding.large + anchors.rightMargin: Appearance.padding.large + Appearance.padding.normal / 2 + + spacing: Appearance.spacing.small + + RowLayout { + spacing: Appearance.spacing.smaller + + StyledText { + text: qsTr("Settings") + font.pointSize: Appearance.font.size.large + font.weight: 500 + } + + Item { + Layout.fillWidth: true + } + } + + StyledText { + Layout.topMargin: Appearance.spacing.large + text: qsTr("Theme mode") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } + + StyledText { + text: qsTr("Light or dark theme") + color: Colours.palette.m3outline + } + + StyledRect { + Layout.fillWidth: true + implicitHeight: modeToggle.implicitHeight + Appearance.padding.large * 2 + + radius: Appearance.rounding.normal + color: Colours.tPalette.m3surfaceContainer + + RowLayout { + id: modeToggle + + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.large + + spacing: Appearance.spacing.normal + + StyledText { + Layout.fillWidth: true + text: qsTr("Dark mode") + } + + StyledSwitch { + checked: !Colours.currentLight + onToggled: { + Colours.setMode(checked ? "dark" : "light"); + } + } + } + } + + StyledText { + Layout.topMargin: Appearance.spacing.large + text: qsTr("Color variant") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } + + StyledText { + text: qsTr("Material theme variant") + color: Colours.palette.m3outline + } + + StyledListView { + Layout.fillWidth: true + Layout.fillHeight: true + + model: M3Variants.list + spacing: Appearance.spacing.small / 2 + clip: true + + StyledScrollBar.vertical: StyledScrollBar { + flickable: parent + } + + delegate: StyledRect { + required property var modelData + + anchors.left: parent.left + anchors.right: parent.right + + color: Qt.alpha(Colours.tPalette.m3surfaceContainer, modelData.variant === Schemes.currentVariant ? Colours.tPalette.m3surfaceContainer.a : 0) + radius: Appearance.rounding.normal + border.width: modelData.variant === Schemes.currentVariant ? 1 : 0 + border.color: Colours.palette.m3primary + + StateLayer { + function onClicked(): void { + Quickshell.execDetached(["caelestia", "scheme", "set", "-v", modelData.variant]); + Schemes.reload(); + } + } + + RowLayout { + id: variantRow + + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.normal + + spacing: Appearance.spacing.normal + + MaterialIcon { + text: modelData.icon + font.pointSize: Appearance.font.size.large + fill: modelData.variant === Schemes.currentVariant ? 1 : 0 + } + + StyledText { + Layout.fillWidth: true + text: modelData.name + font.weight: modelData.variant === Schemes.currentVariant ? 500 : 400 + } + + MaterialIcon { + visible: modelData.variant === Schemes.currentVariant + text: "check" + color: Colours.palette.m3primary + font.pointSize: Appearance.font.size.large + } + } + + implicitHeight: variantRow.implicitHeight + Appearance.padding.normal * 2 + } + } + + StyledText { + Layout.topMargin: Appearance.spacing.large + text: qsTr("Color scheme") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } + + StyledText { + text: qsTr("Available color schemes") + color: Colours.palette.m3outline + } + + StyledListView { + Layout.fillWidth: true + Layout.fillHeight: true + + model: Schemes.list + spacing: Appearance.spacing.small / 2 + clip: true + + StyledScrollBar.vertical: StyledScrollBar { + flickable: parent + } + + delegate: StyledRect { + required property var modelData + + anchors.left: parent.left + anchors.right: parent.right + + readonly property string schemeKey: `${modelData.name} ${modelData.flavour}` + readonly property bool isCurrent: schemeKey === Schemes.currentScheme + + color: Qt.alpha(Colours.tPalette.m3surfaceContainer, isCurrent ? Colours.tPalette.m3surfaceContainer.a : 0) + radius: Appearance.rounding.normal + border.width: isCurrent ? 1 : 0 + border.color: Colours.palette.m3primary + + StateLayer { + function onClicked(): void { + Quickshell.execDetached(["caelestia", "scheme", "set", "-n", modelData.name, "-f", modelData.flavour]); + Schemes.reload(); + } + } + + RowLayout { + id: schemeRow + + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.normal + + spacing: Appearance.spacing.normal + + Item { + readonly property real itemHeight: schemeRow.implicitHeight || 50 + Layout.preferredWidth: itemHeight * 0.8 + Layout.preferredHeight: itemHeight * 0.8 + + StyledRect { + id: preview + + anchors.verticalCenter: parent.verticalCenter + + border.width: 1 + border.color: Qt.alpha(`#${modelData.colours?.outline}`, 0.5) + + color: `#${modelData.colours?.surface}` + radius: Appearance.rounding.full + implicitWidth: parent.itemHeight * 0.8 + implicitHeight: parent.itemHeight * 0.8 + + Item { + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.right: parent.right + + implicitWidth: parent.implicitWidth / 2 + clip: true + + StyledRect { + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.right: parent.right + + implicitWidth: preview.implicitWidth + color: `#${modelData.colours?.primary}` + radius: Appearance.rounding.full + } + } + } + } + + ColumnLayout { + Layout.fillWidth: true + spacing: 0 + + StyledText { + text: modelData.name + font.weight: isCurrent ? 500 : 400 + } + + StyledText { + text: modelData.flavour + font.pointSize: Appearance.font.size.small + color: Colours.palette.m3outline + } + } + + MaterialIcon { + visible: isCurrent + text: "check" + color: Colours.palette.m3primary + font.pointSize: Appearance.font.size.large + } + } + + implicitHeight: schemeRow.implicitHeight + Appearance.padding.normal * 2 + } + } + } + + InnerBorder { + leftThickness: 0 + rightThickness: Appearance.padding.normal / 2 + } + } + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + + StyledFlickable { + anchors.fill: parent + anchors.margins: Appearance.padding.large * 2 + + flickableDirection: Flickable.VerticalFlick + contentHeight: contentLayout.implicitHeight + + StyledScrollBar.vertical: StyledScrollBar { + flickable: parent + } + + ColumnLayout { + id: contentLayout + + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + + spacing: Appearance.spacing.normal + + MaterialIcon { + Layout.alignment: Qt.AlignHCenter + text: "palette" + font.pointSize: Appearance.font.size.extraLarge * 3 + font.bold: true + } + + StyledText { + Layout.alignment: Qt.AlignHCenter + text: qsTr("Appearance settings") + font.pointSize: Appearance.font.size.large + font.bold: true + } + + StyledText { + Layout.topMargin: Appearance.spacing.large + Layout.alignment: Qt.AlignHCenter + text: qsTr("Theme mode") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } + + StyledText { + Layout.alignment: Qt.AlignHCenter + text: Colours.currentLight ? qsTr("Light mode") : qsTr("Dark mode") + color: Colours.palette.m3outline + } + + StyledText { + Layout.topMargin: Appearance.spacing.large + Layout.alignment: Qt.AlignHCenter + text: qsTr("Wallpaper") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } + + StyledText { + Layout.alignment: Qt.AlignHCenter + text: qsTr("Select a wallpaper") + color: Colours.palette.m3outline + } + + GridLayout { + Layout.fillWidth: true + Layout.topMargin: Appearance.spacing.normal + Layout.alignment: Qt.AlignHCenter + + columns: Math.max(1, Math.floor(parent.width / 200)) + rowSpacing: Appearance.spacing.normal + columnSpacing: Appearance.spacing.normal + + // Center the grid content + Layout.maximumWidth: { + const cols = columns; + const itemWidth = 180; + const spacing = columnSpacing; + return cols * itemWidth + (cols - 1) * spacing; + } + + Repeater { + model: Wallpapers.list + + delegate: Item { + required property var modelData + + Layout.preferredWidth: 180 + Layout.preferredHeight: 120 + Layout.minimumWidth: 180 + Layout.minimumHeight: 120 + + readonly property bool isCurrent: modelData.path === Wallpapers.actualCurrent + readonly property real imageWidth: Math.max(1, width) + readonly property real imageHeight: Math.max(1, height) + + StateLayer { + radius: Appearance.rounding.normal + + function onClicked(): void { + Wallpapers.setWallpaper(modelData.path); + } + } + + StyledClippingRect { + id: image + + anchors.fill: parent + color: Colours.tPalette.m3surfaceContainer + radius: Appearance.rounding.normal + + border.width: isCurrent ? 2 : 0 + border.color: Colours.palette.m3primary + + CachingImage { + id: cachingImage + + path: modelData.path + anchors.fill: parent + + // Ensure sourceSize is always set to valid dimensions + sourceSize: Qt.size( + Math.max(1, Math.floor(parent.width)), + Math.max(1, Math.floor(parent.height)) + ) + + // Show when ready, hide if fallback is showing + opacity: status === Image.Ready && !fallbackImage.visible ? 1 : 0 + + Behavior on opacity { + NumberAnimation { + duration: 200 + } + } + } + + // Fallback: Direct image load if caching fails or is slow + Image { + id: fallbackImage + + anchors.fill: parent + source: modelData.path + asynchronous: true + fillMode: Image.PreserveAspectCrop + sourceSize: Qt.size( + Math.max(1, Math.floor(parent.width)), + Math.max(1, Math.floor(parent.height)) + ) + + // Show if caching image hasn't loaded after a delay + visible: opacity > 0 + opacity: 0 + + Timer { + id: fallbackTimer + interval: 500 + running: cachingImage.status === Image.Loading || (cachingImage.status !== Image.Ready && cachingImage.status !== Image.Null) + onTriggered: { + if (cachingImage.status !== Image.Ready && fallbackImage.status === Image.Ready) { + fallbackImage.opacity = 1; + } + } + } + + // Also check status changes + onStatusChanged: { + if (status === Image.Ready && cachingImage.status !== Image.Ready) { + Qt.callLater(() => { + if (cachingImage.status !== Image.Ready) { + fallbackImage.opacity = 1; + } + }); + } + } + + Behavior on opacity { + NumberAnimation { + duration: 200 + } + } + } + + MaterialIcon { + anchors.right: parent.right + anchors.top: parent.top + anchors.margins: Appearance.padding.small + + visible: isCurrent + text: "check_circle" + color: Colours.palette.m3primary + font.pointSize: Appearance.font.size.large + } + } + + StyledText { + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + anchors.margins: Appearance.padding.small + + text: modelData.relativePath + font.pointSize: Appearance.font.size.small + color: isCurrent ? Colours.palette.m3primary : Colours.palette.m3onSurface + elide: Text.ElideRight + } + } + } + } + } + } + + InnerBorder { + leftThickness: Appearance.padding.normal / 2 + } + } +} + + -- cgit v1.2.3-freya From 1debf488ee1ed24763a01c2e1bf5c3f4119de18f Mon Sep 17 00:00:00 2001 From: ATMDA Date: Mon, 10 Nov 2025 10:00:04 -0500 Subject: controlcenter: ethernet panel (debug) --- modules/controlcenter/NavRail.qml | 9 +- modules/controlcenter/Panes.qml | 14 +- modules/controlcenter/Session.qml | 7 +- modules/controlcenter/ethernet/EthernetDetails.qml | 204 ++++++++++++++++ modules/controlcenter/ethernet/EthernetList.qml | 262 +++++++++++++++++++++ modules/controlcenter/ethernet/EthernetPane.qml | 156 ++++++++++++ .../controlcenter/ethernet/EthernetSettings.qml | 155 ++++++++++++ services/Network.qml | 183 +++++++++++++- 8 files changed, 983 insertions(+), 7 deletions(-) create mode 100644 modules/controlcenter/ethernet/EthernetDetails.qml create mode 100644 modules/controlcenter/ethernet/EthernetList.qml create mode 100644 modules/controlcenter/ethernet/EthernetPane.qml create mode 100644 modules/controlcenter/ethernet/EthernetSettings.qml (limited to 'modules/controlcenter/Session.qml') diff --git a/modules/controlcenter/NavRail.qml b/modules/controlcenter/NavRail.qml index 96bbb65..b4fbf94 100644 --- a/modules/controlcenter/NavRail.qml +++ b/modules/controlcenter/NavRail.qml @@ -158,8 +158,13 @@ Item { NavItem { Layout.topMargin: Appearance.spacing.large * 2 - icon: "network_manage" - label: "network" + icon: "cable" + label: "ethernet" + } + + NavItem { + icon: "wifi" + label: "wireless" } NavItem { diff --git a/modules/controlcenter/Panes.qml b/modules/controlcenter/Panes.qml index 5b1039c..4f4a337 100644 --- a/modules/controlcenter/Panes.qml +++ b/modules/controlcenter/Panes.qml @@ -1,5 +1,6 @@ pragma ComponentBehavior: Bound +import "ethernet" import "bluetooth" import "network" import "audio" @@ -26,27 +27,34 @@ ClippingRectangle { Pane { index: 0 - sourceComponent: NetworkPane { + sourceComponent: EthernetPane { session: root.session } } Pane { index: 1 - sourceComponent: BtPane { + sourceComponent: NetworkPane { session: root.session } } Pane { index: 2 - sourceComponent: AudioPane { + sourceComponent: BtPane { session: root.session } } Pane { index: 3 + sourceComponent: AudioPane { + session: root.session + } + } + + Pane { + index: 4 sourceComponent: AppearancePane { session: root.session } diff --git a/modules/controlcenter/Session.qml b/modules/controlcenter/Session.qml index 4ac09a4..f7c07e4 100644 --- a/modules/controlcenter/Session.qml +++ b/modules/controlcenter/Session.qml @@ -2,7 +2,7 @@ import Quickshell.Bluetooth import QtQuick QtObject { - readonly property list panes: ["network", "bluetooth", "audio", "appearance"] + readonly property list panes: ["ethernet", "wireless", "bluetooth", "audio", "appearance"] required property var root property bool floating: false @@ -12,6 +12,7 @@ QtObject { readonly property Bt bt: Bt {} readonly property Network network: Network {} + readonly property Ethernet ethernet: Ethernet {} onActiveChanged: activeIndex = panes.indexOf(active) onActiveIndexChanged: active = panes[activeIndex] @@ -29,4 +30,8 @@ QtObject { property bool showPasswordDialog: false property var pendingNetwork } + + component Ethernet: QtObject { + property var active + } } diff --git a/modules/controlcenter/ethernet/EthernetDetails.qml b/modules/controlcenter/ethernet/EthernetDetails.qml new file mode 100644 index 0000000..9be3ddc --- /dev/null +++ b/modules/controlcenter/ethernet/EthernetDetails.qml @@ -0,0 +1,204 @@ +pragma ComponentBehavior: Bound + +import ".." +import qs.components +import qs.components.controls +import qs.components.effects +import qs.components.containers +import qs.services +import qs.config +import QtQuick +import QtQuick.Layouts + +Item { + id: root + + required property Session session + readonly property var device: session.ethernet.active + + StyledFlickable { + anchors.fill: parent + + flickableDirection: Flickable.VerticalFlick + contentHeight: layout.height + + ColumnLayout { + id: layout + + anchors.left: parent.left + anchors.right: parent.right + spacing: Appearance.spacing.normal + + MaterialIcon { + Layout.alignment: Qt.AlignHCenter + animate: true + text: "cable" + font.pointSize: Appearance.font.size.extraLarge * 3 + font.bold: true + } + + StyledText { + Layout.alignment: Qt.AlignHCenter + animate: true + text: root.device?.interface ?? qsTr("Unknown") + font.pointSize: Appearance.font.size.large + font.bold: true + } + + StyledText { + Layout.topMargin: Appearance.spacing.large + text: qsTr("Connection status") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } + + StyledText { + text: qsTr("Connection settings for this device") + color: Colours.palette.m3outline + } + + 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) { + if (root.device?.connection) { + Network.connectEthernet(root.device.connection); + } + } else { + if (root.device?.connection) { + Network.disconnectEthernet(root.device.connection); + } + } + } + } + } + } + + StyledText { + Layout.topMargin: Appearance.spacing.large + text: qsTr("Device properties") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } + + StyledText { + text: qsTr("Additional information") + color: Colours.palette.m3outline + } + + StyledRect { + Layout.fillWidth: true + implicitHeight: deviceProps.implicitHeight + Appearance.padding.large * 2 + + radius: Appearance.rounding.normal + color: Colours.tPalette.m3surfaceContainer + + ColumnLayout { + id: deviceProps + + 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("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 + } + } + } + + } + } + + 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 + } + } +} + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/controlcenter/ethernet/EthernetList.qml b/modules/controlcenter/ethernet/EthernetList.qml new file mode 100644 index 0000000..d239fc6 --- /dev/null +++ b/modules/controlcenter/ethernet/EthernetList.qml @@ -0,0 +1,262 @@ +pragma ComponentBehavior: Bound + +import ".." +import qs.components +import qs.components.controls +import qs.components.containers +import qs.services +import qs.config +import QtQuick +import QtQuick.Layouts + +ColumnLayout { + id: root + + required property Session session + + spacing: Appearance.spacing.small + + RowLayout { + spacing: Appearance.spacing.smaller + + StyledText { + text: qsTr("Settings") + font.pointSize: Appearance.font.size.large + font.weight: 500 + } + + Item { + Layout.fillWidth: true + } + + ToggleButton { + toggled: !root.session.ethernet.active + icon: "settings" + accent: "Primary" + + function onClicked(): void { + if (root.session.ethernet.active) + root.session.ethernet.active = null; + else { + root.session.ethernet.active = view.model.get(0)?.modelData ?? null; + } + } + } + } + + RowLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.small + + StyledText { + text: qsTr("Devices (%1)").arg(Network.ethernetDeviceCount || Network.ethernetDevices.length) + font.pointSize: Appearance.font.size.large + font.weight: 500 + } + } + + StyledText { + text: qsTr("All available ethernet devices") + color: Colours.palette.m3outline + } + + StyledListView { + id: view + + Layout.fillWidth: true + Layout.fillHeight: true + + model: Network.ethernetDevices + + spacing: Appearance.spacing.small / 2 + clip: true + + StyledScrollBar.vertical: StyledScrollBar { + flickable: view + } + + delegate: StyledRect { + required property var modelData + + anchors.left: parent.left + anchors.right: parent.right + + color: Qt.alpha(Colours.tPalette.m3surfaceContainer, root.session.ethernet.active === modelData ? Colours.tPalette.m3surfaceContainer.a : 0) + radius: Appearance.rounding.normal + border.width: root.session.ethernet.active === modelData ? 1 : 0 + border.color: Colours.palette.m3primary + + StateLayer { + function onClicked(): void { + root.session.ethernet.active = modelData; + } + } + + RowLayout { + id: rowLayout + + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.normal + + spacing: Appearance.spacing.normal + + StyledRect { + implicitWidth: implicitHeight + implicitHeight: icon.implicitHeight + Appearance.padding.normal * 2 + + radius: Appearance.rounding.normal + color: modelData.connected ? Colours.palette.m3primaryContainer : Colours.tPalette.m3surfaceContainerHigh + + MaterialIcon { + id: icon + + anchors.centerIn: parent + text: "cable" + font.pointSize: Appearance.font.size.large + fill: modelData.connected ? 1 : 0 + color: modelData.connected ? Colours.palette.m3onPrimaryContainer : Colours.palette.m3onSurface + } + } + + StyledText { + Layout.fillWidth: true + + text: modelData.interface || qsTr("Unknown") + } + + StyledText { + text: modelData.connected ? qsTr("Connected") : qsTr("Disconnected") + color: modelData.connected ? Colours.palette.m3primary : Colours.palette.m3outline + font.pointSize: Appearance.font.size.small + font.weight: modelData.connected ? 500 : 400 + } + + StyledRect { + implicitWidth: implicitHeight + implicitHeight: connectIcon.implicitHeight + Appearance.padding.smaller * 2 + + radius: Appearance.rounding.full + color: Qt.alpha(Colours.palette.m3primaryContainer, modelData.connected ? 1 : 0) + + StateLayer { + function onClicked(): void { + if (modelData.connected && modelData.connection) { + Network.disconnectEthernet(modelData.connection); + } else if (modelData.connection) { + Network.connectEthernet(modelData.connection); + } + } + } + + MaterialIcon { + id: connectIcon + + anchors.centerIn: parent + text: modelData.connected ? "link_off" : "link" + color: modelData.connected ? Colours.palette.m3onPrimaryContainer : Colours.palette.m3onSurface + } + } + } + + implicitHeight: rowLayout.implicitHeight + Appearance.padding.normal * 2 + } + } + + component ToggleButton: StyledRect { + id: toggleBtn + + required property bool toggled + property string icon + property string label + property string accent: "Secondary" + + function onClicked(): void { + } + + Layout.preferredWidth: implicitWidth + (toggleStateLayer.pressed ? Appearance.padding.normal * 2 : toggled ? Appearance.padding.small * 2 : 0) + implicitWidth: toggleBtnInner.implicitWidth + Appearance.padding.large * 2 + implicitHeight: toggleBtnIcon.implicitHeight + Appearance.padding.normal * 2 + + radius: toggled || toggleStateLayer.pressed ? Appearance.rounding.small : Math.min(width, height) / 2 * Math.min(1, Appearance.rounding.scale) + color: toggled ? Colours.palette[`m3${accent.toLowerCase()}`] : Colours.palette[`m3${accent.toLowerCase()}Container`] + + StateLayer { + id: toggleStateLayer + + color: toggleBtn.toggled ? Colours.palette[`m3on${toggleBtn.accent}`] : Colours.palette[`m3on${toggleBtn.accent}Container`] + + function onClicked(): void { + toggleBtn.onClicked(); + } + } + + RowLayout { + id: toggleBtnInner + + anchors.centerIn: parent + spacing: Appearance.spacing.normal + + MaterialIcon { + id: toggleBtnIcon + + visible: !!text + fill: toggleBtn.toggled ? 1 : 0 + text: toggleBtn.icon + color: toggleBtn.toggled ? Colours.palette[`m3on${toggleBtn.accent}`] : Colours.palette[`m3on${toggleBtn.accent}Container`] + font.pointSize: Appearance.font.size.large + + Behavior on fill { + Anim {} + } + } + + Loader { + asynchronous: true + active: !!toggleBtn.label + visible: active + + sourceComponent: StyledText { + text: toggleBtn.label + color: toggleBtn.toggled ? Colours.palette[`m3on${toggleBtn.accent}`] : Colours.palette[`m3on${toggleBtn.accent}Container`] + } + } + } + + Behavior on radius { + Anim { + duration: Appearance.anim.durations.expressiveFastSpatial + easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial + } + } + + Behavior on Layout.preferredWidth { + Anim { + duration: Appearance.anim.durations.expressiveFastSpatial + easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial + } + } + } +} + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/controlcenter/ethernet/EthernetPane.qml b/modules/controlcenter/ethernet/EthernetPane.qml new file mode 100644 index 0000000..fc3e1c0 --- /dev/null +++ b/modules/controlcenter/ethernet/EthernetPane.qml @@ -0,0 +1,156 @@ +pragma ComponentBehavior: Bound + +import ".." +import qs.components +import qs.components.effects +import qs.components.containers +import qs.config +import Quickshell.Widgets +import QtQuick +import QtQuick.Layouts + +RowLayout { + id: root + + required property Session session + + anchors.fill: parent + + spacing: 0 + + Item { + Layout.preferredWidth: Math.floor(parent.width * 0.4) + Layout.minimumWidth: 420 + Layout.fillHeight: true + + EthernetList { + anchors.fill: parent + anchors.margins: Appearance.padding.large + Appearance.padding.normal + anchors.leftMargin: Appearance.padding.large + anchors.rightMargin: Appearance.padding.large + Appearance.padding.normal / 2 + + session: root.session + } + + InnerBorder { + leftThickness: 0 + rightThickness: Appearance.padding.normal / 2 + } + } + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + + ClippingRectangle { + anchors.fill: parent + anchors.margins: Appearance.padding.normal + anchors.leftMargin: 0 + anchors.rightMargin: Appearance.padding.normal / 2 + + radius: rightBorder.innerRadius + color: "transparent" + + Loader { + id: loader + + property var pane: root.session.ethernet.active + property string paneId: pane ? (pane.interface || "") : "" + + anchors.fill: parent + anchors.margins: Appearance.padding.large * 2 + + opacity: 1 + scale: 1 + transformOrigin: Item.Center + + clip: false + asynchronous: true + sourceComponent: pane ? details : settings + + Behavior on paneId { + SequentialAnimation { + ParallelAnimation { + Anim { + target: loader + property: "opacity" + to: 0 + easing.bezierCurve: Appearance.anim.curves.standardAccel + } + Anim { + target: loader + property: "scale" + to: 0.8 + easing.bezierCurve: Appearance.anim.curves.standardAccel + } + } + PropertyAction {} + ParallelAnimation { + Anim { + target: loader + property: "opacity" + to: 1 + easing.bezierCurve: Appearance.anim.curves.standardDecel + } + Anim { + target: loader + property: "scale" + to: 1 + easing.bezierCurve: Appearance.anim.curves.standardDecel + } + } + } + } + + onPaneChanged: { + paneId = pane ? (pane.interface || "") : ""; + } + } + } + + InnerBorder { + id: rightBorder + + leftThickness: Appearance.padding.normal / 2 + } + + Component { + id: settings + + StyledFlickable { + flickableDirection: Flickable.VerticalFlick + contentHeight: settingsInner.height + + EthernetSettings { + id: settingsInner + + anchors.left: parent.left + anchors.right: parent.right + session: root.session + } + } + } + + Component { + id: details + + EthernetDetails { + session: root.session + } + } + } + + component Anim: NumberAnimation { + target: loader + duration: Appearance.anim.durations.normal / 2 + easing.type: Easing.BezierSpline + } +} + + + + + + + + diff --git a/modules/controlcenter/ethernet/EthernetSettings.qml b/modules/controlcenter/ethernet/EthernetSettings.qml new file mode 100644 index 0000000..b780b55 --- /dev/null +++ b/modules/controlcenter/ethernet/EthernetSettings.qml @@ -0,0 +1,155 @@ +pragma ComponentBehavior: Bound + +import ".." +import qs.components +import qs.components.controls +import qs.components.effects +import qs.services +import qs.config +import QtQuick +import QtQuick.Layouts + +ColumnLayout { + id: root + + required property Session session + + spacing: Appearance.spacing.normal + + MaterialIcon { + Layout.alignment: Qt.AlignHCenter + text: "cable" + font.pointSize: Appearance.font.size.extraLarge * 3 + font.bold: true + } + + StyledText { + Layout.alignment: Qt.AlignHCenter + text: qsTr("Ethernet settings") + font.pointSize: Appearance.font.size.large + font.bold: true + } + + StyledText { + Layout.topMargin: Appearance.spacing.large + text: qsTr("Ethernet devices") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } + + StyledText { + text: qsTr("Available ethernet devices") + color: Colours.palette.m3outline + } + + StyledRect { + Layout.fillWidth: true + implicitHeight: ethernetInfo.implicitHeight + Appearance.padding.large * 2 + + radius: Appearance.rounding.normal + color: Colours.tPalette.m3surfaceContainer + + ColumnLayout { + id: ethernetInfo + + 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("Total devices") + } + + StyledText { + text: qsTr("%1").arg(Network.ethernetDeviceCount || Network.ethernetDevices.length) + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.small + } + + StyledText { + Layout.topMargin: Appearance.spacing.normal + text: qsTr("Connected devices") + } + + StyledText { + text: qsTr("%1").arg(Network.ethernetDevices.filter(d => d.connected).length) + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.small + } + } + } + + StyledText { + Layout.topMargin: Appearance.spacing.large + text: qsTr("Debug Info") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } + + StyledRect { + Layout.fillWidth: true + implicitHeight: debugInfo.implicitHeight + Appearance.padding.large * 2 + + radius: Appearance.rounding.normal + color: Colours.tPalette.m3surfaceContainer + + ColumnLayout { + id: debugInfo + + 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("Process running: %1").arg(Network.ethernetProcessRunning ? "Yes" : "No") + font.pointSize: Appearance.font.size.small + } + + StyledText { + text: qsTr("List length: %1").arg(Network.ethernetDevices.length) + font.pointSize: Appearance.font.size.small + } + + StyledText { + text: qsTr("Device count: %1").arg(Network.ethernetDeviceCount) + font.pointSize: Appearance.font.size.small + } + + StyledText { + Layout.topMargin: Appearance.spacing.normal + text: qsTr("Debug: %1").arg(Network.ethernetDebugInfo || "No info") + font.pointSize: Appearance.font.size.small + color: Colours.palette.m3outline + wrapMode: Text.Wrap + Layout.maximumWidth: parent.width + } + } + } +} + + + + + + + + + + + + + + + + + + + + + diff --git a/services/Network.qml b/services/Network.qml index 3ceadab..1dee367 100644 --- a/services/Network.qml +++ b/services/Network.qml @@ -7,11 +7,24 @@ import QtQuick Singleton { id: root + Component.onCompleted: { + // Trigger ethernet device detection after initialization + Qt.callLater(() => { + getEthernetDevices(); + }); + } + readonly property list networks: [] readonly property AccessPoint active: networks.find(n => n.active) ?? null property bool wifiEnabled: true readonly property bool scanning: rescanProc.running + property list ethernetDevices: [] + readonly property var activeEthernet: ethernetDevices.find(d => d.connected) ?? null + property int ethernetDeviceCount: 0 + property string ethernetDebugInfo: "" + property bool ethernetProcessRunning: false + function enableWifi(enabled: bool): void { const cmd = enabled ? "on" : "off"; enableWifiProc.exec(["nmcli", "radio", "wifi", cmd]); @@ -70,11 +83,27 @@ Singleton { wifiStatusProc.running = true; } + function getEthernetDevices(): void { + getEthernetDevicesProc.running = true; + } + + + function connectEthernet(connectionName: string): void { + connectEthernetProc.exec(["nmcli", "connection", "up", connectionName]); + } + + function disconnectEthernet(connectionName: string): void { + disconnectEthernetProc.exec(["nmcli", "connection", "down", connectionName]); + } + Process { running: true command: ["nmcli", "m"] stdout: SplitParser { - onRead: getNetworks.running = true + onRead: { + getNetworks.running = true; + getEthernetDevices(); + } } } @@ -336,6 +365,158 @@ Singleton { } } + Process { + id: getEthernetDevicesProc + + running: false + command: ["nmcli", "-g", "DEVICE,TYPE,STATE,CONNECTION", "device", "status"] + environment: ({ + LANG: "C.UTF-8", + LC_ALL: "C.UTF-8" + }) + onRunningChanged: { + root.ethernetProcessRunning = running; + if (!running) { + // Process finished, update debug info + Qt.callLater(() => { + if (root.ethernetDebugInfo === "" || root.ethernetDebugInfo.includes("Process exited")) { + root.ethernetDebugInfo = "Process finished, waiting for output..."; + } + }); + } + } + onExited: { + Qt.callLater(() => { + const outputLength = ethernetStdout.text ? ethernetStdout.text.length : 0; + root.ethernetDebugInfo = "Process exited with code: " + exitCode + ", output length: " + outputLength; + if (outputLength > 0) { + // Output was captured, process it + const output = ethernetStdout.text.trim(); + root.ethernetDebugInfo = "Processing output from onExited, length: " + output.length + "\nOutput: " + output.substring(0, 200); + root.processEthernetOutput(output); + } else { + root.ethernetDebugInfo = "No output captured in onExited"; + } + }); + } + stdout: StdioCollector { + id: ethernetStdout + onStreamFinished: { + const output = text.trim(); + root.ethernetDebugInfo = "Output received in onStreamFinished! Length: " + output.length + ", First 100 chars: " + output.substring(0, 100); + + if (!output || output.length === 0) { + root.ethernetDebugInfo = "No output received (empty)"; + return; + } + + root.processEthernetOutput(output); + } + } + } + + function processEthernetOutput(output: string): void { + const PLACEHOLDER = "STRINGWHICHHOPEFULLYWONTBEUSED"; + const rep = new RegExp("\\\\:", "g"); + const rep2 = new RegExp(PLACEHOLDER, "g"); + + const lines = output.split("\n"); + root.ethernetDebugInfo = "Processing " + lines.length + " lines"; + + const allDevices = lines.map(d => { + const dev = d.replace(rep, PLACEHOLDER).split(":"); + return { + interface: dev[0]?.replace(rep2, ":") ?? "", + type: dev[1]?.replace(rep2, ":") ?? "", + state: dev[2]?.replace(rep2, ":") ?? "", + connection: dev[3]?.replace(rep2, ":") ?? "" + }; + }); + + root.ethernetDebugInfo = "All devices: " + allDevices.length + ", Types: " + allDevices.map(d => d.type).join(", "); + + const ethernetOnly = allDevices.filter(d => d.type === "ethernet"); + root.ethernetDebugInfo = "Ethernet devices found: " + ethernetOnly.length; + + const ethernetDevices = ethernetOnly.map(d => { + const state = d.state || ""; + const connected = state === "100 (connected)" || state === "connected" || state.startsWith("connected"); + return { + interface: d.interface, + type: d.type, + state: state, + connection: d.connection, + connected: connected, + ipAddress: "", + gateway: "", + dns: [], + subnet: "", + macAddress: "", + speed: "" + }; + }); + + root.ethernetDebugInfo = "Ethernet devices processed: " + ethernetDevices.length + ", First device: " + (ethernetDevices[0]?.interface || "none"); + + // Update the list - replace the entire array to ensure QML detects the change + // Create a new array and assign it to the property + const newDevices = []; + for (let i = 0; i < ethernetDevices.length; i++) { + newDevices.push(ethernetDevices[i]); + } + + // Replace the entire list + root.ethernetDevices = newDevices; + + // Force QML to detect the change by updating a property + root.ethernetDeviceCount = ethernetDevices.length; + + // Force QML to re-evaluate the list by accessing it + Qt.callLater(() => { + const count = root.ethernetDevices.length; + root.ethernetDebugInfo = "Final: Found " + ethernetDevices.length + " devices, List length: " + count + ", Parsed all: " + allDevices.length + ", Output length: " + output.length; + }); + } + + + Process { + id: connectEthernetProc + + onExited: { + getEthernetDevices(); + } + stdout: SplitParser { + onRead: getEthernetDevices() + } + stderr: StdioCollector { + onStreamFinished: { + const error = text.trim(); + if (error && error.length > 0 && !error.includes("successfully") && !error.includes("Connection activated")) { + console.warn("Ethernet connection error:", error); + } + } + } + } + + Process { + id: disconnectEthernetProc + + onExited: { + getEthernetDevices(); + } + stdout: SplitParser { + onRead: getEthernetDevices() + } + stderr: StdioCollector { + onStreamFinished: { + const error = text.trim(); + if (error && error.length > 0 && !error.includes("successfully") && !error.includes("disconnected")) { + console.warn("Ethernet disconnection error:", error); + } + } + } + } + component AccessPoint: QtObject { required property var lastIpcObject readonly property string ssid: lastIpcObject.ssid -- cgit v1.2.3-freya From 7ad940ea9f651187118048179689dfdbbdc71427 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Mon, 10 Nov 2025 15:57:11 -0500 Subject: controlcenter: taskbar object toggles --- modules/controlcenter/NavRail.qml | 5 + modules/controlcenter/Panes.qml | 8 + modules/controlcenter/Session.qml | 2 +- modules/controlcenter/taskbar/TaskbarPane.qml | 402 ++++++++++++++++++++++++++ 4 files changed, 416 insertions(+), 1 deletion(-) create mode 100644 modules/controlcenter/taskbar/TaskbarPane.qml (limited to 'modules/controlcenter/Session.qml') diff --git a/modules/controlcenter/NavRail.qml b/modules/controlcenter/NavRail.qml index b4fbf94..1f8c6b3 100644 --- a/modules/controlcenter/NavRail.qml +++ b/modules/controlcenter/NavRail.qml @@ -181,6 +181,11 @@ Item { icon: "palette" label: "appearance" } + + NavItem { + icon: "task_alt" + label: "taskbar" + } } component NavItem: Item { diff --git a/modules/controlcenter/Panes.qml b/modules/controlcenter/Panes.qml index 4f4a337..b30bcf2 100644 --- a/modules/controlcenter/Panes.qml +++ b/modules/controlcenter/Panes.qml @@ -5,6 +5,7 @@ import "bluetooth" import "network" import "audio" import "appearance" +import "taskbar" import qs.components import qs.services import qs.config @@ -60,6 +61,13 @@ ClippingRectangle { } } + Pane { + index: 5 + sourceComponent: TaskbarPane { + session: root.session + } + } + Behavior on y { Anim {} } diff --git a/modules/controlcenter/Session.qml b/modules/controlcenter/Session.qml index f7c07e4..cf31ec3 100644 --- a/modules/controlcenter/Session.qml +++ b/modules/controlcenter/Session.qml @@ -2,7 +2,7 @@ import Quickshell.Bluetooth import QtQuick QtObject { - readonly property list panes: ["ethernet", "wireless", "bluetooth", "audio", "appearance"] + readonly property list panes: ["ethernet", "wireless", "bluetooth", "audio", "appearance", "taskbar"] required property var root property bool floating: false diff --git a/modules/controlcenter/taskbar/TaskbarPane.qml b/modules/controlcenter/taskbar/TaskbarPane.qml new file mode 100644 index 0000000..a367635 --- /dev/null +++ b/modules/controlcenter/taskbar/TaskbarPane.qml @@ -0,0 +1,402 @@ +pragma ComponentBehavior: Bound + +import ".." +import qs.components +import qs.components.controls +import qs.components.effects +import qs.components.containers +import qs.services +import qs.config +import qs.utils +import Quickshell +import Quickshell.Io +import QtQuick +import QtQuick.Layouts + +RowLayout { + id: root + + required property Session session + + anchors.fill: parent + + spacing: 0 + + FileView { + id: configFile + + path: `${Paths.config}/shell.json` + watchChanges: true + + onLoaded: { + try { + const config = JSON.parse(text()); + updateFromConfig(config); + } catch (e) { + console.error("Failed to parse config:", e); + } + } + } + + function updateFromConfig(config) { + // Update clock toggle + if (config.bar && config.bar.clock) { + clockShowIconSwitch.checked = config.bar.clock.showIcon !== false; + } + + // Update entries + if (config.bar && config.bar.entries) { + entriesModel.clear(); + for (const entry of config.bar.entries) { + entriesModel.append({ + id: entry.id, + enabled: entry.enabled !== false + }); + } + } + } + + function saveConfig(entryIndex, entryEnabled) { + if (!configFile.loaded) { + root.lastSaveStatus = "Error: Config file not loaded yet"; + root.debugInfo = "Config file not loaded yet, cannot save"; + return; + } + + try { + const config = JSON.parse(configFile.text()); + + // Update clock setting (same simple approach - read directly from the switch) + if (!config.bar) config.bar = {}; + if (!config.bar.clock) config.bar.clock = {}; + config.bar.clock.showIcon = clockShowIconSwitch.checked; + + // Update entries from the model (same approach as clock - use provided value if available) + if (!config.bar.entries) config.bar.entries = []; + config.bar.entries = []; + + let debugInfo = `saveConfig called\n`; + debugInfo += `entryIndex: ${entryIndex}\n`; + debugInfo += `entryEnabled: ${entryEnabled}\n`; + debugInfo += `entriesModel.count: ${entriesModel.count}\n\n`; + + for (let i = 0; i < entriesModel.count; i++) { + const entry = entriesModel.get(i); + // If this is the entry being updated, use the provided value (same as clock toggle reads from switch) + // Otherwise use the value from the model + let enabled = entry.enabled; + if (entryIndex !== undefined && i === entryIndex) { + enabled = entryEnabled; + debugInfo += `Entry ${i} (${entry.id}): Using provided value = ${entryEnabled}\n`; + } else { + debugInfo += `Entry ${i} (${entry.id}): Using model value = ${entry.enabled}\n`; + } + config.bar.entries.push({ + id: entry.id, + enabled: enabled + }); + } + + debugInfo += `\nFinal entries array:\n${JSON.stringify(config.bar.entries, null, 2)}\n`; + debugInfo += `\nFinal clock.showIcon: ${config.bar.clock.showIcon}\n`; + root.debugInfo = debugInfo; + + // Write back to file using setText (same simple approach that worked for clock) + const jsonString = JSON.stringify(config, null, 4); + configFile.setText(jsonString); + root.lastSaveStatus = `Saved! Entries count: ${config.bar.entries.length}`; + } catch (e) { + root.lastSaveStatus = `Error: ${e.message}`; + root.debugInfo = `Failed to save config:\n${e.message}\n${e.stack}`; + } + } + + ListModel { + id: entriesModel + } + + // Debug info + property string debugInfo: "" + property string lastSaveStatus: "" + + Item { + Layout.preferredWidth: Math.floor(parent.width * 0.4) + Layout.minimumWidth: 420 + Layout.fillHeight: true + + ColumnLayout { + anchors.fill: parent + anchors.margins: Appearance.padding.large + Appearance.padding.normal + anchors.leftMargin: Appearance.padding.large + anchors.rightMargin: Appearance.padding.large + Appearance.padding.normal / 2 + + spacing: Appearance.spacing.small + + RowLayout { + spacing: Appearance.spacing.smaller + + StyledText { + text: qsTr("Settings") + font.pointSize: Appearance.font.size.large + font.weight: 500 + } + + Item { + Layout.fillWidth: true + } + } + + StyledText { + Layout.topMargin: Appearance.spacing.large + text: qsTr("Clock") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } + + StyledText { + text: qsTr("Clock display settings") + color: Colours.palette.m3outline + } + + StyledRect { + Layout.fillWidth: true + implicitHeight: clockRow.implicitHeight + Appearance.padding.large * 2 + + radius: Appearance.rounding.normal + color: Colours.tPalette.m3surfaceContainer + + RowLayout { + id: clockRow + + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.large + + spacing: Appearance.spacing.normal + + StyledText { + Layout.fillWidth: true + text: qsTr("Show clock icon") + } + + StyledSwitch { + id: clockShowIconSwitch + checked: true + onToggled: { + root.saveConfig(); + } + } + } + } + + StyledText { + Layout.topMargin: Appearance.spacing.large + text: qsTr("Taskbar Entries") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } + + StyledText { + text: qsTr("Enable or disable taskbar entries") + color: Colours.palette.m3outline + } + + StyledListView { + Layout.fillWidth: true + Layout.fillHeight: true + Layout.topMargin: 0 + + model: entriesModel + spacing: Appearance.spacing.small / 2 + clip: true + + StyledScrollBar.vertical: StyledScrollBar { + flickable: parent + } + + delegate: StyledRect { + id: delegate + required property string id + required property bool enabled + required property int index + + anchors.left: parent.left + anchors.right: parent.right + + color: Colours.tPalette.m3surfaceContainer + radius: Appearance.rounding.normal + + RowLayout { + id: entryRow + + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.normal + + spacing: Appearance.spacing.normal + + StyledText { + Layout.fillWidth: true + text: id.charAt(0).toUpperCase() + id.slice(1) + font.weight: enabled ? 500 : 400 + } + + StyledSwitch { + checked: enabled + onToggled: { + // Store the values in local variables to ensure they're accessible + // Access index from the delegate + const entryIndex = delegate.index; + const entryEnabled = checked; + console.log(`Entry toggle: index=${entryIndex}, checked=${entryEnabled}`); + // Update the model first + entriesModel.setProperty(entryIndex, "enabled", entryEnabled); + // Save immediately with the value directly (same technique as clock toggle) + // Clock toggle reads directly from clockShowIconSwitch.checked + // We pass the value directly here (same approach) + root.saveConfig(entryIndex, entryEnabled); + } + } + } + + implicitHeight: entryRow.implicitHeight + Appearance.padding.normal * 2 + } + } + } + + InnerBorder { + leftThickness: 0 + rightThickness: Appearance.padding.normal / 2 + } + } + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + + StyledFlickable { + anchors.fill: parent + anchors.margins: Appearance.padding.large * 2 + + flickableDirection: Flickable.VerticalFlick + contentHeight: contentLayout.implicitHeight + + StyledScrollBar.vertical: StyledScrollBar { + flickable: parent + } + + ColumnLayout { + id: contentLayout + + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + + spacing: Appearance.spacing.normal + + MaterialIcon { + Layout.alignment: Qt.AlignHCenter + text: "task_alt" + font.pointSize: Appearance.font.size.extraLarge * 3 + font.bold: true + } + + StyledText { + Layout.alignment: Qt.AlignHCenter + text: qsTr("Taskbar settings") + font.pointSize: Appearance.font.size.large + font.bold: true + } + + StyledText { + Layout.topMargin: Appearance.spacing.large + Layout.alignment: Qt.AlignHCenter + text: qsTr("Clock") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } + + StyledText { + Layout.alignment: Qt.AlignHCenter + text: clockShowIconSwitch.checked ? qsTr("Clock icon enabled") : qsTr("Clock icon disabled") + color: Colours.palette.m3outline + } + + StyledText { + Layout.topMargin: Appearance.spacing.large + Layout.alignment: Qt.AlignHCenter + text: qsTr("Taskbar Entries") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } + + StyledText { + Layout.alignment: Qt.AlignHCenter + text: qsTr("Configure which entries appear in the taskbar") + color: Colours.palette.m3outline + } + + StyledText { + Layout.topMargin: Appearance.spacing.large + Layout.alignment: Qt.AlignHCenter + text: qsTr("Debug Info") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } + + StyledRect { + Layout.fillWidth: true + Layout.preferredHeight: 200 + Layout.maximumHeight: 300 + + radius: Appearance.rounding.normal + color: Colours.tPalette.m3surfaceContainer + clip: true + + StyledFlickable { + id: debugFlickable + anchors.fill: parent + anchors.margins: Appearance.padding.normal + contentHeight: debugText.implicitHeight + clip: true + + StyledScrollBar.vertical: StyledScrollBar { + flickable: debugFlickable + } + + TextEdit { + id: debugText + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + width: parent.width + + text: root.debugInfo || "No debug info yet" + font.pointSize: Appearance.font.size.small + color: Colours.palette.m3onSurface + wrapMode: TextEdit.Wrap + readOnly: true + selectByMouse: true + selectByKeyboard: true + } + } + } + + StyledText { + Layout.topMargin: Appearance.spacing.small + Layout.alignment: Qt.AlignHCenter + text: root.lastSaveStatus || "" + font.pointSize: Appearance.font.size.small + color: root.lastSaveStatus.includes("Error") ? Colours.palette.m3error : Colours.palette.m3primary + } + } + } + + InnerBorder { + leftThickness: Appearance.padding.normal / 2 + } + } +} + -- cgit v1.2.3-freya From 97d461871a5fbfedb3aff080303145efb8a6fffc Mon Sep 17 00:00:00 2001 From: ATMDA Date: Tue, 11 Nov 2025 11:41:30 -0500 Subject: controlcenter: launcher panel, can hide/show applications --- modules/controlcenter/NavRail.qml | 5 + modules/controlcenter/Panes.qml | 8 + modules/controlcenter/Session.qml | 2 +- modules/controlcenter/launcher/LauncherPane.qml | 446 ++++++++++++++++++++++++ 4 files changed, 460 insertions(+), 1 deletion(-) create mode 100644 modules/controlcenter/launcher/LauncherPane.qml (limited to 'modules/controlcenter/Session.qml') diff --git a/modules/controlcenter/NavRail.qml b/modules/controlcenter/NavRail.qml index 1f8c6b3..ca36048 100644 --- a/modules/controlcenter/NavRail.qml +++ b/modules/controlcenter/NavRail.qml @@ -186,6 +186,11 @@ Item { icon: "task_alt" label: "taskbar" } + + NavItem { + icon: "apps" + label: "launcher" + } } component NavItem: Item { diff --git a/modules/controlcenter/Panes.qml b/modules/controlcenter/Panes.qml index b30bcf2..94bea9a 100644 --- a/modules/controlcenter/Panes.qml +++ b/modules/controlcenter/Panes.qml @@ -6,6 +6,7 @@ import "network" import "audio" import "appearance" import "taskbar" +import "launcher" import qs.components import qs.services import qs.config @@ -68,6 +69,13 @@ ClippingRectangle { } } + Pane { + index: 6 + sourceComponent: LauncherPane { + session: root.session + } + } + Behavior on y { Anim {} } diff --git a/modules/controlcenter/Session.qml b/modules/controlcenter/Session.qml index cf31ec3..d44898e 100644 --- a/modules/controlcenter/Session.qml +++ b/modules/controlcenter/Session.qml @@ -2,7 +2,7 @@ import Quickshell.Bluetooth import QtQuick QtObject { - readonly property list panes: ["ethernet", "wireless", "bluetooth", "audio", "appearance", "taskbar"] + readonly property list panes: ["ethernet", "wireless", "bluetooth", "audio", "appearance", "taskbar", "launcher"] required property var root property bool floating: false diff --git a/modules/controlcenter/launcher/LauncherPane.qml b/modules/controlcenter/launcher/LauncherPane.qml new file mode 100644 index 0000000..7377f42 --- /dev/null +++ b/modules/controlcenter/launcher/LauncherPane.qml @@ -0,0 +1,446 @@ +pragma ComponentBehavior: Bound + +import ".." +import "../../launcher/services" +import qs.components +import qs.components.controls +import qs.components.effects +import qs.components.containers +import qs.services +import qs.config +import qs.utils +import Caelestia +import Quickshell +import Quickshell.Io +import Quickshell.Widgets +import QtQuick +import QtQuick.Layouts + +RowLayout { + id: root + + required property Session session + + property var selectedApp: null + property bool showDebugInfo: false + + anchors.fill: parent + + spacing: 0 + + FileView { + id: configFile + + path: `${Paths.config}/shell.json` + watchChanges: true + + onLoaded: { + try { + const config = JSON.parse(text()); + updateToggleState(); + } catch (e) { + console.error("Failed to parse config:", e); + } + } + } + + function updateToggleState() { + if (!root.selectedApp || !configFile.loaded) { + hideFromLauncherSwitch.checked = false; + return; + } + + try { + const config = JSON.parse(configFile.text()); + const appId = root.selectedApp.id || root.selectedApp.entry?.id; + + if (config.launcher && config.launcher.hiddenApps) { + hideFromLauncherSwitch.checked = config.launcher.hiddenApps.includes(appId); + } else { + hideFromLauncherSwitch.checked = false; + } + } catch (e) { + console.error("Failed to update toggle state:", e); + } + } + + function saveHiddenApps() { + if (!configFile.loaded || !root.selectedApp) { + return; + } + + try { + const config = JSON.parse(configFile.text()); + const appId = root.selectedApp.id || root.selectedApp.entry?.id; + + if (!config.launcher) config.launcher = {}; + if (!config.launcher.hiddenApps) config.launcher.hiddenApps = []; + + const hiddenApps = config.launcher.hiddenApps; + const isHidden = hideFromLauncherSwitch.checked; + + if (isHidden) { + // Add to hiddenApps if not already there + if (!hiddenApps.includes(appId)) { + hiddenApps.push(appId); + } + } else { + // Remove from hiddenApps + const index = hiddenApps.indexOf(appId); + if (index !== -1) { + hiddenApps.splice(index, 1); + } + } + + const jsonString = JSON.stringify(config, null, 4); + configFile.setText(jsonString); + } catch (e) { + console.error("Failed to save config:", e); + } + } + + onSelectedAppChanged: { + updateToggleState(); + } + + AppDb { + id: allAppsDb + + path: `${Paths.state}/apps.sqlite` + entries: DesktopEntries.applications.values // No filter - show all apps + } + + Item { + Layout.preferredWidth: Math.floor(parent.width * 0.4) + Layout.minimumWidth: 420 + Layout.fillHeight: true + + ColumnLayout { + anchors.fill: parent + anchors.margins: Appearance.padding.large + Appearance.padding.normal + anchors.leftMargin: Appearance.padding.large + anchors.rightMargin: Appearance.padding.large + Appearance.padding.normal / 2 + + spacing: Appearance.spacing.small + + RowLayout { + spacing: Appearance.spacing.smaller + + StyledText { + text: qsTr("Settings") + font.pointSize: Appearance.font.size.large + font.weight: 500 + } + + Item { + Layout.fillWidth: true + } + } + + StyledText { + Layout.topMargin: Appearance.spacing.large + text: qsTr("Applications (%1)").arg(allAppsDb.apps.length) + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } + + StyledText { + text: qsTr("All applications available in the launcher") + color: Colours.palette.m3outline + } + + StyledListView { + Layout.fillWidth: true + Layout.fillHeight: true + Layout.topMargin: Appearance.spacing.normal + + model: allAppsDb.apps + spacing: Appearance.spacing.small / 2 + clip: true + + StyledScrollBar.vertical: StyledScrollBar { + flickable: parent + } + + delegate: StyledRect { + required property var modelData + + anchors.left: parent.left + anchors.right: parent.right + + readonly property bool isSelected: root.selectedApp === modelData + + color: Qt.alpha(Colours.tPalette.m3surfaceContainer, isSelected ? Colours.tPalette.m3surfaceContainer.a : 0) + radius: Appearance.rounding.normal + border.width: isSelected ? 1 : 0 + border.color: Colours.palette.m3primary + + StateLayer { + function onClicked(): void { + root.selectedApp = modelData; + } + } + + RowLayout { + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.normal + + spacing: Appearance.spacing.normal + + IconImage { + Layout.alignment: Qt.AlignVCenter + implicitSize: 32 + source: { + const entry = modelData.entry; + return entry ? Quickshell.iconPath(entry.icon, "image-missing") : "image-missing"; + } + } + + StyledText { + Layout.fillWidth: true + text: modelData.name || modelData.entry?.name || qsTr("Unknown") + font.pointSize: Appearance.font.size.normal + } + } + + implicitHeight: 40 + } + } + } + + InnerBorder { + leftThickness: 0 + rightThickness: Appearance.padding.normal / 2 + } + } + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + + ColumnLayout { + anchors.fill: parent + anchors.margins: Appearance.padding.large * 2 + + spacing: Appearance.spacing.normal + + Item { + Layout.alignment: Qt.AlignHCenter + implicitWidth: iconLoader.implicitWidth + implicitHeight: iconLoader.implicitHeight + + Loader { + id: iconLoader + sourceComponent: root.selectedApp ? appIconComponent : defaultIconComponent + } + + Component { + id: appIconComponent + IconImage { + implicitSize: Appearance.font.size.extraLarge * 3 * 2 + source: { + if (!root.selectedApp) return "image-missing"; + const entry = root.selectedApp.entry; + if (entry && entry.icon) { + return Quickshell.iconPath(entry.icon, "image-missing"); + } + return "image-missing"; + } + } + } + + Component { + id: defaultIconComponent + MaterialIcon { + text: "apps" + font.pointSize: Appearance.font.size.extraLarge * 3 + font.bold: true + } + } + } + + StyledText { + Layout.alignment: Qt.AlignHCenter + text: root.selectedApp ? (root.selectedApp.name || root.selectedApp.entry?.name || qsTr("Application Details")) : qsTr("Launcher Applications") + font.pointSize: Appearance.font.size.large + font.bold: true + } + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + Layout.topMargin: Appearance.spacing.large + + StyledFlickable { + anchors.fill: parent + flickableDirection: Flickable.VerticalFlick + contentHeight: debugLayout.implicitHeight + + StyledScrollBar.vertical: StyledScrollBar { + flickable: parent + } + + ColumnLayout { + id: debugLayout + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + spacing: Appearance.spacing.normal + + StyledRect { + Layout.fillWidth: true + Layout.topMargin: Appearance.spacing.normal + implicitHeight: hideToggleRow.implicitHeight + Appearance.padding.large * 2 + color: Colours.tPalette.m3surfaceContainer + radius: Appearance.rounding.normal + + RowLayout { + id: hideToggleRow + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.large + spacing: Appearance.spacing.normal + + StyledText { + Layout.fillWidth: true + text: qsTr("Hide from launcher") + font.pointSize: Appearance.font.size.normal + } + + StyledSwitch { + id: hideFromLauncherSwitch + checked: false + enabled: root.selectedApp !== null && configFile.loaded + onToggled: { + root.saveHiddenApps(); + } + } + } + } + + StyledRect { + Layout.fillWidth: true + Layout.topMargin: Appearance.spacing.normal + implicitHeight: debugToggleRow.implicitHeight + Appearance.padding.large * 2 + color: Colours.tPalette.m3surfaceContainer + radius: Appearance.rounding.normal + + RowLayout { + id: debugToggleRow + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.large + spacing: Appearance.spacing.normal + + StyledText { + Layout.fillWidth: true + text: qsTr("Show debug information") + font.pointSize: Appearance.font.size.normal + } + + StyledSwitch { + id: showDebugInfoSwitch + checked: root.showDebugInfo + onToggled: { + root.showDebugInfo = checked; + } + } + } + } + + StyledRect { + Layout.fillWidth: true + Layout.topMargin: Appearance.spacing.normal + Layout.preferredHeight: 300 + visible: root.showDebugInfo + color: Colours.tPalette.m3surfaceContainer + radius: Appearance.rounding.normal + clip: true + + ColumnLayout { + anchors.fill: parent + anchors.margins: Appearance.padding.normal + spacing: Appearance.spacing.small + + StyledText { + Layout.alignment: Qt.AlignHCenter + text: qsTr("Debug Info - All Available Properties") + font.pointSize: Appearance.font.size.normal + font.weight: 500 + } + + StyledFlickable { + Layout.fillWidth: true + Layout.fillHeight: true + flickableDirection: Flickable.VerticalFlick + contentHeight: debugText.implicitHeight + clip: true + + StyledScrollBar.vertical: StyledScrollBar { + flickable: parent + } + + TextEdit { + id: debugText + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + width: parent.width + + text: { + if (!root.selectedApp) return "No app selected"; + + let debug = ""; + const app = root.selectedApp; + const entry = app.entry; + + debug += "=== App Properties ===\n"; + for (let prop in app) { + try { + const value = app[prop]; + debug += prop + ": " + (value !== null && value !== undefined ? String(value) : "null/undefined") + "\n"; + } catch (e) { + debug += prop + ": [error accessing]\n"; + } + } + + debug += "\n=== Entry Properties ===\n"; + if (entry) { + for (let prop in entry) { + try { + const value = entry[prop]; + debug += prop + ": " + (value !== null && value !== undefined ? String(value) : "null/undefined") + "\n"; + } catch (e) { + debug += prop + ": [error accessing]\n"; + } + } + } else { + debug += "entry is null\n"; + } + + return debug; + } + font.pointSize: Appearance.font.size.small + color: Colours.palette.m3onSurfaceVariant + wrapMode: TextEdit.Wrap + readOnly: true + selectByMouse: true + selectByKeyboard: true + } + } + } + } + } + } + } + } + + InnerBorder { + leftThickness: Appearance.padding.normal / 2 + } + } +} -- cgit v1.2.3-freya From 540cebdbbdafe51e0f946e7a256320537a332612 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Fri, 14 Nov 2025 11:59:36 -0500 Subject: controlcenter: merging wireless/ethernet panes (wip/stash) --- modules/controlcenter/NavRail.qml | 5 + modules/controlcenter/Panes.qml | 7 + modules/controlcenter/Session.qml | 2 +- modules/controlcenter/network/NetworkingPane.qml | 579 +++++++++++++++++++++ .../controlcenter/network/NetworkingSettings.qml | 106 ++++ 5 files changed, 698 insertions(+), 1 deletion(-) create mode 100644 modules/controlcenter/network/NetworkingPane.qml create mode 100644 modules/controlcenter/network/NetworkingSettings.qml (limited to 'modules/controlcenter/Session.qml') diff --git a/modules/controlcenter/NavRail.qml b/modules/controlcenter/NavRail.qml index ca36048..abe2ea2 100644 --- a/modules/controlcenter/NavRail.qml +++ b/modules/controlcenter/NavRail.qml @@ -191,6 +191,11 @@ Item { icon: "apps" label: "launcher" } + + NavItem { + icon: "router" + label: "networking" + } } component NavItem: Item { diff --git a/modules/controlcenter/Panes.qml b/modules/controlcenter/Panes.qml index 11e60d3..0958902 100644 --- a/modules/controlcenter/Panes.qml +++ b/modules/controlcenter/Panes.qml @@ -76,6 +76,13 @@ ClippingRectangle { } } + Pane { + index: 7 + sourceComponent: NetworkingPane { + session: root.session + } + } + Behavior on y { Anim {} } diff --git a/modules/controlcenter/Session.qml b/modules/controlcenter/Session.qml index d44898e..1ff9688 100644 --- a/modules/controlcenter/Session.qml +++ b/modules/controlcenter/Session.qml @@ -2,7 +2,7 @@ import Quickshell.Bluetooth import QtQuick QtObject { - readonly property list panes: ["ethernet", "wireless", "bluetooth", "audio", "appearance", "taskbar", "launcher"] + readonly property list panes: ["ethernet", "wireless", "bluetooth", "audio", "appearance", "taskbar", "launcher", "networking"] required property var root property bool floating: false diff --git a/modules/controlcenter/network/NetworkingPane.qml b/modules/controlcenter/network/NetworkingPane.qml new file mode 100644 index 0000000..f220960 --- /dev/null +++ b/modules/controlcenter/network/NetworkingPane.qml @@ -0,0 +1,579 @@ +pragma ComponentBehavior: Bound + +import ".." +import "../ethernet" +import "." +import qs.components +import qs.components.controls +import qs.components.effects +import qs.components.containers +import qs.services +import qs.config +import Quickshell.Widgets +import QtQuick +import QtQuick.Layouts + +RowLayout { + id: root + + required property Session session + + anchors.fill: parent + + spacing: 0 + + Item { + Layout.preferredWidth: Math.floor(parent.width * 0.4) + Layout.minimumWidth: 420 + Layout.fillHeight: true + + // Left pane - networking list with collapsible sections + StyledFlickable { + anchors.fill: parent + anchors.margins: Appearance.padding.large + Appearance.padding.normal + anchors.leftMargin: Appearance.padding.large + anchors.rightMargin: Appearance.padding.large + Appearance.padding.normal / 2 + flickableDirection: Flickable.VerticalFlick + contentHeight: leftContent.height + + ColumnLayout { + id: leftContent + + anchors.left: parent.left + anchors.right: parent.right + spacing: Appearance.spacing.normal + + // Settings header above the collapsible sections + RowLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.smaller + + StyledText { + text: qsTr("Settings") + font.pointSize: Appearance.font.size.large + font.weight: 500 + } + + Item { + Layout.fillWidth: true + } + + ToggleButton { + toggled: Nmcli.wifiEnabled + icon: "wifi" + accent: "Tertiary" + + onClicked: { + Nmcli.toggleWifi(null); + } + } + + ToggleButton { + toggled: Nmcli.scanning + icon: "wifi_find" + accent: "Secondary" + + onClicked: { + Nmcli.rescanWifi(); + } + } + + ToggleButton { + toggled: !root.session.ethernet.active && !root.session.network.active + icon: "settings" + accent: "Primary" + + onClicked: { + if (root.session.ethernet.active || root.session.network.active) { + root.session.ethernet.active = null; + root.session.network.active = null; + } else { + // Toggle to show settings - prefer ethernet if available, otherwise wireless + if (Nmcli.ethernetDevices.length > 0) { + root.session.ethernet.active = ethernetListView.model.get(0)?.modelData ?? null; + } else if (Nmcli.networks.length > 0) { + root.session.network.active = wirelessListView.model.get(0)?.modelData ?? null; + } + } + } + } + } + + CollapsibleSection { + id: ethernetListSection + + Layout.fillWidth: true + title: qsTr("Ethernet") + expanded: true + + ColumnLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.small + + RowLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.small + + StyledText { + text: qsTr("Devices (%1)").arg(Nmcli.ethernetDevices.length) + font.pointSize: Appearance.font.size.large + font.weight: 500 + } + } + + StyledText { + Layout.fillWidth: true + text: qsTr("All available ethernet devices") + color: Colours.palette.m3outline + } + + Item { + Layout.fillWidth: true + Layout.preferredHeight: Nmcli.ethernetDevices.length > 0 ? Math.min(400, Math.max(200, Nmcli.ethernetDevices.length * 80)) : 200 + + StyledListView { + id: ethernetListView + + anchors.fill: parent + + model: Nmcli.ethernetDevices + + spacing: Appearance.spacing.small / 2 + clip: true + + StyledScrollBar.vertical: StyledScrollBar { + flickable: ethernetListView + } + + delegate: StyledRect { + required property var modelData + + anchors.left: parent.left + anchors.right: parent.right + + color: Qt.alpha(Colours.tPalette.m3surfaceContainer, root.session.ethernet.active === modelData ? Colours.tPalette.m3surfaceContainer.a : 0) + radius: Appearance.rounding.normal + border.width: root.session.ethernet.active === modelData ? 1 : 0 + border.color: Colours.palette.m3primary + + StateLayer { + function onClicked(): void { + root.session.ethernet.active = modelData; + } + } + + RowLayout { + id: rowLayout + + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.normal + + spacing: Appearance.spacing.normal + + StyledRect { + implicitWidth: implicitHeight + implicitHeight: icon.implicitHeight + Appearance.padding.normal * 2 + + radius: Appearance.rounding.normal + color: modelData.connected ? Colours.palette.m3primaryContainer : Colours.tPalette.m3surfaceContainerHigh + + MaterialIcon { + id: icon + + anchors.centerIn: parent + text: "cable" + font.pointSize: Appearance.font.size.large + fill: modelData.connected ? 1 : 0 + color: modelData.connected ? Colours.palette.m3onPrimaryContainer : Colours.palette.m3onSurface + } + } + + StyledText { + Layout.fillWidth: true + elide: Text.ElideRight + maximumLineCount: 1 + + text: modelData.interface || qsTr("Unknown") + } + + StyledText { + text: modelData.connected ? qsTr("Connected") : qsTr("Disconnected") + color: modelData.connected ? Colours.palette.m3primary : Colours.palette.m3outline + font.pointSize: Appearance.font.size.small + font.weight: modelData.connected ? 500 : 400 + } + + StyledRect { + implicitWidth: implicitHeight + implicitHeight: connectIcon.implicitHeight + Appearance.padding.smaller * 2 + + radius: Appearance.rounding.full + color: Qt.alpha(Colours.palette.m3primaryContainer, modelData.connected ? 1 : 0) + + StateLayer { + function onClicked(): void { + if (modelData.connected && modelData.connection) { + Nmcli.disconnectEthernet(modelData.connection, () => {}); + } else { + Nmcli.connectEthernet(modelData.connection || "", modelData.interface || "", () => {}); + } + } + } + + MaterialIcon { + id: connectIcon + + anchors.centerIn: parent + text: modelData.connected ? "link_off" : "link" + color: modelData.connected ? Colours.palette.m3onPrimaryContainer : Colours.palette.m3onSurface + } + } + } + + implicitHeight: rowLayout.implicitHeight + Appearance.padding.normal * 2 + } + } + } + } + } + + CollapsibleSection { + id: wirelessListSection + + Layout.fillWidth: true + title: qsTr("Wireless") + expanded: true + + ColumnLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.small + + RowLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.small + + StyledText { + text: qsTr("Networks (%1)").arg(Nmcli.networks.length) + font.pointSize: Appearance.font.size.large + font.weight: 500 + } + + StyledText { + visible: Nmcli.scanning + text: qsTr("Scanning...") + color: Colours.palette.m3primary + font.pointSize: Appearance.font.size.small + } + } + + StyledText { + Layout.fillWidth: true + text: qsTr("All available WiFi networks") + color: Colours.palette.m3outline + } + + Item { + Layout.fillWidth: true + Layout.preferredHeight: Nmcli.networks.length > 0 ? Math.min(400, Math.max(200, Nmcli.networks.length * 80)) : 200 + + StyledListView { + id: wirelessListView + + anchors.fill: parent + + model: Nmcli.networks + + spacing: Appearance.spacing.small / 2 + clip: true + + StyledScrollBar.vertical: StyledScrollBar { + flickable: wirelessListView + } + + delegate: StyledRect { + required property var modelData + + anchors.left: parent.left + anchors.right: parent.right + + color: Qt.alpha(Colours.tPalette.m3surfaceContainer, root.session.network.active === modelData ? Colours.tPalette.m3surfaceContainer.a : 0) + radius: Appearance.rounding.normal + border.width: root.session.network.active === modelData ? 1 : 0 + border.color: Colours.palette.m3primary + + StateLayer { + function onClicked(): void { + root.session.network.active = modelData; + // Check if we need to refresh saved connections when selecting a network + if (modelData && modelData.ssid) { + checkSavedProfileForNetwork(modelData.ssid); + } + } + } + + RowLayout { + id: wirelessRowLayout + + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.normal + + spacing: Appearance.spacing.normal + + StyledRect { + implicitWidth: implicitHeight + implicitHeight: wirelessIcon.implicitHeight + Appearance.padding.normal * 2 + + radius: Appearance.rounding.normal + color: modelData.active ? Colours.palette.m3primaryContainer : Colours.tPalette.m3surfaceContainerHigh + + MaterialIcon { + id: wirelessIcon + + anchors.centerIn: parent + text: modelData.isSecure ? "lock" : "wifi" + font.pointSize: Appearance.font.size.large + fill: modelData.active ? 1 : 0 + color: modelData.active ? Colours.palette.m3onPrimaryContainer : Colours.palette.m3onSurface + } + } + + StyledText { + Layout.fillWidth: true + elide: Text.ElideRight + maximumLineCount: 1 + + text: modelData.ssid || qsTr("Unknown") + } + + StyledText { + text: modelData.active ? qsTr("Connected") : (modelData.isSecure ? qsTr("Secured") : qsTr("Open")) + color: modelData.active ? Colours.palette.m3primary : Colours.palette.m3outline + font.pointSize: Appearance.font.size.small + font.weight: modelData.active ? 500 : 400 + } + + StyledText { + text: qsTr("%1%").arg(modelData.strength) + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.small + } + + StyledRect { + implicitWidth: implicitHeight + implicitHeight: wirelessConnectIcon.implicitHeight + Appearance.padding.smaller * 2 + + radius: Appearance.rounding.full + color: Qt.alpha(Colours.palette.m3primaryContainer, modelData.active ? 1 : 0) + + StateLayer { + function onClicked(): void { + if (modelData.active) { + Nmcli.disconnectFromNetwork(); + } else { + handleWirelessConnect(modelData); + } + } + } + + MaterialIcon { + id: wirelessConnectIcon + + anchors.centerIn: parent + text: modelData.active ? "link_off" : "link" + color: modelData.active ? Colours.palette.m3onPrimaryContainer : Colours.palette.m3onSurface + } + } + } + + implicitHeight: wirelessRowLayout.implicitHeight + Appearance.padding.normal * 2 + } + } + } + } + } + } + } + + InnerBorder { + leftThickness: 0 + rightThickness: Appearance.padding.normal / 2 + } + } + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + + ClippingRectangle { + anchors.fill: parent + anchors.margins: Appearance.padding.normal + anchors.leftMargin: 0 + anchors.rightMargin: Appearance.padding.normal / 2 + + radius: rightBorder.innerRadius + color: "transparent" + + // Right pane - networking details/settings + Loader { + id: loader + + property var ethernetPane: root.session.ethernet.active + property var wirelessPane: root.session.network.active + property var pane: ethernetPane || wirelessPane + property string paneId: ethernetPane ? (ethernetPane.interface || "") : (wirelessPane ? (wirelessPane.ssid || wirelessPane.bssid || "") : "") + + anchors.fill: parent + anchors.margins: Appearance.padding.large * 2 + + opacity: 1 + scale: 1 + transformOrigin: Item.Center + + clip: false + asynchronous: true + sourceComponent: pane ? (ethernetPane ? ethernetDetails : wirelessDetails) : settings + + Behavior on paneId { + SequentialAnimation { + ParallelAnimation { + Anim { + target: loader + property: "opacity" + to: 0 + easing.bezierCurve: Appearance.anim.curves.standardAccel + } + Anim { + target: loader + property: "scale" + to: 0.8 + easing.bezierCurve: Appearance.anim.curves.standardAccel + } + } + PropertyAction {} + ParallelAnimation { + Anim { + target: loader + property: "opacity" + to: 1 + easing.bezierCurve: Appearance.anim.curves.standardDecel + } + Anim { + target: loader + property: "scale" + to: 1 + easing.bezierCurve: Appearance.anim.curves.standardDecel + } + } + } + } + + onPaneChanged: { + paneId = ethernetPane ? (ethernetPane.interface || "") : (wirelessPane ? (wirelessPane.ssid || wirelessPane.bssid || "") : ""); + } + } + } + + InnerBorder { + id: rightBorder + + leftThickness: Appearance.padding.normal / 2 + } + + Component { + id: settings + + StyledFlickable { + flickableDirection: Flickable.VerticalFlick + contentHeight: settingsInner.height + + NetworkingSettings { + id: settingsInner + + anchors.left: parent.left + anchors.right: parent.right + session: root.session + } + } + } + + Component { + id: ethernetDetails + + EthernetDetails { + session: root.session + } + } + + Component { + id: wirelessDetails + + WirelessDetails { + session: root.session + } + } + } + + WirelessPasswordDialog { + anchors.fill: parent + session: root.session + z: 1000 + } + + component Anim: NumberAnimation { + target: loader + duration: Appearance.anim.durations.normal / 2 + easing.type: Easing.BezierSpline + } + + function checkSavedProfileForNetwork(ssid: string): void { + if (ssid && ssid.length > 0) { + Nmcli.loadSavedConnections(() => {}); + } + } + + function handleWirelessConnect(network): void { + if (Nmcli.active && Nmcli.active.ssid !== network.ssid) { + Nmcli.disconnectFromNetwork(); + Qt.callLater(() => { + connectToWirelessNetwork(network); + }); + } else { + connectToWirelessNetwork(network); + } + } + + function connectToWirelessNetwork(network): void { + if (network.isSecure) { + const hasSavedProfile = Nmcli.hasSavedProfile(network.ssid); + + if (hasSavedProfile) { + Nmcli.connectToNetwork(network.ssid, "", network.bssid, null); + } else { + Nmcli.connectToNetworkWithPasswordCheck( + network.ssid, + network.isSecure, + (result) => { + if (result.needsPassword) { + if (Nmcli.pendingConnection) { + Nmcli.connectionCheckTimer.stop(); + Nmcli.immediateCheckTimer.stop(); + Nmcli.immediateCheckTimer.checkCount = 0; + Nmcli.pendingConnection = null; + } + root.session.network.showPasswordDialog = true; + root.session.network.pendingNetwork = network; + } + }, + network.bssid + ); + } + } else { + Nmcli.connectToNetwork(network.ssid, "", network.bssid, null); + } + } +} + diff --git a/modules/controlcenter/network/NetworkingSettings.qml b/modules/controlcenter/network/NetworkingSettings.qml new file mode 100644 index 0000000..2475fed --- /dev/null +++ b/modules/controlcenter/network/NetworkingSettings.qml @@ -0,0 +1,106 @@ +pragma ComponentBehavior: Bound + +import ".." +import qs.components +import qs.components.controls +import qs.components.effects +import qs.services +import qs.config +import QtQuick +import QtQuick.Layouts + +ColumnLayout { + id: root + + required property Session session + + spacing: Appearance.spacing.normal + + MaterialIcon { + Layout.alignment: Qt.AlignHCenter + text: "router" + font.pointSize: Appearance.font.size.extraLarge * 3 + font.bold: true + } + + StyledText { + Layout.alignment: Qt.AlignHCenter + text: qsTr("Networking settings") + font.pointSize: Appearance.font.size.large + font.bold: true + } + + SectionHeader { + Layout.topMargin: Appearance.spacing.large + title: qsTr("Ethernet") + description: qsTr("Ethernet device information") + } + + SectionContainer { + contentSpacing: Appearance.spacing.small / 2 + + PropertyRow { + label: qsTr("Total devices") + value: qsTr("%1").arg(Nmcli.ethernetDevices.length) + } + + PropertyRow { + showTopMargin: true + label: qsTr("Connected devices") + value: qsTr("%1").arg(Nmcli.ethernetDevices.filter(d => d.connected).length) + } + } + + SectionHeader { + Layout.topMargin: Appearance.spacing.large + title: qsTr("Wireless") + description: qsTr("WiFi network settings") + } + + SectionContainer { + ToggleRow { + label: qsTr("WiFi enabled") + checked: Nmcli.wifiEnabled + toggle.onToggled: { + Nmcli.enableWifi(checked); + } + } + } + + SectionHeader { + Layout.topMargin: Appearance.spacing.large + title: qsTr("Current connection") + description: qsTr("Active network connection information") + } + + SectionContainer { + contentSpacing: Appearance.spacing.small / 2 + + PropertyRow { + label: qsTr("Network") + value: Nmcli.active ? Nmcli.active.ssid : (Nmcli.activeEthernet ? Nmcli.activeEthernet.interface : qsTr("Not connected")) + } + + PropertyRow { + showTopMargin: true + visible: Nmcli.active !== null + label: qsTr("Signal strength") + value: Nmcli.active ? qsTr("%1%").arg(Nmcli.active.strength) : qsTr("N/A") + } + + PropertyRow { + showTopMargin: true + visible: Nmcli.active !== null + label: qsTr("Security") + value: Nmcli.active ? (Nmcli.active.isSecure ? qsTr("Secured") : qsTr("Open")) : qsTr("N/A") + } + + PropertyRow { + showTopMargin: true + visible: Nmcli.active !== null + label: qsTr("Frequency") + value: Nmcli.active ? qsTr("%1 MHz").arg(Nmcli.active.frequency) : qsTr("N/A") + } + } +} + -- cgit v1.2.3-freya From 28ebba0c1b10b03647706928e8b31b099aa12b97 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Fri, 14 Nov 2025 13:41:57 -0500 Subject: controlcenter: fixed default pane --- modules/controlcenter/Session.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'modules/controlcenter/Session.qml') diff --git a/modules/controlcenter/Session.qml b/modules/controlcenter/Session.qml index 1ff9688..d09d977 100644 --- a/modules/controlcenter/Session.qml +++ b/modules/controlcenter/Session.qml @@ -6,8 +6,8 @@ QtObject { required property var root property bool floating: false - property string active: panes[0] - property int activeIndex: 0 + property string active: "networking" + property int activeIndex: 7 property bool navExpanded: false readonly property Bt bt: Bt {} -- cgit v1.2.3-freya From ebc2772b3ef54e9f93865ad5c497665b9fffe1cd Mon Sep 17 00:00:00 2001 From: ATMDA Date: Fri, 14 Nov 2025 13:44:15 -0500 Subject: controlcenter: corrected index of panes --- modules/controlcenter/Panes.qml | 25 +++++-------------------- modules/controlcenter/Session.qml | 4 ++-- 2 files changed, 7 insertions(+), 22 deletions(-) (limited to 'modules/controlcenter/Session.qml') diff --git a/modules/controlcenter/Panes.qml b/modules/controlcenter/Panes.qml index 0958902..8a46088 100644 --- a/modules/controlcenter/Panes.qml +++ b/modules/controlcenter/Panes.qml @@ -1,6 +1,5 @@ pragma ComponentBehavior: Bound -import "ethernet" import "bluetooth" import "network" import "audio" @@ -29,60 +28,46 @@ ClippingRectangle { Pane { index: 0 - sourceComponent: EthernetPane { + sourceComponent: NetworkingPane { session: root.session } } Pane { index: 1 - sourceComponent: WirelessPane { - session: root.session - } - } - - Pane { - index: 2 sourceComponent: BtPane { session: root.session } } Pane { - index: 3 + index: 2 sourceComponent: AudioPane { session: root.session } } Pane { - index: 4 + index: 3 sourceComponent: AppearancePane { session: root.session } } Pane { - index: 5 + index: 4 sourceComponent: TaskbarPane { session: root.session } } Pane { - index: 6 + index: 5 sourceComponent: LauncherPane { session: root.session } } - Pane { - index: 7 - sourceComponent: NetworkingPane { - session: root.session - } - } - Behavior on y { Anim {} } diff --git a/modules/controlcenter/Session.qml b/modules/controlcenter/Session.qml index d09d977..0aad702 100644 --- a/modules/controlcenter/Session.qml +++ b/modules/controlcenter/Session.qml @@ -2,12 +2,12 @@ import Quickshell.Bluetooth import QtQuick QtObject { - readonly property list panes: ["ethernet", "wireless", "bluetooth", "audio", "appearance", "taskbar", "launcher", "networking"] + readonly property list panes: ["networking", "bluetooth", "audio", "appearance", "taskbar", "launcher"] required property var root property bool floating: false property string active: "networking" - property int activeIndex: 7 + property int activeIndex: 0 property bool navExpanded: false readonly property Bt bt: Bt {} -- cgit v1.2.3-freya From 7c2d49240c8fdd3c5eeb53f03691f681e9cc8eab Mon Sep 17 00:00:00 2001 From: ATMDA Date: Fri, 14 Nov 2025 15:34:22 -0500 Subject: controlcenter: changed labels for networking to network to match codebase --- modules/controlcenter/NavRail.qml | 2 +- modules/controlcenter/Session.qml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'modules/controlcenter/Session.qml') diff --git a/modules/controlcenter/NavRail.qml b/modules/controlcenter/NavRail.qml index 80a1ee3..234f447 100644 --- a/modules/controlcenter/NavRail.qml +++ b/modules/controlcenter/NavRail.qml @@ -159,7 +159,7 @@ Item { NavItem { Layout.topMargin: Appearance.spacing.large * 2 icon: "router" - label: "networking" + label: "network" } NavItem { diff --git a/modules/controlcenter/Session.qml b/modules/controlcenter/Session.qml index 0aad702..efd360d 100644 --- a/modules/controlcenter/Session.qml +++ b/modules/controlcenter/Session.qml @@ -2,11 +2,11 @@ import Quickshell.Bluetooth import QtQuick QtObject { - readonly property list panes: ["networking", "bluetooth", "audio", "appearance", "taskbar", "launcher"] + readonly property list panes: ["network", "bluetooth", "audio", "appearance", "taskbar", "launcher"] required property var root property bool floating: false - property string active: "networking" + property string active: "network" property int activeIndex: 0 property bool navExpanded: false -- cgit v1.2.3-freya From 4de4a787def43587a230ae3de10febc37b34d0ac Mon Sep 17 00:00:00 2001 From: ATMDA Date: Mon, 17 Nov 2025 22:01:40 -0500 Subject: controlcenter: launcher pane refreshment --- modules/controlcenter/Session.qml | 5 + modules/controlcenter/launcher/LauncherPane.qml | 133 +++++++++++++- modules/controlcenter/launcher/Settings.qml | 226 ++++++++++++++++++++++++ 3 files changed, 357 insertions(+), 7 deletions(-) create mode 100644 modules/controlcenter/launcher/Settings.qml (limited to 'modules/controlcenter/Session.qml') diff --git a/modules/controlcenter/Session.qml b/modules/controlcenter/Session.qml index efd360d..6f560f5 100644 --- a/modules/controlcenter/Session.qml +++ b/modules/controlcenter/Session.qml @@ -13,6 +13,7 @@ QtObject { readonly property Bt bt: Bt {} readonly property Network network: Network {} readonly property Ethernet ethernet: Ethernet {} + readonly property Launcher launcher: Launcher {} onActiveChanged: activeIndex = panes.indexOf(active) onActiveIndexChanged: active = panes[activeIndex] @@ -34,4 +35,8 @@ QtObject { component Ethernet: QtObject { property var active } + + component Launcher: QtObject { + property var active + } } diff --git a/modules/controlcenter/launcher/LauncherPane.qml b/modules/controlcenter/launcher/LauncherPane.qml index 300117a..85db6df 100644 --- a/modules/controlcenter/launcher/LauncherPane.qml +++ b/modules/controlcenter/launcher/LauncherPane.qml @@ -21,13 +21,26 @@ RowLayout { required property Session session - property var selectedApp: null + property var selectedApp: root.session.launcher.active property bool hideFromLauncherChecked: false anchors.fill: parent spacing: 0 + onSelectedAppChanged: { + root.session.launcher.active = root.selectedApp; + updateToggleState(); + } + + Connections { + target: root.session.launcher + function onActiveChanged() { + root.selectedApp = root.session.launcher.active; + updateToggleState(); + } + } + function updateToggleState() { if (!root.selectedApp) { root.hideFromLauncherChecked = false; @@ -73,9 +86,6 @@ RowLayout { Config.save(); } - onSelectedAppChanged: { - updateToggleState(); - } AppDb { id: allAppsDb @@ -202,6 +212,23 @@ RowLayout { Item { Layout.fillWidth: true } + + ToggleButton { + toggled: !root.session.launcher.active + icon: "settings" + accent: "Primary" + + onClicked: { + if (root.session.launcher.active) { + root.session.launcher.active = null; + } else { + // Toggle to show settings - if there are apps, select the first one, otherwise show settings + if (root.filteredApps.length > 0) { + root.session.launcher.active = root.filteredApps[0]; + } + } + } + } } StyledText { @@ -351,7 +378,7 @@ RowLayout { StateLayer { function onClicked(): void { - root.selectedApp = modelData; + root.session.launcher.active = modelData; } } @@ -405,11 +432,74 @@ RowLayout { Loader { id: rightLauncherLoader + property var pane: root.session.launcher.active + property string paneId: pane ? (pane.id || pane.entry?.id || "") : "" + anchors.fill: parent anchors.margins: Appearance.padding.large * 2 + opacity: 1 + scale: 1 + transformOrigin: Item.Center + clip: false + asynchronous: true - sourceComponent: rightContentComponent + sourceComponent: pane ? appDetails : settings + + Component.onCompleted: { + targetComponent = pane ? appDetails : settings; + nextComponent = targetComponent; + } + + property Component targetComponent: settings + property Component nextComponent: settings + + function getComponentForPane() { + return pane ? appDetails : settings; + } + + Behavior on paneId { + SequentialAnimation { + ParallelAnimation { + Anim { + target: rightLauncherLoader + property: "opacity" + to: 0 + easing.bezierCurve: Appearance.anim.curves.standardAccel + } + Anim { + target: rightLauncherLoader + property: "scale" + to: 0.8 + easing.bezierCurve: Appearance.anim.curves.standardAccel + } + } + PropertyAction { + target: rightLauncherLoader + property: "targetComponent" + value: rightLauncherLoader.nextComponent + } + ParallelAnimation { + Anim { + target: rightLauncherLoader + property: "opacity" + to: 1 + easing.bezierCurve: Appearance.anim.curves.standardDecel + } + Anim { + target: rightLauncherLoader + property: "scale" + to: 1 + easing.bezierCurve: Appearance.anim.curves.standardDecel + } + } + } + } + + onPaneChanged: { + nextComponent = getComponentForPane(); + paneId = pane ? (pane.id || pane.entry?.id || "") : ""; + } } } @@ -420,7 +510,30 @@ RowLayout { } Component { - id: rightContentComponent + id: settings + + StyledFlickable { + id: settingsFlickable + flickableDirection: Flickable.VerticalFlick + contentHeight: settingsInner.height + + StyledScrollBar.vertical: StyledScrollBar { + flickable: settingsFlickable + } + + Settings { + id: settingsInner + + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + session: root.session + } + } + } + + Component { + id: appDetails ColumnLayout { anchors.fill: parent @@ -516,4 +629,10 @@ RowLayout { } } } + + component Anim: NumberAnimation { + target: rightLauncherLoader + duration: Appearance.anim.durations.normal / 2 + easing.type: Easing.BezierSpline + } } diff --git a/modules/controlcenter/launcher/Settings.qml b/modules/controlcenter/launcher/Settings.qml new file mode 100644 index 0000000..1fef7f5 --- /dev/null +++ b/modules/controlcenter/launcher/Settings.qml @@ -0,0 +1,226 @@ +pragma ComponentBehavior: Bound + +import ".." +import qs.components +import qs.components.controls +import qs.components.effects +import qs.services +import qs.config +import QtQuick +import QtQuick.Layouts + +ColumnLayout { + id: root + + required property Session session + + spacing: Appearance.spacing.normal + + MaterialIcon { + Layout.alignment: Qt.AlignHCenter + text: "apps" + font.pointSize: Appearance.font.size.extraLarge * 3 + font.bold: true + } + + StyledText { + Layout.alignment: Qt.AlignHCenter + text: qsTr("Launcher Settings") + font.pointSize: Appearance.font.size.large + font.bold: true + } + + SectionHeader { + Layout.topMargin: Appearance.spacing.large + title: qsTr("General") + description: qsTr("General launcher settings") + } + + SectionContainer { + ToggleRow { + label: qsTr("Enabled") + checked: Config.launcher.enabled + toggle.onToggled: { + Config.launcher.enabled = checked; + Config.save(); + } + } + + ToggleRow { + label: qsTr("Show on hover") + checked: Config.launcher.showOnHover + toggle.onToggled: { + Config.launcher.showOnHover = checked; + Config.save(); + } + } + + ToggleRow { + label: qsTr("Vim keybinds") + checked: Config.launcher.vimKeybinds + toggle.onToggled: { + Config.launcher.vimKeybinds = checked; + Config.save(); + } + } + + ToggleRow { + label: qsTr("Enable dangerous actions") + checked: Config.launcher.enableDangerousActions + toggle.onToggled: { + Config.launcher.enableDangerousActions = checked; + Config.save(); + } + } + } + + SectionHeader { + Layout.topMargin: Appearance.spacing.large + title: qsTr("Display") + description: qsTr("Display and appearance settings") + } + + SectionContainer { + contentSpacing: Appearance.spacing.small / 2 + + PropertyRow { + label: qsTr("Max shown items") + value: qsTr("%1").arg(Config.launcher.maxShown) + } + + PropertyRow { + showTopMargin: true + label: qsTr("Max wallpapers") + value: qsTr("%1").arg(Config.launcher.maxWallpapers) + } + + PropertyRow { + showTopMargin: true + label: qsTr("Drag threshold") + value: qsTr("%1 px").arg(Config.launcher.dragThreshold) + } + } + + SectionHeader { + Layout.topMargin: Appearance.spacing.large + title: qsTr("Prefixes") + description: qsTr("Command prefix settings") + } + + SectionContainer { + contentSpacing: Appearance.spacing.small / 2 + + PropertyRow { + label: qsTr("Special prefix") + value: Config.launcher.specialPrefix || qsTr("None") + } + + PropertyRow { + showTopMargin: true + label: qsTr("Action prefix") + value: Config.launcher.actionPrefix || qsTr("None") + } + } + + SectionHeader { + Layout.topMargin: Appearance.spacing.large + title: qsTr("Fuzzy search") + description: qsTr("Fuzzy search settings") + } + + SectionContainer { + ToggleRow { + label: qsTr("Apps") + checked: Config.launcher.useFuzzy.apps + toggle.onToggled: { + Config.launcher.useFuzzy.apps = checked; + Config.save(); + } + } + + ToggleRow { + label: qsTr("Actions") + checked: Config.launcher.useFuzzy.actions + toggle.onToggled: { + Config.launcher.useFuzzy.actions = checked; + Config.save(); + } + } + + ToggleRow { + label: qsTr("Schemes") + checked: Config.launcher.useFuzzy.schemes + toggle.onToggled: { + Config.launcher.useFuzzy.schemes = checked; + Config.save(); + } + } + + ToggleRow { + label: qsTr("Variants") + checked: Config.launcher.useFuzzy.variants + toggle.onToggled: { + Config.launcher.useFuzzy.variants = checked; + Config.save(); + } + } + + ToggleRow { + label: qsTr("Wallpapers") + checked: Config.launcher.useFuzzy.wallpapers + toggle.onToggled: { + Config.launcher.useFuzzy.wallpapers = checked; + Config.save(); + } + } + } + + SectionHeader { + Layout.topMargin: Appearance.spacing.large + title: qsTr("Sizes") + description: qsTr("Size settings for launcher items") + } + + SectionContainer { + contentSpacing: Appearance.spacing.small / 2 + + PropertyRow { + label: qsTr("Item width") + value: qsTr("%1 px").arg(Config.launcher.sizes.itemWidth) + } + + PropertyRow { + showTopMargin: true + label: qsTr("Item height") + value: qsTr("%1 px").arg(Config.launcher.sizes.itemHeight) + } + + PropertyRow { + showTopMargin: true + label: qsTr("Wallpaper width") + value: qsTr("%1 px").arg(Config.launcher.sizes.wallpaperWidth) + } + + PropertyRow { + showTopMargin: true + label: qsTr("Wallpaper height") + value: qsTr("%1 px").arg(Config.launcher.sizes.wallpaperHeight) + } + } + + SectionHeader { + Layout.topMargin: Appearance.spacing.large + title: qsTr("Hidden apps") + description: qsTr("Applications hidden from launcher") + } + + SectionContainer { + contentSpacing: Appearance.spacing.small / 2 + + PropertyRow { + label: qsTr("Total hidden") + value: qsTr("%1").arg(Config.launcher.hiddenApps ? Config.launcher.hiddenApps.length : 0) + } + } +} + -- cgit v1.2.3-freya From 1c523240d84edc8a947d0063e3b9a707b6d818c0 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Wed, 19 Nov 2025 13:48:06 -0500 Subject: refactor: added state module --- modules/controlcenter/Session.qml | 33 +++++--------------------- modules/controlcenter/state/BluetoothState.qml | 18 ++++++++++++++ modules/controlcenter/state/EthernetState.qml | 9 +++++++ modules/controlcenter/state/LauncherState.qml | 9 +++++++ modules/controlcenter/state/NetworkState.qml | 13 ++++++++++ 5 files changed, 55 insertions(+), 27 deletions(-) create mode 100644 modules/controlcenter/state/BluetoothState.qml create mode 100644 modules/controlcenter/state/EthernetState.qml create mode 100644 modules/controlcenter/state/LauncherState.qml create mode 100644 modules/controlcenter/state/NetworkState.qml (limited to 'modules/controlcenter/Session.qml') diff --git a/modules/controlcenter/Session.qml b/modules/controlcenter/Session.qml index 6f560f5..5d7b094 100644 --- a/modules/controlcenter/Session.qml +++ b/modules/controlcenter/Session.qml @@ -1,5 +1,5 @@ -import Quickshell.Bluetooth import QtQuick +import "./state" QtObject { readonly property list panes: ["network", "bluetooth", "audio", "appearance", "taskbar", "launcher"] @@ -10,33 +10,12 @@ QtObject { property int activeIndex: 0 property bool navExpanded: false - readonly property Bt bt: Bt {} - readonly property Network network: Network {} - readonly property Ethernet ethernet: Ethernet {} - readonly property Launcher launcher: Launcher {} + // Pane-specific state objects + readonly property BluetoothState bt: BluetoothState {} + readonly property NetworkState network: NetworkState {} + readonly property EthernetState ethernet: EthernetState {} + readonly property LauncherState launcher: LauncherState {} onActiveChanged: activeIndex = panes.indexOf(active) onActiveIndexChanged: active = panes[activeIndex] - - component Bt: QtObject { - property BluetoothDevice active - property BluetoothAdapter currentAdapter: Bluetooth.defaultAdapter - property bool editingAdapterName - property bool fabMenuOpen - property bool editingDeviceName - } - - component Network: QtObject { - property var active - property bool showPasswordDialog: false - property var pendingNetwork - } - - component Ethernet: QtObject { - property var active - } - - component Launcher: QtObject { - property var active - } } diff --git a/modules/controlcenter/state/BluetoothState.qml b/modules/controlcenter/state/BluetoothState.qml new file mode 100644 index 0000000..db8c7e1 --- /dev/null +++ b/modules/controlcenter/state/BluetoothState.qml @@ -0,0 +1,18 @@ +import Quickshell.Bluetooth +import QtQuick + +QtObject { + id: root + + // Active selected device + property BluetoothDevice active: null + + // Current adapter being used + property BluetoothAdapter currentAdapter: Bluetooth.defaultAdapter + + // UI state flags + property bool editingAdapterName: false + property bool fabMenuOpen: false + property bool editingDeviceName: false +} + diff --git a/modules/controlcenter/state/EthernetState.qml b/modules/controlcenter/state/EthernetState.qml new file mode 100644 index 0000000..25b243a --- /dev/null +++ b/modules/controlcenter/state/EthernetState.qml @@ -0,0 +1,9 @@ +import QtQuick + +QtObject { + id: root + + // Active selected ethernet interface + property var active: null +} + diff --git a/modules/controlcenter/state/LauncherState.qml b/modules/controlcenter/state/LauncherState.qml new file mode 100644 index 0000000..cd9eeb6 --- /dev/null +++ b/modules/controlcenter/state/LauncherState.qml @@ -0,0 +1,9 @@ +import QtQuick + +QtObject { + id: root + + // Active selected application + property var active: null +} + diff --git a/modules/controlcenter/state/NetworkState.qml b/modules/controlcenter/state/NetworkState.qml new file mode 100644 index 0000000..651a35c --- /dev/null +++ b/modules/controlcenter/state/NetworkState.qml @@ -0,0 +1,13 @@ +import QtQuick + +QtObject { + id: root + + // Active selected wireless network + property var active: null + + // Password dialog state + property bool showPasswordDialog: false + property var pendingNetwork: null +} + -- cgit v1.2.3-freya From 50dd4e1c441db43bc4dee5d5007f28c52cef848b Mon Sep 17 00:00:00 2001 From: ATMDA Date: Wed, 19 Nov 2025 14:07:53 -0500 Subject: refactor: created pane registry --- modules/controlcenter/NavRail.qml | 38 +++------- modules/controlcenter/PaneRegistry.qml | 133 +++++++++++++++++++++++++++++++++ modules/controlcenter/Panes.qml | 58 +++++--------- modules/controlcenter/Session.qml | 3 +- 4 files changed, 161 insertions(+), 71 deletions(-) create mode 100644 modules/controlcenter/PaneRegistry.qml (limited to 'modules/controlcenter/Session.qml') diff --git a/modules/controlcenter/NavRail.qml b/modules/controlcenter/NavRail.qml index 1de1a9e..ef338b2 100644 --- a/modules/controlcenter/NavRail.qml +++ b/modules/controlcenter/NavRail.qml @@ -117,35 +117,15 @@ Item { } } - NavItem { - Layout.topMargin: Appearance.spacing.large * 2 - icon: "router" - label: "network" - } - - NavItem { - icon: "settings_bluetooth" - label: "bluetooth" - } - - NavItem { - icon: "volume_up" - label: "audio" - } - - NavItem { - icon: "palette" - label: "appearance" - } - - NavItem { - icon: "task_alt" - label: "taskbar" - } - - NavItem { - icon: "apps" - label: "launcher" + Repeater { + model: PaneRegistry.count + + NavItem { + required property int index + Layout.topMargin: index === 0 ? Appearance.spacing.large * 2 : 0 + icon: PaneRegistry.getByIndex(index).icon + label: PaneRegistry.getByIndex(index).label + } } } diff --git a/modules/controlcenter/PaneRegistry.qml b/modules/controlcenter/PaneRegistry.qml new file mode 100644 index 0000000..df63c6e --- /dev/null +++ b/modules/controlcenter/PaneRegistry.qml @@ -0,0 +1,133 @@ +pragma Singleton + +import QtQuick + +/** + * PaneRegistry + * + * Centralized registry for Control Center panes. This singleton provides a single + * source of truth for pane metadata (id, label, icon, component), eliminating + * the need for manual index management and making adding/removing panes trivial. + * + * Usage: + * - Panes.qml: Dynamically creates panes from registry + * - Session.qml: Derives panes list from registry + * - NavRail.qml: Uses registry for navigation items + */ +QtObject { + id: root + + /** + * Pane metadata structure: + * - id: Unique identifier for the pane (string) + * - label: Display label for the pane (string) + * - icon: Material icon name (string) + * - component: Component path relative to controlcenter module (string) + */ + readonly property list panes: [ + QtObject { + readonly property string id: "network" + readonly property string label: "network" + readonly property string icon: "router" + readonly property string component: "network/NetworkingPane.qml" + }, + QtObject { + readonly property string id: "bluetooth" + readonly property string label: "bluetooth" + readonly property string icon: "settings_bluetooth" + readonly property string component: "bluetooth/BtPane.qml" + }, + QtObject { + readonly property string id: "audio" + readonly property string label: "audio" + readonly property string icon: "volume_up" + readonly property string component: "audio/AudioPane.qml" + }, + QtObject { + readonly property string id: "appearance" + readonly property string label: "appearance" + readonly property string icon: "palette" + readonly property string component: "appearance/AppearancePane.qml" + }, + QtObject { + readonly property string id: "taskbar" + readonly property string label: "taskbar" + readonly property string icon: "task_alt" + readonly property string component: "taskbar/TaskbarPane.qml" + }, + QtObject { + readonly property string id: "launcher" + readonly property string label: "launcher" + readonly property string icon: "apps" + readonly property string component: "launcher/LauncherPane.qml" + } + ] + + /** + * Get the count of registered panes + */ + readonly property int count: panes.length + + /** + * Get pane labels as a list of strings + * Useful for Session.qml's panes property + */ + readonly property var labels: { + const result = []; + for (let i = 0; i < panes.length; i++) { + result.push(panes[i].label); + } + return result; + } + + /** + * Get pane metadata by index + * @param index The index of the pane + * @return The pane metadata object or null if index is out of bounds + */ + function getByIndex(index: int): QtObject { + if (index >= 0 && index < panes.length) { + return panes[index]; + } + return null; + } + + /** + * Get pane index by label + * @param label The label to search for + * @return The index of the pane or -1 if not found + */ + function getIndexByLabel(label: string): int { + for (let i = 0; i < panes.length; i++) { + if (panes[i].label === label) { + return i; + } + } + return -1; + } + + /** + * Get pane metadata by label + * @param label The label to search for + * @return The pane metadata object or null if not found + */ + function getByLabel(label: string): QtObject { + const index = getIndexByLabel(label); + return getByIndex(index); + } + + /** + * Get pane metadata by id + * @param id The id to search for + * @return The pane metadata object or null if not found + */ + function getById(id: string): QtObject { + for (let i = 0; i < panes.length; i++) { + if (panes[i].id === id) { + return panes[i]; + } + } + return null; + } +} + diff --git a/modules/controlcenter/Panes.qml b/modules/controlcenter/Panes.qml index 756d73a..b9256a9 100644 --- a/modules/controlcenter/Panes.qml +++ b/modules/controlcenter/Panes.qml @@ -9,6 +9,7 @@ import "launcher" import qs.components import qs.services import qs.config +import qs.modules.controlcenter import Quickshell.Widgets import QtQuick import QtQuick.Layouts @@ -76,45 +77,13 @@ ClippingRectangle { } } - Pane { - index: 0 - sourceComponent: NetworkingPane { - session: root.session - } - } - - Pane { - index: 1 - sourceComponent: BtPane { - session: root.session - } - } - - Pane { - index: 2 - sourceComponent: AudioPane { - session: root.session - } - } + Repeater { + model: PaneRegistry.count - Pane { - index: 3 - sourceComponent: AppearancePane { - session: root.session - } - } - - Pane { - index: 4 - sourceComponent: TaskbarPane { - session: root.session - } - } - - Pane { - index: 5 - sourceComponent: LauncherPane { - session: root.session + Pane { + required property int index + paneIndex: index + componentPath: PaneRegistry.getByIndex(index).component } } @@ -135,8 +104,8 @@ ClippingRectangle { component Pane: Item { id: pane - required property int index - property alias sourceComponent: loader.sourceComponent + required property int paneIndex + required property string componentPath implicitWidth: root.width implicitHeight: root.height @@ -146,7 +115,7 @@ ClippingRectangle { // Function to compute if this pane should be active function updateActive(): void { - const diff = Math.abs(root.session.activeIndex - pane.index); + const diff = Math.abs(root.session.activeIndex - pane.paneIndex); const isActivePane = diff === 0; let shouldBeActive = false; @@ -187,6 +156,13 @@ ClippingRectangle { if (active && !pane.hasBeenLoaded) { pane.hasBeenLoaded = true; } + + // Load the component with initial properties when activated + if (active && !item) { + loader.setSource(pane.componentPath, { + "session": root.session + }); + } } onItemChanged: { diff --git a/modules/controlcenter/Session.qml b/modules/controlcenter/Session.qml index 5d7b094..9c6a754 100644 --- a/modules/controlcenter/Session.qml +++ b/modules/controlcenter/Session.qml @@ -1,8 +1,9 @@ import QtQuick import "./state" +import qs.modules.controlcenter QtObject { - readonly property list panes: ["network", "bluetooth", "audio", "appearance", "taskbar", "launcher"] + readonly property list panes: PaneRegistry.labels required property var root property bool floating: false -- cgit v1.2.3-freya From 147410e39bf4e0474deca3980dcaa724464cf5c3 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Wed, 19 Nov 2025 21:15:40 -0500 Subject: cleanup: removed unnecessary comments --- modules/controlcenter/ControlCenter.qml | 1 - modules/controlcenter/Panes.qml | 18 ------------ modules/controlcenter/Session.qml | 1 - .../controlcenter/appearance/AppearancePane.qml | 28 ++----------------- modules/controlcenter/audio/AudioPane.qml | 1 - modules/controlcenter/bluetooth/Details.qml | 1 - modules/controlcenter/bluetooth/Settings.qml | 4 +-- modules/controlcenter/components/DeviceDetails.qml | 7 ----- modules/controlcenter/components/DeviceList.qml | 10 ++----- .../controlcenter/components/PaneTransition.qml | 12 -------- .../controlcenter/components/SplitPaneLayout.qml | 8 ------ .../components/SplitPaneWithDetails.qml | 2 -- modules/controlcenter/launcher/LauncherPane.qml | 27 ++---------------- modules/controlcenter/network/NetworkingPane.qml | 2 -- modules/controlcenter/network/WirelessDetails.qml | 9 ------ modules/controlcenter/network/WirelessList.qml | 3 -- .../network/WirelessPasswordDialog.qml | 27 ++---------------- modules/controlcenter/state/BluetoothState.qml | 5 ---- modules/controlcenter/state/EthernetState.qml | 1 - modules/controlcenter/state/LauncherState.qml | 1 - modules/controlcenter/state/NetworkState.qml | 3 -- modules/controlcenter/taskbar/TaskbarPane.qml | 32 ---------------------- 22 files changed, 9 insertions(+), 194 deletions(-) (limited to 'modules/controlcenter/Session.qml') diff --git a/modules/controlcenter/ControlCenter.qml b/modules/controlcenter/ControlCenter.qml index 3642a33..fdb824e 100644 --- a/modules/controlcenter/ControlCenter.qml +++ b/modules/controlcenter/ControlCenter.qml @@ -97,6 +97,5 @@ Item { } } - // Expose initialOpeningComplete for NavRail to prevent tab switching during opening animation readonly property bool initialOpeningComplete: panes.initialOpeningComplete } diff --git a/modules/controlcenter/Panes.qml b/modules/controlcenter/Panes.qml index b9256a9..833a411 100644 --- a/modules/controlcenter/Panes.qml +++ b/modules/controlcenter/Panes.qml @@ -19,7 +19,6 @@ ClippingRectangle { required property Session session - // Expose initialOpeningComplete so parent can check if opening animation is done readonly property bool initialOpeningComplete: layout.initialOpeningComplete color: "transparent" @@ -27,7 +26,6 @@ ClippingRectangle { focus: false activeFocusOnTab: false - // Clear focus when clicking anywhere in the panes area MouseArea { anchors.fill: parent z: -1 @@ -37,7 +35,6 @@ ClippingRectangle { } } - // Clear focus when switching panes Connections { target: root.session @@ -54,8 +51,6 @@ ClippingRectangle { clip: true property bool animationComplete: true - // Track if initial opening animation has completed - // During initial opening, only the active pane loads to avoid hiccups property bool initialOpeningComplete: false Timer { @@ -66,8 +61,6 @@ ClippingRectangle { } } - // Timer to detect when initial opening animation completes - // Uses large duration to cover both normal and detached opening cases Timer { id: initialOpeningTimer interval: Appearance.anim.durations.large @@ -94,7 +87,6 @@ ClippingRectangle { Connections { target: root.session function onActiveIndexChanged(): void { - // Mark animation as incomplete and start delay timer layout.animationComplete = false; animationDelayTimer.restart(); } @@ -110,28 +102,21 @@ ClippingRectangle { implicitWidth: root.width implicitHeight: root.height - // Track if this pane has ever been loaded to enable caching property bool hasBeenLoaded: false - // Function to compute if this pane should be active function updateActive(): void { const diff = Math.abs(root.session.activeIndex - pane.paneIndex); const isActivePane = diff === 0; let shouldBeActive = false; - // During initial opening animation, only load the active pane - // This prevents hiccups from multiple panes loading simultaneously if (!layout.initialOpeningComplete) { shouldBeActive = isActivePane; } else { - // After initial opening, allow current and adjacent panes for smooth transitions if (diff <= 1) { shouldBeActive = true; } else if (pane.hasBeenLoaded) { - // For distant panes that have been loaded before, keep them active to preserve cached data shouldBeActive = true; } else { - // For new distant panes, wait until animation completes to avoid heavy loading during transition shouldBeActive = layout.animationComplete; } } @@ -152,12 +137,10 @@ ClippingRectangle { } onActiveChanged: { - // Mark pane as loaded when it becomes active if (active && !pane.hasBeenLoaded) { pane.hasBeenLoaded = true; } - // Load the component with initial properties when activated if (active && !item) { loader.setSource(pane.componentPath, { "session": root.session @@ -166,7 +149,6 @@ ClippingRectangle { } onItemChanged: { - // Mark pane as loaded when item is created if (item) { pane.hasBeenLoaded = true; } diff --git a/modules/controlcenter/Session.qml b/modules/controlcenter/Session.qml index 9c6a754..0408a1a 100644 --- a/modules/controlcenter/Session.qml +++ b/modules/controlcenter/Session.qml @@ -11,7 +11,6 @@ QtObject { property int activeIndex: 0 property bool navExpanded: false - // Pane-specific state objects readonly property BluetoothState bt: BluetoothState {} readonly property NetworkState network: NetworkState {} readonly property EthernetState ethernet: EthernetState {} diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index 3ba0549..5b7e859 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -22,7 +22,6 @@ Item { required property Session session - // Appearance settings property real animDurationsScale: Config.appearance.anim.durations.scale ?? 1 property string fontFamilyMaterial: Config.appearance.font.family.material ?? "Material Symbols Rounded" property string fontFamilyMono: Config.appearance.font.family.mono ?? "CaskaydiaCove NF" @@ -37,7 +36,6 @@ Item { property real borderRounding: Config.border.rounding ?? 1 property real borderThickness: Config.border.thickness ?? 1 - // Background settings property bool desktopClockEnabled: Config.background.desktopClock.enabled ?? false property bool backgroundEnabled: Config.background.enabled ?? true property bool visualiserEnabled: Config.background.visualiser.enabled ?? false @@ -127,13 +125,8 @@ Item { anchors.fill: parent asynchronous: true active: { - // Lazy load: only activate when: - // 1. Right pane is loaded AND - // 2. Appearance pane is active (index 3) or adjacent (for smooth transitions) - // This prevents loading all wallpapers when control center opens but appearance pane isn't visible const isActive = root.session.activeIndex === 3; const isAdjacent = Math.abs(root.session.activeIndex - 3) === 1; - // Access loader through SplitPaneLayout's rightLoader const splitLayout = root.children[0]; const loader = splitLayout && splitLayout.rightLoader ? splitLayout.rightLoader : null; const shouldActivate = loader && loader.item !== null && (isActive || isAdjacent); @@ -150,7 +143,6 @@ Item { onActiveChanged: { if (!active && wallpaperLoader.item) { const container = wallpaperLoader.item; - // Access timer through wallpaperGrid if (container && container.wallpaperGrid) { const grid = container.wallpaperGrid; if (grid.imageUpdateTimer) { @@ -186,20 +178,17 @@ Item { } } - // Lazy loading model: loads one image at a time, only when touching bottom - // This prevents GridView from creating all delegates at once QtObject { id: lazyModel property var sourceList: null - property int loadedCount: 0 // Total items available to load - property int visibleCount: 0 // Items actually exposed to GridView (only visible + buffer) + property int loadedCount: 0 + property int visibleCount: 0 property int totalCount: 0 function initialize(list) { sourceList = list; totalCount = list ? list.length : 0; - // Start with enough items to fill the initial viewport (~3 rows) const initialRows = 3; const cols = wallpaperGrid.columnsCount > 0 ? wallpaperGrid.columnsCount : 3; const initialCount = Math.min(initialRows * cols, totalCount); @@ -216,7 +205,6 @@ Item { } function updateVisibleCount(neededCount) { - // Always round up to complete rows to avoid incomplete rows in the grid const cols = wallpaperGrid.columnsCount > 0 ? wallpaperGrid.columnsCount : 1; const maxVisible = Math.min(neededCount, loadedCount); const rows = Math.ceil(maxVisible / cols); @@ -237,7 +225,6 @@ Item { readonly property int minCellWidth: 200 + Appearance.spacing.normal readonly property int columnsCount: Math.max(1, Math.floor(parent.width / minCellWidth)) - // Height based on visible items only - prevents GridView from creating all delegates readonly property int layoutPreferredHeight: { if (!lazyModel || lazyModel.visibleCount === 0 || columnsCount === 0) { return 0; @@ -255,7 +242,6 @@ Item { topMargin: 0 bottomMargin: 0 - // Use ListModel for incremental updates to prevent flashing when new items are added ListModel { id: wallpaperListModel } @@ -270,7 +256,6 @@ Item { const newCount = lazyModel.visibleCount; const currentCount = wallpaperListModel.count; - // Only append new items - never remove or replace existing ones if (newCount > currentCount) { const flickable = wallpaperGridContainer.parentFlickable; const oldScrollY = flickable ? flickable.contentY : 0; @@ -279,7 +264,6 @@ Item { wallpaperListModel.append({modelData: lazyModel.sourceList[i]}); } - // Preserve scroll position after model update if (flickable) { Qt.callLater(function() { if (Math.abs(flickable.contentY - oldScrollY) < 1) { @@ -382,7 +366,6 @@ Item { const neededCount = Math.min((neededBottomRow + 1) * wallpaperGrid.columnsCount, lazyModel.loadedCount); lazyModel.updateVisibleCount(neededCount); - // Load more when we're within 1 row of running out of loaded items const loadedRows = Math.ceil(lazyModel.loadedCount / wallpaperGrid.columnsCount); const rowsRemaining = loadedRows - (bottomRow + 1); @@ -434,7 +417,6 @@ Item { const neededCount = Math.min((neededBottomRow + 1) * wallpaperGrid.columnsCount, lazyModel.loadedCount); lazyModel.updateVisibleCount(neededCount); - // Load more when we're within 1 row of running out of loaded items const loadedRows = Math.ceil(lazyModel.loadedCount / wallpaperGrid.columnsCount); const rowsRemaining = loadedRows - (bottomRow + 1); @@ -450,8 +432,6 @@ Item { } } - - // Parent Flickable handles scrolling interactive: false @@ -786,11 +766,9 @@ Item { function onClicked(): void { const variant = modelData.variant; - // Optimistic update - set immediately for responsive UI Schemes.currentVariant = variant; Quickshell.execDetached(["caelestia", "scheme", "set", "-v", variant]); - // Reload after a delay to confirm changes Qt.callLater(() => { reloadTimer.restart(); }); @@ -873,11 +851,9 @@ Item { const flavour = modelData.flavour; const schemeKey = `${name} ${flavour}`; - // Optimistic update - set immediately for responsive UI Schemes.currentScheme = schemeKey; Quickshell.execDetached(["caelestia", "scheme", "set", "-n", name, "-f", flavour]); - // Reload after a delay to confirm changes Qt.callLater(() => { reloadTimer.restart(); }); diff --git a/modules/controlcenter/audio/AudioPane.qml b/modules/controlcenter/audio/AudioPane.qml index 9b0c7d2..694e178 100644 --- a/modules/controlcenter/audio/AudioPane.qml +++ b/modules/controlcenter/audio/AudioPane.qml @@ -40,7 +40,6 @@ Item { anchors.right: parent.right spacing: Appearance.spacing.normal - // Audio header above the collapsible sections RowLayout { Layout.fillWidth: true spacing: Appearance.spacing.smaller diff --git a/modules/controlcenter/bluetooth/Details.qml b/modules/controlcenter/bluetooth/Details.qml index b260458..5299045 100644 --- a/modules/controlcenter/bluetooth/Details.qml +++ b/modules/controlcenter/bluetooth/Details.qml @@ -440,7 +440,6 @@ StyledFlickable { } } - // FAB Menu (positioned absolutely relative to flickable) ColumnLayout { anchors.right: fabRoot.right anchors.bottom: fabRoot.top diff --git a/modules/controlcenter/bluetooth/Settings.qml b/modules/controlcenter/bluetooth/Settings.qml index b3245ab..c547240 100644 --- a/modules/controlcenter/bluetooth/Settings.qml +++ b/modules/controlcenter/bluetooth/Settings.qml @@ -328,7 +328,7 @@ ColumnLayout { anchors.left: parent.left - text: qsTr("Rename adapter (currently does not work)") // FIXME: remove disclaimer when fixed + text: qsTr("Rename adapter (currently does not work)") color: Colours.palette.m3outline font.pointSize: Appearance.font.size.small } @@ -345,8 +345,6 @@ ColumnLayout { readOnly: !root.session.bt.editingAdapterName onAccepted: { root.session.bt.editingAdapterName = false; - // Doesn't work for now, will be added to QS later - // root.session.bt.currentAdapter.name = text; } leftPadding: Appearance.padding.normal diff --git a/modules/controlcenter/components/DeviceDetails.qml b/modules/controlcenter/components/DeviceDetails.qml index 768e77a..d2e8835 100644 --- a/modules/controlcenter/components/DeviceDetails.qml +++ b/modules/controlcenter/components/DeviceDetails.qml @@ -18,10 +18,7 @@ Item { property Component headerComponent: null property list sections: [] - // Optional: Custom content to insert after header but before sections property Component topContent: null - - // Optional: Custom content to insert after all sections property Component bottomContent: null implicitWidth: layout.implicitWidth @@ -35,7 +32,6 @@ Item { anchors.top: parent.top spacing: Appearance.spacing.normal - // Header component (e.g., ConnectionHeader or SettingsHeader) Loader { id: headerLoader @@ -44,7 +40,6 @@ Item { visible: root.headerComponent !== null } - // Top content (optional) Loader { id: topContentLoader @@ -53,7 +48,6 @@ Item { visible: root.topContent !== null } - // Sections Repeater { model: root.sections @@ -65,7 +59,6 @@ Item { } } - // Bottom content (optional) Loader { id: bottomContentLoader diff --git a/modules/controlcenter/components/DeviceList.qml b/modules/controlcenter/components/DeviceList.qml index a6821d8..75dd913 100644 --- a/modules/controlcenter/components/DeviceList.qml +++ b/modules/controlcenter/components/DeviceList.qml @@ -28,7 +28,6 @@ ColumnLayout { spacing: Appearance.spacing.small - // Header with action buttons (optional) Loader { id: headerLoader @@ -37,7 +36,6 @@ ColumnLayout { visible: root.headerComponent !== null && root.showHeader } - // Title and description row RowLayout { Layout.fillWidth: true Layout.topMargin: root.headerComponent ? 0 : 0 @@ -61,10 +59,8 @@ ColumnLayout { } } - // Expose view for access from parent components property alias view: view - // Description text StyledText { visible: root.description !== "" Layout.fillWidth: true @@ -72,20 +68,18 @@ ColumnLayout { color: Colours.palette.m3outline } - // List view StyledListView { id: view Layout.fillWidth: true - // Use contentHeight to show all items without estimation implicitHeight: contentHeight model: root.model delegate: root.delegate spacing: Appearance.spacing.small / 2 - interactive: false // Disable individual scrolling - parent pane handles it - clip: false // Don't clip - let parent handle scrolling + interactive: false + clip: false } } diff --git a/modules/controlcenter/components/PaneTransition.qml b/modules/controlcenter/components/PaneTransition.qml index 1da4afb..d1814b5 100644 --- a/modules/controlcenter/components/PaneTransition.qml +++ b/modules/controlcenter/components/PaneTransition.qml @@ -3,26 +3,17 @@ pragma ComponentBehavior: Bound import qs.config import QtQuick -// Reusable pane transition animation component -// Provides standard fade-out/scale-down → update → fade-in/scale-up animation -// Used when switching between detail/settings views in panes SequentialAnimation { id: root - // The Loader element to animate required property Item target - - // Optional list of PropertyActions to execute during the transition - // These typically update the component being displayed property list propertyActions - // Animation parameters (with sensible defaults) property real scaleFrom: 1.0 property real scaleTo: 0.8 property real opacityFrom: 1.0 property real opacityTo: 0.0 - // Fade out and scale down ParallelAnimation { NumberAnimation { target: root.target @@ -45,8 +36,6 @@ SequentialAnimation { } } - // Execute property actions (component switching, state updates, etc.) - // This is where the component change happens while invisible ScriptAction { script: { for (let i = 0; i < root.propertyActions.length; i++) { @@ -58,7 +47,6 @@ SequentialAnimation { } } - // Fade in and scale up ParallelAnimation { NumberAnimation { target: root.target diff --git a/modules/controlcenter/components/SplitPaneLayout.qml b/modules/controlcenter/components/SplitPaneLayout.qml index 7bd7db0..8b4f0d9 100644 --- a/modules/controlcenter/components/SplitPaneLayout.qml +++ b/modules/controlcenter/components/SplitPaneLayout.qml @@ -15,19 +15,14 @@ RowLayout { property Component leftContent: null property Component rightContent: null - // Left pane configuration property real leftWidthRatio: 0.4 property int leftMinimumWidth: 420 property var leftLoaderProperties: ({}) - - // Right pane configuration property var rightLoaderProperties: ({}) - // Expose loaders for customization (access via splitLayout.leftLoader or splitLayout.rightLoader) property alias leftLoader: leftLoader property alias rightLoader: rightLoader - // Left pane Item { id: leftPane @@ -57,7 +52,6 @@ RowLayout { asynchronous: true sourceComponent: root.leftContent - // Apply any additional properties from leftLoaderProperties Component.onCompleted: { for (const key in root.leftLoaderProperties) { leftLoader[key] = root.leftLoaderProperties[key]; @@ -74,7 +68,6 @@ RowLayout { } } - // Right pane Item { id: rightPane @@ -101,7 +94,6 @@ RowLayout { asynchronous: true sourceComponent: root.rightContent - // Apply any additional properties from rightLoaderProperties Component.onCompleted: { for (const key in root.rightLoaderProperties) { rightLoader[key] = root.rightLoaderProperties[key]; diff --git a/modules/controlcenter/components/SplitPaneWithDetails.qml b/modules/controlcenter/components/SplitPaneWithDetails.qml index 6af8c1a..e873923 100644 --- a/modules/controlcenter/components/SplitPaneWithDetails.qml +++ b/modules/controlcenter/components/SplitPaneWithDetails.qml @@ -19,7 +19,6 @@ Item { property var activeItem: null property var paneIdGenerator: function(item) { return item ? String(item) : ""; } - // Optional: Additional component to overlay on top (e.g., password dialogs) property Component overlayComponent: null SplitPaneLayout { @@ -82,7 +81,6 @@ Item { } } - // Overlay component (e.g., password dialogs) Loader { id: overlayLoader diff --git a/modules/controlcenter/launcher/LauncherPane.qml b/modules/controlcenter/launcher/LauncherPane.qml index 803d7e0..47f87cc 100644 --- a/modules/controlcenter/launcher/LauncherPane.qml +++ b/modules/controlcenter/launcher/LauncherPane.qml @@ -62,26 +62,20 @@ Item { const appId = root.selectedApp.id || root.selectedApp.entry?.id; - // Create a new array to ensure change detection const hiddenApps = Config.launcher.hiddenApps ? [...Config.launcher.hiddenApps] : []; if (isHidden) { - // Add to hiddenApps if not already there if (!hiddenApps.includes(appId)) { hiddenApps.push(appId); } } else { - // Remove from hiddenApps const index = hiddenApps.indexOf(appId); if (index !== -1) { hiddenApps.splice(index, 1); } } - // Update Config Config.launcher.hiddenApps = hiddenApps; - - // Persist changes to disk Config.save(); } @@ -90,15 +84,13 @@ Item { id: allAppsDb path: `${Paths.state}/apps.sqlite` - entries: DesktopEntries.applications.values // No filter - show all apps + entries: DesktopEntries.applications.values } property string searchText: "" function filterApps(search: string): list { - // If search is empty, return all apps directly if (!search || search.trim() === "") { - // Convert QQmlListProperty to array const apps = []; for (let i = 0; i < allAppsDb.apps.length; i++) { apps.push(allAppsDb.apps[i]); @@ -110,7 +102,6 @@ Item { return []; } - // Prepare apps for fuzzy search const preparedApps = []; for (let i = 0; i < allAppsDb.apps.length; i++) { const app = allAppsDb.apps[i]; @@ -121,14 +112,12 @@ Item { }); } - // Perform fuzzy search const results = Fuzzy.go(search, preparedApps, { all: true, keys: ["name"], scoreFn: r => r[0].score }); - // Return sorted by score (highest first) return results .sort((a, b) => b._score - a._score) .map(r => r.obj._item); @@ -192,7 +181,6 @@ Item { if (root.session.launcher.active) { root.session.launcher.active = null; } else { - // Toggle to show settings - if there are apps, select the first one, otherwise show settings if (root.filteredApps.length > 0) { root.session.launcher.active = root.filteredApps[0]; } @@ -302,13 +290,7 @@ Item { Layout.fillWidth: true Layout.fillHeight: true asynchronous: true - active: { - // Lazy load: activate when left pane is loaded - // The ListView will load asynchronously, and search will work because filteredApps - // is updated regardless of whether the ListView is loaded - // Access loader through parent - this will be set when component loads - return true; - } + active: true sourceComponent: StyledListView { id: appsListView @@ -418,11 +400,9 @@ Item { sourceComponent: rightLauncherPane.targetComponent active: true - // Expose displayedApp to loaded components property var displayedApp: rightLauncherPane.displayedApp onItemChanged: { - // Ensure displayedApp is set when item is created (for async loading) if (item && rightLauncherPane.pane && rightLauncherPane.displayedApp !== rightLauncherPane.pane) { rightLauncherPane.displayedApp = rightLauncherPane.pane; } @@ -508,12 +488,10 @@ Item { id: appDetailsLayout anchors.fill: parent - // Get displayedApp from parent Loader (the Loader has displayedApp property we set) readonly property var displayedApp: parent && parent.displayedApp !== undefined ? parent.displayedApp : null spacing: Appearance.spacing.normal - // Show SettingsHeader when no app is selected, or show app icon + title when app is selected SettingsHeader { Layout.leftMargin: Appearance.padding.large * 2 Layout.rightMargin: Appearance.padding.large * 2 @@ -523,7 +501,6 @@ Item { title: qsTr("Launcher Applications") } - // App icon and title display (shown when app is selected) Item { Layout.alignment: Qt.AlignHCenter Layout.leftMargin: Appearance.padding.large * 2 diff --git a/modules/controlcenter/network/NetworkingPane.qml b/modules/controlcenter/network/NetworkingPane.qml index e28d35c..4446428 100644 --- a/modules/controlcenter/network/NetworkingPane.qml +++ b/modules/controlcenter/network/NetworkingPane.qml @@ -45,7 +45,6 @@ Item { anchors.right: parent.right spacing: Appearance.spacing.normal - // Network header above the collapsible sections RowLayout { Layout.fillWidth: true spacing: Appearance.spacing.smaller @@ -102,7 +101,6 @@ Item { root.session.ethernet.active = null; root.session.network.active = null; } else { - // Toggle to show settings - prefer ethernet if available, otherwise wireless if (Nmcli.ethernetDevices.length > 0) { root.session.ethernet.active = Nmcli.ethernetDevices[0]; } else if (Nmcli.networks.length > 0) { diff --git a/modules/controlcenter/network/WirelessDetails.qml b/modules/controlcenter/network/WirelessDetails.qml index 7f6a4aa..cf16400 100644 --- a/modules/controlcenter/network/WirelessDetails.qml +++ b/modules/controlcenter/network/WirelessDetails.qml @@ -27,7 +27,6 @@ DeviceDetails { } onNetworkChanged: { - // Restart timer when network changes connectionUpdateTimer.stop(); if (network && network.ssid) { connectionUpdateTimer.start(); @@ -48,11 +47,9 @@ DeviceDetails { updateDeviceDetails(); } function onWirelessDeviceDetailsChanged() { - // When details are updated, check if we should stop the timer if (network && network.ssid) { const isActive = network.active || (Nmcli.active && Nmcli.active.ssid === network.ssid); if (isActive && Nmcli.wirelessDeviceDetails && Nmcli.wirelessDeviceDetails !== null) { - // We have details for the active network, stop the timer connectionUpdateTimer.stop(); } } @@ -65,22 +62,16 @@ DeviceDetails { repeat: true running: network && network.ssid onTriggered: { - // Periodically check if network becomes active and update details if (network) { const isActive = network.active || (Nmcli.active && Nmcli.active.ssid === network.ssid); if (isActive) { - // Network is active - check if we have details if (!Nmcli.wirelessDeviceDetails || Nmcli.wirelessDeviceDetails === null) { - // Network is active but we don't have details yet, fetch them Nmcli.getWirelessDeviceDetails("", () => { - // After fetching, check if we got details - if not, timer will try again }); } else { - // We have details, can stop the timer connectionUpdateTimer.stop(); } } else { - // Network is not active, clear details if (Nmcli.wirelessDeviceDetails !== null) { Nmcli.wirelessDeviceDetails = null; } diff --git a/modules/controlcenter/network/WirelessList.qml b/modules/controlcenter/network/WirelessList.qml index 4726712..9dabe9d 100644 --- a/modules/controlcenter/network/WirelessList.qml +++ b/modules/controlcenter/network/WirelessList.qml @@ -33,10 +33,8 @@ DeviceList { model: ScriptModel { values: [...Nmcli.networks].sort((a, b) => { - // Put active/connected network first if (a.active !== b.active) return b.active - a.active; - // Then sort by signal strength return b.strength - a.strength; }) } @@ -114,7 +112,6 @@ DeviceList { StateLayer { function onClicked(): void { root.session.network.active = modelData; - // Check if we need to refresh saved connections when selecting a network if (modelData && modelData.ssid) { root.checkSavedProfileForNetwork(modelData.ssid); } diff --git a/modules/controlcenter/network/WirelessPasswordDialog.qml b/modules/controlcenter/network/WirelessPasswordDialog.qml index 0f1a5cd..7c046af 100644 --- a/modules/controlcenter/network/WirelessPasswordDialog.qml +++ b/modules/controlcenter/network/WirelessPasswordDialog.qml @@ -19,7 +19,6 @@ Item { required property Session session readonly property var network: { - // Prefer pendingNetwork, then active network if (session.network.pendingNetwork) { return session.network.pendingNetwork; } @@ -157,12 +156,10 @@ Item { focus: true Keys.onPressed: event => { - // Ensure we have focus when receiving keyboard input if (!activeFocus) { forceActiveFocus(); } - // Clear error when user starts typing if (connectButton.hasError && event.text && event.text.length > 0) { connectButton.hasError = false; } @@ -191,7 +188,6 @@ Item { target: root.session.network function onShowPasswordDialogChanged(): void { if (root.session.network.showPasswordDialog) { - // Use callLater to ensure focus happens after dialog is fully rendered Qt.callLater(() => { passwordContainer.forceActiveFocus(); passwordContainer.passwordBuffer = ""; @@ -205,7 +201,6 @@ Item { target: root function onVisibleChanged(): void { if (root.visible) { - // Use callLater to ensure focus happens after dialog is fully rendered Qt.callLater(() => { passwordContainer.forceActiveFocus(); }); @@ -383,46 +378,36 @@ Item { return; } - // Clear any previous error hasError = false; - - // Set connecting state connecting = true; enabled = false; text = qsTr("Connecting..."); - // Connect to network NetworkConnection.connectWithPassword(root.network, password, result => { - if (result && result.success) - // Connection successful, monitor will handle the rest - {} else if (result && result.needsPassword) { - // Shouldn't happen since we provided password + if (result && result.success) { + } else if (result && result.needsPassword) { connectionMonitor.stop(); connecting = false; hasError = true; enabled = true; text = qsTr("Connect"); passwordContainer.passwordBuffer = ""; - // Delete the failed connection if (root.network && root.network.ssid) { Nmcli.forgetNetwork(root.network.ssid); } } else { - // Connection failed immediately - show error connectionMonitor.stop(); connecting = false; hasError = true; enabled = true; text = qsTr("Connect"); passwordContainer.passwordBuffer = ""; - // Delete the failed connection if (root.network && root.network.ssid) { Nmcli.forgetNetwork(root.network.ssid); } } }); - // Start monitoring connection connectionMonitor.start(); } } @@ -435,19 +420,14 @@ Item { return; } - // Check if we're connected to the target network (case-insensitive SSID comparison) const isConnected = root.network && Nmcli.active && Nmcli.active.ssid && Nmcli.active.ssid.toLowerCase().trim() === root.network.ssid.toLowerCase().trim(); if (isConnected) { - // Successfully connected - give it a moment for network list to update - // Use Timer for actual delay connectionSuccessTimer.start(); return; } - // Check for connection failures - if pending connection was cleared but we're not connected if (Nmcli.pendingConnection === null && connectButton.connecting) { - // Wait a bit more before giving up (allow time for connection to establish) if (connectionMonitor.repeatCount > 10) { connectionMonitor.stop(); connectButton.connecting = false; @@ -455,7 +435,6 @@ Item { connectButton.enabled = true; connectButton.text = qsTr("Connect"); passwordContainer.passwordBuffer = ""; - // Delete the failed connection if (root.network && root.network.ssid) { Nmcli.forgetNetwork(root.network.ssid); } @@ -486,7 +465,6 @@ Item { id: connectionSuccessTimer interval: 500 onTriggered: { - // Double-check connection is still active if (root.visible && Nmcli.active && Nmcli.active.ssid) { const stillConnected = Nmcli.active.ssid.toLowerCase().trim() === root.network.ssid.toLowerCase().trim(); if (stillConnected) { @@ -514,7 +492,6 @@ Item { connectButton.enabled = true; connectButton.text = qsTr("Connect"); passwordContainer.passwordBuffer = ""; - // Delete the failed connection Nmcli.forgetNetwork(ssid); } } diff --git a/modules/controlcenter/state/BluetoothState.qml b/modules/controlcenter/state/BluetoothState.qml index db8c7e1..00497ce 100644 --- a/modules/controlcenter/state/BluetoothState.qml +++ b/modules/controlcenter/state/BluetoothState.qml @@ -4,13 +4,8 @@ import QtQuick QtObject { id: root - // Active selected device property BluetoothDevice active: null - - // Current adapter being used property BluetoothAdapter currentAdapter: Bluetooth.defaultAdapter - - // UI state flags property bool editingAdapterName: false property bool fabMenuOpen: false property bool editingDeviceName: false diff --git a/modules/controlcenter/state/EthernetState.qml b/modules/controlcenter/state/EthernetState.qml index 25b243a..c5da7aa 100644 --- a/modules/controlcenter/state/EthernetState.qml +++ b/modules/controlcenter/state/EthernetState.qml @@ -3,7 +3,6 @@ import QtQuick QtObject { id: root - // Active selected ethernet interface property var active: null } diff --git a/modules/controlcenter/state/LauncherState.qml b/modules/controlcenter/state/LauncherState.qml index cd9eeb6..c5da7aa 100644 --- a/modules/controlcenter/state/LauncherState.qml +++ b/modules/controlcenter/state/LauncherState.qml @@ -3,7 +3,6 @@ import QtQuick QtObject { id: root - // Active selected application property var active: null } diff --git a/modules/controlcenter/state/NetworkState.qml b/modules/controlcenter/state/NetworkState.qml index 651a35c..da13e65 100644 --- a/modules/controlcenter/state/NetworkState.qml +++ b/modules/controlcenter/state/NetworkState.qml @@ -3,10 +3,7 @@ import QtQuick QtObject { id: root - // Active selected wireless network property var active: null - - // Password dialog state property bool showPasswordDialog: false property var pendingNetwork: null } diff --git a/modules/controlcenter/taskbar/TaskbarPane.qml b/modules/controlcenter/taskbar/TaskbarPane.qml index 1c3adbc..f452b07 100644 --- a/modules/controlcenter/taskbar/TaskbarPane.qml +++ b/modules/controlcenter/taskbar/TaskbarPane.qml @@ -18,15 +18,10 @@ Item { required property Session session - // Clock property bool clockShowIcon: Config.bar.clock.showIcon ?? true - - // Bar Behavior property bool persistent: Config.bar.persistent ?? true property bool showOnHover: Config.bar.showOnHover ?? true property int dragThreshold: Config.bar.dragThreshold ?? 20 - - // Status Icons property bool showAudio: Config.bar.status.showAudio ?? true property bool showMicrophone: Config.bar.status.showMicrophone ?? true property bool showKbLayout: Config.bar.status.showKbLayout ?? false @@ -34,25 +29,17 @@ Item { property bool showBluetooth: Config.bar.status.showBluetooth ?? true property bool showBattery: Config.bar.status.showBattery ?? true property bool showLockStatus: Config.bar.status.showLockStatus ?? true - - // Tray Settings property bool trayBackground: Config.bar.tray.background ?? false property bool trayCompact: Config.bar.tray.compact ?? false property bool trayRecolour: Config.bar.tray.recolour ?? false - - // Workspaces property int workspacesShown: Config.bar.workspaces.shown ?? 5 property bool workspacesActiveIndicator: Config.bar.workspaces.activeIndicator ?? true property bool workspacesOccupiedBg: Config.bar.workspaces.occupiedBg ?? false property bool workspacesShowWindows: Config.bar.workspaces.showWindows ?? false property bool workspacesPerMonitor: Config.bar.workspaces.perMonitorWorkspaces ?? true - - // Scroll Actions property bool scrollWorkspaces: Config.bar.scrollActions.workspaces ?? true property bool scrollVolume: Config.bar.scrollActions.volume ?? true property bool scrollBrightness: Config.bar.scrollActions.brightness ?? true - - // Popouts property bool popoutActiveWindow: Config.bar.popouts.activeWindow ?? true property bool popoutTray: Config.bar.popouts.tray ?? true property bool popoutStatusIcons: Config.bar.popouts.statusIcons ?? true @@ -60,7 +47,6 @@ Item { anchors.fill: parent Component.onCompleted: { - // Update entries if (Config.bar.entries) { entriesModel.clear(); for (let i = 0; i < Config.bar.entries.length; i++) { @@ -74,15 +60,10 @@ Item { } function saveConfig(entryIndex, entryEnabled) { - // Update clock setting Config.bar.clock.showIcon = root.clockShowIcon; - - // Update bar behavior Config.bar.persistent = root.persistent; Config.bar.showOnHover = root.showOnHover; Config.bar.dragThreshold = root.dragThreshold; - - // Update status icons Config.bar.status.showAudio = root.showAudio; Config.bar.status.showMicrophone = root.showMicrophone; Config.bar.status.showKbLayout = root.showKbLayout; @@ -90,35 +71,24 @@ Item { Config.bar.status.showBluetooth = root.showBluetooth; Config.bar.status.showBattery = root.showBattery; Config.bar.status.showLockStatus = root.showLockStatus; - - // Update tray settings Config.bar.tray.background = root.trayBackground; Config.bar.tray.compact = root.trayCompact; Config.bar.tray.recolour = root.trayRecolour; - - // Update workspaces Config.bar.workspaces.shown = root.workspacesShown; Config.bar.workspaces.activeIndicator = root.workspacesActiveIndicator; Config.bar.workspaces.occupiedBg = root.workspacesOccupiedBg; Config.bar.workspaces.showWindows = root.workspacesShowWindows; Config.bar.workspaces.perMonitorWorkspaces = root.workspacesPerMonitor; - - // Update scroll actions Config.bar.scrollActions.workspaces = root.scrollWorkspaces; Config.bar.scrollActions.volume = root.scrollVolume; Config.bar.scrollActions.brightness = root.scrollBrightness; - - // Update popouts Config.bar.popouts.activeWindow = root.popoutActiveWindow; Config.bar.popouts.tray = root.popoutTray; Config.bar.popouts.statusIcons = root.popoutStatusIcons; - // Update entries from the model (same approach as clock - use provided value if available) const entries = []; for (let i = 0; i < entriesModel.count; i++) { const entry = entriesModel.get(i); - // If this is the entry being updated, use the provided value (same as clock toggle reads from switch) - // Otherwise use the value from the model let enabled = entry.enabled; if (entryIndex !== undefined && i === entryIndex) { enabled = entryEnabled; @@ -129,8 +99,6 @@ Item { }); } Config.bar.entries = entries; - - // Persist changes to disk Config.save(); } -- cgit v1.2.3-freya