From e92187293e4afa046ca05bd80796c1fa193097e5 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Wed, 12 Nov 2025 15:58:42 -0500 Subject: controlcenter: refactoring into components --- components/controls/CollapsibleSection.qml | 85 ++++++++++++++++++++++++++++++ components/controls/SpinBoxRow.qml | 51 ++++++++++++++++++ components/controls/SwitchRow.qml | 47 +++++++++++++++++ components/controls/ToggleButton.qml | 82 ++++++++++++++++++++++++++++ 4 files changed, 265 insertions(+) create mode 100644 components/controls/CollapsibleSection.qml create mode 100644 components/controls/SpinBoxRow.qml create mode 100644 components/controls/SwitchRow.qml create mode 100644 components/controls/ToggleButton.qml (limited to 'components') diff --git a/components/controls/CollapsibleSection.qml b/components/controls/CollapsibleSection.qml new file mode 100644 index 0000000..945386c --- /dev/null +++ b/components/controls/CollapsibleSection.qml @@ -0,0 +1,85 @@ +import ".." +import qs.components +import qs.components.effects +import qs.services +import qs.config +import QtQuick +import QtQuick.Layouts + +ColumnLayout { + id: root + + required property string title + property string description: "" + property bool expanded: false + + signal toggleRequested + + spacing: Appearance.spacing.small / 2 + Layout.fillWidth: true + + Item { + id: sectionHeaderItem + Layout.fillWidth: true + Layout.preferredHeight: sectionHeader.implicitHeight + + ColumnLayout { + id: sectionHeader + anchors.left: parent.left + anchors.right: parent.right + spacing: Appearance.spacing.small + + RowLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.small + + StyledText { + Layout.topMargin: Appearance.spacing.large + text: root.title + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } + + Item { + Layout.fillWidth: true + } + + MaterialIcon { + text: "expand_more" + rotation: root.expanded ? 180 : 0 + color: Colours.palette.m3onSurface + Behavior on rotation { + Anim {} + } + } + } + + StateLayer { + anchors.fill: parent + anchors.leftMargin: -Appearance.padding.normal + anchors.rightMargin: -Appearance.padding.normal + function onClicked(): void { + root.toggleRequested(); + root.expanded = !root.expanded; + } + } + + StyledText { + visible: root.expanded && root.description !== "" + text: root.description + color: Colours.palette.m3outline + Layout.fillWidth: true + } + } + } + + default property alias content: contentColumn.data + + ColumnLayout { + id: contentColumn + Layout.fillWidth: true + visible: root.expanded + spacing: Appearance.spacing.small / 2 + } +} + diff --git a/components/controls/SpinBoxRow.qml b/components/controls/SpinBoxRow.qml new file mode 100644 index 0000000..a4441c5 --- /dev/null +++ b/components/controls/SpinBoxRow.qml @@ -0,0 +1,51 @@ +import ".." +import qs.components +import qs.components.effects +import qs.services +import qs.config +import QtQuick +import QtQuick.Layouts + +StyledRect { + id: root + + required property string label + required property real value + required property real min + required property real max + property var onValueModified: function(value) {} + + Layout.fillWidth: true + implicitHeight: row.implicitHeight + Appearance.padding.large * 2 + radius: Appearance.rounding.normal + color: Colours.tPalette.m3surfaceContainer + + Behavior on implicitHeight { + Anim {} + } + + RowLayout { + id: row + + 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: root.label + } + + CustomSpinBox { + min: root.min + max: root.max + value: root.value + onValueModified: value => { + root.onValueModified(value); + } + } + } +} + diff --git a/components/controls/SwitchRow.qml b/components/controls/SwitchRow.qml new file mode 100644 index 0000000..a486ee2 --- /dev/null +++ b/components/controls/SwitchRow.qml @@ -0,0 +1,47 @@ +import ".." +import qs.components +import qs.components.effects +import qs.services +import qs.config +import QtQuick +import QtQuick.Layouts + +StyledRect { + id: root + + required property string label + required property bool checked + property var onToggled: function(checked) {} + + Layout.fillWidth: true + implicitHeight: row.implicitHeight + Appearance.padding.large * 2 + radius: Appearance.rounding.normal + color: Colours.tPalette.m3surfaceContainer + + Behavior on implicitHeight { + Anim {} + } + + RowLayout { + id: row + + 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: root.label + } + + StyledSwitch { + checked: root.checked + onToggled: { + root.onToggled(checked); + } + } + } +} + diff --git a/components/controls/ToggleButton.qml b/components/controls/ToggleButton.qml new file mode 100644 index 0000000..9d8e094 --- /dev/null +++ b/components/controls/ToggleButton.qml @@ -0,0 +1,82 @@ +import ".." +import qs.components +import qs.components.effects +import qs.services +import qs.config +import QtQuick +import QtQuick.Layouts + +StyledRect { + id: root + + required property bool toggled + property string icon + property string label + property string accent: "Secondary" + + signal clicked + + 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: root.toggled ? Colours.palette[`m3on${root.accent}`] : Colours.palette[`m3on${root.accent}Container`] + + function onClicked(): void { + root.clicked(); + } + } + + RowLayout { + id: toggleBtnInner + + anchors.centerIn: parent + spacing: Appearance.spacing.normal + + MaterialIcon { + id: toggleBtnIcon + + visible: !!text + fill: root.toggled ? 1 : 0 + text: root.icon + color: root.toggled ? Colours.palette[`m3on${root.accent}`] : Colours.palette[`m3on${root.accent}Container`] + font.pointSize: Appearance.font.size.large + + Behavior on fill { + Anim {} + } + } + + Loader { + asynchronous: true + active: !!root.label + visible: active + + sourceComponent: StyledText { + text: root.label + color: root.toggled ? Colours.palette[`m3on${root.accent}`] : Colours.palette[`m3on${root.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 + } + } +} + -- cgit v1.2.3-freya From e21a1519b0b59da0847f8c215201720fef3d854e Mon Sep 17 00:00:00 2001 From: ATMDA Date: Wed, 12 Nov 2025 16:06:16 -0500 Subject: controlcenter: refactoring into components --- components/controls/SwitchRow.qml | 2 + .../controlcenter/appearance/AppearancePane.qml | 78 ++++------------------ modules/controlcenter/launcher/LauncherPane.qml | 45 ++++--------- 3 files changed, 27 insertions(+), 98 deletions(-) (limited to 'components') diff --git a/components/controls/SwitchRow.qml b/components/controls/SwitchRow.qml index a486ee2..0ec7aa5 100644 --- a/components/controls/SwitchRow.qml +++ b/components/controls/SwitchRow.qml @@ -11,6 +11,7 @@ StyledRect { required property string label required property bool checked + property bool enabled: true property var onToggled: function(checked) {} Layout.fillWidth: true @@ -38,6 +39,7 @@ StyledRect { StyledSwitch { checked: root.checked + enabled: root.enabled onToggled: { root.onToggled(checked); } diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index cfe5b56..68e2e2d 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -263,38 +263,11 @@ RowLayout { root.collapseAllSections(themeModeSection); } - StyledRect { - Layout.fillWidth: true - implicitHeight: modeToggle.implicitHeight + Appearance.padding.large * 2 - - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - - Behavior on implicitHeight { - Anim {} - } - - 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"); - } - } + SwitchRow { + label: qsTr("Dark mode") + checked: !Colours.currentLight + onToggled: checked => { + Colours.setMode(checked ? "dark" : "light"); } } } @@ -803,39 +776,14 @@ RowLayout { } } - StyledRect { - Layout.fillWidth: true - implicitHeight: fontSizeScaleRow.implicitHeight + Appearance.padding.large * 2 - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - - Behavior on implicitHeight { - Anim {} - } - - RowLayout { - id: fontSizeScaleRow - 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("Font size scale") - } - - CustomSpinBox { - id: fontSizeScaleSpinBox - min: 0.1 - max: 5 - value: root.fontSizeScale - onValueModified: value => { - root.fontSizeScale = value; - root.saveConfig(); - } - } + SpinBoxRow { + label: qsTr("Font size scale") + min: 0.1 + max: 5 + value: root.fontSizeScale + onValueModified: value => { + root.fontSizeScale = value; + root.saveConfig(); } } } diff --git a/modules/controlcenter/launcher/LauncherPane.qml b/modules/controlcenter/launcher/LauncherPane.qml index 82e145a..9b2570a 100644 --- a/modules/controlcenter/launcher/LauncherPane.qml +++ b/modules/controlcenter/launcher/LauncherPane.qml @@ -22,6 +22,7 @@ RowLayout { required property Session session property var selectedApp: null + property bool hideFromLauncherChecked: false anchors.fill: parent @@ -45,7 +46,7 @@ RowLayout { function updateToggleState() { if (!root.selectedApp || !configFile.loaded) { - hideFromLauncherSwitch.checked = false; + root.hideFromLauncherChecked = false; return; } @@ -54,16 +55,16 @@ RowLayout { const appId = root.selectedApp.id || root.selectedApp.entry?.id; if (config.launcher && config.launcher.hiddenApps) { - hideFromLauncherSwitch.checked = config.launcher.hiddenApps.includes(appId); + root.hideFromLauncherChecked = config.launcher.hiddenApps.includes(appId); } else { - hideFromLauncherSwitch.checked = false; + root.hideFromLauncherChecked = false; } } catch (e) { console.error("Failed to update toggle state:", e); } } - function saveHiddenApps() { + function saveHiddenApps(isHidden) { if (!configFile.loaded || !root.selectedApp) { return; } @@ -76,7 +77,6 @@ RowLayout { 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 @@ -288,35 +288,14 @@ RowLayout { anchors.top: parent.top spacing: Appearance.spacing.normal - StyledRect { - Layout.fillWidth: true + SwitchRow { 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(); - } - } + label: qsTr("Hide from launcher") + checked: root.hideFromLauncherChecked + enabled: root.selectedApp !== null && configFile.loaded + onToggled: checked => { + root.hideFromLauncherChecked = checked; + root.saveHiddenApps(checked); } } -- cgit v1.2.3-freya From 893a91a95a11cb57ffe2fbfa0ec221c1a309528b Mon Sep 17 00:00:00 2001 From: ATMDA Date: Wed, 12 Nov 2025 16:36:42 -0500 Subject: controlcenter: refactored wifi/ethernet panels --- components/ConnectionHeader.qml | 32 +++ components/ConnectionInfoSection.qml | 60 ++++ components/PropertyRow.qml | 27 ++ components/SectionContainer.qml | 31 +++ components/SectionHeader.qml | 28 ++ components/controls/ToggleRow.qml | 29 ++ modules/controlcenter/ethernet/EthernetDetails.qml | 238 +++------------- modules/controlcenter/network/Details.qml | 305 +++++---------------- plan.plan.md | 113 ++++++++ 9 files changed, 432 insertions(+), 431 deletions(-) create mode 100644 components/ConnectionHeader.qml create mode 100644 components/ConnectionInfoSection.qml create mode 100644 components/PropertyRow.qml create mode 100644 components/SectionContainer.qml create mode 100644 components/SectionHeader.qml create mode 100644 components/controls/ToggleRow.qml create mode 100644 plan.plan.md (limited to 'components') diff --git a/components/ConnectionHeader.qml b/components/ConnectionHeader.qml new file mode 100644 index 0000000..3f77fd9 --- /dev/null +++ b/components/ConnectionHeader.qml @@ -0,0 +1,32 @@ +import qs.components +import qs.services +import qs.config +import QtQuick +import QtQuick.Layouts + +ColumnLayout { + id: root + + required property string icon + required property string title + + spacing: Appearance.spacing.normal + Layout.alignment: Qt.AlignHCenter + + MaterialIcon { + Layout.alignment: Qt.AlignHCenter + animate: true + text: root.icon + font.pointSize: Appearance.font.size.extraLarge * 3 + font.bold: true + } + + StyledText { + Layout.alignment: Qt.AlignHCenter + animate: true + text: root.title + font.pointSize: Appearance.font.size.large + font.bold: true + } +} + diff --git a/components/ConnectionInfoSection.qml b/components/ConnectionInfoSection.qml new file mode 100644 index 0000000..88c6b3a --- /dev/null +++ b/components/ConnectionInfoSection.qml @@ -0,0 +1,60 @@ +import qs.components +import qs.components.effects +import qs.services +import qs.config +import QtQuick +import QtQuick.Layouts + +ColumnLayout { + id: root + + required property var deviceDetails + + spacing: Appearance.spacing.small / 2 + + StyledText { + text: qsTr("IP Address") + } + + StyledText { + text: root.deviceDetails?.ipAddress || qsTr("Not available") + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.small + } + + StyledText { + Layout.topMargin: Appearance.spacing.normal + text: qsTr("Subnet Mask") + } + + StyledText { + text: root.deviceDetails?.subnet || qsTr("Not available") + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.small + } + + StyledText { + Layout.topMargin: Appearance.spacing.normal + text: qsTr("Gateway") + } + + StyledText { + text: root.deviceDetails?.gateway || qsTr("Not available") + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.small + } + + StyledText { + Layout.topMargin: Appearance.spacing.normal + text: qsTr("DNS Servers") + } + + StyledText { + text: (root.deviceDetails && root.deviceDetails.dns && root.deviceDetails.dns.length > 0) ? root.deviceDetails.dns.join(", ") : qsTr("Not available") + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.small + wrapMode: Text.Wrap + Layout.maximumWidth: parent.width + } +} + diff --git a/components/PropertyRow.qml b/components/PropertyRow.qml new file mode 100644 index 0000000..697830a --- /dev/null +++ b/components/PropertyRow.qml @@ -0,0 +1,27 @@ +import qs.components +import qs.services +import qs.config +import QtQuick +import QtQuick.Layouts + +ColumnLayout { + id: root + + required property string label + required property string value + property bool showTopMargin: false + + spacing: Appearance.spacing.small / 2 + + StyledText { + Layout.topMargin: root.showTopMargin ? Appearance.spacing.normal : 0 + text: root.label + } + + StyledText { + text: root.value + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.small + } +} + diff --git a/components/SectionContainer.qml b/components/SectionContainer.qml new file mode 100644 index 0000000..d41254b --- /dev/null +++ b/components/SectionContainer.qml @@ -0,0 +1,31 @@ +import qs.components +import qs.components.effects +import qs.services +import qs.config +import QtQuick +import QtQuick.Layouts + +StyledRect { + id: root + + default property alias content: contentColumn.data + property real contentSpacing: Appearance.spacing.larger + + Layout.fillWidth: true + implicitHeight: contentColumn.implicitHeight + Appearance.padding.large * 2 + + radius: Appearance.rounding.normal + color: Colours.tPalette.m3surfaceContainer + + ColumnLayout { + id: contentColumn + + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.large + + spacing: root.contentSpacing + } +} + diff --git a/components/SectionHeader.qml b/components/SectionHeader.qml new file mode 100644 index 0000000..897e63a --- /dev/null +++ b/components/SectionHeader.qml @@ -0,0 +1,28 @@ +import qs.components +import qs.services +import qs.config +import QtQuick +import QtQuick.Layouts + +ColumnLayout { + id: root + + required property string title + property string description: "" + + spacing: 0 + + StyledText { + Layout.topMargin: Appearance.spacing.large + text: root.title + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } + + StyledText { + visible: root.description !== "" + text: root.description + color: Colours.palette.m3outline + } +} + diff --git a/components/controls/ToggleRow.qml b/components/controls/ToggleRow.qml new file mode 100644 index 0000000..23dc2a2 --- /dev/null +++ b/components/controls/ToggleRow.qml @@ -0,0 +1,29 @@ +import qs.components +import qs.components.controls +import qs.services +import qs.config +import QtQuick +import QtQuick.Layouts + +RowLayout { + id: root + + required property string label + property alias checked: toggle.checked + property alias toggle: toggle + + Layout.fillWidth: true + spacing: Appearance.spacing.normal + + StyledText { + Layout.fillWidth: true + text: root.label + } + + StyledSwitch { + id: toggle + + cLayer: 2 + } +} + diff --git a/modules/controlcenter/ethernet/EthernetDetails.qml b/modules/controlcenter/ethernet/EthernetDetails.qml index 1db3db0..a49eb4f 100644 --- a/modules/controlcenter/ethernet/EthernetDetails.qml +++ b/modules/controlcenter/ethernet/EthernetDetails.qml @@ -43,229 +43,73 @@ Item { 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 + ConnectionHeader { + icon: "cable" + title: root.device?.interface ?? qsTr("Unknown") } - StyledText { - Layout.alignment: Qt.AlignHCenter - animate: true - text: root.device?.interface ?? qsTr("Unknown") - font.pointSize: Appearance.font.size.large - font.bold: true + SectionHeader { + title: qsTr("Connection status") + description: qsTr("Connection settings for this device") } - 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) { - // Use connection name if available, otherwise use interface - Network.connectEthernet(root.device?.connection || "", root.device?.interface || ""); - } else { - if (root.device?.connection) { - Network.disconnectEthernet(root.device.connection); - } + SectionContainer { + ToggleRow { + label: qsTr("Connected") + checked: root.device?.connected ?? false + toggle.onToggled: { + if (checked) { + // Use connection name if available, otherwise use interface + Network.connectEthernet(root.device?.connection || "", root.device?.interface || ""); + } else { + if (root.device?.connection) { + Network.disconnectEthernet(root.device.connection); } } } } } - StyledText { - Layout.topMargin: Appearance.spacing.large - text: qsTr("Device properties") - font.pointSize: Appearance.font.size.larger - font.weight: 500 + SectionHeader { + title: qsTr("Device properties") + description: qsTr("Additional information") } - 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 + SectionContainer { + contentSpacing: Appearance.spacing.small / 2 - 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") - } + PropertyRow { + label: qsTr("Interface") + value: root.device?.interface ?? qsTr("Unknown") + } - StyledText { - text: root.device?.state ?? qsTr("Unknown") - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.small - } + PropertyRow { + showTopMargin: true + label: qsTr("Connection") + value: root.device?.connection || qsTr("Not connected") } - } - StyledText { - Layout.topMargin: Appearance.spacing.large - text: qsTr("Connection information") - font.pointSize: Appearance.font.size.larger - font.weight: 500 + PropertyRow { + showTopMargin: true + label: qsTr("State") + value: root.device?.state ?? qsTr("Unknown") + } } - StyledText { - text: qsTr("Network connection details") - color: Colours.palette.m3outline + SectionHeader { + title: qsTr("Connection information") + description: qsTr("Network connection details") } - StyledRect { - Layout.fillWidth: true - implicitHeight: connectionInfo.implicitHeight + Appearance.padding.large * 2 - - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - - ColumnLayout { - id: connectionInfo - - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.large - - spacing: Appearance.spacing.small / 2 - - StyledText { - text: qsTr("IP Address") - } - - StyledText { - text: Network.ethernetDeviceDetails?.ipAddress || qsTr("Not available") - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.small - } - - StyledText { - Layout.topMargin: Appearance.spacing.normal - text: qsTr("Subnet Mask") - } - - StyledText { - text: Network.ethernetDeviceDetails?.subnet || qsTr("Not available") - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.small - } - - StyledText { - Layout.topMargin: Appearance.spacing.normal - text: qsTr("Gateway") - } - - StyledText { - text: Network.ethernetDeviceDetails?.gateway || qsTr("Not available") - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.small - } - - StyledText { - Layout.topMargin: Appearance.spacing.normal - text: qsTr("DNS Servers") - } - - StyledText { - text: (Network.ethernetDeviceDetails && Network.ethernetDeviceDetails.dns && Network.ethernetDeviceDetails.dns.length > 0) ? Network.ethernetDeviceDetails.dns.join(", ") : qsTr("Not available") - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.small - wrapMode: Text.Wrap - Layout.maximumWidth: parent.width - } + SectionContainer { + ConnectionInfoSection { + deviceDetails: Network.ethernetDeviceDetails } } } } - 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/network/Details.qml b/modules/controlcenter/network/Details.qml index 31d20bc..a53f62e 100644 --- a/modules/controlcenter/network/Details.qml +++ b/modules/controlcenter/network/Details.qml @@ -54,272 +54,109 @@ Item { anchors.right: parent.right spacing: Appearance.spacing.normal - MaterialIcon { - Layout.alignment: Qt.AlignHCenter - animate: true - text: root.network?.isSecure ? "lock" : "wifi" - font.pointSize: Appearance.font.size.extraLarge * 3 - font.bold: true + ConnectionHeader { + icon: root.network?.isSecure ? "lock" : "wifi" + title: root.network?.ssid ?? qsTr("Unknown") } - StyledText { - Layout.alignment: Qt.AlignHCenter - animate: true - text: root.network?.ssid ?? qsTr("Unknown") - font.pointSize: Appearance.font.size.large - font.bold: true + SectionHeader { + title: qsTr("Connection status") + description: qsTr("Connection settings for this network") } - 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 network") - color: Colours.palette.m3outline - } - - StyledRect { - Layout.fillWidth: true - implicitHeight: networkStatus.implicitHeight + Appearance.padding.large * 2 - - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - - ColumnLayout { - id: networkStatus - - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.large - - spacing: Appearance.spacing.larger - - Toggle { - label: qsTr("Connected") - checked: root.network?.active ?? false - toggle.onToggled: { - if (checked) { - // If already connected to a different network, disconnect first - if (Network.active && Network.active.ssid !== root.network.ssid) { - Network.disconnectFromNetwork(); - // Wait a moment before connecting to new network - Qt.callLater(() => { - connectToNetwork(); - }); - } else { + SectionContainer { + ToggleRow { + label: qsTr("Connected") + checked: root.network?.active ?? false + toggle.onToggled: { + if (checked) { + // If already connected to a different network, disconnect first + if (Network.active && Network.active.ssid !== root.network.ssid) { + Network.disconnectFromNetwork(); + // Wait a moment before connecting to new network + Qt.callLater(() => { connectToNetwork(); - } + }); } else { - Network.disconnectFromNetwork(); + connectToNetwork(); } + } else { + Network.disconnectFromNetwork(); } + } - function connectToNetwork(): void { - if (root.network.isSecure) { - // Try connecting without password first (in case it's saved) - Network.connectToNetworkWithPasswordCheck( - root.network.ssid, - root.network.isSecure, - () => { - // Callback: connection failed, show password dialog - root.session.network.showPasswordDialog = true; - root.session.network.pendingNetwork = root.network; - } - ); - } else { - Network.connectToNetwork(root.network.ssid, ""); - } + function connectToNetwork(): void { + if (root.network.isSecure) { + // Try connecting without password first (in case it's saved) + Network.connectToNetworkWithPasswordCheck( + root.network.ssid, + root.network.isSecure, + () => { + // Callback: connection failed, show password dialog + root.session.network.showPasswordDialog = true; + root.session.network.pendingNetwork = root.network; + } + ); + } else { + Network.connectToNetwork(root.network.ssid, ""); } } } } - StyledText { - Layout.topMargin: Appearance.spacing.large - text: qsTr("Network properties") - font.pointSize: Appearance.font.size.larger - font.weight: 500 - } - - StyledText { - text: qsTr("Additional information") - color: Colours.palette.m3outline + SectionHeader { + title: qsTr("Network properties") + description: qsTr("Additional information") } - StyledRect { - Layout.fillWidth: true - implicitHeight: networkProps.implicitHeight + Appearance.padding.large * 2 + SectionContainer { + contentSpacing: Appearance.spacing.small / 2 - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - - ColumnLayout { - id: networkProps - - 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("SSID") - } - - StyledText { - text: root.network?.ssid ?? qsTr("Unknown") - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.small - } - - StyledText { - Layout.topMargin: Appearance.spacing.normal - text: qsTr("BSSID") - } - - StyledText { - text: root.network?.bssid ?? qsTr("Unknown") - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.small - } - - StyledText { - Layout.topMargin: Appearance.spacing.normal - text: qsTr("Signal strength") - } - - StyledText { - text: root.network ? qsTr("%1%").arg(root.network.strength) : qsTr("N/A") - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.small - } - - StyledText { - Layout.topMargin: Appearance.spacing.normal - text: qsTr("Frequency") - } + PropertyRow { + label: qsTr("SSID") + value: root.network?.ssid ?? qsTr("Unknown") + } - StyledText { - text: root.network ? qsTr("%1 MHz").arg(root.network.frequency) : qsTr("N/A") - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.small - } + PropertyRow { + showTopMargin: true + label: qsTr("BSSID") + value: root.network?.bssid ?? qsTr("Unknown") + } - StyledText { - Layout.topMargin: Appearance.spacing.normal - text: qsTr("Security") - } + PropertyRow { + showTopMargin: true + label: qsTr("Signal strength") + value: root.network ? qsTr("%1%").arg(root.network.strength) : qsTr("N/A") + } - StyledText { - text: root.network ? (root.network.isSecure ? root.network.security : qsTr("Open")) : qsTr("N/A") - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.small - } + PropertyRow { + showTopMargin: true + label: qsTr("Frequency") + value: root.network ? qsTr("%1 MHz").arg(root.network.frequency) : qsTr("N/A") } - } - StyledText { - Layout.topMargin: Appearance.spacing.large - text: qsTr("Connection information") - font.pointSize: Appearance.font.size.larger - font.weight: 500 + PropertyRow { + showTopMargin: true + label: qsTr("Security") + value: root.network ? (root.network.isSecure ? root.network.security : qsTr("Open")) : qsTr("N/A") + } } - StyledText { - text: qsTr("Network connection details") - color: Colours.palette.m3outline + SectionHeader { + title: qsTr("Connection information") + description: qsTr("Network connection details") } - StyledRect { - Layout.fillWidth: true - implicitHeight: connectionInfo.implicitHeight + Appearance.padding.large * 2 - - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - - ColumnLayout { - id: connectionInfo - - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.large - - spacing: Appearance.spacing.small / 2 - - StyledText { - text: qsTr("IP Address") - } - - StyledText { - text: Network.wirelessDeviceDetails?.ipAddress || qsTr("Not available") - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.small - } - - StyledText { - Layout.topMargin: Appearance.spacing.normal - text: qsTr("Subnet Mask") - } - - StyledText { - text: Network.wirelessDeviceDetails?.subnet || qsTr("Not available") - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.small - } - - StyledText { - Layout.topMargin: Appearance.spacing.normal - text: qsTr("Gateway") - } - - StyledText { - text: Network.wirelessDeviceDetails?.gateway || qsTr("Not available") - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.small - } - - StyledText { - Layout.topMargin: Appearance.spacing.normal - text: qsTr("DNS Servers") - } - - StyledText { - text: (Network.wirelessDeviceDetails && Network.wirelessDeviceDetails.dns && Network.wirelessDeviceDetails.dns.length > 0) ? Network.wirelessDeviceDetails.dns.join(", ") : qsTr("Not available") - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.small - wrapMode: Text.Wrap - Layout.maximumWidth: parent.width - } + SectionContainer { + ConnectionInfoSection { + deviceDetails: Network.wirelessDeviceDetails } } } } - 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/plan.plan.md b/plan.plan.md new file mode 100644 index 0000000..5b6f1f2 --- /dev/null +++ b/plan.plan.md @@ -0,0 +1,113 @@ +# Refactoring Plan: Control Center Panes + +## Overview + +After analyzing the last 30 commits, I've identified significant code duplication and opportunities for modularization in the control center panels. This plan focuses on extracting common patterns into reusable components. + +## Key Refactoring Opportunities + +### 1. Details Component Consolidation + +**Files affected:** `modules/controlcenter/network/Details.qml`, `modules/controlcenter/ethernet/EthernetDetails.qml` + +**Issue:** Both files share identical structure: + +- Header with icon and title +- Connection status section +- Properties section +- Connection information section (IP, subnet, gateway, DNS) + +**Solution:** Create `components/ConnectionDetails.qml` that accepts: + +- Device/network object +- Icon name +- Title property path +- Details source (wirelessDeviceDetails vs ethernetDeviceDetails) + +**Impact:** Reduces ~200 lines of duplication. + +### 2. ToggleButton Component Extraction + +**Files affected:** `modules/controlcenter/network/NetworkList.qml`, `modules/controlcenter/ethernet/EthernetList.qml` + +**Issue:** Both files define identical `ToggleButton` component (lines 228-301 in NetworkList, 170-243 in EthernetList). + +**Solution:** Move to `components/controls/ToggleButton.qml` and import in both files. + +**Impact:** Eliminates ~70 lines of duplication. + +### 3. Switch/SpinBox Row Components + +**Files affected:** `modules/controlcenter/appearance/AppearancePane.qml`, `modules/controlcenter/taskbar/TaskbarPane.qml` + +**Issue:** Repeated patterns for: + +- Switch rows (label + StyledSwitch) +- SpinBox rows (label + CustomSpinBox) +- Same layout, spacing, and styling + +**Solution:** Create: + +- `components/controls/SwitchRow.qml` - label + switch with config save callback +- `components/controls/SpinBoxRow.qml` - label + spinbox with config save callback + +**Impact:** Reduces ~30-40 lines per row instance (20+ instances total). + +### 4. Font List Delegate Consolidation + +**Files affected:** `modules/controlcenter/appearance/AppearancePane.qml` + +**Issue:** Three nearly identical font list implementations (Material, Mono, Sans) with only the property binding differing. + +**Solution:** Create `components/FontList.qml` that accepts: + +- Current font property +- Save callback function +- Title text + +**Impact:** Reduces ~150 lines of duplication. + +### 5. List Item Selection Pattern + +**Files affected:** Multiple list delegates across panes + +**Issue:** Repeated pattern for selected item highlighting: + +- Color with alpha based on selection +- Border width/color based on selection +- StateLayer click handler + +**Solution:** Create `components/SelectableListItem.qml` wrapper that handles selection styling. + +**Impact:** Reduces ~10-15 lines per list delegate. + +## Implementation Order + +1. **ConnectionDetails consolidation** (medium impact) +2. **FontList consolidation** (low-medium impact) +3. **SelectableListItem pattern** (nice-to-have, lower priority) + +## Files to Create + +- `components/controls/SelectableListItem.qml` +- `components/ConnectionDetails.qml` +- `components/FontList.qml` + +## Completed Items + +- ✅ `components/controls/CollapsibleSection.qml` - DONE +- ✅ `components/controls/SwitchRow.qml` - DONE +- ✅ `components/controls/SpinBoxRow.qml` - DONE +- ✅ `components/controls/ToggleButton.qml` - DONE + +## Estimated Impact + +- **Lines removed:** ~400-500 lines of duplicated code (from remaining items) +- **Maintainability:** Significantly improved - changes to common patterns only need to be made once +- **Readability:** Panes become more declarative and easier to understand +- **Testability:** Reusable components can be tested independently + +## Completed Refactoring + +- **Lines removed so far:** ~1300+ lines of duplicated code +- **Components created:** CollapsibleSection, SwitchRow, SpinBoxRow, ToggleButton \ No newline at end of file -- cgit v1.2.3-freya From 1da9c68be8f336a671f9514cf5feaaf5998da981 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Thu, 13 Nov 2025 14:41:14 -0500 Subject: cleanup: trailing whitespace removeal (entire project) --- components/controls/CollapsibleSection.qml | 2 +- .../controlcenter/appearance/AppearancePane.qml | 50 +++---- modules/controlcenter/launcher/LauncherPane.qml | 10 +- modules/controlcenter/network/WirelessDetails.qml | 6 +- modules/controlcenter/network/WirelessList.qml | 2 +- .../network/WirelessPasswordDialog.qml | 10 +- modules/controlcenter/taskbar/TaskbarPane.qml | 8 +- modules/drawers/Interactions.qml | 22 +-- services/Network.qml | 154 ++++++++++----------- services/VPN.qml | 6 +- utils/Icons.qml | 4 +- 11 files changed, 137 insertions(+), 137 deletions(-) (limited to 'components') diff --git a/components/controls/CollapsibleSection.qml b/components/controls/CollapsibleSection.qml index 945386c..cb6e62a 100644 --- a/components/controls/CollapsibleSection.qml +++ b/components/controls/CollapsibleSection.qml @@ -12,7 +12,7 @@ ColumnLayout { required property string title property string description: "" property bool expanded: false - + signal toggleRequested spacing: Appearance.spacing.small / 2 diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index 68e2e2d..fc338f9 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -310,20 +310,20 @@ RowLayout { StateLayer { function onClicked(): void { const variant = modelData.variant; - + // Optimistic update - set immediately Schemes.currentVariant = variant; - + // Execute the command Quickshell.execDetached(["caelestia", "scheme", "set", "-v", variant]); - + // Reload after a delay to confirm Qt.callLater(() => { reloadTimer.restart(); }); } } - + Timer { id: reloadTimer interval: 300 @@ -410,20 +410,20 @@ RowLayout { const name = modelData.name; const flavour = modelData.flavour; const schemeKey = `${name} ${flavour}`; - + // Optimistic update - set immediately Schemes.currentScheme = schemeKey; - + // Execute the command Quickshell.execDetached(["caelestia", "scheme", "set", "-n", name, "-f", flavour]); - + // Reload after a delay to confirm Qt.callLater(() => { reloadTimer.restart(); }); } } - + Timer { id: reloadTimer interval: 300 @@ -1053,7 +1053,7 @@ RowLayout { 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; @@ -1100,16 +1100,16 @@ RowLayout { 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 @@ -1129,11 +1129,11 @@ RowLayout { 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 @@ -1144,7 +1144,7 @@ RowLayout { } } } - + // Also check status changes onStatusChanged: { if (status === Image.Ready && cachingImage.status !== Image.Ready) { @@ -1155,7 +1155,7 @@ RowLayout { }); } } - + Behavior on opacity { NumberAnimation { duration: 200 @@ -1182,26 +1182,26 @@ RowLayout { anchors.right: parent.right anchors.bottom: parent.bottom height: filenameText.implicitHeight + Appearance.padding.normal * 2 - + // Match the parent's rounded corners at the bottom radius: Appearance.rounding.normal - + gradient: Gradient { GradientStop { position: 0.0; color: Qt.rgba(0, 0, 0, 0) } GradientStop { position: 0.3; color: Qt.rgba(0, 0, 0, 0.3) } GradientStop { position: 0.7; color: Qt.rgba(0, 0, 0, 0.75) } GradientStop { position: 1.0; color: Qt.rgba(0, 0, 0, 0.85) } } - + opacity: 0 - + Behavior on opacity { NumberAnimation { duration: 200 easing.type: Easing.OutCubic } } - + Component.onCompleted: { opacity = 1; } @@ -1228,20 +1228,20 @@ RowLayout { color: isCurrent ? Colours.palette.m3primary : "#FFFFFF" elide: Text.ElideMiddle maximumLineCount: 1 - + // Text shadow for better readability style: Text.Outline styleColor: Qt.rgba(0, 0, 0, 0.6) - + opacity: 0 - + Behavior on opacity { NumberAnimation { duration: 200 easing.type: Easing.OutCubic } } - + Component.onCompleted: { opacity = 1; } diff --git a/modules/controlcenter/launcher/LauncherPane.qml b/modules/controlcenter/launcher/LauncherPane.qml index 9b2570a..dd00877 100644 --- a/modules/controlcenter/launcher/LauncherPane.qml +++ b/modules/controlcenter/launcher/LauncherPane.qml @@ -53,7 +53,7 @@ RowLayout { try { const config = JSON.parse(configFile.text()); const appId = root.selectedApp.id || root.selectedApp.entry?.id; - + if (config.launcher && config.launcher.hiddenApps) { root.hideFromLauncherChecked = config.launcher.hiddenApps.includes(appId); } else { @@ -72,12 +72,12 @@ RowLayout { 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; - + if (isHidden) { // Add to hiddenApps if not already there if (!hiddenApps.includes(appId)) { @@ -90,7 +90,7 @@ RowLayout { hiddenApps.splice(index, 1); } } - + const jsonString = JSON.stringify(config, null, 4); configFile.setText(jsonString); } catch (e) { diff --git a/modules/controlcenter/network/WirelessDetails.qml b/modules/controlcenter/network/WirelessDetails.qml index 7039720..d5abc9d 100644 --- a/modules/controlcenter/network/WirelessDetails.qml +++ b/modules/controlcenter/network/WirelessDetails.qml @@ -26,7 +26,7 @@ Item { updateDeviceDetails(); checkSavedProfile(); } - + function checkSavedProfile(): void { // Refresh saved connections list to ensure it's up to date // This ensures the "Forget Network" button visibility is accurate @@ -102,7 +102,7 @@ Item { color: Colours.palette.m3errorContainer onColor: Colours.palette.m3onErrorContainer text: qsTr("Forget Network") - + onClicked: { if (root.network && root.network.ssid) { // Disconnect first if connected @@ -184,7 +184,7 @@ Item { if (root.network.isSecure) { // Check if we have a saved connection profile for this network (by SSID) const hasSavedProfile = Network.hasSavedProfile(root.network.ssid); - + if (hasSavedProfile) { // Try connecting with saved password - don't show dialog if it fails // The saved password should work, but if connection fails for other reasons, diff --git a/modules/controlcenter/network/WirelessList.qml b/modules/controlcenter/network/WirelessList.qml index f861db4..ca6947a 100644 --- a/modules/controlcenter/network/WirelessList.qml +++ b/modules/controlcenter/network/WirelessList.qml @@ -230,7 +230,7 @@ ColumnLayout { if (network.isSecure) { // Check if we have a saved connection profile for this network (by SSID) const hasSavedProfile = Network.hasSavedProfile(network.ssid); - + if (hasSavedProfile) { // Try connecting with saved password - don't show dialog if it fails // The saved password should work, but if connection fails for other reasons, diff --git a/modules/controlcenter/network/WirelessPasswordDialog.qml b/modules/controlcenter/network/WirelessPasswordDialog.qml index 2b33b43..8a71fa8 100644 --- a/modules/controlcenter/network/WirelessPasswordDialog.qml +++ b/modules/controlcenter/network/WirelessPasswordDialog.qml @@ -15,7 +15,7 @@ Item { id: root required property Session session - + readonly property var network: { // Prefer pendingNetwork, then active network if (session.network.pendingNetwork) { @@ -105,7 +105,7 @@ Item { StyledText { id: statusText - + Layout.alignment: Qt.AlignHCenter Layout.topMargin: Appearance.spacing.small visible: Network.connectionStatus.length > 0 || connectButton.connecting @@ -251,15 +251,15 @@ Item { // Check connection status message for success indicators const status = Network.connectionStatus; const statusLower = status.toLowerCase(); - + // Check for success indicators in status message - const hasSuccessIndicator = statusLower.includes("connection activated") || + const hasSuccessIndicator = statusLower.includes("connection activated") || statusLower.includes("successfully") || statusLower.includes("connected successfully") || (statusLower.includes("connected") && !statusLower.includes("error") && !statusLower.includes("failed")); // Check if we're connected to the target network (case-insensitive SSID comparison) - const isConnected = root.network && Network.active && Network.active.ssid && + const isConnected = root.network && Network.active && Network.active.ssid && Network.active.ssid.toLowerCase().trim() === root.network.ssid.toLowerCase().trim(); if (isConnected || hasSuccessIndicator) { diff --git a/modules/controlcenter/taskbar/TaskbarPane.qml b/modules/controlcenter/taskbar/TaskbarPane.qml index 2bb50d8..cf52fd3 100644 --- a/modules/controlcenter/taskbar/TaskbarPane.qml +++ b/modules/controlcenter/taskbar/TaskbarPane.qml @@ -120,13 +120,13 @@ RowLayout { if (!configFile.loaded) { return; } - + try { const config = JSON.parse(configFile.text()); - + // Ensure bar object exists if (!config.bar) config.bar = {}; - + // Update clock setting if (!config.bar.clock) config.bar.clock = {}; config.bar.clock.showIcon = clockShowIconSwitch.checked; @@ -163,7 +163,7 @@ RowLayout { // Update entries from the model (same approach as clock - use provided value if available) if (!config.bar.entries) config.bar.entries = []; config.bar.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) diff --git a/modules/drawers/Interactions.qml b/modules/drawers/Interactions.qml index 10190a4..2d0c115 100644 --- a/modules/drawers/Interactions.qml +++ b/modules/drawers/Interactions.qml @@ -204,18 +204,18 @@ CustomMouseArea { const panelWidth = panels.notifications.width || panels.notifications.implicitWidth || Config.notifs.sizes.width; const panelX = bar.implicitWidth + panels.notifications.x; const isPanelCollapsed = panelHeight < 10; // Consider collapsed if height is very small - + let showNotifications = inTopPanel(panels.notifications, x, y); - + // Only use fallback corner detection when panel is collapsed if (!showNotifications && isPanelCollapsed) { // Use panel's actual width and position for fallback, with some padding const cornerPadding = Config.border.rounding || 20; - showNotifications = x >= panelX - cornerPadding && - x <= panelX + panelWidth + cornerPadding && + showNotifications = x >= panelX - cornerPadding && + x <= panelX + panelWidth + cornerPadding && y < Config.border.thickness + cornerPadding; } - + // Check if mouse is over the clear all button area // Button is positioned to the left of the notification panel if (!showNotifications && panels.notifications.height > 0 && panels.clearAllButton && panels.clearAllButton.visible) { @@ -223,17 +223,17 @@ CustomMouseArea { const buttonY = Config.border.thickness + panels.clearAllButton.y; const buttonWidth = panels.clearAllButton.width; const buttonHeight = panels.clearAllButton.height; - - const inButtonArea = x >= buttonX && - x <= buttonX + buttonWidth && - y >= buttonY && + + const inButtonArea = x >= buttonX && + x <= buttonX + buttonWidth && + y >= buttonY && y <= buttonY + buttonHeight; - + if (inButtonArea) { showNotifications = true; } } - + // Show or hide notification panel based on hover if (panels.notifications.content) { if (showNotifications) { diff --git a/services/Network.qml b/services/Network.qml index 0b936b8..7732a1c 100644 --- a/services/Network.qml +++ b/services/Network.qml @@ -30,17 +30,17 @@ Singleton { property var wirelessDeviceDetails: null property string connectionStatus: "" property string connectionDebug: "" - + function clearConnectionStatus(): void { connectionStatus = ""; // Don't clear debug - keep it for reference // connectionDebug = ""; } - + function setConnectionStatus(status: string): void { connectionStatus = status; } - + function addDebugInfo(info: string): void { const timestamp = new Date().toLocaleTimeString(); const newInfo = "[" + timestamp + "] " + info; @@ -79,23 +79,23 @@ Singleton { // When no password, use SSID (will use saved password if available) const hasBssid = bssid !== undefined && bssid !== null && bssid.length > 0; let cmd = []; - + // Set up pending connection tracking if callback provided if (callback) { root.pendingConnection = { ssid: ssid, bssid: hasBssid ? bssid : "", callback: callback }; } - + if (password && password.length > 0) { // When password is provided, try BSSID first if available, otherwise use SSID if (hasBssid) { // Use BSSID when password is provided - ensure BSSID is uppercase const bssidUpper = bssid.toUpperCase(); - + // Check if a connection with this SSID already exists - const existingConnection = root.savedConnections.find(conn => + const existingConnection = root.savedConnections.find(conn => conn && conn.toLowerCase().trim() === ssid.toLowerCase().trim() ); - + if (existingConnection) { // Connection already exists - delete it first, then create new one with updated password root.addDebugInfo(qsTr("Connection '%1' already exists, deleting it first...").arg(existingConnection)); @@ -122,7 +122,7 @@ Singleton { root.setConnectionStatus(qsTr("Connecting to %1 (using saved password)...").arg(ssid)); root.addDebugInfo(qsTr("Using saved password for: %1").arg(ssid)); } - + // Show the exact command being executed const cmdStr = cmd.join(" "); root.addDebugInfo(qsTr("=== COMMAND TO EXECUTE ===")); @@ -130,17 +130,17 @@ Singleton { root.addDebugInfo(qsTr("Command array: [%1]").arg(cmd.map((arg, i) => `"${arg}"`).join(", "))); root.addDebugInfo(qsTr("Command array length: %1").arg(cmd.length)); root.addDebugInfo(qsTr("===========================")); - + // Set command and start process root.addDebugInfo(qsTr("Setting command property...")); connectProc.command = cmd; const setCmdStr = connectProc.command ? connectProc.command.join(" ") : "null"; root.addDebugInfo(qsTr("Command property set, value: %1").arg(setCmdStr)); root.addDebugInfo(qsTr("Command property verified: %1").arg(setCmdStr === cmdStr ? "Match" : "MISMATCH")); - + // If we're creating a connection profile, we need to activate it after creation const isConnectionAdd = cmd.length > 0 && cmd[0] === "nmcli" && cmd[1] === "connection" && cmd[2] === "add"; - + // Wait a moment before starting to ensure command is set Qt.callLater(() => { root.addDebugInfo(qsTr("=== STARTING PROCESS ===")); @@ -150,7 +150,7 @@ Singleton { connectProc.running = true; root.addDebugInfo(qsTr("Process running set to: %1").arg(connectProc.running)); root.addDebugInfo(qsTr("========================")); - + // Check if process actually started after a short delay Qt.callLater(() => { root.addDebugInfo(qsTr("Process status check (100ms later):")); @@ -162,7 +162,7 @@ Singleton { } }, 100); }); - + // Start connection check timer if we have a callback if (callback) { root.addDebugInfo(qsTr("Starting connection check timer (4 second interval)")); @@ -171,25 +171,25 @@ Singleton { root.addDebugInfo(qsTr("No callback provided - not starting connection check timer")); } } - + function createConnectionWithPassword(ssid: string, bssidUpper: string, password: string): void { // Create connection profile with all required properties for BSSID + password - const cmd = ["nmcli", "connection", "add", - "type", "wifi", + const cmd = ["nmcli", "connection", "add", + "type", "wifi", "con-name", ssid, "ifname", "*", "ssid", ssid, "802-11-wireless.bssid", bssidUpper, "802-11-wireless-security.key-mgmt", "wpa-psk", "802-11-wireless-security.psk", password]; - + root.setConnectionStatus(qsTr("Connecting to %1 (BSSID: %2)...").arg(ssid).arg(bssidUpper)); root.addDebugInfo(qsTr("Using BSSID: %1 for SSID: %2").arg(bssidUpper).arg(ssid)); root.addDebugInfo(qsTr("Creating connection profile with password and key-mgmt")); - + // Set command and start process connectProc.command = cmd; - + Qt.callLater(() => { connectProc.running = true; }); @@ -198,7 +198,7 @@ Singleton { function connectToNetworkWithPasswordCheck(ssid: string, isSecure: bool, callback: var, bssid: string): void { root.addDebugInfo(qsTr("=== connectToNetworkWithPasswordCheck ===")); root.addDebugInfo(qsTr("SSID: %1, isSecure: %2").arg(ssid).arg(isSecure)); - + // For secure networks, try connecting without password first // If connection succeeds (saved password exists), we're done // If it fails with password error, callback will be called to show password dialog @@ -228,7 +228,7 @@ Singleton { disconnectProc.exec(["nmcli", "device", "disconnect", "wifi"]); } } - + function forgetNetwork(ssid: string): void { // Delete the connection profile for this network // This will remove the saved password and connection settings @@ -240,7 +240,7 @@ Singleton { }, 500); } } - + function hasConnectionProfile(ssid: string): bool { // Check if a connection profile exists for this SSID // This is synchronous check - returns true if connection exists @@ -252,12 +252,12 @@ Singleton { // The actual check will be done asynchronously return false; } - + property list savedConnections: [] property list savedConnectionSsids: [] property var wifiConnectionQueue: [] property int currentSsidQueryIndex: 0 - + Process { id: listConnectionsProc command: ["nmcli", "-t", "-f", "NAME,TYPE", "connection", "show"] @@ -276,12 +276,12 @@ Singleton { } } } - + function parseConnectionList(output: string): void { const lines = output.trim().split("\n").filter(line => line.length > 0); const wifiConnections = []; const connections = []; - + // First pass: identify WiFi connections for (const line of lines) { const parts = line.split(":"); @@ -289,15 +289,15 @@ Singleton { const name = parts[0]; const type = parts[1]; connections.push(name); - + if (type === "802-11-wireless") { wifiConnections.push(name); } } } - + root.savedConnections = connections; - + // Second pass: get SSIDs for WiFi connections if (wifiConnections.length > 0) { root.wifiConnectionQueue = wifiConnections; @@ -310,10 +310,10 @@ Singleton { root.wifiConnectionQueue = []; } } - + Process { id: getSsidProc - + environment: ({ LANG: "C.UTF-8", LC_ALL: "C.UTF-8" @@ -332,7 +332,7 @@ Singleton { } } } - + function processSsidOutput(output: string): void { // Parse "802-11-wireless.ssid:SSID_NAME" format const lines = output.trim().split("\n"); @@ -351,11 +351,11 @@ Singleton { } } } - + // Query next connection queryNextSsid(); } - + function queryNextSsid(): void { if (root.currentSsidQueryIndex < root.wifiConnectionQueue.length) { const connectionName = root.wifiConnectionQueue[root.currentSsidQueryIndex]; @@ -368,13 +368,13 @@ Singleton { root.currentSsidQueryIndex = 0; } } - + function hasSavedProfile(ssid: string): bool { if (!ssid || ssid.length === 0) { return false; } const ssidLower = ssid.toLowerCase().trim(); - + // If currently connected to this network, it definitely has a saved profile if (root.active && root.active.ssid) { const activeSsidLower = root.active.ssid.toLowerCase().trim(); @@ -382,21 +382,21 @@ Singleton { return true; } } - + // Check if SSID is in saved connections (case-insensitive comparison) - const hasSsid = root.savedConnectionSsids.some(savedSsid => + const hasSsid = root.savedConnectionSsids.some(savedSsid => savedSsid && savedSsid.toLowerCase().trim() === ssidLower ); - + if (hasSsid) { return true; } - + // Fallback: also check if connection name matches SSID (some connections use SSID as name) - const hasConnectionName = root.savedConnections.some(connName => + const hasConnectionName = root.savedConnections.some(connName => connName && connName.toLowerCase().trim() === ssidLower ); - + return hasConnectionName; } @@ -442,7 +442,7 @@ Singleton { if (isNaN(cidrNum) || cidrNum < 0 || cidrNum > 32) { return ""; } - + const mask = (0xffffffff << (32 - cidrNum)) >>> 0; const octets = [ (mask >>> 24) & 0xff, @@ -450,7 +450,7 @@ Singleton { (mask >>> 8) & 0xff, mask & 0xff ]; - + return octets.join("."); } @@ -510,7 +510,7 @@ Singleton { root.addDebugInfo(qsTr(" Pending SSID: %1").arg(root.pendingConnection.ssid)); root.addDebugInfo(qsTr(" Active SSID: %1").arg(root.active ? root.active.ssid : "None")); root.addDebugInfo(qsTr(" Connected: %1").arg(connected)); - + if (!connected && root.pendingConnection.callback) { // Connection didn't succeed after multiple checks, show password dialog root.addDebugInfo(qsTr("Connection failed - calling password dialog callback")); @@ -543,19 +543,19 @@ Singleton { repeat: true triggeredOnStart: false property int checkCount: 0 - + onRunningChanged: { if (running) { root.addDebugInfo(qsTr("Immediate check timer started (checks every 500ms)")); } } - + onTriggered: { if (root.pendingConnection) { checkCount++; const connected = root.active && root.active.ssid === root.pendingConnection.ssid; root.addDebugInfo(qsTr("Immediate check #%1: Connected=%2").arg(checkCount).arg(connected)); - + if (connected) { // Connection succeeded, stop timers and clear pending root.addDebugInfo(qsTr("Connection succeeded on check #%1!").arg(checkCount)); @@ -586,32 +586,32 @@ Singleton { onRunningChanged: { root.addDebugInfo(qsTr("Process running changed to: %1").arg(running)); } - + onStarted: { root.addDebugInfo(qsTr("Process started successfully")); } - + onExited: { root.addDebugInfo(qsTr("=== PROCESS EXITED ===")); root.addDebugInfo(qsTr("Exit code: %1").arg(exitCode)); root.addDebugInfo(qsTr("(Exit code 0 = success, non-zero = error)")); - + // Check if this was a "connection add" command - if so, we need to activate it - const wasConnectionAdd = connectProc.command && connectProc.command.length > 0 - && connectProc.command[0] === "nmcli" - && connectProc.command[1] === "connection" + const wasConnectionAdd = connectProc.command && connectProc.command.length > 0 + && connectProc.command[0] === "nmcli" + && connectProc.command[1] === "connection" && connectProc.command[2] === "add"; - + if (wasConnectionAdd && root.pendingConnection) { const ssid = root.pendingConnection.ssid; - + // Check for duplicate connection warning in stderr text const stderrText = connectProc.stderr ? connectProc.stderr.text : ""; const hasDuplicateWarning = stderrText && ( stderrText.includes("another connection with the name") || stderrText.includes("Reference the connection by its uuid") ); - + // Even with duplicate warning (or if connection already exists), we should try to activate it // Also try if exit code is non-zero but small (might be a warning, not a real error) if (exitCode === 0 || hasDuplicateWarning || (exitCode > 0 && exitCode < 10)) { @@ -622,10 +622,10 @@ Singleton { root.addDebugInfo(qsTr("Connection profile created successfully, now activating: %1").arg(ssid)); root.setConnectionStatus(qsTr("Activating connection...")); } - + // Update saved connections list listConnectionsProc.running = true; - + // Try to activate the connection by SSID (connection name) connectProc.command = ["nmcli", "connection", "up", ssid]; Qt.callLater(() => { @@ -644,7 +644,7 @@ Singleton { password = connectProc.command[pskIndex + 1]; } } - + if (password && password.length > 0) { root.addDebugInfo(qsTr("Using device wifi connect with password as fallback")); connectProc.command = ["nmcli", "device", "wifi", "connect", ssid, "password", password]; @@ -655,10 +655,10 @@ Singleton { } } } - + // Refresh network list after connection attempt getNetworks.running = true; - + // Check if connection succeeded after a short delay (network list needs to update) if (root.pendingConnection) { if (exitCode === 0) { @@ -704,10 +704,10 @@ Singleton { root.addDebugInfo(qsTr("STDERR: %1").arg(line)); } } - + // Check for specific errors that indicate password is needed // Be careful not to match success messages - const needsPassword = (error.includes("Secrets were required") || + const needsPassword = (error.includes("Secrets were required") || error.includes("No secrets provided") || error.includes("802-11-wireless-security.psk") || (error.includes("password") && !error.includes("Connection activated")) || @@ -715,7 +715,7 @@ Singleton { (error.includes("802.11") && !error.includes("Connection activated"))) && !error.includes("Connection activated") && !error.includes("successfully"); - + if (needsPassword && root.pendingConnection && root.pendingConnection.callback) { // Connection failed because password is needed - show dialog immediately connectionCheckTimer.stop(); @@ -784,7 +784,7 @@ Singleton { Process { id: deleteConnectionProc - + // Delete connection profile - refresh network list and saved connections after deletion onExited: { // Refresh network list and saved connections after deletion @@ -924,12 +924,12 @@ Singleton { 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); } } @@ -942,7 +942,7 @@ Singleton { const lines = output.split("\n"); root.ethernetDebugInfo = "Processing " + lines.length + " lines"; - + const allDevices = lines.map(d => { const dev = d.replace(rep, PLACEHOLDER).split(":"); return { @@ -952,9 +952,9 @@ Singleton { 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; @@ -975,7 +975,7 @@ Singleton { 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 @@ -984,13 +984,13 @@ Singleton { 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; @@ -1130,7 +1130,7 @@ const line = lines[i]; // Find the connected wifi interface from device status const lines = output.split("\n"); let wifiInterface = ""; - + for (let i = 0; i < lines.length; i++) { const line = lines[i]; const parts = line.split(/\s+/); diff --git a/services/VPN.qml b/services/VPN.qml index 10e5e7e..412bda4 100644 --- a/services/VPN.qml +++ b/services/VPN.qml @@ -21,7 +21,7 @@ Singleton { const name = providerName; const iface = interfaceName; const defaults = getBuiltinDefaults(name, iface); - + if (isCustomProvider) { const custom = providerInput; return { @@ -31,7 +31,7 @@ Singleton { displayName: custom.displayName || defaults.displayName }; } - + return defaults; } @@ -62,7 +62,7 @@ Singleton { displayName: "Tailscale" } }; - + return builtins[name] || { connectCmd: [name, "up"], disconnectCmd: [name, "down"], diff --git a/utils/Icons.qml b/utils/Icons.qml index e946c4f..389eca3 100644 --- a/utils/Icons.qml +++ b/utils/Icons.qml @@ -194,13 +194,13 @@ Singleton { function getSpecialWsIcon(name: string): string { name = name.toLowerCase().slice("special:".length); - + for (const iconConfig of Config.bar.workspaces.specialWorkspaceIcons) { if (iconConfig.name === name) { return iconConfig.icon; } } - + if (name === "special") return "star"; if (name === "communication") -- cgit v1.2.3-freya From 78d032e9462b6678691747bfacc21032ee2e5685 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Thu, 13 Nov 2025 22:16:47 -0500 Subject: controlcenter: collapsiblegroups animation and styling tweaked --- components/StateLayer.qml | 3 +- components/controls/CollapsibleSection.qml | 111 +++++++++++++-------- .../controlcenter/appearance/AppearancePane.qml | 20 ---- 3 files changed, 70 insertions(+), 64 deletions(-) (limited to 'components') diff --git a/components/StateLayer.qml b/components/StateLayer.qml index d86e782..a20e266 100644 --- a/components/StateLayer.qml +++ b/components/StateLayer.qml @@ -6,6 +6,7 @@ MouseArea { id: root property bool disabled + property bool showHoverBackground: true property color color: Colours.palette.m3onSurface property real radius: parent?.radius ?? 0 property alias rect: hoverLayer @@ -75,7 +76,7 @@ MouseArea { anchors.fill: parent - color: Qt.alpha(root.color, root.disabled ? 0 : root.pressed ? 0.1 : root.containsMouse ? 0.08 : 0) + color: Qt.alpha(root.color, root.disabled ? 0 : root.pressed ? 0.12 : (root.showHoverBackground && root.containsMouse) ? 0.08 : 0) radius: root.radius StyledRect { diff --git a/components/controls/CollapsibleSection.qml b/components/controls/CollapsibleSection.qml index cb6e62a..a22ad99 100644 --- a/components/controls/CollapsibleSection.qml +++ b/components/controls/CollapsibleSection.qml @@ -15,71 +15,96 @@ ColumnLayout { signal toggleRequested - spacing: Appearance.spacing.small / 2 + spacing: 0 Layout.fillWidth: true Item { id: sectionHeaderItem Layout.fillWidth: true - Layout.preferredHeight: sectionHeader.implicitHeight + Layout.preferredHeight: Math.max(titleRow.implicitHeight + Appearance.padding.normal * 2, 48) - ColumnLayout { - id: sectionHeader + RowLayout { + id: titleRow anchors.left: parent.left anchors.right: parent.right - spacing: Appearance.spacing.small - - RowLayout { - Layout.fillWidth: true - spacing: Appearance.spacing.small + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: Appearance.padding.normal + anchors.rightMargin: Appearance.padding.normal + spacing: Appearance.spacing.normal - StyledText { - Layout.topMargin: Appearance.spacing.large - text: root.title - font.pointSize: Appearance.font.size.larger - font.weight: 500 - } + StyledText { + text: root.title + font.pointSize: Appearance.font.size.normal + font.weight: 500 + } - Item { - Layout.fillWidth: true - } + Item { + Layout.fillWidth: true + } - MaterialIcon { - text: "expand_more" - rotation: root.expanded ? 180 : 0 - color: Colours.palette.m3onSurface - Behavior on rotation { - Anim {} + MaterialIcon { + text: "expand_more" + rotation: root.expanded ? 180 : 0 + color: Colours.palette.m3onSurfaceVariant + font.pointSize: Appearance.font.size.normal + Behavior on rotation { + Anim { + duration: Appearance.anim.durations.short + easing.bezierCurve: Appearance.anim.curves.standard } } } + } - StateLayer { - anchors.fill: parent - anchors.leftMargin: -Appearance.padding.normal - anchors.rightMargin: -Appearance.padding.normal - function onClicked(): void { - root.toggleRequested(); - root.expanded = !root.expanded; - } - } - - StyledText { - visible: root.expanded && root.description !== "" - text: root.description - color: Colours.palette.m3outline - Layout.fillWidth: true + StateLayer { + anchors.fill: parent + color: Colours.palette.m3onSurface + radius: Appearance.rounding.normal + showHoverBackground: false + function onClicked(): void { + root.toggleRequested(); + root.expanded = !root.expanded; } } } + StyledText { + visible: root.expanded && root.description !== "" + Layout.fillWidth: true + Layout.leftMargin: Appearance.padding.normal + Layout.rightMargin: Appearance.padding.normal + Layout.topMargin: Appearance.spacing.smaller + Layout.bottomMargin: Appearance.spacing.small + text: root.description + color: Colours.palette.m3onSurfaceVariant + font.pointSize: Appearance.font.size.small + } + default property alias content: contentColumn.data - ColumnLayout { - id: contentColumn + Item { + id: contentWrapper Layout.fillWidth: true - visible: root.expanded - spacing: Appearance.spacing.small / 2 + Layout.preferredHeight: root.expanded ? (contentColumn.implicitHeight + Appearance.spacing.small * 2) : 0 + clip: true + + Behavior on Layout.preferredHeight { + Anim { + easing.bezierCurve: Appearance.anim.curves.standard + } + } + + ColumnLayout { + id: contentColumn + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + anchors.leftMargin: Appearance.padding.normal + anchors.rightMargin: Appearance.padding.normal + anchors.topMargin: Appearance.spacing.small + anchors.bottomMargin: Appearance.spacing.small + spacing: Appearance.spacing.small + } } } diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index fc338f9..e4d5892 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -284,10 +284,6 @@ RowLayout { Layout.fillWidth: true implicitHeight: colorVariantSection.expanded ? Math.min(400, M3Variants.list.length * 60) : 0 - Behavior on implicitHeight { - Anim {} - } - model: M3Variants.list spacing: Appearance.spacing.small / 2 clip: true @@ -379,10 +375,6 @@ RowLayout { Layout.fillWidth: true implicitHeight: colorSchemeSection.expanded ? Math.min(400, Schemes.list.length * 80) : 0 - Behavior on implicitHeight { - Anim {} - } - model: Schemes.list spacing: Appearance.spacing.small / 2 clip: true @@ -556,10 +548,6 @@ RowLayout { Layout.fillWidth: true implicitHeight: fontsSection.expanded ? Math.min(300, Qt.fontFamilies().length * 50) : 0 - Behavior on implicitHeight { - Anim {} - } - model: Qt.fontFamilies() spacing: Appearance.spacing.small / 2 clip: true @@ -633,10 +621,6 @@ RowLayout { Layout.fillWidth: true implicitHeight: fontsSection.expanded ? Math.min(300, Qt.fontFamilies().length * 50) : 0 - Behavior on implicitHeight { - Anim {} - } - model: Qt.fontFamilies() spacing: Appearance.spacing.small / 2 clip: true @@ -710,10 +694,6 @@ RowLayout { Layout.fillWidth: true implicitHeight: fontsSection.expanded ? Math.min(300, Qt.fontFamilies().length * 50) : 0 - Behavior on implicitHeight { - Anim {} - } - model: Qt.fontFamilies() spacing: Appearance.spacing.small / 2 clip: true -- cgit v1.2.3-freya From 144d04b08b132c9ee662fbde79be7f31e18b3d5b Mon Sep 17 00:00:00 2001 From: ATMDA Date: Thu, 13 Nov 2025 22:42:46 -0500 Subject: controlcenter: password dialog matches lockscreen --- components/controls/StyledTextField.qml | 2 +- .../network/WirelessPasswordDialog.qml | 216 +++++++++++++++++---- 2 files changed, 180 insertions(+), 38 deletions(-) (limited to 'components') diff --git a/components/controls/StyledTextField.qml b/components/controls/StyledTextField.qml index 4db87e9..60bcff2 100644 --- a/components/controls/StyledTextField.qml +++ b/components/controls/StyledTextField.qml @@ -13,7 +13,7 @@ TextField { placeholderTextColor: Colours.palette.m3outline font.family: Appearance.font.family.sans font.pointSize: Appearance.font.size.smaller - renderType: TextField.NativeRendering + renderType: echoMode === TextField.Password ? TextField.QtRendering : TextField.NativeRendering cursorVisible: !readOnly background: null diff --git a/modules/controlcenter/network/WirelessPasswordDialog.qml b/modules/controlcenter/network/WirelessPasswordDialog.qml index e416016..1ea39c9 100644 --- a/modules/controlcenter/network/WirelessPasswordDialog.qml +++ b/modules/controlcenter/network/WirelessPasswordDialog.qml @@ -8,6 +8,7 @@ import qs.components.effects import qs.components.containers import qs.services import qs.config +import Quickshell import QtQuick import QtQuick.Layouts @@ -27,7 +28,8 @@ Item { return null; } - visible: session.network.showPasswordDialog + property bool isClosing: false + visible: session.network.showPasswordDialog && !isClosing enabled: visible focus: visible @@ -38,10 +40,14 @@ Item { Rectangle { anchors.fill: parent color: Qt.rgba(0, 0, 0, 0.5) - opacity: root.visible ? 1 : 0 + opacity: root.visible && !root.isClosing ? 1 : 0 Behavior on opacity { - NumberAnimation { duration: 200 } + Anim { + property: "opacity" + duration: Appearance.anim.durations.normal + easing.bezierCurve: Appearance.anim.curves.standardDecel + } } MouseArea { @@ -60,15 +66,23 @@ Item { radius: Appearance.rounding.normal color: Colours.tPalette.m3surface - opacity: root.visible ? 1 : 0 - scale: root.visible ? 1 : 0.9 + opacity: root.visible && !root.isClosing ? 1 : 0 + scale: root.visible && !root.isClosing ? 1 : 0.9 Behavior on opacity { - NumberAnimation { duration: 200 } + Anim { + property: "opacity" + duration: Appearance.anim.durations.normal + easing.bezierCurve: Appearance.anim.curves.standardDecel + } } Behavior on scale { - NumberAnimation { duration: 200 } + Anim { + property: "scale" + duration: Appearance.anim.durations.normal + easing.bezierCurve: Appearance.anim.curves.standardDecel + } } Keys.onEscapePressed: closeDialog(); @@ -123,53 +137,151 @@ Item { } Item { + id: passwordContainer Layout.topMargin: Appearance.spacing.large Layout.fillWidth: true - implicitHeight: passwordField.implicitHeight + Appearance.padding.normal * 2 + implicitHeight: Math.max(48, charList.implicitHeight + Appearance.padding.normal * 2) + + focus: true + Keys.onPressed: event => { + if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) { + if (connectButton.enabled) { + connectButton.clicked(); + } + } else if (event.key === Qt.Key_Backspace) { + if (event.modifiers & Qt.ControlModifier) { + passwordBuffer = ""; + } else { + passwordBuffer = passwordBuffer.slice(0, -1); + } + } else if (event.text && event.text.length > 0) { + passwordBuffer += event.text; + } + } + + property string passwordBuffer: "" + + Connections { + target: root + function onVisibleChanged(): void { + if (root.visible) { + passwordContainer.forceActiveFocus(); + passwordContainer.passwordBuffer = ""; + } + } + } StyledRect { anchors.fill: parent radius: Appearance.rounding.normal color: Colours.tPalette.m3surfaceContainer - border.width: passwordField.activeFocus ? 2 : 1 - border.color: passwordField.activeFocus ? Colours.palette.m3primary : Colours.palette.m3outline + border.width: passwordContainer.activeFocus ? 2 : 1 + border.color: passwordContainer.activeFocus ? Colours.palette.m3primary : Colours.palette.m3outline Behavior on border.color { CAnim {} } } - StyledTextField { - id: passwordField + StateLayer { + hoverEnabled: false + cursorShape: Qt.IBeamCursor + + function onClicked(): void { + passwordContainer.forceActiveFocus(); + } + } + + StyledText { + id: placeholder + anchors.centerIn: parent + text: qsTr("Password") + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.normal + font.family: Appearance.font.family.mono + opacity: passwordContainer.passwordBuffer ? 0 : 1 + + Behavior on opacity { + Anim {} + } + } + + ListView { + id: charList + + readonly property int fullWidth: count * (implicitHeight + spacing) - spacing + + anchors.centerIn: parent + implicitWidth: fullWidth + implicitHeight: Appearance.font.size.normal + + orientation: Qt.Horizontal + spacing: Appearance.spacing.small / 2 + interactive: false + + model: ScriptModel { + values: passwordContainer.passwordBuffer.split("") + } + + delegate: StyledRect { + id: ch - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.normal + implicitWidth: implicitHeight + implicitHeight: charList.implicitHeight - echoMode: TextField.Password - placeholderText: qsTr("Password") + color: Colours.palette.m3onSurface + radius: Appearance.rounding.small / 2 + + opacity: 0 + scale: 0 + Component.onCompleted: { + opacity = 1; + scale = 1; + } + ListView.onRemove: removeAnim.start() - Connections { - target: root - function onVisibleChanged(): void { - if (root.visible) { - passwordField.forceActiveFocus(); - passwordField.text = ""; + SequentialAnimation { + id: removeAnim + + PropertyAction { + target: ch + property: "ListView.delayRemove" + value: true + } + ParallelAnimation { + Anim { + target: ch + property: "opacity" + to: 0 + } + Anim { + target: ch + property: "scale" + to: 0.5 + } + } + PropertyAction { + target: ch + property: "ListView.delayRemove" + value: false } } - } - Keys.onReturnPressed: { - if (connectButton.enabled) { - connectButton.clicked(); + Behavior on opacity { + Anim {} } - } - Keys.onEnterPressed: { - if (connectButton.enabled) { - connectButton.clicked(); + + Behavior on scale { + Anim { + duration: Appearance.anim.durations.expressiveFastSpatial + easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial + } } } + + Behavior on implicitWidth { + Anim {} + } } } @@ -196,7 +308,7 @@ Item { color: Colours.palette.m3primary onColor: Colours.palette.m3onPrimary text: qsTr("Connect") - enabled: passwordField.text.length > 0 && !connecting + enabled: passwordContainer.passwordBuffer.length > 0 && !connecting property bool connecting: false @@ -205,7 +317,7 @@ Item { return; } - const password = passwordField.text; + const password = passwordContainer.passwordBuffer; if (!password || password.length === 0) { return; } @@ -220,7 +332,19 @@ Item { root.network.ssid, password, root.network.bssid || "", - null + (result) => { + if (result && result.success) { + // Connection successful, monitor will handle the rest + } else if (result && result.needsPassword) { + // Shouldn't happen since we provided password + connectionMonitor.stop(); + connecting = false; + enabled = true; + text = qsTr("Connect"); + } else { + // Connection failed, monitor will handle timeout + } + } ); // Start monitoring connection @@ -295,13 +419,31 @@ Item { checkConnectionStatus(); } } + function onConnectionFailed(ssid: string) { + if (root.visible && root.network && root.network.ssid === ssid && connectButton.connecting) { + connectionMonitor.stop(); + connectButton.connecting = false; + connectButton.enabled = true; + connectButton.text = qsTr("Connect"); + } + } } function closeDialog(): void { - session.network.showPasswordDialog = false; - passwordField.text = ""; + if (isClosing) { + return; + } + + isClosing = true; + passwordContainer.passwordBuffer = ""; connectButton.connecting = false; connectButton.text = qsTr("Connect"); connectionMonitor.stop(); + + // Wait for fade-out animation to complete before actually hiding + Qt.callLater(() => { + session.network.showPasswordDialog = false; + isClosing = false; + }, Appearance.anim.durations.normal); } } -- cgit v1.2.3-freya From 9cdc30058a0d53602f6c065315b3956497a2f68c Mon Sep 17 00:00:00 2001 From: ATMDA Date: Fri, 14 Nov 2025 14:03:10 -0500 Subject: controlcenter: font size adjustments on headings --- components/controls/CollapsibleSection.qml | 2 +- modules/controlcenter/audio/AudioPane.qml | 4 ++-- modules/controlcenter/bluetooth/DeviceList.qml | 2 +- modules/controlcenter/launcher/LauncherPane.qml | 2 +- modules/controlcenter/network/NetworkingPane.qml | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) (limited to 'components') diff --git a/components/controls/CollapsibleSection.qml b/components/controls/CollapsibleSection.qml index a22ad99..5bec5f8 100644 --- a/components/controls/CollapsibleSection.qml +++ b/components/controls/CollapsibleSection.qml @@ -34,7 +34,7 @@ ColumnLayout { StyledText { text: root.title - font.pointSize: Appearance.font.size.normal + font.pointSize: Appearance.font.size.larger font.weight: 500 } diff --git a/modules/controlcenter/audio/AudioPane.qml b/modules/controlcenter/audio/AudioPane.qml index 502134a..cf6e85a 100644 --- a/modules/controlcenter/audio/AudioPane.qml +++ b/modules/controlcenter/audio/AudioPane.qml @@ -48,7 +48,7 @@ RowLayout { StyledText { text: qsTr("Output devices (%1)").arg(Audio.sinks.length) - font.pointSize: Appearance.font.size.large + font.pointSize: Appearance.font.size.normal font.weight: 500 } @@ -119,7 +119,7 @@ RowLayout { StyledText { Layout.topMargin: Appearance.spacing.large text: qsTr("Input devices (%1)").arg(Audio.sources.length) - font.pointSize: Appearance.font.size.large + font.pointSize: Appearance.font.size.normal font.weight: 500 } diff --git a/modules/controlcenter/bluetooth/DeviceList.qml b/modules/controlcenter/bluetooth/DeviceList.qml index 3831e4a..8bf5daa 100644 --- a/modules/controlcenter/bluetooth/DeviceList.qml +++ b/modules/controlcenter/bluetooth/DeviceList.qml @@ -97,7 +97,7 @@ ColumnLayout { StyledText { Layout.fillWidth: true text: qsTr("Devices (%1)").arg(Bluetooth.devices.values.length) - font.pointSize: Appearance.font.size.large + font.pointSize: Appearance.font.size.normal font.weight: 500 } diff --git a/modules/controlcenter/launcher/LauncherPane.qml b/modules/controlcenter/launcher/LauncherPane.qml index dd00877..7408101 100644 --- a/modules/controlcenter/launcher/LauncherPane.qml +++ b/modules/controlcenter/launcher/LauncherPane.qml @@ -139,7 +139,7 @@ RowLayout { StyledText { Layout.topMargin: Appearance.spacing.large text: qsTr("Applications (%1)").arg(allAppsDb.apps.length) - font.pointSize: Appearance.font.size.larger + font.pointSize: Appearance.font.size.normal font.weight: 500 } diff --git a/modules/controlcenter/network/NetworkingPane.qml b/modules/controlcenter/network/NetworkingPane.qml index 5271c56..7db6f54 100644 --- a/modules/controlcenter/network/NetworkingPane.qml +++ b/modules/controlcenter/network/NetworkingPane.qml @@ -116,7 +116,7 @@ RowLayout { StyledText { text: qsTr("Devices (%1)").arg(Nmcli.ethernetDevices.length) - font.pointSize: Appearance.font.size.large + font.pointSize: Appearance.font.size.normal font.weight: 500 } } @@ -243,7 +243,7 @@ RowLayout { StyledText { text: qsTr("Networks (%1)").arg(Nmcli.networks.length) - font.pointSize: Appearance.font.size.large + font.pointSize: Appearance.font.size.normal font.weight: 500 } -- cgit v1.2.3-freya From 90e920bf2e002b4e6e0aa1896687c4800d770605 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sat, 15 Nov 2025 00:25:31 -0500 Subject: controlcenter: fixed anchors vs parent logs --- components/controls/CollapsibleSection.qml | 2 +- modules/controlcenter/appearance/AppearancePane.qml | 12 ++++-------- modules/controlcenter/launcher/LauncherPane.qml | 3 +-- modules/controlcenter/network/NetworkingPane.qml | 3 ++- modules/controlcenter/taskbar/TaskbarPane.qml | 8 ++++---- 5 files changed, 12 insertions(+), 16 deletions(-) (limited to 'components') diff --git a/components/controls/CollapsibleSection.qml b/components/controls/CollapsibleSection.qml index 5bec5f8..3fba1c3 100644 --- a/components/controls/CollapsibleSection.qml +++ b/components/controls/CollapsibleSection.qml @@ -49,7 +49,7 @@ ColumnLayout { font.pointSize: Appearance.font.size.normal Behavior on rotation { Anim { - duration: Appearance.anim.durations.short + duration: Appearance.anim.durations.small easing.bezierCurve: Appearance.anim.curves.standard } } diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index 719e9b3..5ddcb06 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -295,8 +295,7 @@ RowLayout { delegate: StyledRect { required property var modelData - anchors.left: parent.left - anchors.right: parent.right + width: parent ? parent.width : 0 color: Qt.alpha(Colours.tPalette.m3surfaceContainer, modelData.variant === Schemes.currentVariant ? Colours.tPalette.m3surfaceContainer.a : 0) radius: Appearance.rounding.normal @@ -386,8 +385,7 @@ RowLayout { delegate: StyledRect { required property var modelData - anchors.left: parent.left - anchors.right: parent.right + width: parent ? parent.width : 0 readonly property string schemeKey: `${modelData.name} ${modelData.flavour}` readonly property bool isCurrent: schemeKey === Schemes.currentScheme @@ -427,9 +425,7 @@ RowLayout { RowLayout { id: schemeRow - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter + anchors.fill: parent anchors.margins: Appearance.padding.normal spacing: Appearance.spacing.normal @@ -437,7 +433,7 @@ RowLayout { StyledRect { id: preview - anchors.verticalCenter: parent.verticalCenter + Layout.alignment: Qt.AlignVCenter border.width: 1 border.color: Qt.alpha(`#${modelData.colours?.outline}`, 0.5) diff --git a/modules/controlcenter/launcher/LauncherPane.qml b/modules/controlcenter/launcher/LauncherPane.qml index d0bd406..0645860 100644 --- a/modules/controlcenter/launcher/LauncherPane.qml +++ b/modules/controlcenter/launcher/LauncherPane.qml @@ -310,8 +310,7 @@ RowLayout { delegate: StyledRect { required property var modelData - anchors.left: parent.left - anchors.right: parent.right + width: parent ? parent.width : 0 readonly property bool isSelected: root.selectedApp === modelData diff --git a/modules/controlcenter/network/NetworkingPane.qml b/modules/controlcenter/network/NetworkingPane.qml index 0560581..620e6f7 100644 --- a/modules/controlcenter/network/NetworkingPane.qml +++ b/modules/controlcenter/network/NetworkingPane.qml @@ -501,7 +501,8 @@ RowLayout { } WirelessPasswordDialog { - anchors.fill: parent + Layout.fillWidth: true + Layout.fillHeight: true session: root.session z: 1000 } diff --git a/modules/controlcenter/taskbar/TaskbarPane.qml b/modules/controlcenter/taskbar/TaskbarPane.qml index 0dcc152..ee47683 100644 --- a/modules/controlcenter/taskbar/TaskbarPane.qml +++ b/modules/controlcenter/taskbar/TaskbarPane.qml @@ -250,10 +250,10 @@ RowLayout { RowLayout { id: clockRow - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.large + Layout.fillWidth: true + Layout.leftMargin: Appearance.padding.large + Layout.rightMargin: Appearance.padding.large + Layout.alignment: Qt.AlignVCenter spacing: Appearance.spacing.normal -- cgit v1.2.3-freya From 6a92f985d8739ef2c397714f79e18b74f48fb705 Mon Sep 17 00:00:00 2001 From: 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> Date: Sat, 15 Nov 2025 17:15:02 +1100 Subject: internal: use existing button controls Instead of SimpleButton --- components/controls/IconTextButton.qml | 9 +- modules/bar/popouts/Audio.qml | 11 +- modules/bar/popouts/Bluetooth.qml | 11 +- modules/bar/popouts/Network.qml | 15 +- modules/bar/popouts/WirelessPasswordPopout.qml | 165 +++++++-------------- modules/controlcenter/network/SimpleButton.qml | 50 ------- modules/controlcenter/network/WirelessDetails.qml | 44 +++--- .../network/WirelessPasswordDialog.qml | 54 +++---- utils/Icons.qml | 1 + 9 files changed, 120 insertions(+), 240 deletions(-) delete mode 100644 modules/controlcenter/network/SimpleButton.qml (limited to 'components') diff --git a/components/controls/IconTextButton.qml b/components/controls/IconTextButton.qml index 78e7c5b..0badd7a 100644 --- a/components/controls/IconTextButton.qml +++ b/components/controls/IconTextButton.qml @@ -2,6 +2,7 @@ import ".." import qs.services import qs.config import QtQuick +import QtQuick.Layouts StyledRect { id: root @@ -53,7 +54,7 @@ StyledRect { } } - Row { + RowLayout { id: row anchors.centerIn: parent @@ -62,7 +63,8 @@ StyledRect { MaterialIcon { id: iconLabel - anchors.verticalCenter: parent.verticalCenter + Layout.alignment: Qt.AlignVCenter + Layout.topMargin: Math.round(fontInfo.pointSize * 0.0575) color: root.internalChecked ? root.activeOnColour : root.inactiveOnColour fill: root.internalChecked ? 1 : 0 @@ -74,7 +76,8 @@ StyledRect { StyledText { id: label - anchors.verticalCenter: parent.verticalCenter + Layout.alignment: Qt.AlignVCenter + Layout.topMargin: -Math.round(iconLabel.fontInfo.pointSize * 0.0575) color: root.internalChecked ? root.activeOnColour : root.inactiveOnColour } } diff --git a/modules/bar/popouts/Audio.qml b/modules/bar/popouts/Audio.qml index 40c09d3..58b29ba 100644 --- a/modules/bar/popouts/Audio.qml +++ b/modules/bar/popouts/Audio.qml @@ -105,17 +105,16 @@ Item { } } - SimpleButton { + IconTextButton { Layout.fillWidth: true Layout.topMargin: Appearance.spacing.normal - color: Colours.palette.m3primaryContainer - onColor: Colours.palette.m3onPrimaryContainer + inactiveColour: Colours.palette.m3primaryContainer + inactiveOnColour: Colours.palette.m3onPrimaryContainer + verticalPadding: Appearance.padding.small text: qsTr("Open settings") icon: "settings" - onClicked: { - root.wrapper.detach("audio"); - } + onClicked: root.wrapper.detach("audio") } } } diff --git a/modules/bar/popouts/Bluetooth.qml b/modules/bar/popouts/Bluetooth.qml index 63621c2..4674905 100644 --- a/modules/bar/popouts/Bluetooth.qml +++ b/modules/bar/popouts/Bluetooth.qml @@ -165,17 +165,16 @@ ColumnLayout { } } - SimpleButton { + IconTextButton { Layout.fillWidth: true Layout.topMargin: Appearance.spacing.normal - color: Colours.palette.m3primaryContainer - onColor: Colours.palette.m3onPrimaryContainer + inactiveColour: Colours.palette.m3primaryContainer + inactiveOnColour: Colours.palette.m3onPrimaryContainer + verticalPadding: Appearance.padding.small text: qsTr("Open settings") icon: "settings" - onClicked: { - root.wrapper.detach("bluetooth"); - } + onClicked: root.wrapper.detach("bluetooth") } component Toggle: RowLayout { diff --git a/modules/bar/popouts/Network.qml b/modules/bar/popouts/Network.qml index 93ff867..b9f66c4 100644 --- a/modules/bar/popouts/Network.qml +++ b/modules/bar/popouts/Network.qml @@ -111,10 +111,8 @@ ColumnLayout { } StyledRect { - id: connectBtn - implicitWidth: implicitHeight - implicitHeight: connectIcon.implicitHeight + Appearance.padding.small + implicitHeight: wirelessConnectIcon.implicitHeight + Appearance.padding.small radius: Appearance.rounding.full color: Qt.alpha(Colours.palette.m3primary, networkItem.modelData.active ? 1 : 0) @@ -136,7 +134,7 @@ ColumnLayout { // Check if network is secure if (networkItem.modelData.isSecure) { // Try to connect first - will show password dialog if password is needed - Nmcli.connectToNetwork(networkItem.modelData.ssid, "", networkItem.modelData.bssid, (result) => { + Nmcli.connectToNetwork(networkItem.modelData.ssid, "", networkItem.modelData.bssid, result => { if (result && result.needsPassword) { // Password is required - show password dialog root.passwordNetwork = networkItem.modelData; @@ -159,7 +157,7 @@ ColumnLayout { } MaterialIcon { - id: connectIcon + id: wirelessConnectIcon anchors.centerIn: parent animate: true @@ -205,12 +203,14 @@ ColumnLayout { MaterialIcon { id: scanIcon + Layout.topMargin: Math.round(fontInfo.pointSize * 0.0575) animate: true text: "wifi_find" color: Colours.palette.m3onPrimaryContainer } StyledText { + Layout.topMargin: -Math.round(scanIcon.fontInfo.pointSize * 0.0575) text: qsTr("Rescan networks") color: Colours.palette.m3onPrimaryContainer } @@ -303,8 +303,6 @@ ColumnLayout { } StyledRect { - id: connectBtn - implicitWidth: implicitHeight implicitHeight: connectIcon.implicitHeight + Appearance.padding.small @@ -354,8 +352,7 @@ ColumnLayout { if (Nmcli.active && root.connectingToSsid === Nmcli.active.ssid) { root.connectingToSsid = ""; // Close password dialog if we successfully connected - if (root.showPasswordDialog && root.passwordNetwork && - Nmcli.active.ssid === root.passwordNetwork.ssid) { + if (root.showPasswordDialog && root.passwordNetwork && Nmcli.active.ssid === root.passwordNetwork.ssid) { root.showPasswordDialog = false; root.passwordNetwork = null; if (root.wrapper.currentName === "wirelesspassword") { diff --git a/modules/bar/popouts/WirelessPasswordPopout.qml b/modules/bar/popouts/WirelessPasswordPopout.qml index 2fd063f..aa7f40f 100644 --- a/modules/bar/popouts/WirelessPasswordPopout.qml +++ b/modules/bar/popouts/WirelessPasswordPopout.qml @@ -41,14 +41,14 @@ ColumnLayout { } spacing: Appearance.spacing.normal - + implicitWidth: 400 implicitHeight: content.implicitHeight + Appearance.padding.large * 2 visible: shouldBeVisible || isClosing enabled: shouldBeVisible && !isClosing focus: enabled - + Component.onCompleted: { if (shouldBeVisible) { Qt.callLater(() => { @@ -57,7 +57,7 @@ ColumnLayout { }, 150); } } - + onShouldBeVisibleChanged: { if (shouldBeVisible) { Qt.callLater(() => { @@ -67,9 +67,7 @@ ColumnLayout { } } - Keys.onEscapePressed: { - closeDialog(); - } + Keys.onEscapePressed: closeDialog() StyledRect { Layout.fillWidth: true @@ -109,7 +107,7 @@ ColumnLayout { } } - Keys.onEscapePressed: closeDialog(); + Keys.onEscapePressed: root.closeDialog() ColumnLayout { id: content @@ -149,7 +147,7 @@ ColumnLayout { color: Colours.palette.m3outline font.pointSize: Appearance.font.size.small } - + Timer { interval: 50 running: root.shouldBeVisible && (!root.network || !root.network.ssid) @@ -217,12 +215,12 @@ ColumnLayout { if (!activeFocus) { forceActiveFocus(); } - + // Clear error when user starts typing if (connectButton.hasError && event.text && event.text.length > 0) { connectButton.hasError = false; } - + if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) { if (connectButton.enabled) { connectButton.clicked(); @@ -253,7 +251,7 @@ ColumnLayout { } } } - + Component.onCompleted: { if (root.shouldBeVisible) { Qt.callLater(() => { @@ -265,9 +263,7 @@ ColumnLayout { StyledRect { anchors.fill: parent radius: Appearance.rounding.normal - color: passwordContainer.activeFocus - ? Qt.lighter(Colours.tPalette.m3surfaceContainer, 1.05) - : Colours.tPalette.m3surfaceContainer + color: passwordContainer.activeFocus ? Qt.lighter(Colours.tPalette.m3surfaceContainer, 1.05) : Colours.tPalette.m3surfaceContainer border.width: passwordContainer.activeFocus || connectButton.hasError ? 4 : (root.shouldBeVisible ? 1 : 0) border.color: { if (connectButton.hasError) { @@ -401,31 +397,31 @@ ColumnLayout { Layout.fillWidth: true spacing: Appearance.spacing.normal - SimpleButton { + TextButton { id: cancelButton Layout.fillWidth: true Layout.minimumHeight: Appearance.font.size.normal + Appearance.padding.normal * 2 - color: Colours.palette.m3secondaryContainer - onColor: Colours.palette.m3onSecondaryContainer + inactiveColour: Colours.palette.m3secondaryContainer + inactiveOnColour: Colours.palette.m3onSecondaryContainer text: qsTr("Cancel") - onClicked: closeDialog(); + onClicked: root.closeDialog() } - SimpleButton { + TextButton { id: connectButton + property bool connecting: false + property bool hasError: false + Layout.fillWidth: true Layout.minimumHeight: Appearance.font.size.normal + Appearance.padding.normal * 2 - color: Colours.palette.m3primary - onColor: Colours.palette.m3onPrimary + inactiveColour: Colours.palette.m3primary + inactiveOnColour: Colours.palette.m3onPrimary text: qsTr("Connect") enabled: passwordContainer.passwordBuffer.length > 0 && !connecting - property bool connecting: false - property bool hasError: false - onClicked: { if (!root.network || connecting) { return; @@ -445,40 +441,35 @@ ColumnLayout { text = qsTr("Connecting..."); // Connect to network - Nmcli.connectToNetwork( - root.network.ssid, - password, - root.network.bssid || "", - (result) => { - if (result && result.success) { - // Connection successful, monitor will handle the rest - } else if (result && result.needsPassword) { - // Shouldn't happen since we provided password - 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, () => {}); - } + Nmcli.connectToNetwork(root.network.ssid, password, root.network.bssid || "", result => { + if (result && result.success) + // Connection successful, monitor will handle the rest + {} else if (result && result.needsPassword) { + // Shouldn't happen since we provided password + 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(); @@ -494,8 +485,7 @@ ColumnLayout { } // 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(); + 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 @@ -530,7 +520,7 @@ ColumnLayout { passwordContainer.passwordBuffer = ""; // Delete the failed connection if (root.network && root.network.ssid) { - Nmcli.forgetNetwork(root.network.ssid, () => {}); + Nmcli.forgetNetwork(root.network.ssid); } } } @@ -545,7 +535,7 @@ ColumnLayout { onTriggered: { repeatCount++; - checkConnectionStatus(); + root.checkConnectionStatus(); } onRunningChanged: { @@ -559,7 +549,7 @@ ColumnLayout { target: Nmcli function onActiveChanged() { if (root.shouldBeVisible) { - checkConnectionStatus(); + root.checkConnectionStatus(); } } function onConnectionFailed(ssid: string) { @@ -571,7 +561,7 @@ ColumnLayout { connectButton.text = qsTr("Connect"); passwordContainer.passwordBuffer = ""; // Delete the failed connection - Nmcli.forgetNetwork(ssid, () => {}); + Nmcli.forgetNetwork(ssid); } } } @@ -580,64 +570,17 @@ ColumnLayout { if (isClosing) { return; } - + isClosing = true; passwordContainer.passwordBuffer = ""; connectButton.connecting = false; connectButton.hasError = false; connectButton.text = qsTr("Connect"); connectionMonitor.stop(); - + // Return to network popout if (root.wrapper.currentName === "wirelesspassword") { root.wrapper.currentName = "network"; } } - - component SimpleButton: StyledRect { - id: button - - property color onColor: Colours.palette.m3onSurface - property alias disabled: stateLayer.disabled - property alias text: label.text - property alias enabled: stateLayer.enabled - property string icon: "" - - implicitWidth: rowLayout.implicitWidth + Appearance.padding.normal * 2 - implicitHeight: rowLayout.implicitHeight + Appearance.padding.small - radius: Appearance.rounding.small - - StateLayer { - id: stateLayer - color: parent.onColor - function onClicked(): void { - if (parent.enabled !== false) { - parent.clicked(); - } - } - } - - RowLayout { - id: rowLayout - anchors.centerIn: parent - spacing: Appearance.spacing.small - - MaterialIcon { - id: iconItem - visible: button.icon.length > 0 - text: button.icon - color: button.onColor - font.pointSize: Appearance.font.size.large - } - - StyledText { - id: label - Layout.leftMargin: button.icon.length > 0 ? Appearance.padding.smaller : 0 - color: parent.parent.onColor - } - } - - signal clicked - } } - diff --git a/modules/controlcenter/network/SimpleButton.qml b/modules/controlcenter/network/SimpleButton.qml deleted file mode 100644 index a73f751..0000000 --- a/modules/controlcenter/network/SimpleButton.qml +++ /dev/null @@ -1,50 +0,0 @@ -import qs.components -import qs.components.controls -import qs.components.effects -import qs.config -import QtQuick -import QtQuick.Layouts - -StyledRect { - id: root - - property color onColor: Colours.palette.m3onSurface - property alias disabled: stateLayer.disabled - property alias text: label.text - property alias enabled: stateLayer.enabled - property string icon: "" - - implicitWidth: rowLayout.implicitWidth + Appearance.padding.normal * 2 - implicitHeight: rowLayout.implicitHeight + Appearance.padding.small * 2 - radius: Appearance.rounding.normal - - StateLayer { - id: stateLayer - color: parent.onColor - function onClicked(): void { - if (parent.enabled !== false) { - parent.clicked(); - } - } - } - - RowLayout { - id: rowLayout - anchors.centerIn: parent - spacing: Appearance.spacing.small - - MaterialIcon { - id: iconItem - visible: root.icon.length > 0 - text: root.icon - color: root.onColor - } - - StyledText { - id: label - color: parent.parent.onColor - } - } - - signal clicked -} \ No newline at end of file diff --git a/modules/controlcenter/network/WirelessDetails.qml b/modules/controlcenter/network/WirelessDetails.qml index 5b4f541..651f1fb 100644 --- a/modules/controlcenter/network/WirelessDetails.qml +++ b/modules/controlcenter/network/WirelessDetails.qml @@ -69,7 +69,7 @@ Item { 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 + // After fetching, check if we got details - if not, timer will try again }); } else { // We have details, can stop the timer @@ -89,7 +89,7 @@ Item { if (network && network.ssid) { const isActive = network.active || (Nmcli.active && Nmcli.active.ssid === network.ssid); if (isActive) { - Nmcli.getWirelessDeviceDetails("", () => {}); + Nmcli.getWirelessDeviceDetails(""); } else { Nmcli.wirelessDeviceDetails = null; } @@ -134,14 +134,14 @@ Item { checked: root.network?.active ?? false toggle.onToggled: { if (checked) { - handleConnect(); + root.handleConnect(); } else { Nmcli.disconnectFromNetwork(); } } } - SimpleButton { + TextButton { Layout.fillWidth: true Layout.topMargin: Appearance.spacing.normal Layout.minimumHeight: Appearance.font.size.normal + Appearance.padding.normal * 2 @@ -151,8 +151,8 @@ Item { } return Nmcli.hasSavedProfile(root.network.ssid); } - color: Colours.palette.m3errorContainer - onColor: Colours.palette.m3onErrorContainer + inactiveColour: Colours.palette.m3errorContainer + inactiveOnColour: Colours.palette.m3onErrorContainer text: qsTr("Forget Network") onClicked: { @@ -160,7 +160,7 @@ Item { if (root.network.active) { Nmcli.disconnectFromNetwork(); } - Nmcli.forgetNetwork(root.network.ssid, () => {}); + Nmcli.forgetNetwork(root.network.ssid); } } } @@ -214,7 +214,6 @@ Item { deviceDetails: Nmcli.wirelessDeviceDetails } } - } } @@ -236,26 +235,21 @@ Item { if (hasSavedProfile) { Nmcli.connectToNetwork(root.network.ssid, "", root.network.bssid, null); } else { - Nmcli.connectToNetworkWithPasswordCheck( - root.network.ssid, - root.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 = root.network; + Nmcli.connectToNetworkWithPasswordCheck(root.network.ssid, root.network.isSecure, result => { + if (result.needsPassword) { + if (Nmcli.pendingConnection) { + Nmcli.connectionCheckTimer.stop(); + Nmcli.immediateCheckTimer.stop(); + Nmcli.immediateCheckTimer.checkCount = 0; + Nmcli.pendingConnection = null; } - }, - root.network.bssid - ); + root.session.network.showPasswordDialog = true; + root.session.network.pendingNetwork = root.network; + } + }, root.network.bssid); } } else { Nmcli.connectToNetwork(root.network.ssid, "", root.network.bssid, null); } } -} \ No newline at end of file +} diff --git a/modules/controlcenter/network/WirelessPasswordDialog.qml b/modules/controlcenter/network/WirelessPasswordDialog.qml index d0c023f..f3381b7 100644 --- a/modules/controlcenter/network/WirelessPasswordDialog.qml +++ b/modules/controlcenter/network/WirelessPasswordDialog.qml @@ -48,7 +48,7 @@ Item { MouseArea { anchors.fill: parent - onClicked: closeDialog(); + onClicked: closeDialog() } } @@ -94,7 +94,7 @@ Item { } } - Keys.onEscapePressed: closeDialog(); + Keys.onEscapePressed: closeDialog() ColumnLayout { id: content @@ -314,25 +314,25 @@ Item { Layout.fillWidth: true spacing: Appearance.spacing.normal - SimpleButton { + TextButton { id: cancelButton Layout.fillWidth: true Layout.minimumHeight: Appearance.font.size.normal + Appearance.padding.normal * 2 - color: Colours.palette.m3secondaryContainer - onColor: Colours.palette.m3onSecondaryContainer + inactiveColour: Colours.palette.m3secondaryContainer + inactiveOnColour: Colours.palette.m3onSecondaryContainer text: qsTr("Cancel") - onClicked: closeDialog(); + onClicked: root.closeDialog() } - SimpleButton { + TextButton { id: connectButton Layout.fillWidth: true Layout.minimumHeight: Appearance.font.size.normal + Appearance.padding.normal * 2 - color: Colours.palette.m3primary - onColor: Colours.palette.m3onPrimary + inactiveColour: Colours.palette.m3primary + inactiveOnColour: Colours.palette.m3onPrimary text: qsTr("Connect") enabled: passwordContainer.passwordBuffer.length > 0 && !connecting @@ -354,24 +354,19 @@ Item { text = qsTr("Connecting..."); // Connect to network - Nmcli.connectToNetwork( - root.network.ssid, - password, - root.network.bssid || "", - (result) => { - if (result && result.success) { - // Connection successful, monitor will handle the rest - } else if (result && result.needsPassword) { - // Shouldn't happen since we provided password - connectionMonitor.stop(); - connecting = false; - enabled = true; - text = qsTr("Connect"); - } else { - // Connection failed, monitor will handle timeout - } - } - ); + Nmcli.connectToNetwork(root.network.ssid, password, root.network.bssid || "", result => { + if (result && result.success) + // Connection successful, monitor will handle the rest + {} else if (result && result.needsPassword) { + // Shouldn't happen since we provided password + connectionMonitor.stop(); + connecting = false; + enabled = true; + text = qsTr("Connect"); + } else + // Connection failed, monitor will handle timeout + {} + }); // Start monitoring connection connectionMonitor.start(); @@ -387,8 +382,7 @@ Item { } // 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(); + 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 @@ -459,7 +453,7 @@ Item { if (isClosing) { return; } - + isClosing = true; passwordContainer.passwordBuffer = ""; connectButton.connecting = false; diff --git a/utils/Icons.qml b/utils/Icons.qml index 389eca3..b3fe8ab 100644 --- a/utils/Icons.qml +++ b/utils/Icons.qml @@ -3,6 +3,7 @@ pragma Singleton import qs.config import Quickshell import Quickshell.Services.Notifications +import QtQuick Singleton { id: root -- cgit v1.2.3-freya From 442b49e0e1975d51efcef90d10566c90dedbf7e1 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sat, 15 Nov 2025 11:20:06 -0500 Subject: controlcenter: appearance pane corrections to fp/int values such as scales and border --- components/controls/CustomSpinBox.qml | 74 ++++++++++++++++++++-- components/controls/SpinBoxRow.qml | 2 + .../controlcenter/appearance/AppearancePane.qml | 11 +++- 3 files changed, 79 insertions(+), 8 deletions(-) (limited to 'components') diff --git a/components/controls/CustomSpinBox.qml b/components/controls/CustomSpinBox.qml index e2ed508..438dc08 100644 --- a/components/controls/CustomSpinBox.qml +++ b/components/controls/CustomSpinBox.qml @@ -9,19 +9,69 @@ import QtQuick.Layouts RowLayout { id: root - property int value + property real value property real max: Infinity property real min: -Infinity + property real step: 1 property alias repeatRate: timer.interval - signal valueModified(value: int) + signal valueModified(value: real) spacing: Appearance.spacing.small + property bool isEditing: false + property string displayText: root.value.toString() + + onValueChanged: { + if (!root.isEditing) { + root.displayText = root.value.toString(); + } + } + StyledTextField { + id: textField + inputMethodHints: Qt.ImhFormattedNumbersOnly - text: root.value - onAccepted: root.valueModified(text) + text: root.isEditing ? text : root.displayText + validator: DoubleValidator { + bottom: root.min + top: root.max + decimals: root.step < 1 ? Math.max(1, Math.ceil(-Math.log10(root.step))) : 0 + } + onActiveFocusChanged: { + if (activeFocus) { + root.isEditing = true; + } else { + root.isEditing = false; + root.displayText = root.value.toString(); + } + } + onAccepted: { + const numValue = parseFloat(text); + if (!isNaN(numValue)) { + const clampedValue = Math.max(root.min, Math.min(root.max, numValue)); + root.value = clampedValue; + root.displayText = clampedValue.toString(); + root.valueModified(clampedValue); + } else { + text = root.displayText; + } + root.isEditing = false; + } + onEditingFinished: { + if (text !== root.displayText) { + const numValue = parseFloat(text); + if (!isNaN(numValue)) { + const clampedValue = Math.max(root.min, Math.min(root.max, numValue)); + root.value = clampedValue; + root.displayText = clampedValue.toString(); + root.valueModified(clampedValue); + } else { + text = root.displayText; + } + } + root.isEditing = false; + } padding: Appearance.padding.small leftPadding: Appearance.padding.normal @@ -50,7 +100,13 @@ RowLayout { onReleased: timer.stop() function onClicked(): void { - root.valueModified(Math.min(root.max, root.value + 1)); + let newValue = Math.min(root.max, root.value + root.step); + // Round to avoid floating point precision errors + const decimals = root.step < 1 ? Math.max(1, Math.ceil(-Math.log10(root.step))) : 0; + newValue = Math.round(newValue * Math.pow(10, decimals)) / Math.pow(10, decimals); + root.value = newValue; + root.displayText = newValue.toString(); + root.valueModified(newValue); } } @@ -79,7 +135,13 @@ RowLayout { onReleased: timer.stop() function onClicked(): void { - root.valueModified(Math.max(root.min, root.value - 1)); + let newValue = Math.max(root.min, root.value - root.step); + // Round to avoid floating point precision errors + const decimals = root.step < 1 ? Math.max(1, Math.ceil(-Math.log10(root.step))) : 0; + newValue = Math.round(newValue * Math.pow(10, decimals)) / Math.pow(10, decimals); + root.value = newValue; + root.displayText = newValue.toString(); + root.valueModified(newValue); } } diff --git a/components/controls/SpinBoxRow.qml b/components/controls/SpinBoxRow.qml index a4441c5..2507b80 100644 --- a/components/controls/SpinBoxRow.qml +++ b/components/controls/SpinBoxRow.qml @@ -13,6 +13,7 @@ StyledRect { required property real value required property real min required property real max + property real step: 1 property var onValueModified: function(value) {} Layout.fillWidth: true @@ -41,6 +42,7 @@ StyledRect { CustomSpinBox { min: root.min max: root.max + step: root.step value: root.value onValueModified: value => { root.onValueModified(value); diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index d78ab21..6781cf0 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -408,6 +408,7 @@ RowLayout { label: qsTr("Animation duration scale") min: 0.1 max: 5 + step: 0.1 value: root.animDurationsScale onValueModified: value => { root.animDurationsScale = value; @@ -646,6 +647,7 @@ RowLayout { label: qsTr("Font size scale") min: 0.1 max: 5 + step: 0.1 value: root.fontSizeScale onValueModified: value => { root.fontSizeScale = value; @@ -665,6 +667,7 @@ RowLayout { label: qsTr("Padding scale") min: 0.1 max: 5 + step: 0.1 value: root.paddingScale onValueModified: value => { root.paddingScale = value; @@ -676,6 +679,7 @@ RowLayout { label: qsTr("Rounding scale") min: 0.1 max: 5 + step: 0.1 value: root.roundingScale onValueModified: value => { root.roundingScale = value; @@ -687,6 +691,7 @@ RowLayout { label: qsTr("Spacing scale") min: 0.1 max: 5 + step: 0.1 value: root.spacingScale onValueModified: value => { root.spacingScale = value; @@ -810,7 +815,8 @@ RowLayout { SpinBoxRow { label: qsTr("Border rounding") min: 0.1 - max: 5 + max: 100 + step: 0.1 value: root.borderRounding onValueModified: value => { root.borderRounding = value; @@ -821,7 +827,8 @@ RowLayout { SpinBoxRow { label: qsTr("Border thickness") min: 0.1 - max: 5 + max: 100 + step: 0.1 value: root.borderThickness onValueModified: value => { root.borderThickness = value; -- cgit v1.2.3-freya From 4ada7ea608711aa21f8bfc51698e7a3ec261aeeb Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sat, 15 Nov 2025 17:16:52 -0500 Subject: controlcenter: corrected certain containers not respecting transparency layers --- components/SectionContainer.qml | 2 +- components/controls/FilledSlider.qml | 2 +- components/controls/SpinBoxRow.qml | 2 +- components/controls/StyledSlider.qml | 2 +- components/controls/SwitchRow.qml | 2 +- modules/controlcenter/audio/AudioPane.qml | 4 ++-- modules/controlcenter/launcher/LauncherPane.qml | 4 ++-- modules/launcher/Content.qml | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) (limited to 'components') diff --git a/components/SectionContainer.qml b/components/SectionContainer.qml index d41254b..f7dfef4 100644 --- a/components/SectionContainer.qml +++ b/components/SectionContainer.qml @@ -15,7 +15,7 @@ StyledRect { implicitHeight: contentColumn.implicitHeight + Appearance.padding.large * 2 radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer + color: Colours.layer(Colours.palette.m3surfaceContainer, 2) ColumnLayout { id: contentColumn diff --git a/components/controls/FilledSlider.qml b/components/controls/FilledSlider.qml index 78b8a5c..80dd44c 100644 --- a/components/controls/FilledSlider.qml +++ b/components/controls/FilledSlider.qml @@ -15,7 +15,7 @@ Slider { orientation: Qt.Vertical background: StyledRect { - color: Colours.tPalette.m3surfaceContainer + color: Colours.layer(Colours.palette.m3surfaceContainer, 2) radius: Appearance.rounding.full StyledRect { diff --git a/components/controls/SpinBoxRow.qml b/components/controls/SpinBoxRow.qml index 2507b80..4902627 100644 --- a/components/controls/SpinBoxRow.qml +++ b/components/controls/SpinBoxRow.qml @@ -19,7 +19,7 @@ StyledRect { Layout.fillWidth: true implicitHeight: row.implicitHeight + Appearance.padding.large * 2 radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer + color: Colours.layer(Colours.palette.m3surfaceContainer, 2) Behavior on implicitHeight { Anim {} diff --git a/components/controls/StyledSlider.qml b/components/controls/StyledSlider.qml index 92c8aa8..55e8c8d 100644 --- a/components/controls/StyledSlider.qml +++ b/components/controls/StyledSlider.qml @@ -32,7 +32,7 @@ Slider { implicitWidth: parent.width - root.handle.x - root.handle.implicitWidth - root.implicitHeight / 6 - color: Colours.tPalette.m3surfaceContainer + color: Colours.layer(Colours.palette.m3surfaceContainer, 2) radius: Appearance.rounding.full topLeftRadius: root.implicitHeight / 15 bottomLeftRadius: root.implicitHeight / 15 diff --git a/components/controls/SwitchRow.qml b/components/controls/SwitchRow.qml index 0ec7aa5..7fa3e1b 100644 --- a/components/controls/SwitchRow.qml +++ b/components/controls/SwitchRow.qml @@ -17,7 +17,7 @@ StyledRect { Layout.fillWidth: true implicitHeight: row.implicitHeight + Appearance.padding.large * 2 radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer + color: Colours.layer(Colours.palette.m3surfaceContainer, 2) Behavior on implicitHeight { Anim {} diff --git a/modules/controlcenter/audio/AudioPane.qml b/modules/controlcenter/audio/AudioPane.qml index 005de3a..1c0c770 100644 --- a/modules/controlcenter/audio/AudioPane.qml +++ b/modules/controlcenter/audio/AudioPane.qml @@ -127,7 +127,7 @@ RowLayout { Layout.fillWidth: true - color: Qt.alpha(Colours.tPalette.m3surfaceContainer, Audio.sink?.id === modelData.id ? Colours.tPalette.m3surfaceContainer.a : 0) + color: Audio.sink?.id === modelData.id ? Colours.layer(Colours.palette.m3surfaceContainer, 2) : "transparent" radius: Appearance.rounding.normal StateLayer { @@ -205,7 +205,7 @@ RowLayout { Layout.fillWidth: true - color: Qt.alpha(Colours.tPalette.m3surfaceContainer, Audio.source?.id === modelData.id ? Colours.tPalette.m3surfaceContainer.a : 0) + color: Audio.source?.id === modelData.id ? Colours.layer(Colours.palette.m3surfaceContainer, 2) : "transparent" radius: Appearance.rounding.normal StateLayer { diff --git a/modules/controlcenter/launcher/LauncherPane.qml b/modules/controlcenter/launcher/LauncherPane.qml index 12abc1e..dc7a7a7 100644 --- a/modules/controlcenter/launcher/LauncherPane.qml +++ b/modules/controlcenter/launcher/LauncherPane.qml @@ -218,7 +218,7 @@ RowLayout { Layout.topMargin: Appearance.spacing.normal Layout.bottomMargin: Appearance.spacing.small - color: Colours.tPalette.m3surfaceContainer + color: Colours.layer(Colours.palette.m3surfaceContainer, 2) radius: Appearance.rounding.full implicitHeight: Math.max(searchIcon.implicitHeight, searchField.implicitHeight, clearIcon.implicitHeight) @@ -317,7 +317,7 @@ RowLayout { readonly property bool isSelected: root.selectedApp === modelData - color: Qt.alpha(Colours.tPalette.m3surfaceContainer, isSelected ? Colours.tPalette.m3surfaceContainer.a : 0) + color: isSelected ? Colours.layer(Colours.palette.m3surfaceContainer, 2) : "transparent" radius: Appearance.rounding.normal StateLayer { diff --git a/modules/launcher/Content.qml b/modules/launcher/Content.qml index f674569..c085976 100644 --- a/modules/launcher/Content.qml +++ b/modules/launcher/Content.qml @@ -47,7 +47,7 @@ Item { StyledRect { id: searchWrapper - color: Colours.tPalette.m3surfaceContainer + color: Colours.layer(Colours.palette.m3surfaceContainer, 2) radius: Appearance.rounding.full anchors.left: parent.left -- cgit v1.2.3-freya From 28be7e8d33301ea5ea40b0606ff1c02711b076e1 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sat, 15 Nov 2025 21:09:10 -0500 Subject: controlcenter: slider component color fix for transparency layers --- components/controls/StyledSlider.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'components') diff --git a/components/controls/StyledSlider.qml b/components/controls/StyledSlider.qml index 55e8c8d..0ef229d 100644 --- a/components/controls/StyledSlider.qml +++ b/components/controls/StyledSlider.qml @@ -32,7 +32,7 @@ Slider { implicitWidth: parent.width - root.handle.x - root.handle.implicitWidth - root.implicitHeight / 6 - color: Colours.layer(Colours.palette.m3surfaceContainer, 2) + color: Colours.palette.m3surfaceContainerHighest radius: Appearance.rounding.full topLeftRadius: root.implicitHeight / 15 bottomLeftRadius: root.implicitHeight / 15 -- cgit v1.2.3-freya From 90bda07a9031f88ff6d7df52ac39a09feba98a6d Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sat, 15 Nov 2025 21:22:14 -0500 Subject: controlcenter: animation bug fix on flickables --- components/controls/CollapsibleSection.qml | 7 + .../controlcenter/appearance/AppearancePane.qml | 546 ++++++++++----------- 2 files changed, 270 insertions(+), 283 deletions(-) (limited to 'components') diff --git a/components/controls/CollapsibleSection.qml b/components/controls/CollapsibleSection.qml index 3fba1c3..600b662 100644 --- a/components/controls/CollapsibleSection.qml +++ b/components/controls/CollapsibleSection.qml @@ -104,6 +104,13 @@ ColumnLayout { anchors.topMargin: Appearance.spacing.small anchors.bottomMargin: Appearance.spacing.small spacing: Appearance.spacing.small + opacity: root.expanded ? 1.0 : 0.0 + + Behavior on opacity { + Anim { + easing.bezierCurve: Appearance.anim.curves.standard + } + } } } } diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index 37397ae..459f4c1 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -200,84 +200,80 @@ RowLayout { sidebarFlickable.collapseAllSections(colorVariantSection); } - StyledListView { + ColumnLayout { Layout.fillWidth: true - implicitHeight: colorVariantSection.expanded ? Math.min(400, M3Variants.list.length * 60) : 0 - - model: M3Variants.list spacing: Appearance.spacing.small / 2 - clip: true - StyledScrollBar.vertical: StyledScrollBar { - flickable: parent - } + Repeater { + model: M3Variants.list - delegate: StyledRect { - required property var modelData + delegate: StyledRect { + required property var modelData - width: parent ? parent.width : 0 + Layout.fillWidth: true - 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 + 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 { - const variant = modelData.variant; + StateLayer { + function onClicked(): void { + const variant = modelData.variant; - // Optimistic update - set immediately - Schemes.currentVariant = variant; + // Optimistic update - set immediately + Schemes.currentVariant = variant; - // Execute the command - Quickshell.execDetached(["caelestia", "scheme", "set", "-v", variant]); + // Execute the command + Quickshell.execDetached(["caelestia", "scheme", "set", "-v", variant]); - // Reload after a delay to confirm - Qt.callLater(() => { - reloadTimer.restart(); - }); + // Reload after a delay to confirm + Qt.callLater(() => { + reloadTimer.restart(); + }); + } } - } - Timer { - id: reloadTimer - interval: 300 - onTriggered: { - Schemes.reload(); + Timer { + id: reloadTimer + interval: 300 + onTriggered: { + Schemes.reload(); + } } - } - RowLayout { - id: variantRow + RowLayout { + id: variantRow - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.normal + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.normal - spacing: Appearance.spacing.normal + spacing: Appearance.spacing.normal - MaterialIcon { - text: modelData.icon - font.pointSize: Appearance.font.size.large - fill: modelData.variant === Schemes.currentVariant ? 1 : 0 - } + 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 - } + 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 + 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 + implicitHeight: variantRow.implicitHeight + Appearance.padding.normal * 2 + } } } } @@ -290,139 +286,135 @@ RowLayout { sidebarFlickable.collapseAllSections(colorSchemeSection); } - StyledListView { + ColumnLayout { Layout.fillWidth: true - implicitHeight: colorSchemeSection.expanded ? Math.min(400, Schemes.list.length * 80) : 0 - - model: Schemes.list spacing: Appearance.spacing.small / 2 - clip: true - StyledScrollBar.vertical: StyledScrollBar { - flickable: parent - } + Repeater { + model: Schemes.list - delegate: StyledRect { - required property var modelData + delegate: StyledRect { + required property var modelData - width: parent ? parent.width : 0 + Layout.fillWidth: true - readonly property string schemeKey: `${modelData.name} ${modelData.flavour}` - readonly property bool isCurrent: schemeKey === Schemes.currentScheme + 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 + 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 { - const name = modelData.name; - const flavour = modelData.flavour; - const schemeKey = `${name} ${flavour}`; + StateLayer { + function onClicked(): void { + const name = modelData.name; + const flavour = modelData.flavour; + const schemeKey = `${name} ${flavour}`; - // Optimistic update - set immediately - Schemes.currentScheme = schemeKey; + // Optimistic update - set immediately + Schemes.currentScheme = schemeKey; - // Execute the command - Quickshell.execDetached(["caelestia", "scheme", "set", "-n", name, "-f", flavour]); + // Execute the command + Quickshell.execDetached(["caelestia", "scheme", "set", "-n", name, "-f", flavour]); - // Reload after a delay to confirm - Qt.callLater(() => { - reloadTimer.restart(); - }); + // Reload after a delay to confirm + Qt.callLater(() => { + reloadTimer.restart(); + }); + } } - } - Timer { - id: reloadTimer - interval: 300 - onTriggered: { - Schemes.reload(); + Timer { + id: reloadTimer + interval: 300 + onTriggered: { + Schemes.reload(); + } } - } - RowLayout { - id: schemeRow - - anchors.fill: parent - anchors.margins: Appearance.padding.normal + RowLayout { + id: schemeRow - spacing: Appearance.spacing.normal - - StyledRect { - id: preview + anchors.fill: parent + anchors.margins: Appearance.padding.normal - Layout.alignment: Qt.AlignVCenter + spacing: Appearance.spacing.normal - border.width: 1 - border.color: Qt.alpha(`#${modelData.colours?.outline}`, 0.5) + StyledRect { + id: preview - color: `#${modelData.colours?.surface}` - radius: Appearance.rounding.full - implicitWidth: iconPlaceholder.implicitWidth - implicitHeight: iconPlaceholder.implicitWidth + Layout.alignment: Qt.AlignVCenter - MaterialIcon { - id: iconPlaceholder - visible: false - text: "circle" - font.pointSize: Appearance.font.size.large - } + border.width: 1 + border.color: Qt.alpha(`#${modelData.colours?.outline}`, 0.5) - Item { - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.right: parent.right + color: `#${modelData.colours?.surface}` + radius: Appearance.rounding.full + implicitWidth: iconPlaceholder.implicitWidth + implicitHeight: iconPlaceholder.implicitWidth - implicitWidth: parent.implicitWidth / 2 - clip: true + MaterialIcon { + id: iconPlaceholder + visible: false + text: "circle" + font.pointSize: Appearance.font.size.large + } - StyledRect { + Item { anchors.top: parent.top anchors.bottom: parent.bottom anchors.right: parent.right - implicitWidth: preview.implicitWidth - color: `#${modelData.colours?.primary}` - radius: Appearance.rounding.full + 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 + } } } - } - Column { - Layout.fillWidth: true - spacing: 0 + Column { + Layout.fillWidth: true + spacing: 0 - StyledText { - text: modelData.flavour ?? "" - font.pointSize: Appearance.font.size.normal - } + StyledText { + text: modelData.flavour ?? "" + font.pointSize: Appearance.font.size.normal + } - StyledText { - text: modelData.name ?? "" - font.pointSize: Appearance.font.size.small - color: Colours.palette.m3outline + StyledText { + text: modelData.name ?? "" + font.pointSize: Appearance.font.size.small + color: Colours.palette.m3outline - elide: Text.ElideRight - anchors.left: parent.left - anchors.right: parent.right + elide: Text.ElideRight + anchors.left: parent.left + anchors.right: parent.right + } } - } - Loader { - active: isCurrent - asynchronous: true + Loader { + active: isCurrent + asynchronous: true - sourceComponent: MaterialIcon { - text: "check" - color: Colours.palette.m3onSurfaceVariant - font.pointSize: Appearance.font.size.large + sourceComponent: MaterialIcon { + text: "check" + color: Colours.palette.m3onSurfaceVariant + font.pointSize: Appearance.font.size.large + } } } - } - implicitHeight: schemeRow.implicitHeight + Appearance.padding.normal * 2 + implicitHeight: schemeRow.implicitHeight + Appearance.padding.normal * 2 + } } } } @@ -548,68 +540,64 @@ RowLayout { font.weight: 500 } - StyledListView { + ColumnLayout { Layout.fillWidth: true - implicitHeight: fontsSection.expanded ? Math.min(300, Qt.fontFamilies().length * 50) : 0 - - model: Qt.fontFamilies() spacing: Appearance.spacing.small / 2 - clip: true - StyledScrollBar.vertical: StyledScrollBar { - flickable: parent - } + Repeater { + model: Qt.fontFamilies() - delegate: StyledRect { - required property string modelData + delegate: StyledRect { + required property string modelData - width: parent ? parent.width : 0 + Layout.fillWidth: true - readonly property bool isCurrent: modelData === rootPane.fontFamilyMaterial - 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 + readonly property bool isCurrent: modelData === rootPane.fontFamilyMaterial + 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 { - rootPane.fontFamilyMaterial = modelData; - rootPane.saveConfig(); + StateLayer { + function onClicked(): void { + rootPane.fontFamilyMaterial = modelData; + rootPane.saveConfig(); + } } - } - RowLayout { - id: fontFamilyMaterialRow + RowLayout { + id: fontFamilyMaterialRow - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.normal + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.normal - spacing: Appearance.spacing.normal + spacing: Appearance.spacing.normal - StyledText { - text: modelData - font.pointSize: Appearance.font.size.normal - } + StyledText { + text: modelData + font.pointSize: Appearance.font.size.normal + } - Item { - Layout.fillWidth: true - } + Item { + Layout.fillWidth: true + } - Loader { - active: isCurrent - asynchronous: true + Loader { + active: isCurrent + asynchronous: true - sourceComponent: MaterialIcon { - text: "check" - color: Colours.palette.m3onSurfaceVariant - font.pointSize: Appearance.font.size.large + sourceComponent: MaterialIcon { + text: "check" + color: Colours.palette.m3onSurfaceVariant + font.pointSize: Appearance.font.size.large + } } } - } - implicitHeight: fontFamilyMaterialRow.implicitHeight + Appearance.padding.normal * 2 + implicitHeight: fontFamilyMaterialRow.implicitHeight + Appearance.padding.normal * 2 + } } } @@ -620,68 +608,64 @@ RowLayout { font.weight: 500 } - StyledListView { + ColumnLayout { Layout.fillWidth: true - implicitHeight: fontsSection.expanded ? Math.min(300, Qt.fontFamilies().length * 50) : 0 - - model: Qt.fontFamilies() spacing: Appearance.spacing.small / 2 - clip: true - StyledScrollBar.vertical: StyledScrollBar { - flickable: parent - } + Repeater { + model: Qt.fontFamilies() - delegate: StyledRect { - required property string modelData + delegate: StyledRect { + required property string modelData - width: parent ? parent.width : 0 + Layout.fillWidth: true - readonly property bool isCurrent: modelData === rootPane.fontFamilyMono - 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 + readonly property bool isCurrent: modelData === rootPane.fontFamilyMono + 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 { - rootPane.fontFamilyMono = modelData; - rootPane.saveConfig(); + StateLayer { + function onClicked(): void { + rootPane.fontFamilyMono = modelData; + rootPane.saveConfig(); + } } - } - RowLayout { - id: fontFamilyMonoRow + RowLayout { + id: fontFamilyMonoRow - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.normal + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.normal - spacing: Appearance.spacing.normal + spacing: Appearance.spacing.normal - StyledText { - text: modelData - font.pointSize: Appearance.font.size.normal - } + StyledText { + text: modelData + font.pointSize: Appearance.font.size.normal + } - Item { - Layout.fillWidth: true - } + Item { + Layout.fillWidth: true + } - Loader { - active: isCurrent - asynchronous: true + Loader { + active: isCurrent + asynchronous: true - sourceComponent: MaterialIcon { - text: "check" - color: Colours.palette.m3onSurfaceVariant - font.pointSize: Appearance.font.size.large + sourceComponent: MaterialIcon { + text: "check" + color: Colours.palette.m3onSurfaceVariant + font.pointSize: Appearance.font.size.large + } } } - } - implicitHeight: fontFamilyMonoRow.implicitHeight + Appearance.padding.normal * 2 + implicitHeight: fontFamilyMonoRow.implicitHeight + Appearance.padding.normal * 2 + } } } @@ -692,68 +676,64 @@ RowLayout { font.weight: 500 } - StyledListView { + ColumnLayout { Layout.fillWidth: true - implicitHeight: fontsSection.expanded ? Math.min(300, Qt.fontFamilies().length * 50) : 0 - - model: Qt.fontFamilies() spacing: Appearance.spacing.small / 2 - clip: true - StyledScrollBar.vertical: StyledScrollBar { - flickable: parent - } + Repeater { + model: Qt.fontFamilies() - delegate: StyledRect { - required property string modelData + delegate: StyledRect { + required property string modelData - width: parent ? parent.width : 0 + Layout.fillWidth: true - readonly property bool isCurrent: modelData === rootPane.fontFamilySans - 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 + readonly property bool isCurrent: modelData === rootPane.fontFamilySans + 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 { - rootPane.fontFamilySans = modelData; - rootPane.saveConfig(); + StateLayer { + function onClicked(): void { + rootPane.fontFamilySans = modelData; + rootPane.saveConfig(); + } } - } - RowLayout { - id: fontFamilySansRow + RowLayout { + id: fontFamilySansRow - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.normal + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.normal - spacing: Appearance.spacing.normal + spacing: Appearance.spacing.normal - StyledText { - text: modelData - font.pointSize: Appearance.font.size.normal - } + StyledText { + text: modelData + font.pointSize: Appearance.font.size.normal + } - Item { - Layout.fillWidth: true - } + Item { + Layout.fillWidth: true + } - Loader { - active: isCurrent - asynchronous: true + Loader { + active: isCurrent + asynchronous: true - sourceComponent: MaterialIcon { - text: "check" - color: Colours.palette.m3onSurfaceVariant - font.pointSize: Appearance.font.size.large + sourceComponent: MaterialIcon { + text: "check" + color: Colours.palette.m3onSurfaceVariant + font.pointSize: Appearance.font.size.large + } } } - } - implicitHeight: fontFamilySansRow.implicitHeight + Appearance.padding.normal * 2 + implicitHeight: fontFamilySansRow.implicitHeight + Appearance.padding.normal * 2 + } } } -- cgit v1.2.3-freya From 483a6fe8e4297d56e9f4dc5cb33c046520edc538 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sat, 15 Nov 2025 21:54:33 -0500 Subject: controlcenter: collapsible section sub heading animation correction --- components/controls/CollapsibleSection.qml | 39 +++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 9 deletions(-) (limited to 'components') diff --git a/components/controls/CollapsibleSection.qml b/components/controls/CollapsibleSection.qml index 600b662..e1cd35f 100644 --- a/components/controls/CollapsibleSection.qml +++ b/components/controls/CollapsibleSection.qml @@ -68,16 +68,37 @@ ColumnLayout { } } - StyledText { - visible: root.expanded && root.description !== "" + Item { + visible: root.description !== "" Layout.fillWidth: true - Layout.leftMargin: Appearance.padding.normal - Layout.rightMargin: Appearance.padding.normal - Layout.topMargin: Appearance.spacing.smaller - Layout.bottomMargin: Appearance.spacing.small - text: root.description - color: Colours.palette.m3onSurfaceVariant - font.pointSize: Appearance.font.size.small + Layout.preferredHeight: root.expanded ? descriptionText.implicitHeight + Appearance.spacing.smaller + Appearance.spacing.small : 0 + clip: true + + Behavior on Layout.preferredHeight { + Anim { + easing.bezierCurve: Appearance.anim.curves.standard + } + } + + StyledText { + id: descriptionText + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + anchors.leftMargin: Appearance.padding.normal + anchors.rightMargin: Appearance.padding.normal + anchors.topMargin: Appearance.spacing.smaller + text: root.description + color: Colours.palette.m3onSurfaceVariant + font.pointSize: Appearance.font.size.small + opacity: root.expanded ? 1.0 : 0.0 + + Behavior on opacity { + Anim { + easing.bezierCurve: Appearance.anim.curves.standard + } + } + } } default property alias content: contentColumn.data -- cgit v1.2.3-freya From c9edb4d0645ebbfb513bb56e731a46bd83b591c6 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sun, 16 Nov 2025 09:07:16 -0500 Subject: controlcenter: animation subtext in collapsiblesection correction --- components/controls/CollapsibleSection.qml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'components') diff --git a/components/controls/CollapsibleSection.qml b/components/controls/CollapsibleSection.qml index e1cd35f..2df169a 100644 --- a/components/controls/CollapsibleSection.qml +++ b/components/controls/CollapsibleSection.qml @@ -69,9 +69,8 @@ ColumnLayout { } Item { - visible: root.description !== "" Layout.fillWidth: true - Layout.preferredHeight: root.expanded ? descriptionText.implicitHeight + Appearance.spacing.smaller + Appearance.spacing.small : 0 + Layout.preferredHeight: (root.expanded && root.description !== "") ? descriptionText.implicitHeight + Appearance.spacing.smaller + Appearance.spacing.small : 0 clip: true Behavior on Layout.preferredHeight { @@ -91,7 +90,7 @@ ColumnLayout { text: root.description color: Colours.palette.m3onSurfaceVariant font.pointSize: Appearance.font.size.small - opacity: root.expanded ? 1.0 : 0.0 + opacity: (root.expanded && root.description !== "") ? 1.0 : 0.0 Behavior on opacity { Anim { -- cgit v1.2.3-freya From 18d117557a8e1c29fddc150c6d7cd50b2fc41f2c Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sun, 16 Nov 2025 15:09:58 -0500 Subject: controlcenter: clip/fade issue on certain collapsiblesections --- components/controls/CollapsibleSection.qml | 40 ++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) (limited to 'components') diff --git a/components/controls/CollapsibleSection.qml b/components/controls/CollapsibleSection.qml index 2df169a..7cb14a0 100644 --- a/components/controls/CollapsibleSection.qml +++ b/components/controls/CollapsibleSection.qml @@ -70,8 +70,15 @@ ColumnLayout { Item { Layout.fillWidth: true - Layout.preferredHeight: (root.expanded && root.description !== "") ? descriptionText.implicitHeight + Appearance.spacing.smaller + Appearance.spacing.small : 0 - clip: true + Layout.preferredHeight: (root.expanded && root.description !== "") ? Math.min(descriptionText.implicitHeight + Appearance.spacing.smaller + Appearance.spacing.small, maxDescriptionHeight) : 0 + + readonly property real maxDescriptionHeight: 60 + + layer.enabled: true + layer.smooth: true + layer.effect: OpacityMask { + maskSource: descriptionMask + } Behavior on Layout.preferredHeight { Anim { @@ -79,6 +86,34 @@ ColumnLayout { } } + Item { + id: descriptionMask + anchors.fill: parent + layer.enabled: true + visible: false + + Rectangle { + anchors.fill: parent + + gradient: Gradient { + orientation: Gradient.Vertical + + GradientStop { + position: 0.0 + color: Qt.rgba(0, 0, 0, 1) + } + GradientStop { + position: 0.7 + color: Qt.rgba(0, 0, 0, 1) + } + GradientStop { + position: 1.0 + color: Qt.rgba(0, 0, 0, 0) + } + } + } + } + StyledText { id: descriptionText anchors.left: parent.left @@ -90,6 +125,7 @@ ColumnLayout { text: root.description color: Colours.palette.m3onSurfaceVariant font.pointSize: Appearance.font.size.small + wrapMode: Text.Wrap opacity: (root.expanded && root.description !== "") ? 1.0 : 0.0 Behavior on opacity { -- cgit v1.2.3-freya From b3d54fa0737bef45d04a049178ce8dba54ffbfcf Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sun, 16 Nov 2025 16:49:54 -0500 Subject: controlcenter: animation correction on certain collapsiblesections --- components/controls/CollapsibleSection.qml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'components') diff --git a/components/controls/CollapsibleSection.qml b/components/controls/CollapsibleSection.qml index 7cb14a0..aa206ba 100644 --- a/components/controls/CollapsibleSection.qml +++ b/components/controls/CollapsibleSection.qml @@ -154,10 +154,9 @@ ColumnLayout { id: contentColumn anchors.left: parent.left anchors.right: parent.right - anchors.top: parent.top + y: Appearance.spacing.small anchors.leftMargin: Appearance.padding.normal anchors.rightMargin: Appearance.padding.normal - anchors.topMargin: Appearance.spacing.small anchors.bottomMargin: Appearance.spacing.small spacing: Appearance.spacing.small opacity: root.expanded ? 1.0 : 0.0 -- cgit v1.2.3-freya From d2373e03a5cbd0cb97e9c5e32966a0c0afd80f10 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sun, 16 Nov 2025 19:07:51 -0500 Subject: controlcenter: FIXED animation bug with sub-text --- components/controls/CollapsibleSection.qml | 80 +++++------------------------- 1 file changed, 12 insertions(+), 68 deletions(-) (limited to 'components') diff --git a/components/controls/CollapsibleSection.qml b/components/controls/CollapsibleSection.qml index aa206ba..35acdec 100644 --- a/components/controls/CollapsibleSection.qml +++ b/components/controls/CollapsibleSection.qml @@ -68,74 +68,6 @@ ColumnLayout { } } - Item { - Layout.fillWidth: true - Layout.preferredHeight: (root.expanded && root.description !== "") ? Math.min(descriptionText.implicitHeight + Appearance.spacing.smaller + Appearance.spacing.small, maxDescriptionHeight) : 0 - - readonly property real maxDescriptionHeight: 60 - - layer.enabled: true - layer.smooth: true - layer.effect: OpacityMask { - maskSource: descriptionMask - } - - Behavior on Layout.preferredHeight { - Anim { - easing.bezierCurve: Appearance.anim.curves.standard - } - } - - Item { - id: descriptionMask - anchors.fill: parent - layer.enabled: true - visible: false - - Rectangle { - anchors.fill: parent - - gradient: Gradient { - orientation: Gradient.Vertical - - GradientStop { - position: 0.0 - color: Qt.rgba(0, 0, 0, 1) - } - GradientStop { - position: 0.7 - color: Qt.rgba(0, 0, 0, 1) - } - GradientStop { - position: 1.0 - color: Qt.rgba(0, 0, 0, 0) - } - } - } - } - - StyledText { - id: descriptionText - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - anchors.leftMargin: Appearance.padding.normal - anchors.rightMargin: Appearance.padding.normal - anchors.topMargin: Appearance.spacing.smaller - text: root.description - color: Colours.palette.m3onSurfaceVariant - font.pointSize: Appearance.font.size.small - wrapMode: Text.Wrap - opacity: (root.expanded && root.description !== "") ? 1.0 : 0.0 - - Behavior on opacity { - Anim { - easing.bezierCurve: Appearance.anim.curves.standard - } - } - } - } - default property alias content: contentColumn.data Item { @@ -166,6 +98,18 @@ ColumnLayout { easing.bezierCurve: Appearance.anim.curves.standard } } + + StyledText { + id: descriptionText + Layout.fillWidth: true + Layout.topMargin: root.description !== "" ? Appearance.spacing.smaller : 0 + Layout.bottomMargin: root.description !== "" ? Appearance.spacing.small : 0 + visible: root.description !== "" + text: root.description + color: Colours.palette.m3onSurfaceVariant + font.pointSize: Appearance.font.size.small + wrapMode: Text.Wrap + } } } } -- cgit v1.2.3-freya From 821ee4389ab2a81c5abba910936662d22e5f0480 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Mon, 17 Nov 2025 12:21:44 -0500 Subject: controlcenter: taskbar panel layout reorg --- components/SectionContainer.qml | 4 +- .../controlcenter/taskbar/ConnectedButtonGroup.qml | 18 + modules/controlcenter/taskbar/TaskbarPane.qml | 578 +++++++++++---------- 3 files changed, 328 insertions(+), 272 deletions(-) (limited to 'components') diff --git a/components/SectionContainer.qml b/components/SectionContainer.qml index f7dfef4..60e3d59 100644 --- a/components/SectionContainer.qml +++ b/components/SectionContainer.qml @@ -10,6 +10,7 @@ StyledRect { default property alias content: contentColumn.data property real contentSpacing: Appearance.spacing.larger + property bool alignTop: false Layout.fillWidth: true implicitHeight: contentColumn.implicitHeight + Appearance.padding.large * 2 @@ -22,7 +23,8 @@ StyledRect { anchors.left: parent.left anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter + anchors.top: root.alignTop ? parent.top : undefined + anchors.verticalCenter: root.alignTop ? undefined : parent.verticalCenter anchors.margins: Appearance.padding.large spacing: root.contentSpacing diff --git a/modules/controlcenter/taskbar/ConnectedButtonGroup.qml b/modules/controlcenter/taskbar/ConnectedButtonGroup.qml index 83ff95e..e35ccfc 100644 --- a/modules/controlcenter/taskbar/ConnectedButtonGroup.qml +++ b/modules/controlcenter/taskbar/ConnectedButtonGroup.qml @@ -113,6 +113,24 @@ StyledRect { button.isChecked = root.rootItem.showLockStatus; } } + + function onTrayBackgroundChanged() { + if (modelData.propertyName === "trayBackground") { + button.isChecked = root.rootItem.trayBackground; + } + } + + function onTrayCompactChanged() { + if (modelData.propertyName === "trayCompact") { + button.isChecked = root.rootItem.trayCompact; + } + } + + function onTrayRecolourChanged() { + if (modelData.propertyName === "trayRecolour") { + button.isChecked = root.rootItem.trayRecolour; + } + } } // Match utilities Toggles radius styling diff --git a/modules/controlcenter/taskbar/TaskbarPane.qml b/modules/controlcenter/taskbar/TaskbarPane.qml index 915e611..3f14dd6 100644 --- a/modules/controlcenter/taskbar/TaskbarPane.qml +++ b/modules/controlcenter/taskbar/TaskbarPane.qml @@ -161,11 +161,7 @@ Item { spacing: Appearance.spacing.small - readonly property bool allSectionsExpanded: - clockSection.expanded && - barBehaviorSection.expanded && - traySettingsSection.expanded && - workspacesSection.expanded + readonly property bool allSectionsExpanded: true RowLayout { spacing: Appearance.spacing.smaller @@ -185,11 +181,7 @@ Item { type: IconButton.Text label.animate: true onClicked: { - const shouldExpand = !sidebarLayout.allSectionsExpanded; - clockSection.expanded = shouldExpand; - barBehaviorSection.expanded = shouldExpand; - traySettingsSection.expanded = shouldExpand; - workspacesSection.expanded = shouldExpand; + // No collapsible sections remaining } } } @@ -258,345 +250,389 @@ Item { ] } - CollapsibleSection { - id: clockSection - title: qsTr("Clock") - - SwitchRow { - label: qsTr("Show clock icon") - checked: root.clockShowIcon - onToggled: checked => { - root.clockShowIcon = checked; - root.saveConfig(); - } - } - } - - CollapsibleSection { - id: barBehaviorSection - title: qsTr("Bar Behavior") + RowLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.small - SwitchRow { - label: qsTr("Persistent") - checked: root.persistent - onToggled: checked => { - root.persistent = checked; - root.saveConfig(); - } - } + ColumnLayout { + Layout.fillWidth: true + Layout.alignment: Qt.AlignTop + spacing: 0 - SwitchRow { - label: qsTr("Show on hover") - checked: root.showOnHover - onToggled: checked => { - root.showOnHover = checked; - root.saveConfig(); - } - } + SectionContainer { + Layout.fillWidth: true + alignTop: true - SectionContainer { - contentSpacing: Appearance.spacing.normal + StyledText { + text: qsTr("Workspaces") + font.pointSize: Appearance.font.size.normal + } - ColumnLayout { + StyledRect { Layout.fillWidth: true - spacing: Appearance.spacing.small + implicitHeight: workspacesShownRow.implicitHeight + Appearance.padding.large * 2 + radius: Appearance.rounding.normal + color: Colours.layer(Colours.palette.m3surfaceContainer, 2) + + Behavior on implicitHeight { + Anim {} + } RowLayout { - Layout.fillWidth: true + id: workspacesShownRow + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.large spacing: Appearance.spacing.normal StyledText { - text: qsTr("Drag threshold") - font.pointSize: Appearance.font.size.normal - } - - Item { Layout.fillWidth: true + text: qsTr("Shown") } - StyledRect { - Layout.preferredWidth: 70 - implicitHeight: dragThresholdInput.implicitHeight + Appearance.padding.small * 2 - color: dragThresholdInputHover.containsMouse || dragThresholdInput.activeFocus - ? Colours.layer(Colours.palette.m3surfaceContainer, 3) - : Colours.layer(Colours.palette.m3surfaceContainer, 2) - radius: Appearance.rounding.small - border.width: 1 - border.color: dragThresholdInput.activeFocus - ? Colours.palette.m3primary - : Qt.alpha(Colours.palette.m3outline, 0.3) - - Behavior on color { CAnim {} } - Behavior on border.color { CAnim {} } - - MouseArea { - id: dragThresholdInputHover - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.IBeamCursor - acceptedButtons: Qt.NoButton - } - - StyledTextField { - id: dragThresholdInput - anchors.centerIn: parent - width: parent.width - Appearance.padding.normal - horizontalAlignment: TextInput.AlignHCenter - validator: IntValidator { bottom: 0; top: 100 } - - Component.onCompleted: { - text = root.dragThreshold.toString(); - } - - onTextChanged: { - if (activeFocus) { - const val = parseInt(text); - if (!isNaN(val) && val >= 0 && val <= 100) { - root.dragThreshold = val; - root.saveConfig(); - } - } - } - onEditingFinished: { - const val = parseInt(text); - if (isNaN(val) || val < 0 || val > 100) { - text = root.dragThreshold.toString(); - } - } + CustomSpinBox { + min: 1 + max: 20 + value: root.workspacesShown + onValueModified: value => { + root.workspacesShown = value; + root.saveConfig(); } } + } + } - StyledText { - text: "px" - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.normal - } + StyledRect { + Layout.fillWidth: true + implicitHeight: workspacesActiveIndicatorRow.implicitHeight + Appearance.padding.large * 2 + radius: Appearance.rounding.normal + color: Colours.layer(Colours.palette.m3surfaceContainer, 2) + + Behavior on implicitHeight { + Anim {} } - StyledSlider { - id: dragThresholdSlider + RowLayout { + id: workspacesActiveIndicatorRow + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.large + spacing: Appearance.spacing.normal - Layout.fillWidth: true - implicitHeight: Appearance.padding.normal * 3 + StyledText { + Layout.fillWidth: true + text: qsTr("Active indicator") + } - from: 0 - to: 100 - value: root.dragThreshold - onMoved: { - root.dragThreshold = Math.round(dragThresholdSlider.value); - if (!dragThresholdInput.activeFocus) { - dragThresholdInput.text = Math.round(dragThresholdSlider.value).toString(); + StyledSwitch { + checked: root.workspacesActiveIndicator + onToggled: { + root.workspacesActiveIndicator = checked; + root.saveConfig(); } - root.saveConfig(); } } } - } - } - CollapsibleSection { - id: traySettingsSection - title: qsTr("Tray Settings") - - SwitchRow { - label: qsTr("Background") - checked: root.trayBackground - onToggled: checked => { - root.trayBackground = checked; - root.saveConfig(); - } - } - - SwitchRow { - label: qsTr("Compact") - checked: root.trayCompact - onToggled: checked => { - root.trayCompact = checked; - root.saveConfig(); - } - } + StyledRect { + Layout.fillWidth: true + implicitHeight: workspacesOccupiedBgRow.implicitHeight + Appearance.padding.large * 2 + radius: Appearance.rounding.normal + color: Colours.layer(Colours.palette.m3surfaceContainer, 2) - SwitchRow { - label: qsTr("Recolour") - checked: root.trayRecolour - onToggled: checked => { - root.trayRecolour = checked; - root.saveConfig(); - } - } - } + Behavior on implicitHeight { + Anim {} + } - CollapsibleSection { - id: workspacesSection - title: qsTr("Workspaces") + RowLayout { + id: workspacesOccupiedBgRow + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.large + spacing: Appearance.spacing.normal - StyledRect { - Layout.fillWidth: true - implicitHeight: workspacesShownRow.implicitHeight + Appearance.padding.large * 2 - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer + StyledText { + Layout.fillWidth: true + text: qsTr("Occupied background") + } - Behavior on implicitHeight { - Anim {} + StyledSwitch { + checked: root.workspacesOccupiedBg + onToggled: { + root.workspacesOccupiedBg = checked; + root.saveConfig(); + } + } + } } - RowLayout { - id: workspacesShownRow - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.large - spacing: Appearance.spacing.normal + StyledRect { + Layout.fillWidth: true + implicitHeight: workspacesShowWindowsRow.implicitHeight + Appearance.padding.large * 2 + radius: Appearance.rounding.normal + color: Colours.layer(Colours.palette.m3surfaceContainer, 2) - StyledText { - Layout.fillWidth: true - text: qsTr("Shown") + Behavior on implicitHeight { + Anim {} } - CustomSpinBox { - min: 1 - max: 20 - value: root.workspacesShown - onValueModified: value => { - root.workspacesShown = value; - root.saveConfig(); + RowLayout { + id: workspacesShowWindowsRow + 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 windows") + } + + StyledSwitch { + checked: root.workspacesShowWindows + onToggled: { + root.workspacesShowWindows = checked; + root.saveConfig(); + } } } } - } - StyledRect { - Layout.fillWidth: true - implicitHeight: workspacesActiveIndicatorRow.implicitHeight + Appearance.padding.large * 2 - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer + StyledRect { + Layout.fillWidth: true + implicitHeight: workspacesPerMonitorRow.implicitHeight + Appearance.padding.large * 2 + radius: Appearance.rounding.normal + color: Colours.layer(Colours.palette.m3surfaceContainer, 2) - Behavior on implicitHeight { - Anim {} - } + Behavior on implicitHeight { + Anim {} + } - RowLayout { - id: workspacesActiveIndicatorRow - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.large - spacing: Appearance.spacing.normal + RowLayout { + id: workspacesPerMonitorRow + 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("Active indicator") - } + StyledText { + Layout.fillWidth: true + text: qsTr("Per monitor workspaces") + } - StyledSwitch { - checked: root.workspacesActiveIndicator - onToggled: { - root.workspacesActiveIndicator = checked; - root.saveConfig(); + StyledSwitch { + checked: root.workspacesPerMonitor + onToggled: { + root.workspacesPerMonitor = checked; + root.saveConfig(); + } } } } + } } - StyledRect { + ColumnLayout { Layout.fillWidth: true - implicitHeight: workspacesOccupiedBgRow.implicitHeight + Appearance.padding.large * 2 - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - - Behavior on implicitHeight { - Anim {} - } + Layout.alignment: Qt.AlignTop + spacing: Appearance.spacing.small - RowLayout { - id: workspacesOccupiedBgRow - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.large - spacing: Appearance.spacing.normal + SectionContainer { + Layout.fillWidth: true + alignTop: true StyledText { - Layout.fillWidth: true - text: qsTr("Occupied background") + text: qsTr("Clock") + font.pointSize: Appearance.font.size.normal } - StyledSwitch { - checked: root.workspacesOccupiedBg - onToggled: { - root.workspacesOccupiedBg = checked; + SwitchRow { + label: qsTr("Show clock icon") + checked: root.clockShowIcon + onToggled: checked => { + root.clockShowIcon = checked; root.saveConfig(); } } } + + SectionContainer { + Layout.fillWidth: true + alignTop: true + + ConnectedButtonGroup { + rootItem: root + title: qsTr("Tray Settings") + + options: [ + { + label: qsTr("Background"), + propertyName: "trayBackground", + onToggled: function(checked) { + root.trayBackground = checked; + root.saveConfig(); + } + }, + { + label: qsTr("Compact"), + propertyName: "trayCompact", + onToggled: function(checked) { + root.trayCompact = checked; + root.saveConfig(); + } + }, + { + label: qsTr("Recolour"), + propertyName: "trayRecolour", + onToggled: function(checked) { + root.trayRecolour = checked; + root.saveConfig(); + } + } + ] + } + } } - StyledRect { + ColumnLayout { Layout.fillWidth: true - implicitHeight: workspacesShowWindowsRow.implicitHeight + Appearance.padding.large * 2 - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - - Behavior on implicitHeight { - Anim {} - } + Layout.alignment: Qt.AlignTop + spacing: 0 - RowLayout { - id: workspacesShowWindowsRow - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.large - spacing: Appearance.spacing.normal + SectionContainer { + Layout.fillWidth: true + alignTop: true StyledText { - Layout.fillWidth: true - text: qsTr("Show windows") + text: qsTr("Bar Behavior") + font.pointSize: Appearance.font.size.normal } - StyledSwitch { - checked: root.workspacesShowWindows - onToggled: { - root.workspacesShowWindows = checked; + SwitchRow { + label: qsTr("Persistent") + checked: root.persistent + onToggled: checked => { + root.persistent = checked; root.saveConfig(); } } - } - } - StyledRect { - Layout.fillWidth: true - implicitHeight: workspacesPerMonitorRow.implicitHeight + Appearance.padding.large * 2 - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer + SwitchRow { + label: qsTr("Show on hover") + checked: root.showOnHover + onToggled: checked => { + root.showOnHover = checked; + root.saveConfig(); + } + } - Behavior on implicitHeight { - Anim {} - } + SectionContainer { + contentSpacing: Appearance.spacing.normal - RowLayout { - id: workspacesPerMonitorRow - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.large - spacing: Appearance.spacing.normal + ColumnLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.small - StyledText { - Layout.fillWidth: true - text: qsTr("Per monitor workspaces") - } + RowLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.normal - StyledSwitch { - checked: root.workspacesPerMonitor - onToggled: { - root.workspacesPerMonitor = checked; - root.saveConfig(); + StyledText { + text: qsTr("Drag threshold") + font.pointSize: Appearance.font.size.normal + } + + Item { + Layout.fillWidth: true + } + + StyledRect { + Layout.preferredWidth: 70 + implicitHeight: dragThresholdInput.implicitHeight + Appearance.padding.small * 2 + color: dragThresholdInputHover.containsMouse || dragThresholdInput.activeFocus + ? Colours.layer(Colours.palette.m3surfaceContainer, 3) + : Colours.layer(Colours.palette.m3surfaceContainer, 2) + radius: Appearance.rounding.small + border.width: 1 + border.color: dragThresholdInput.activeFocus + ? Colours.palette.m3primary + : Qt.alpha(Colours.palette.m3outline, 0.3) + + Behavior on color { CAnim {} } + Behavior on border.color { CAnim {} } + + MouseArea { + id: dragThresholdInputHover + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.IBeamCursor + acceptedButtons: Qt.NoButton + } + + StyledTextField { + id: dragThresholdInput + anchors.centerIn: parent + width: parent.width - Appearance.padding.normal + horizontalAlignment: TextInput.AlignHCenter + validator: IntValidator { bottom: 0; top: 100 } + + Component.onCompleted: { + text = root.dragThreshold.toString(); + } + + onTextChanged: { + if (activeFocus) { + const val = parseInt(text); + if (!isNaN(val) && val >= 0 && val <= 100) { + root.dragThreshold = val; + root.saveConfig(); + } + } + } + onEditingFinished: { + const val = parseInt(text); + if (isNaN(val) || val < 0 || val > 100) { + text = root.dragThreshold.toString(); + } + } + } + } + + StyledText { + text: "px" + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.normal + } + } + + StyledSlider { + id: dragThresholdSlider + + Layout.fillWidth: true + implicitHeight: Appearance.padding.normal * 3 + + from: 0 + to: 100 + value: root.dragThreshold + onMoved: { + root.dragThreshold = Math.round(dragThresholdSlider.value); + if (!dragThresholdInput.activeFocus) { + dragThresholdInput.text = Math.round(dragThresholdSlider.value).toString(); + } + root.saveConfig(); + } + } } } } } } + } } } -- cgit v1.2.3-freya From ba094963bc62a50e41b444217535218b320b83d0 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Mon, 17 Nov 2025 13:55:52 -0500 Subject: controlcenter: added slight background to expanded collapsiblesections on appearance --- components/controls/CollapsibleSection.qml | 16 ++++++++++++++++ modules/controlcenter/appearance/AppearancePane.qml | 12 ++++++++++++ 2 files changed, 28 insertions(+) (limited to 'components') diff --git a/components/controls/CollapsibleSection.qml b/components/controls/CollapsibleSection.qml index 35acdec..a1f038b 100644 --- a/components/controls/CollapsibleSection.qml +++ b/components/controls/CollapsibleSection.qml @@ -12,6 +12,7 @@ ColumnLayout { required property string title property string description: "" property bool expanded: false + property bool showBackground: false signal toggleRequested @@ -82,6 +83,21 @@ ColumnLayout { } } + StyledRect { + id: backgroundRect + anchors.fill: parent + radius: Appearance.rounding.normal + color: Colours.layer(Colours.palette.m3surfaceContainer, 2) + opacity: root.showBackground && root.expanded ? 1.0 : 0.0 + visible: root.showBackground + + Behavior on opacity { + Anim { + easing.bezierCurve: Appearance.anim.curves.standard + } + } + } + ColumnLayout { id: contentColumn anchors.left: parent.left diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index 71b4061..56332f1 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -179,6 +179,7 @@ RowLayout { id: themeModeSection title: qsTr("Theme mode") description: qsTr("Light or dark theme") + showBackground: true SwitchRow { label: qsTr("Dark mode") @@ -193,6 +194,7 @@ RowLayout { id: colorVariantSection title: qsTr("Color variant") description: qsTr("Material theme variant") + showBackground: true ColumnLayout { Layout.fillWidth: true @@ -274,6 +276,7 @@ RowLayout { id: colorSchemeSection title: qsTr("Color scheme") description: qsTr("Available color schemes") + showBackground: true ColumnLayout { Layout.fillWidth: true @@ -409,6 +412,7 @@ RowLayout { CollapsibleSection { id: animationsSection title: qsTr("Animations") + showBackground: true SectionContainer { contentSpacing: Appearance.spacing.normal @@ -513,11 +517,13 @@ RowLayout { CollapsibleSection { id: fontsSection title: qsTr("Fonts") + showBackground: true CollapsibleSection { id: materialFontSection title: qsTr("Material font family") expanded: true + showBackground: true Loader { Layout.fillWidth: true @@ -593,6 +599,7 @@ RowLayout { id: monoFontSection title: qsTr("Monospace font family") expanded: false + showBackground: true Loader { Layout.fillWidth: true @@ -668,6 +675,7 @@ RowLayout { id: sansFontSection title: qsTr("Sans-serif font family") expanded: false + showBackground: true Loader { Layout.fillWidth: true @@ -842,6 +850,7 @@ RowLayout { CollapsibleSection { id: scalesSection title: qsTr("Scales") + showBackground: true SectionContainer { contentSpacing: Appearance.spacing.normal @@ -1144,6 +1153,7 @@ RowLayout { CollapsibleSection { id: transparencySection title: qsTr("Transparency") + showBackground: true SwitchRow { label: qsTr("Transparency enabled") @@ -1356,6 +1366,7 @@ RowLayout { CollapsibleSection { id: borderSection title: qsTr("Border") + showBackground: true SectionContainer { contentSpacing: Appearance.spacing.normal @@ -1547,6 +1558,7 @@ RowLayout { CollapsibleSection { id: backgroundSection title: qsTr("Background") + showBackground: true SwitchRow { label: qsTr("Desktop clock") -- cgit v1.2.3-freya From 678f7e33437a39b9499033be1cefe5cd62375291 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Mon, 17 Nov 2025 14:16:05 -0500 Subject: controlcenter: inner scrollbar for larger sections such as fonts --- components/controls/StyledScrollBar.qml | 96 ++++++++++++++++++++-- .../controlcenter/appearance/AppearancePane.qml | 14 ++++ 2 files changed, 103 insertions(+), 7 deletions(-) (limited to 'components') diff --git a/components/controls/StyledScrollBar.qml b/components/controls/StyledScrollBar.qml index fc641b5..de8b679 100644 --- a/components/controls/StyledScrollBar.qml +++ b/components/controls/StyledScrollBar.qml @@ -19,14 +19,51 @@ ScrollBar { shouldBeActive = flickable.moving; } + property bool _updatingFromFlickable: false + property bool _updatingFromUser: false + + // Sync nonAnimPosition with Qt's automatic position binding onPositionChanged: { - if (position === nonAnimPosition) + if (_updatingFromUser) { + _updatingFromUser = false; + return; + } + if (position === nonAnimPosition) { animating = false; - else if (!animating) + return; + } + if (!animating && !_updatingFromFlickable && !fullMouse.pressed) { nonAnimPosition = position; + } } - position: nonAnimPosition + // Sync nonAnimPosition with flickable when not animating + Connections { + target: flickable + function onContentYChanged() { + if (!animating && !fullMouse.pressed) { + _updatingFromFlickable = true; + const contentHeight = flickable.contentHeight; + const height = flickable.height; + if (contentHeight > height) { + nonAnimPosition = Math.max(0, Math.min(1, flickable.contentY / (contentHeight - height))); + } else { + nonAnimPosition = 0; + } + _updatingFromFlickable = false; + } + } + } + + Component.onCompleted: { + if (flickable) { + const contentHeight = flickable.contentHeight; + const height = flickable.height; + if (contentHeight > height) { + nonAnimPosition = Math.max(0, Math.min(1, flickable.contentY / (contentHeight - height))); + } + } + } implicitWidth: Appearance.padding.small contentItem: StyledRect { @@ -86,17 +123,62 @@ ScrollBar { onPressed: event => { root.animating = true; - root.nonAnimPosition = Math.max(0, Math.min(1 - root.size, event.y / root.height - root.size / 2)); + root._updatingFromUser = true; + const newPos = Math.max(0, Math.min(1 - root.size, event.y / root.height - root.size / 2)); + root.nonAnimPosition = newPos; + // Update flickable position + // Map scrollbar position [0, 1-size] to contentY [0, maxContentY] + if (root.flickable) { + const contentHeight = root.flickable.contentHeight; + const height = root.flickable.height; + if (contentHeight > height) { + const maxContentY = contentHeight - height; + const maxPos = 1 - root.size; + const contentY = maxPos > 0 ? (newPos / maxPos) * maxContentY : 0; + root.flickable.contentY = Math.max(0, Math.min(maxContentY, contentY)); + } + } } - onPositionChanged: event => root.nonAnimPosition = Math.max(0, Math.min(1 - root.size, event.y / root.height - root.size / 2)) + onPositionChanged: event => { + root._updatingFromUser = true; + const newPos = Math.max(0, Math.min(1 - root.size, event.y / root.height - root.size / 2)); + root.nonAnimPosition = newPos; + // Update flickable position + // Map scrollbar position [0, 1-size] to contentY [0, maxContentY] + if (root.flickable) { + const contentHeight = root.flickable.contentHeight; + const height = root.flickable.height; + if (contentHeight > height) { + const maxContentY = contentHeight - height; + const maxPos = 1 - root.size; + const contentY = maxPos > 0 ? (newPos / maxPos) * maxContentY : 0; + root.flickable.contentY = Math.max(0, Math.min(maxContentY, contentY)); + } + } + } function onWheel(event: WheelEvent): void { root.animating = true; + root._updatingFromUser = true; + let newPos = root.nonAnimPosition; if (event.angleDelta.y > 0) - root.nonAnimPosition = Math.max(0, root.nonAnimPosition - 0.1); + newPos = Math.max(0, root.nonAnimPosition - 0.1); else if (event.angleDelta.y < 0) - root.nonAnimPosition = Math.min(1 - root.size, root.nonAnimPosition + 0.1); + newPos = Math.min(1 - root.size, root.nonAnimPosition + 0.1); + root.nonAnimPosition = newPos; + // Update flickable position + // Map scrollbar position [0, 1-size] to contentY [0, maxContentY] + if (root.flickable) { + const contentHeight = root.flickable.contentHeight; + const height = root.flickable.height; + if (contentHeight > height) { + const maxContentY = contentHeight - height; + const maxPos = 1 - root.size; + const contentY = maxPos > 0 ? (newPos / maxPos) * maxContentY : 0; + root.flickable.contentY = Math.max(0, Math.min(maxContentY, contentY)); + } + } } } diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index 56332f1..177e7b9 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -526,6 +526,7 @@ RowLayout { showBackground: true Loader { + id: materialFontLoader Layout.fillWidth: true Layout.preferredHeight: item ? Math.min(item.contentHeight, 300) : 0 asynchronous: true @@ -539,6 +540,10 @@ RowLayout { spacing: Appearance.spacing.small / 2 model: Qt.fontFamilies() + StyledScrollBar.vertical: StyledScrollBar { + flickable: materialFontList + } + delegate: StyledRect { required property string modelData required property int index @@ -592,6 +597,7 @@ RowLayout { implicitHeight: fontFamilyMaterialRow.implicitHeight + Appearance.padding.normal * 2 } } + } } @@ -615,6 +621,10 @@ RowLayout { spacing: Appearance.spacing.small / 2 model: Qt.fontFamilies() + StyledScrollBar.vertical: StyledScrollBar { + flickable: monoFontList + } + delegate: StyledRect { required property string modelData required property int index @@ -691,6 +701,10 @@ RowLayout { spacing: Appearance.spacing.small / 2 model: Qt.fontFamilies() + StyledScrollBar.vertical: StyledScrollBar { + flickable: sansFontList + } + delegate: StyledRect { required property string modelData required property int index -- cgit v1.2.3-freya From b54ce46b986b39450786f3aec69e2b9aca6cdc5d Mon Sep 17 00:00:00 2001 From: ATMDA Date: Mon, 17 Nov 2025 19:31:31 -0500 Subject: controlcenter: corrected spacing between collapsiblesection headers and content --- components/controls/CollapsibleSection.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'components') diff --git a/components/controls/CollapsibleSection.qml b/components/controls/CollapsibleSection.qml index a1f038b..078145b 100644 --- a/components/controls/CollapsibleSection.qml +++ b/components/controls/CollapsibleSection.qml @@ -16,7 +16,7 @@ ColumnLayout { signal toggleRequested - spacing: 0 + spacing: Appearance.spacing.small Layout.fillWidth: true Item { -- cgit v1.2.3-freya From 50e29f8be35cb773b5bf25a494eddfe665073583 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Wed, 19 Nov 2025 08:01:48 -0500 Subject: controlcenter: corrected nesting of bg when transparency off --- components/SectionContainer.qml | 4 +++- components/controls/CollapsibleSection.qml | 5 ++++- modules/controlcenter/appearance/AppearancePane.qml | 3 +++ 3 files changed, 10 insertions(+), 2 deletions(-) (limited to 'components') diff --git a/components/SectionContainer.qml b/components/SectionContainer.qml index 60e3d59..f133e19 100644 --- a/components/SectionContainer.qml +++ b/components/SectionContainer.qml @@ -16,7 +16,9 @@ StyledRect { implicitHeight: contentColumn.implicitHeight + Appearance.padding.large * 2 radius: Appearance.rounding.normal - color: Colours.layer(Colours.palette.m3surfaceContainer, 2) + color: Colours.transparency.enabled + ? Colours.layer(Colours.palette.m3surfaceContainer, 2) + : Colours.palette.m3surfaceContainerHigh ColumnLayout { id: contentColumn diff --git a/components/controls/CollapsibleSection.qml b/components/controls/CollapsibleSection.qml index 078145b..8940884 100644 --- a/components/controls/CollapsibleSection.qml +++ b/components/controls/CollapsibleSection.qml @@ -13,6 +13,7 @@ ColumnLayout { property string description: "" property bool expanded: false property bool showBackground: false + property bool nested: false signal toggleRequested @@ -87,7 +88,9 @@ ColumnLayout { id: backgroundRect anchors.fill: parent radius: Appearance.rounding.normal - color: Colours.layer(Colours.palette.m3surfaceContainer, 2) + color: Colours.transparency.enabled + ? Colours.layer(Colours.palette.m3surfaceContainer, root.nested ? 3 : 2) + : (root.nested ? Colours.palette.m3surfaceContainerHigh : Colours.palette.m3surfaceContainer) opacity: root.showBackground && root.expanded ? 1.0 : 0.0 visible: root.showBackground diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index 891f64b..61cdcaa 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -524,6 +524,7 @@ RowLayout { title: qsTr("Material font family") expanded: true showBackground: true + nested: true Loader { id: materialFontLoader @@ -606,6 +607,7 @@ RowLayout { title: qsTr("Monospace font family") expanded: false showBackground: true + nested: true Loader { Layout.fillWidth: true @@ -686,6 +688,7 @@ RowLayout { title: qsTr("Sans-serif font family") expanded: false showBackground: true + nested: true Loader { Layout.fillWidth: true -- cgit v1.2.3-freya From ef46a02b2f3561574c1ada5afefbbb806bcb6a3b Mon Sep 17 00:00:00 2001 From: ATMDA Date: Wed, 19 Nov 2025 21:04:32 -0500 Subject: refactor: ToggleButton Tooltips and DeviceList --- components/controls/ToggleButton.qml | 51 +- components/controls/Tooltip.qml | 170 +++++ modules/controlcenter/bluetooth/BtPane.qml | 19 +- modules/controlcenter/bluetooth/Details.qml | 681 +++++++++++---------- modules/controlcenter/bluetooth/DeviceList.qml | 84 ++- modules/controlcenter/components/DeviceDetails.qml | 2 + modules/controlcenter/components/DeviceList.qml | 14 +- modules/controlcenter/launcher/LauncherPane.qml | 4 + modules/controlcenter/network/EthernetDetails.qml | 140 +++-- modules/controlcenter/network/EthernetList.qml | 6 +- modules/controlcenter/network/NetworkingPane.qml | 377 ++---------- modules/controlcenter/network/WirelessDetails.qml | 182 +++--- modules/controlcenter/network/WirelessList.qml | 13 +- 13 files changed, 865 insertions(+), 878 deletions(-) create mode 100644 components/controls/Tooltip.qml (limited to 'components') diff --git a/components/controls/ToggleButton.qml b/components/controls/ToggleButton.qml index 9d8e094..b2c2afe 100644 --- a/components/controls/ToggleButton.qml +++ b/components/controls/ToggleButton.qml @@ -1,5 +1,6 @@ import ".." import qs.components +import qs.components.controls import qs.components.effects import qs.services import qs.config @@ -13,12 +14,31 @@ StyledRect { property string icon property string label property string accent: "Secondary" + property real iconSize: Appearance.font.size.large + property real horizontalPadding: Appearance.padding.large + property real verticalPadding: Appearance.padding.normal + property string tooltip: "" + property bool hovered: false signal clicked + Component.onCompleted: { + hovered = toggleStateLayer.containsMouse; + } + + Connections { + target: toggleStateLayer + function onContainsMouseChanged() { + const newHovered = toggleStateLayer.containsMouse; + if (hovered !== newHovered) { + hovered = newHovered; + } + } + } + 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 + implicitWidth: toggleBtnInner.implicitWidth + horizontalPadding * 2 + implicitHeight: toggleBtnIcon.implicitHeight + verticalPadding * 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`] @@ -46,7 +66,7 @@ StyledRect { fill: root.toggled ? 1 : 0 text: root.icon color: root.toggled ? Colours.palette[`m3on${root.accent}`] : Colours.palette[`m3on${root.accent}Container`] - font.pointSize: Appearance.font.size.large + font.pointSize: root.iconSize Behavior on fill { Anim {} @@ -78,5 +98,30 @@ StyledRect { easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial } } + + // Tooltip - positioned absolutely, doesn't affect layout + Loader { + id: tooltipLoader + active: root.tooltip !== "" + asynchronous: true + z: 10000 + width: 0 + height: 0 + sourceComponent: Component { + Tooltip { + target: root + text: root.tooltip + } + } + // Completely remove from layout + Layout.fillWidth: false + Layout.fillHeight: false + Layout.preferredWidth: 0 + Layout.preferredHeight: 0 + Layout.maximumWidth: 0 + Layout.maximumHeight: 0 + Layout.minimumWidth: 0 + Layout.minimumHeight: 0 + } } diff --git a/components/controls/Tooltip.qml b/components/controls/Tooltip.qml new file mode 100644 index 0000000..bab8086 --- /dev/null +++ b/components/controls/Tooltip.qml @@ -0,0 +1,170 @@ +import ".." +import qs.components.effects +import qs.services +import qs.config +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +Popup { + id: root + + required property Item target + required property string text + property int delay: 500 + property int timeout: 0 + + property bool tooltipVisible: false + property Timer showTimer: Timer { + interval: root.delay + onTriggered: root.tooltipVisible = true + } + property Timer hideTimer: Timer { + interval: root.timeout + onTriggered: root.tooltipVisible = false + } + + // Popup properties - doesn't affect layout + parent: { + let p = target; + // Walk up to find the root Item (usually has anchors.fill: parent) + while (p && p.parent) { + const parentItem = p.parent; + // Check if this looks like a root pane Item + if (parentItem && parentItem.anchors && parentItem.anchors.fill !== undefined) { + return parentItem; + } + p = parentItem; + } + // Fallback + return target.parent?.parent?.parent ?? target.parent?.parent ?? target.parent ?? target; + } + + visible: tooltipVisible + modal: false + closePolicy: Popup.NoAutoClose + padding: 0 + margins: 0 + + // Update position when target moves or tooltip becomes visible + onTooltipVisibleChanged: { + if (tooltipVisible) { + Qt.callLater(updatePosition); + } + } + Connections { + target: root.target + function onXChanged() { if (root.tooltipVisible) root.updatePosition(); } + function onYChanged() { if (root.tooltipVisible) root.updatePosition(); } + function onWidthChanged() { if (root.tooltipVisible) root.updatePosition(); } + function onHeightChanged() { if (root.tooltipVisible) root.updatePosition(); } + } + + function updatePosition() { + if (!target || !parent) return; + + // Wait for tooltipRect to have its size calculated + Qt.callLater(() => { + if (!target || !parent || !tooltipRect) return; + + // Get target position in parent's coordinate system + const targetPos = target.mapToItem(parent, 0, 0); + const targetCenterX = targetPos.x + target.width / 2; + + // Get tooltip size (use width/height if available, otherwise implicit) + const tooltipWidth = tooltipRect.width > 0 ? tooltipRect.width : tooltipRect.implicitWidth; + const tooltipHeight = tooltipRect.height > 0 ? tooltipRect.height : tooltipRect.implicitHeight; + + // Center tooltip horizontally on target + let newX = targetCenterX - tooltipWidth / 2; + + // Position tooltip above target + let newY = targetPos.y - tooltipHeight - Appearance.spacing.small; + + // Keep within bounds + const padding = Appearance.padding.normal; + if (newX < padding) { + newX = padding; + } else if (newX + tooltipWidth > (parent.width - padding)) { + newX = parent.width - tooltipWidth - padding; + } + + // Update popup position + x = newX; + y = newY; + }); + } + + enter: Transition { + Anim { + property: "opacity" + from: 0 + to: 1 + duration: Appearance.anim.durations.expressiveFastSpatial + easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial + } + } + + exit: Transition { + Anim { + property: "opacity" + from: 1 + to: 0 + duration: Appearance.anim.durations.expressiveFastSpatial + easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial + } + } + + // Monitor hover state + Connections { + target: root.target + function onHoveredChanged() { + if (target.hovered) { + showTimer.start(); + if (timeout > 0) { + hideTimer.stop(); + hideTimer.start(); + } + } else { + showTimer.stop(); + hideTimer.stop(); + tooltipVisible = false; + } + } + } + + contentItem: StyledRect { + id: tooltipRect + + implicitWidth: tooltipText.implicitWidth + Appearance.padding.normal * 2 + implicitHeight: tooltipText.implicitHeight + Appearance.padding.smaller * 2 + + color: Colours.palette.m3surfaceContainerHighest + radius: Appearance.rounding.small + + // Add elevation for depth + Elevation { + anchors.fill: parent + radius: parent.radius + z: -1 + level: 3 + } + + StyledText { + id: tooltipText + + anchors.centerIn: parent + + text: root.text + color: Colours.palette.m3onSurface + font.pointSize: Appearance.font.size.small + } + } + + Component.onCompleted: { + if (tooltipVisible) { + updatePosition(); + } + } +} + diff --git a/modules/controlcenter/bluetooth/BtPane.qml b/modules/controlcenter/bluetooth/BtPane.qml index 6877801..a987e75 100644 --- a/modules/controlcenter/bluetooth/BtPane.qml +++ b/modules/controlcenter/bluetooth/BtPane.qml @@ -24,8 +24,23 @@ SplitPaneWithDetails { } leftContent: Component { - DeviceList { - session: root.session + StyledFlickable { + id: leftFlickable + + flickableDirection: Flickable.VerticalFlick + contentHeight: deviceList.height + + StyledScrollBar.vertical: StyledScrollBar { + flickable: leftFlickable + } + + DeviceList { + id: deviceList + + anchors.left: parent.left + anchors.right: parent.right + session: root.session + } } } diff --git a/modules/controlcenter/bluetooth/Details.qml b/modules/controlcenter/bluetooth/Details.qml index 5496966..b260458 100644 --- a/modules/controlcenter/bluetooth/Details.qml +++ b/modules/controlcenter/bluetooth/Details.qml @@ -20,404 +20,427 @@ StyledFlickable { readonly property BluetoothDevice device: session.bt.active flickableDirection: Flickable.VerticalFlick - contentHeight: layoutWrapper.height + contentHeight: detailsWrapper.height StyledScrollBar.vertical: StyledScrollBar { flickable: root } - Item { - id: layoutWrapper + Item { + id: detailsWrapper + + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + implicitHeight: details.implicitHeight + + DeviceDetails { + id: details anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top - implicitHeight: layout.height - - ColumnLayout { - id: layout - - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - spacing: Appearance.spacing.normal - - SettingsHeader { - icon: Icons.getBluetoothIcon(root.device?.icon ?? "") - title: root.device?.name ?? "" - } - - 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 + session: root.session + device: root.device - spacing: Appearance.spacing.larger - - Toggle { - label: qsTr("Connected") - checked: root.device?.connected ?? false - toggle.onToggled: root.device.connected = checked - } - - Toggle { - label: qsTr("Paired") - checked: root.device?.paired ?? false - toggle.onToggled: { - if (root.device.paired) - root.device.forget(); - else - root.device.pair(); - } - } - - Toggle { - label: qsTr("Blocked") - checked: root.device?.blocked ?? false - toggle.onToggled: root.device.blocked = checked - } + headerComponent: Component { + SettingsHeader { + icon: Icons.getBluetoothIcon(root.device?.icon ?? "") + title: root.device?.name ?? "" } } - StyledText { - Layout.topMargin: Appearance.spacing.large - text: qsTr("Device properties") - font.pointSize: Appearance.font.size.larger - font.weight: 500 - } - - StyledText { - text: qsTr("Additional settings") - 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 + sections: [ + Component { + ColumnLayout { + spacing: Appearance.spacing.normal - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.large + StyledText { + Layout.topMargin: Appearance.spacing.large + text: qsTr("Connection status") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } - spacing: Appearance.spacing.larger + StyledText { + text: qsTr("Connection settings for this device") + color: Colours.palette.m3outline + } - RowLayout { - Layout.fillWidth: true - spacing: Appearance.spacing.small + StyledRect { + Layout.fillWidth: true + implicitHeight: deviceStatus.implicitHeight + Appearance.padding.large * 2 - Item { - id: renameDevice + radius: Appearance.rounding.normal + color: Colours.tPalette.m3surfaceContainer - Layout.fillWidth: true - Layout.rightMargin: Appearance.spacing.small + ColumnLayout { + id: deviceStatus - implicitHeight: renameLabel.implicitHeight + deviceNameEdit.implicitHeight + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.large - states: State { - name: "editingDeviceName" - when: root.session.bt.editingDeviceName + spacing: Appearance.spacing.larger - AnchorChanges { - target: deviceNameEdit - anchors.top: renameDevice.top - } - PropertyChanges { - renameDevice.implicitHeight: deviceNameEdit.implicitHeight - renameLabel.opacity: 0 - deviceNameEdit.padding: Appearance.padding.normal + Toggle { + label: qsTr("Connected") + checked: root.device?.connected ?? false + toggle.onToggled: root.device.connected = checked } - } - transitions: Transition { - AnchorAnimation { - duration: Appearance.anim.durations.normal - easing.type: Easing.BezierSpline - easing.bezierCurve: Appearance.anim.curves.standard + Toggle { + label: qsTr("Paired") + checked: root.device?.paired ?? false + toggle.onToggled: { + if (root.device.paired) + root.device.forget(); + else + root.device.pair(); + } } - Anim { - properties: "implicitHeight,opacity,padding" + + Toggle { + label: qsTr("Blocked") + checked: root.device?.blocked ?? false + toggle.onToggled: root.device.blocked = checked } } + } + } + }, + Component { + ColumnLayout { + spacing: Appearance.spacing.normal + + StyledText { + Layout.topMargin: Appearance.spacing.large + text: qsTr("Device properties") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } - StyledText { - id: renameLabel + StyledText { + text: qsTr("Additional settings") + color: Colours.palette.m3outline + } - anchors.left: parent.left + StyledRect { + Layout.fillWidth: true + implicitHeight: deviceProps.implicitHeight + Appearance.padding.large * 2 - text: qsTr("Device name") - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.small - } + radius: Appearance.rounding.normal + color: Colours.tPalette.m3surfaceContainer - StyledTextField { - id: deviceNameEdit + ColumnLayout { + id: deviceProps anchors.left: parent.left anchors.right: parent.right - anchors.top: renameLabel.bottom - anchors.leftMargin: root.session.bt.editingDeviceName ? 0 : -Appearance.padding.normal - - text: root.device?.name ?? "" - readOnly: !root.session.bt.editingDeviceName - onAccepted: { - root.session.bt.editingDeviceName = false; - root.device.name = text; - } - - leftPadding: Appearance.padding.normal - rightPadding: Appearance.padding.normal - - background: StyledRect { - radius: Appearance.rounding.small - border.width: 2 - border.color: Colours.palette.m3primary - opacity: root.session.bt.editingDeviceName ? 1 : 0 + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.large + + spacing: Appearance.spacing.larger + + RowLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.small + + Item { + id: renameDevice + + Layout.fillWidth: true + Layout.rightMargin: Appearance.spacing.small + + implicitHeight: renameLabel.implicitHeight + deviceNameEdit.implicitHeight + + states: State { + name: "editingDeviceName" + when: root.session.bt.editingDeviceName + + AnchorChanges { + target: deviceNameEdit + anchors.top: renameDevice.top + } + PropertyChanges { + renameDevice.implicitHeight: deviceNameEdit.implicitHeight + renameLabel.opacity: 0 + deviceNameEdit.padding: Appearance.padding.normal + } + } + + transitions: Transition { + AnchorAnimation { + duration: Appearance.anim.durations.normal + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.standard + } + Anim { + properties: "implicitHeight,opacity,padding" + } + } + + StyledText { + id: renameLabel + + anchors.left: parent.left + + text: qsTr("Device name") + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.small + } + + StyledTextField { + id: deviceNameEdit + + anchors.left: parent.left + anchors.right: parent.right + anchors.top: renameLabel.bottom + anchors.leftMargin: root.session.bt.editingDeviceName ? 0 : -Appearance.padding.normal + + text: root.device?.name ?? "" + readOnly: !root.session.bt.editingDeviceName + onAccepted: { + root.session.bt.editingDeviceName = false; + root.device.name = text; + } + + leftPadding: Appearance.padding.normal + rightPadding: Appearance.padding.normal + + background: StyledRect { + radius: Appearance.rounding.small + border.width: 2 + border.color: Colours.palette.m3primary + opacity: root.session.bt.editingDeviceName ? 1 : 0 + + Behavior on border.color { + CAnim {} + } + + Behavior on opacity { + Anim {} + } + } + + Behavior on anchors.leftMargin { + Anim {} + } + } + } - Behavior on border.color { - CAnim {} + StyledRect { + implicitWidth: implicitHeight + implicitHeight: cancelEditIcon.implicitHeight + Appearance.padding.smaller * 2 + + radius: Appearance.rounding.small + color: Colours.palette.m3secondaryContainer + opacity: root.session.bt.editingDeviceName ? 1 : 0 + scale: root.session.bt.editingDeviceName ? 1 : 0.5 + + StateLayer { + color: Colours.palette.m3onSecondaryContainer + disabled: !root.session.bt.editingDeviceName + + function onClicked(): void { + root.session.bt.editingDeviceName = false; + deviceNameEdit.text = Qt.binding(() => root.device?.name ?? ""); + } + } + + MaterialIcon { + id: cancelEditIcon + + anchors.centerIn: parent + animate: true + text: "cancel" + color: Colours.palette.m3onSecondaryContainer + } + + Behavior on opacity { + Anim {} + } + + Behavior on scale { + Anim { + duration: Appearance.anim.durations.expressiveFastSpatial + easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial + } + } } - Behavior on opacity { - Anim {} + StyledRect { + implicitWidth: implicitHeight + implicitHeight: editIcon.implicitHeight + Appearance.padding.smaller * 2 + + radius: root.session.bt.editingDeviceName ? Appearance.rounding.small : implicitHeight / 2 * Math.min(1, Appearance.rounding.scale) + color: Qt.alpha(Colours.palette.m3primary, root.session.bt.editingDeviceName ? 1 : 0) + + StateLayer { + color: root.session.bt.editingDeviceName ? Colours.palette.m3onPrimary : Colours.palette.m3onSurface + + function onClicked(): void { + root.session.bt.editingDeviceName = !root.session.bt.editingDeviceName; + if (root.session.bt.editingDeviceName) + deviceNameEdit.forceActiveFocus(); + else + deviceNameEdit.accepted(); + } + } + + MaterialIcon { + id: editIcon + + anchors.centerIn: parent + animate: true + text: root.session.bt.editingDeviceName ? "check_circle" : "edit" + color: root.session.bt.editingDeviceName ? Colours.palette.m3onPrimary : Colours.palette.m3onSurface + } + + Behavior on radius { + Anim {} + } } } - Behavior on anchors.leftMargin { - Anim {} + Toggle { + label: qsTr("Trusted") + checked: root.device?.trusted ?? false + toggle.onToggled: root.device.trusted = checked } - } - } - - StyledRect { - implicitWidth: implicitHeight - implicitHeight: cancelEditIcon.implicitHeight + Appearance.padding.smaller * 2 - - radius: Appearance.rounding.small - color: Colours.palette.m3secondaryContainer - opacity: root.session.bt.editingDeviceName ? 1 : 0 - scale: root.session.bt.editingDeviceName ? 1 : 0.5 - - StateLayer { - color: Colours.palette.m3onSecondaryContainer - disabled: !root.session.bt.editingDeviceName - function onClicked(): void { - root.session.bt.editingDeviceName = false; - deviceNameEdit.text = Qt.binding(() => root.device?.name ?? ""); + Toggle { + label: qsTr("Wake allowed") + checked: root.device?.wakeAllowed ?? false + toggle.onToggled: root.device.wakeAllowed = checked } } + } + } + }, + Component { + ColumnLayout { + spacing: Appearance.spacing.normal + + StyledText { + Layout.topMargin: Appearance.spacing.large + text: qsTr("Device information") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } - MaterialIcon { - id: cancelEditIcon - - anchors.centerIn: parent - animate: true - text: "cancel" - color: Colours.palette.m3onSecondaryContainer - } - - Behavior on opacity { - Anim {} - } - - Behavior on scale { - Anim { - duration: Appearance.anim.durations.expressiveFastSpatial - easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial - } - } + StyledText { + text: qsTr("Information about this device") + color: Colours.palette.m3outline } StyledRect { - implicitWidth: implicitHeight - implicitHeight: editIcon.implicitHeight + Appearance.padding.smaller * 2 - - radius: root.session.bt.editingDeviceName ? Appearance.rounding.small : implicitHeight / 2 * Math.min(1, Appearance.rounding.scale) - color: Qt.alpha(Colours.palette.m3primary, root.session.bt.editingDeviceName ? 1 : 0) - - StateLayer { - color: root.session.bt.editingDeviceName ? Colours.palette.m3onPrimary : Colours.palette.m3onSurface - - function onClicked(): void { - root.session.bt.editingDeviceName = !root.session.bt.editingDeviceName; - if (root.session.bt.editingDeviceName) - deviceNameEdit.forceActiveFocus(); - else - deviceNameEdit.accepted(); - } - } - - MaterialIcon { - id: editIcon - - anchors.centerIn: parent - animate: true - text: root.session.bt.editingDeviceName ? "check_circle" : "edit" - color: root.session.bt.editingDeviceName ? Colours.palette.m3onPrimary : Colours.palette.m3onSurface - } - - Behavior on radius { - Anim {} - } - } - } + Layout.fillWidth: true + implicitHeight: deviceInfo.implicitHeight + Appearance.padding.large * 2 - Toggle { - label: qsTr("Trusted") - checked: root.device?.trusted ?? false - toggle.onToggled: root.device.trusted = checked - } + radius: Appearance.rounding.normal + color: Colours.tPalette.m3surfaceContainer - Toggle { - label: qsTr("Wake allowed") - checked: root.device?.wakeAllowed ?? false - toggle.onToggled: root.device.wakeAllowed = checked - } - } - } + ColumnLayout { + id: deviceInfo - StyledText { - Layout.topMargin: Appearance.spacing.large - text: qsTr("Device information") - font.pointSize: Appearance.font.size.larger - font.weight: 500 - } + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.large - StyledText { - text: qsTr("Information about this device") - color: Colours.palette.m3outline - } + spacing: Appearance.spacing.small / 2 - StyledRect { - Layout.fillWidth: true - implicitHeight: deviceInfo.implicitHeight + Appearance.padding.large * 2 + StyledText { + text: root.device?.batteryAvailable ? qsTr("Device battery (%1%)").arg(root.device.battery * 100) : qsTr("Battery unavailable") + } - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer + RowLayout { + Layout.topMargin: Appearance.spacing.small / 2 + Layout.fillWidth: true + Layout.preferredHeight: Appearance.padding.smaller + spacing: Appearance.spacing.small / 2 + + StyledRect { + Layout.fillHeight: true + implicitWidth: root.device?.batteryAvailable ? parent.width * root.device.battery : 0 + radius: Appearance.rounding.full + color: Colours.palette.m3primary + } - ColumnLayout { - id: deviceInfo + StyledRect { + Layout.fillWidth: true + Layout.fillHeight: true + radius: Appearance.rounding.full + color: Colours.palette.m3secondaryContainer + + StyledRect { + anchors.right: parent.right + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.margins: parent.height * 0.25 + + implicitWidth: height + radius: Appearance.rounding.full + color: Colours.palette.m3primary + } + } + } - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.large + StyledText { + Layout.topMargin: Appearance.spacing.normal + text: qsTr("Dbus path") + } - spacing: Appearance.spacing.small / 2 + StyledText { + text: root.device?.dbusPath ?? "" + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.small + } - StyledText { - text: root.device?.batteryAvailable ? qsTr("Device battery (%1%)").arg(root.device.battery * 100) : qsTr("Battery unavailable") - } + StyledText { + Layout.topMargin: Appearance.spacing.normal + text: qsTr("MAC address") + } - RowLayout { - Layout.topMargin: Appearance.spacing.small / 2 - Layout.fillWidth: true - Layout.preferredHeight: Appearance.padding.smaller - spacing: Appearance.spacing.small / 2 + StyledText { + text: root.device?.address ?? "" + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.small + } - StyledRect { - Layout.fillHeight: true - implicitWidth: root.device?.batteryAvailable ? parent.width * root.device.battery : 0 - radius: Appearance.rounding.full - color: Colours.palette.m3primary - } + StyledText { + Layout.topMargin: Appearance.spacing.normal + text: qsTr("Bonded") + } - StyledRect { - Layout.fillWidth: true - Layout.fillHeight: true - radius: Appearance.rounding.full - color: Colours.palette.m3secondaryContainer + StyledText { + text: root.device?.bonded ? qsTr("Yes") : qsTr("No") + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.small + } - StyledRect { - anchors.right: parent.right - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.margins: parent.height * 0.25 + StyledText { + Layout.topMargin: Appearance.spacing.normal + text: qsTr("System name") + } - implicitWidth: height - radius: Appearance.rounding.full - color: Colours.palette.m3primary + StyledText { + text: root.device?.deviceName ?? "" + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.small + } } } } - - StyledText { - Layout.topMargin: Appearance.spacing.normal - text: qsTr("Dbus path") - } - - StyledText { - text: root.device?.dbusPath ?? "" - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.small - } - - StyledText { - Layout.topMargin: Appearance.spacing.normal - text: qsTr("MAC address") - } - - StyledText { - text: root.device?.address ?? "" - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.small - } - - StyledText { - Layout.topMargin: Appearance.spacing.normal - text: qsTr("Bonded") - } - - StyledText { - text: root.device?.bonded ? qsTr("Yes") : qsTr("No") - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.small - } - - StyledText { - Layout.topMargin: Appearance.spacing.normal - text: qsTr("System name") - } - - StyledText { - text: root.device?.deviceName ?? "" - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.small - } } - } - } + ] } + } + // FAB Menu (positioned absolutely relative to flickable) ColumnLayout { anchors.right: fabRoot.right anchors.bottom: fabRoot.top diff --git a/modules/controlcenter/bluetooth/DeviceList.qml b/modules/controlcenter/bluetooth/DeviceList.qml index b3db236..b978a2d 100644 --- a/modules/controlcenter/bluetooth/DeviceList.qml +++ b/modules/controlcenter/bluetooth/DeviceList.qml @@ -48,8 +48,12 @@ DeviceList { toggled: Bluetooth.defaultAdapter?.enabled ?? false icon: "power" accent: "Tertiary" + iconSize: Appearance.font.size.normal + horizontalPadding: Appearance.padding.normal + verticalPadding: Appearance.padding.smaller + tooltip: qsTr("Toggle Bluetooth") - function onClicked(): void { + onClicked: { const adapter = Bluetooth.defaultAdapter; if (adapter) adapter.enabled = !adapter.enabled; @@ -60,8 +64,12 @@ DeviceList { toggled: Bluetooth.defaultAdapter?.discoverable ?? false icon: root.smallDiscoverable ? "group_search" : "" label: root.smallDiscoverable ? "" : qsTr("Discoverable") + iconSize: Appearance.font.size.normal + horizontalPadding: Appearance.padding.normal + verticalPadding: Appearance.padding.smaller + tooltip: qsTr("Make discoverable") - function onClicked(): void { + onClicked: { const adapter = Bluetooth.defaultAdapter; if (adapter) adapter.discoverable = !adapter.discoverable; @@ -72,20 +80,44 @@ DeviceList { toggled: Bluetooth.defaultAdapter?.pairable ?? false icon: "missing_controller" label: root.smallPairable ? "" : qsTr("Pairable") + iconSize: Appearance.font.size.normal + horizontalPadding: Appearance.padding.normal + verticalPadding: Appearance.padding.smaller + tooltip: qsTr("Make pairable") - function onClicked(): void { + onClicked: { const adapter = Bluetooth.defaultAdapter; if (adapter) adapter.pairable = !adapter.pairable; } } + ToggleButton { + toggled: Bluetooth.defaultAdapter?.discovering ?? false + icon: "bluetooth_searching" + accent: "Secondary" + iconSize: Appearance.font.size.normal + horizontalPadding: Appearance.padding.normal + verticalPadding: Appearance.padding.smaller + tooltip: qsTr("Scan for devices") + + onClicked: { + const adapter = Bluetooth.defaultAdapter; + if (adapter) + adapter.discovering = !adapter.discovering; + } + } + ToggleButton { toggled: !root.session.bt.active icon: "settings" accent: "Primary" + iconSize: Appearance.font.size.normal + horizontalPadding: Appearance.padding.normal + verticalPadding: Appearance.padding.smaller + tooltip: qsTr("Bluetooth settings") - function onClicked(): void { + onClicked: { if (root.session.bt.active) root.session.bt.active = null; else { @@ -96,47 +128,6 @@ DeviceList { } } - titleSuffix: Component { - RowLayout { - spacing: Appearance.spacing.normal - - Item { - Layout.fillWidth: true - } - - StyledRect { - implicitWidth: implicitHeight - implicitHeight: scanIcon.implicitHeight + Appearance.padding.normal * 2 - - radius: Bluetooth.defaultAdapter?.discovering ? Appearance.rounding.normal : implicitHeight / 2 * Math.min(1, Appearance.rounding.scale) - color: Bluetooth.defaultAdapter?.discovering ? Colours.palette.m3secondary : Colours.palette.m3secondaryContainer - - StateLayer { - color: Bluetooth.defaultAdapter?.discovering ? Colours.palette.m3onSecondary : Colours.palette.m3onSecondaryContainer - - function onClicked(): void { - const adapter = Bluetooth.defaultAdapter; - if (adapter) - adapter.discovering = !adapter.discovering; - } - } - - MaterialIcon { - id: scanIcon - - anchors.centerIn: parent - animate: true - text: "bluetooth_searching" - color: Bluetooth.defaultAdapter?.discovering ? Colours.palette.m3onSecondary : Colours.palette.m3onSecondaryContainer - fill: Bluetooth.defaultAdapter?.discovering ? 1 : 0 - } - - Behavior on radius { - Anim {} - } - } - } - } delegate: Component { StyledRect { @@ -146,8 +137,7 @@ DeviceList { readonly property bool loading: modelData && (modelData.state === BluetoothDeviceState.Connecting || modelData.state === BluetoothDeviceState.Disconnecting) readonly property bool connected: modelData && modelData.state === BluetoothDeviceState.Connected - anchors.left: parent.left - anchors.right: parent.right + width: ListView.view ? ListView.view.width : undefined implicitHeight: deviceInner.implicitHeight + Appearance.padding.normal * 2 color: Qt.alpha(Colours.tPalette.m3surfaceContainer, root.activeItem === modelData ? Colours.tPalette.m3surfaceContainer.a : 0) diff --git a/modules/controlcenter/components/DeviceDetails.qml b/modules/controlcenter/components/DeviceDetails.qml index 256e689..eef0aae 100644 --- a/modules/controlcenter/components/DeviceDetails.qml +++ b/modules/controlcenter/components/DeviceDetails.qml @@ -90,6 +90,8 @@ Item { model: root.sections Loader { + required property Component modelData + Layout.fillWidth: true sourceComponent: modelData } diff --git a/modules/controlcenter/components/DeviceList.qml b/modules/controlcenter/components/DeviceList.qml index f8473ff..bf7126f 100644 --- a/modules/controlcenter/components/DeviceList.qml +++ b/modules/controlcenter/components/DeviceList.qml @@ -6,6 +6,7 @@ import qs.components.controls import qs.components.containers import qs.services import qs.config +import Quickshell import QtQuick import QtQuick.Layouts @@ -55,6 +56,7 @@ ColumnLayout { property var activeItem: null property Component headerComponent: null property Component titleSuffix: null + property bool showHeader: true signal itemSelected(var item) @@ -66,7 +68,7 @@ ColumnLayout { Layout.fillWidth: true sourceComponent: root.headerComponent - visible: root.headerComponent !== null + visible: root.headerComponent !== null && root.showHeader } // Title and description row @@ -109,17 +111,15 @@ ColumnLayout { id: view Layout.fillWidth: true - Layout.fillHeight: true + // Use contentHeight to show all items without estimation + implicitHeight: contentHeight model: root.model delegate: root.delegate spacing: Appearance.spacing.small / 2 - clip: true - - StyledScrollBar.vertical: StyledScrollBar { - flickable: view - } + interactive: false // Disable individual scrolling - parent pane handles it + clip: false // Don't clip - let parent handle scrolling } } diff --git a/modules/controlcenter/launcher/LauncherPane.qml b/modules/controlcenter/launcher/LauncherPane.qml index cf965e8..803d7e0 100644 --- a/modules/controlcenter/launcher/LauncherPane.qml +++ b/modules/controlcenter/launcher/LauncherPane.qml @@ -183,6 +183,10 @@ Item { toggled: !root.session.launcher.active icon: "settings" accent: "Primary" + iconSize: Appearance.font.size.normal + horizontalPadding: Appearance.padding.normal + verticalPadding: Appearance.padding.smaller + tooltip: qsTr("Launcher settings") onClicked: { if (root.session.launcher.active) { diff --git a/modules/controlcenter/network/EthernetDetails.qml b/modules/controlcenter/network/EthernetDetails.qml index 7c2534a..ad078ec 100644 --- a/modules/controlcenter/network/EthernetDetails.qml +++ b/modules/controlcenter/network/EthernetDetails.qml @@ -1,6 +1,7 @@ pragma ComponentBehavior: Bound import ".." +import "../components" import qs.components import qs.components.controls import qs.components.effects @@ -10,99 +11,108 @@ import qs.config import QtQuick import QtQuick.Layouts -Item { +DeviceDetails { id: root required property Session session - readonly property var device: session.ethernet.active + readonly property var ethernetDevice: session.ethernet.active - implicitWidth: layout.implicitWidth - implicitHeight: layout.implicitHeight + device: ethernetDevice Component.onCompleted: { - if (device && device.interface) { - Nmcli.getEthernetDeviceDetails(device.interface, () => {}); + if (ethernetDevice && ethernetDevice.interface) { + Nmcli.getEthernetDeviceDetails(ethernetDevice.interface, () => {}); } } - onDeviceChanged: { - if (device && device.interface) { - Nmcli.getEthernetDeviceDetails(device.interface, () => {}); + onEthernetDeviceChanged: { + if (ethernetDevice && ethernetDevice.interface) { + Nmcli.getEthernetDeviceDetails(ethernetDevice.interface, () => {}); } else { Nmcli.ethernetDeviceDetails = null; } } - ColumnLayout { - id: layout - - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - spacing: Appearance.spacing.normal + headerComponent: Component { + ConnectionHeader { + icon: "cable" + title: root.ethernetDevice?.interface ?? qsTr("Unknown") + } + } - ConnectionHeader { - icon: "cable" - title: root.device?.interface ?? qsTr("Unknown") - } + sections: [ + Component { + ColumnLayout { + spacing: Appearance.spacing.normal - SectionHeader { - title: qsTr("Connection status") - description: qsTr("Connection settings for this device") - } + SectionHeader { + title: qsTr("Connection status") + description: qsTr("Connection settings for this device") + } - SectionContainer { - ToggleRow { - label: qsTr("Connected") - checked: root.device?.connected ?? false - toggle.onToggled: { - if (checked) { - Nmcli.connectEthernet(root.device?.connection || "", root.device?.interface || "", () => {}); - } else { - if (root.device?.connection) { - Nmcli.disconnectEthernet(root.device.connection, () => {}); + SectionContainer { + ToggleRow { + label: qsTr("Connected") + checked: root.ethernetDevice?.connected ?? false + toggle.onToggled: { + if (checked) { + Nmcli.connectEthernet(root.ethernetDevice?.connection || "", root.ethernetDevice?.interface || "", () => {}); + } else { + if (root.ethernetDevice?.connection) { + Nmcli.disconnectEthernet(root.ethernetDevice.connection, () => {}); + } } } } } } + }, + Component { + ColumnLayout { + spacing: Appearance.spacing.normal + + SectionHeader { + title: qsTr("Device properties") + description: qsTr("Additional information") + } - SectionHeader { - title: qsTr("Device properties") - description: qsTr("Additional information") - } - - SectionContainer { - contentSpacing: Appearance.spacing.small / 2 + SectionContainer { + contentSpacing: Appearance.spacing.small / 2 - PropertyRow { - label: qsTr("Interface") - value: root.device?.interface ?? qsTr("Unknown") - } + PropertyRow { + label: qsTr("Interface") + value: root.ethernetDevice?.interface ?? qsTr("Unknown") + } - PropertyRow { - showTopMargin: true - label: qsTr("Connection") - value: root.device?.connection || qsTr("Not connected") - } + PropertyRow { + showTopMargin: true + label: qsTr("Connection") + value: root.ethernetDevice?.connection || qsTr("Not connected") + } - PropertyRow { - showTopMargin: true - label: qsTr("State") - value: root.device?.state ?? qsTr("Unknown") + PropertyRow { + showTopMargin: true + label: qsTr("State") + value: root.ethernetDevice?.state ?? qsTr("Unknown") + } } } + }, + Component { + ColumnLayout { + spacing: Appearance.spacing.normal + + SectionHeader { + title: qsTr("Connection information") + description: qsTr("Network connection details") + } - SectionHeader { - title: qsTr("Connection information") - description: qsTr("Network connection details") - } - - SectionContainer { - ConnectionInfoSection { - deviceDetails: Nmcli.ethernetDeviceDetails + SectionContainer { + ConnectionInfoSection { + deviceDetails: Nmcli.ethernetDeviceDetails + } } } - } - -} \ No newline at end of file + } + ] +} diff --git a/modules/controlcenter/network/EthernetList.qml b/modules/controlcenter/network/EthernetList.qml index 03bd37e..ea3ece5 100644 --- a/modules/controlcenter/network/EthernetList.qml +++ b/modules/controlcenter/network/EthernetList.qml @@ -39,6 +39,9 @@ DeviceList { toggled: !root.session.ethernet.active icon: "settings" accent: "Primary" + iconSize: Appearance.font.size.normal + horizontalPadding: Appearance.padding.normal + verticalPadding: Appearance.padding.smaller onClicked: { if (root.session.ethernet.active) @@ -55,8 +58,7 @@ DeviceList { StyledRect { required property var modelData - anchors.left: parent.left - anchors.right: parent.right + width: ListView.view ? ListView.view.width : undefined color: Qt.alpha(Colours.tPalette.m3surfaceContainer, root.activeItem === modelData ? Colours.tPalette.m3surfaceContainer.a : 0) radius: Appearance.rounding.normal diff --git a/modules/controlcenter/network/NetworkingPane.qml b/modules/controlcenter/network/NetworkingPane.qml index d76e8f5..e28d35c 100644 --- a/modules/controlcenter/network/NetworkingPane.qml +++ b/modules/controlcenter/network/NetworkingPane.qml @@ -28,23 +28,22 @@ Item { anchors.fill: parent leftContent: Component { + StyledFlickable { + id: leftFlickable - StyledFlickable { - id: leftFlickable + flickableDirection: Flickable.VerticalFlick + contentHeight: leftContent.height - flickableDirection: Flickable.VerticalFlick - contentHeight: leftContent.height - - StyledScrollBar.vertical: StyledScrollBar { - flickable: leftFlickable - } + StyledScrollBar.vertical: StyledScrollBar { + flickable: leftFlickable + } - ColumnLayout { - id: leftContent + ColumnLayout { + id: leftContent - anchors.left: parent.left - anchors.right: parent.right - spacing: Appearance.spacing.normal + anchors.left: parent.left + anchors.right: parent.right + spacing: Appearance.spacing.normal // Network header above the collapsible sections RowLayout { @@ -65,6 +64,10 @@ Item { toggled: Nmcli.wifiEnabled icon: "wifi" accent: "Tertiary" + iconSize: Appearance.font.size.normal + horizontalPadding: Appearance.padding.normal + verticalPadding: Appearance.padding.smaller + tooltip: qsTr("Toggle WiFi") onClicked: { Nmcli.toggleWifi(null); @@ -75,6 +78,10 @@ Item { toggled: Nmcli.scanning icon: "wifi_find" accent: "Secondary" + iconSize: Appearance.font.size.normal + horizontalPadding: Appearance.padding.normal + verticalPadding: Appearance.padding.smaller + tooltip: qsTr("Scan for networks") onClicked: { Nmcli.rescanWifi(); @@ -85,6 +92,10 @@ Item { toggled: !root.session.ethernet.active && !root.session.network.active icon: "settings" accent: "Primary" + iconSize: Appearance.font.size.normal + horizontalPadding: Appearance.padding.normal + verticalPadding: Appearance.padding.smaller + tooltip: qsTr("Network settings") onClicked: { if (root.session.ethernet.active || root.session.network.active) { @@ -109,127 +120,12 @@ Item { title: qsTr("Ethernet") expanded: true - ColumnLayout { + Loader { 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.normal - font.weight: 500 - } - } - - StyledText { - Layout.fillWidth: true - text: qsTr("All available ethernet devices") - color: Colours.palette.m3outline - } - - Repeater { - id: ethernetRepeater - - Layout.fillWidth: true - model: Nmcli.ethernetDevices - - delegate: StyledRect { - required property var modelData - - Layout.fillWidth: true - - color: Qt.alpha(Colours.tPalette.m3surfaceContainer, root.session.ethernet.active === modelData ? Colours.tPalette.m3surfaceContainer.a : 0) - radius: Appearance.rounding.normal - - StateLayer { - function onClicked(): void { - root.session.network.active = null; - 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 - } - } - - ColumnLayout { - Layout.fillWidth: true - - spacing: 0 - - StyledText { - Layout.fillWidth: true - elide: Text.ElideRight - maximumLineCount: 1 - - text: modelData.interface || qsTr("Unknown") - } - - StyledText { - Layout.fillWidth: true - 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 - elide: Text.ElideRight - } - } - - 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 + sourceComponent: Component { + EthernetList { + session: root.session + showHeader: false } } } @@ -242,195 +138,12 @@ Item { title: qsTr("Wireless") expanded: true - ColumnLayout { + Loader { 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.normal - 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 - } - - Repeater { - id: wirelessRepeater - - Layout.fillWidth: true - 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; - }) - } - - delegate: StyledRect { - required property var modelData - - Layout.fillWidth: true - - color: Qt.alpha(Colours.tPalette.m3surfaceContainer, (modelData && root.session.network.active === modelData) ? Colours.tPalette.m3surfaceContainer.a : 0) - radius: Appearance.rounding.normal - - StateLayer { - function onClicked(): void { - if (!modelData) { - return; - } - root.session.ethernet.active = null; - root.session.network.active = modelData; - // Check if we need to refresh saved connections when selecting a network - if (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 && modelData.active) ? Colours.palette.m3primaryContainer : Colours.tPalette.m3surfaceContainerHigh - - MaterialIcon { - id: wirelessIcon - - anchors.centerIn: parent - text: Icons.getNetworkIcon(modelData && modelData.strength !== undefined ? modelData.strength : 0) - font.pointSize: Appearance.font.size.large - fill: (modelData && modelData.active) ? 1 : 0 - color: (modelData && modelData.active) ? Colours.palette.m3onPrimaryContainer : Colours.palette.m3onSurface - } - - StyledRect { - id: lockBadge - - visible: modelData && modelData.isSecure - anchors.right: parent.right - anchors.bottom: parent.bottom - anchors.margins: -Appearance.padding.smaller / 2 - - implicitWidth: lockIconSize + Appearance.padding.smaller - implicitHeight: lockIconSize + Appearance.padding.smaller - radius: Appearance.rounding.full - color: Colours.palette.m3secondaryContainer - - readonly property real lockIconSize: lockIcon.implicitWidth - - Elevation { - anchors.fill: parent - radius: parent.radius - z: -1 - level: 2 - } - - MaterialIcon { - id: lockIcon - - anchors.centerIn: parent - text: "lock" - font.pointSize: Appearance.font.size.small - fill: 1 - color: Colours.palette.m3onSurface - } - } - } - - ColumnLayout { - Layout.fillWidth: true - - spacing: 0 - - StyledText { - Layout.fillWidth: true - elide: Text.ElideRight - maximumLineCount: 1 - - text: (modelData && modelData.ssid) ? modelData.ssid : qsTr("Unknown") - } - - RowLayout { - Layout.fillWidth: true - spacing: Appearance.spacing.smaller - - StyledText { - Layout.fillWidth: true - text: { - if (!modelData) return qsTr("Open"); - if (modelData.active) return qsTr("Connected"); - if (modelData.isSecure && modelData.security && modelData.security.length > 0) { - return modelData.security; - } - if (modelData.isSecure) return qsTr("Secured"); - return qsTr("Open"); - } - color: (modelData && modelData.active) ? Colours.palette.m3primary : Colours.palette.m3outline - font.pointSize: Appearance.font.size.small - font.weight: (modelData && modelData.active) ? 500 : 400 - elide: Text.ElideRight - } - } - } - - StyledRect { - implicitWidth: implicitHeight - implicitHeight: wirelessConnectIcon.implicitHeight + Appearance.padding.smaller * 2 - - radius: Appearance.rounding.full - color: Qt.alpha(Colours.palette.m3primaryContainer, (modelData && modelData.active) ? 1 : 0) - - StateLayer { - function onClicked(): void { - if (modelData && modelData.active) { - Nmcli.disconnectFromNetwork(); - } else if (modelData) { - NetworkConnection.handleConnect(modelData, root.session, null); - } - } - } - - MaterialIcon { - id: wirelessConnectIcon - - anchors.centerIn: parent - text: (modelData && modelData.active) ? "link_off" : "link" - color: (modelData && modelData.active) ? Colours.palette.m3onPrimaryContainer : Colours.palette.m3onSurface - } - } - } - - implicitHeight: wirelessRowLayout.implicitHeight + Appearance.padding.normal * 2 + sourceComponent: Component { + WirelessList { + session: root.session + showHeader: false } } } @@ -443,16 +156,17 @@ Item { Item { id: rightPaneItem - // Right pane - networking details/settings property var ethernetPane: root.session.ethernet.active property var wirelessPane: root.session.network.active property var pane: ethernetPane || wirelessPane property string paneId: ethernetPane ? ("eth:" + (ethernetPane.interface || "")) : (wirelessPane ? ("wifi:" + (wirelessPane.ssid || wirelessPane.bssid || "")) : "settings") - property Component targetComponent: settings - property Component nextComponent: settings + property Component targetComponent: settingsComponent + property Component nextComponent: settingsComponent function getComponentForPane() { - return pane ? (ethernetPane ? ethernetDetails : wirelessDetails) : settings; + if (ethernetPane) return ethernetDetailsComponent; + if (wirelessPane) return wirelessDetailsComponent; + return settingsComponent; } Component.onCompleted: { @@ -507,7 +221,7 @@ Item { } Component { - id: settings + id: settingsComponent StyledFlickable { id: settingsFlickable @@ -530,7 +244,7 @@ Item { } Component { - id: ethernetDetails + id: ethernetDetailsComponent StyledFlickable { id: ethernetFlickable @@ -553,7 +267,7 @@ Item { } Component { - id: wirelessDetails + id: wirelessDetailsComponent StyledFlickable { id: wirelessFlickable @@ -580,11 +294,4 @@ Item { session: root.session z: 1000 } - - function checkSavedProfileForNetwork(ssid: string): void { - if (ssid && ssid.length > 0) { - Nmcli.loadSavedConnections(() => {}); - } - } } - diff --git a/modules/controlcenter/network/WirelessDetails.qml b/modules/controlcenter/network/WirelessDetails.qml index 57c06c8..7f6a4aa 100644 --- a/modules/controlcenter/network/WirelessDetails.qml +++ b/modules/controlcenter/network/WirelessDetails.qml @@ -1,6 +1,7 @@ pragma ComponentBehavior: Bound import ".." +import "../components" import "." import qs.components import qs.components.controls @@ -12,14 +13,13 @@ import qs.utils import QtQuick import QtQuick.Layouts -Item { +DeviceDetails { id: root required property Session session readonly property var network: session.network.active - implicitWidth: layout.implicitWidth - implicitHeight: layout.implicitHeight + device: network Component.onCompleted: { updateDeviceDetails(); @@ -102,110 +102,120 @@ Item { } } - ColumnLayout { - id: layout - - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - spacing: Appearance.spacing.normal - - ConnectionHeader { - icon: root.network?.isSecure ? "lock" : "wifi" - title: root.network?.ssid ?? qsTr("Unknown") - } + headerComponent: Component { + ConnectionHeader { + icon: root.network?.isSecure ? "lock" : "wifi" + title: root.network?.ssid ?? qsTr("Unknown") + } + } - SectionHeader { - title: qsTr("Connection status") - description: qsTr("Connection settings for this network") - } + sections: [ + Component { + ColumnLayout { + spacing: Appearance.spacing.normal - SectionContainer { - ToggleRow { - label: qsTr("Connected") - checked: root.network?.active ?? false - toggle.onToggled: { - if (checked) { - NetworkConnection.handleConnect(root.network, root.session, null); - } else { - Nmcli.disconnectFromNetwork(); - } - } + SectionHeader { + title: qsTr("Connection status") + description: qsTr("Connection settings for this network") } - TextButton { - Layout.fillWidth: true - Layout.topMargin: Appearance.spacing.normal - Layout.minimumHeight: Appearance.font.size.normal + Appearance.padding.normal * 2 - visible: { - if (!root.network || !root.network.ssid) { - return false; + SectionContainer { + ToggleRow { + label: qsTr("Connected") + checked: root.network?.active ?? false + toggle.onToggled: { + if (checked) { + NetworkConnection.handleConnect(root.network, root.session, null); + } else { + Nmcli.disconnectFromNetwork(); + } } - return Nmcli.hasSavedProfile(root.network.ssid); } - inactiveColour: Colours.palette.m3secondaryContainer - inactiveOnColour: Colours.palette.m3onSecondaryContainer - text: qsTr("Forget Network") - onClicked: { - if (root.network && root.network.ssid) { - if (root.network.active) { - Nmcli.disconnectFromNetwork(); + TextButton { + Layout.fillWidth: true + Layout.topMargin: Appearance.spacing.normal + Layout.minimumHeight: Appearance.font.size.normal + Appearance.padding.normal * 2 + visible: { + if (!root.network || !root.network.ssid) { + return false; + } + return Nmcli.hasSavedProfile(root.network.ssid); + } + inactiveColour: Colours.palette.m3secondaryContainer + inactiveOnColour: Colours.palette.m3onSecondaryContainer + text: qsTr("Forget Network") + + onClicked: { + if (root.network && root.network.ssid) { + if (root.network.active) { + Nmcli.disconnectFromNetwork(); + } + Nmcli.forgetNetwork(root.network.ssid); } - Nmcli.forgetNetwork(root.network.ssid); } } } } + }, + Component { + ColumnLayout { + spacing: Appearance.spacing.normal + + SectionHeader { + title: qsTr("Network properties") + description: qsTr("Additional information") + } - SectionHeader { - title: qsTr("Network properties") - description: qsTr("Additional information") - } - - SectionContainer { - contentSpacing: Appearance.spacing.small / 2 + SectionContainer { + contentSpacing: Appearance.spacing.small / 2 - PropertyRow { - label: qsTr("SSID") - value: root.network?.ssid ?? qsTr("Unknown") - } + PropertyRow { + label: qsTr("SSID") + value: root.network?.ssid ?? qsTr("Unknown") + } - PropertyRow { - showTopMargin: true - label: qsTr("BSSID") - value: root.network?.bssid ?? qsTr("Unknown") - } + PropertyRow { + showTopMargin: true + label: qsTr("BSSID") + value: root.network?.bssid ?? qsTr("Unknown") + } - PropertyRow { - showTopMargin: true - label: qsTr("Signal strength") - value: root.network ? qsTr("%1%").arg(root.network.strength) : qsTr("N/A") - } + PropertyRow { + showTopMargin: true + label: qsTr("Signal strength") + value: root.network ? qsTr("%1%").arg(root.network.strength) : qsTr("N/A") + } - PropertyRow { - showTopMargin: true - label: qsTr("Frequency") - value: root.network ? qsTr("%1 MHz").arg(root.network.frequency) : qsTr("N/A") - } + PropertyRow { + showTopMargin: true + label: qsTr("Frequency") + value: root.network ? qsTr("%1 MHz").arg(root.network.frequency) : qsTr("N/A") + } - PropertyRow { - showTopMargin: true - label: qsTr("Security") - value: root.network ? (root.network.isSecure ? root.network.security : qsTr("Open")) : qsTr("N/A") + PropertyRow { + showTopMargin: true + label: qsTr("Security") + value: root.network ? (root.network.isSecure ? root.network.security : qsTr("Open")) : qsTr("N/A") + } } } + }, + Component { + ColumnLayout { + spacing: Appearance.spacing.normal + + SectionHeader { + title: qsTr("Connection information") + description: qsTr("Network connection details") + } - SectionHeader { - title: qsTr("Connection information") - description: qsTr("Network connection details") - } - - SectionContainer { - ConnectionInfoSection { - deviceDetails: Nmcli.wirelessDeviceDetails + SectionContainer { + ConnectionInfoSection { + deviceDetails: Nmcli.wirelessDeviceDetails + } } } - } - + } + ] } diff --git a/modules/controlcenter/network/WirelessList.qml b/modules/controlcenter/network/WirelessList.qml index 2f0288f..4726712 100644 --- a/modules/controlcenter/network/WirelessList.qml +++ b/modules/controlcenter/network/WirelessList.qml @@ -9,6 +9,7 @@ import qs.components.containers import qs.services import qs.config import qs.utils +import Quickshell import QtQuick import QtQuick.Layouts @@ -58,6 +59,9 @@ DeviceList { toggled: Nmcli.wifiEnabled icon: "wifi" accent: "Tertiary" + iconSize: Appearance.font.size.normal + horizontalPadding: Appearance.padding.normal + verticalPadding: Appearance.padding.smaller onClicked: { Nmcli.toggleWifi(null); @@ -68,6 +72,9 @@ DeviceList { toggled: Nmcli.scanning icon: "wifi_find" accent: "Secondary" + iconSize: Appearance.font.size.normal + horizontalPadding: Appearance.padding.normal + verticalPadding: Appearance.padding.smaller onClicked: { Nmcli.rescanWifi(); @@ -78,6 +85,9 @@ DeviceList { toggled: !root.session.network.active icon: "settings" accent: "Primary" + iconSize: Appearance.font.size.normal + horizontalPadding: Appearance.padding.normal + verticalPadding: Appearance.padding.smaller onClicked: { if (root.session.network.active) @@ -94,8 +104,7 @@ DeviceList { StyledRect { required property var modelData - anchors.left: parent.left - anchors.right: parent.right + width: ListView.view ? ListView.view.width : undefined color: Qt.alpha(Colours.tPalette.m3surfaceContainer, root.activeItem === modelData ? Colours.tPalette.m3surfaceContainer.a : 0) radius: Appearance.rounding.normal -- cgit v1.2.3-freya From a2b00d4a0ea90b84e60159140cb56355cddc3058 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Thu, 20 Nov 2025 13:30:38 -0500 Subject: controlcenter: corrected background transparency on tooltips --- components/controls/Tooltip.qml | 2 ++ 1 file changed, 2 insertions(+) (limited to 'components') diff --git a/components/controls/Tooltip.qml b/components/controls/Tooltip.qml index bab8086..d665083 100644 --- a/components/controls/Tooltip.qml +++ b/components/controls/Tooltip.qml @@ -45,6 +45,7 @@ Popup { closePolicy: Popup.NoAutoClose padding: 0 margins: 0 + background: Item {} // Update position when target moves or tooltip becomes visible onTooltipVisibleChanged: { @@ -141,6 +142,7 @@ Popup { color: Colours.palette.m3surfaceContainerHighest radius: Appearance.rounding.small + antialiasing: true // Add elevation for depth Elevation { -- cgit v1.2.3-freya From 7779de55bbcc87ad4af7bcc4b0f4da6e0fe65847 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Thu, 20 Nov 2025 21:58:55 -0500 Subject: controlcenter: refactor SliderInput and StyledInputFields to use qt components and consolidated SliderInputs to single component --- components/controls/StyledInputField.qml | 80 +++++++++ modules/controlcenter/audio/AudioPane.qml | 158 ++++++------------ modules/controlcenter/components/SliderInput.qml | 106 +++++------- .../components/controls/SliderInput.qml | 179 --------------------- 4 files changed, 172 insertions(+), 351 deletions(-) create mode 100644 components/controls/StyledInputField.qml delete mode 100644 modules/controlcenter/components/controls/SliderInput.qml (limited to 'components') diff --git a/components/controls/StyledInputField.qml b/components/controls/StyledInputField.qml new file mode 100644 index 0000000..fcd0a33 --- /dev/null +++ b/components/controls/StyledInputField.qml @@ -0,0 +1,80 @@ +pragma ComponentBehavior: Bound + +import ".." +import qs.components +import qs.services +import qs.config +import QtQuick + +Item { + id: root + + property string text: "" + property var validator: null + property bool readOnly: false + property int horizontalAlignment: TextInput.AlignHCenter + property int implicitWidth: 70 + property bool enabled: true + + // Expose activeFocus through alias to avoid FINAL property override + readonly property alias hasFocus: inputField.activeFocus + + signal textEdited(string text) + signal editingFinished() + + implicitHeight: inputField.implicitHeight + Appearance.padding.small * 2 + + StyledRect { + id: container + + anchors.fill: parent + color: inputHover.containsMouse || inputField.activeFocus + ? Colours.layer(Colours.palette.m3surfaceContainer, 3) + : Colours.layer(Colours.palette.m3surfaceContainer, 2) + radius: Appearance.rounding.small + border.width: 1 + border.color: inputField.activeFocus + ? Colours.palette.m3primary + : Qt.alpha(Colours.palette.m3outline, 0.3) + opacity: root.enabled ? 1 : 0.5 + + Behavior on color { CAnim {} } + Behavior on border.color { CAnim {} } + + MouseArea { + id: inputHover + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.IBeamCursor + acceptedButtons: Qt.NoButton + enabled: root.enabled + } + + StyledTextField { + id: inputField + anchors.centerIn: parent + width: parent.width - Appearance.padding.normal + horizontalAlignment: root.horizontalAlignment + validator: root.validator + readOnly: root.readOnly + enabled: root.enabled + + Binding { + target: inputField + property: "text" + value: root.text + when: !inputField.activeFocus + } + + onTextChanged: { + root.text = text; + root.textEdited(text); + } + + onEditingFinished: { + root.editingFinished(); + } + } + } +} + diff --git a/modules/controlcenter/audio/AudioPane.qml b/modules/controlcenter/audio/AudioPane.qml index 694e178..76122f9 100644 --- a/modules/controlcenter/audio/AudioPane.qml +++ b/modules/controlcenter/audio/AudioPane.qml @@ -263,67 +263,40 @@ Item { Layout.fillWidth: true } - StyledRect { + StyledInputField { + id: outputVolumeInput Layout.preferredWidth: 70 - implicitHeight: outputVolumeInput.implicitHeight + Appearance.padding.small * 2 - color: outputVolumeInputHover.containsMouse || outputVolumeInput.activeFocus - ? Colours.layer(Colours.palette.m3surfaceContainer, 3) - : Colours.layer(Colours.palette.m3surfaceContainer, 2) - radius: Appearance.rounding.small - border.width: 1 - border.color: outputVolumeInput.activeFocus - ? Colours.palette.m3primary - : Qt.alpha(Colours.palette.m3outline, 0.3) + validator: IntValidator { bottom: 0; top: 100 } enabled: !Audio.muted - opacity: enabled ? 1 : 0.5 - - Behavior on color { CAnim {} } - Behavior on border.color { CAnim {} } - - MouseArea { - id: outputVolumeInputHover - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.IBeamCursor - acceptedButtons: Qt.NoButton + + Component.onCompleted: { + text = Math.round(Audio.volume * 100).toString(); } - - StyledTextField { - id: outputVolumeInput - anchors.centerIn: parent - width: parent.width - Appearance.padding.normal - horizontalAlignment: TextInput.AlignHCenter - validator: IntValidator { bottom: 0; top: 100 } - enabled: !Audio.muted - - Component.onCompleted: { - text = Math.round(Audio.volume * 100).toString(); - } - - Connections { - target: Audio - function onVolumeChanged() { - if (!outputVolumeInput.activeFocus) { - outputVolumeInput.text = Math.round(Audio.volume * 100).toString(); - } + + Connections { + target: Audio + function onVolumeChanged() { + if (!outputVolumeInput.hasFocus) { + outputVolumeInput.text = Math.round(Audio.volume * 100).toString(); } } - - onTextChanged: { - if (activeFocus) { - const val = parseInt(text); - if (!isNaN(val) && val >= 0 && val <= 100) { - Audio.setVolume(val / 100); - } - } - } - onEditingFinished: { + } + + onTextEdited: (text) => { + if (hasFocus) { const val = parseInt(text); - if (isNaN(val) || val < 0 || val > 100) { - text = Math.round(Audio.volume * 100).toString(); + if (!isNaN(val) && val >= 0 && val <= 100) { + Audio.setVolume(val / 100); } } } + + onEditingFinished: { + const val = parseInt(text); + if (isNaN(val) || val < 0 || val > 100) { + text = Math.round(Audio.volume * 100).toString(); + } + } } StyledText { @@ -368,7 +341,7 @@ Item { opacity: enabled ? 1 : 0.5 onMoved: { Audio.setVolume(value); - if (!outputVolumeInput.activeFocus) { + if (!outputVolumeInput.hasFocus) { outputVolumeInput.text = Math.round(value * 100).toString(); } } @@ -402,67 +375,40 @@ Item { Layout.fillWidth: true } - StyledRect { + StyledInputField { + id: inputVolumeInput Layout.preferredWidth: 70 - implicitHeight: inputVolumeInput.implicitHeight + Appearance.padding.small * 2 - color: inputVolumeInputHover.containsMouse || inputVolumeInput.activeFocus - ? Colours.layer(Colours.palette.m3surfaceContainer, 3) - : Colours.layer(Colours.palette.m3surfaceContainer, 2) - radius: Appearance.rounding.small - border.width: 1 - border.color: inputVolumeInput.activeFocus - ? Colours.palette.m3primary - : Qt.alpha(Colours.palette.m3outline, 0.3) + validator: IntValidator { bottom: 0; top: 100 } enabled: !Audio.sourceMuted - opacity: enabled ? 1 : 0.5 - - Behavior on color { CAnim {} } - Behavior on border.color { CAnim {} } - - MouseArea { - id: inputVolumeInputHover - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.IBeamCursor - acceptedButtons: Qt.NoButton + + Component.onCompleted: { + text = Math.round(Audio.sourceVolume * 100).toString(); } - - StyledTextField { - id: inputVolumeInput - anchors.centerIn: parent - width: parent.width - Appearance.padding.normal - horizontalAlignment: TextInput.AlignHCenter - validator: IntValidator { bottom: 0; top: 100 } - enabled: !Audio.sourceMuted - - Component.onCompleted: { - text = Math.round(Audio.sourceVolume * 100).toString(); - } - - Connections { - target: Audio - function onSourceVolumeChanged() { - if (!inputVolumeInput.activeFocus) { - inputVolumeInput.text = Math.round(Audio.sourceVolume * 100).toString(); - } + + Connections { + target: Audio + function onSourceVolumeChanged() { + if (!inputVolumeInput.hasFocus) { + inputVolumeInput.text = Math.round(Audio.sourceVolume * 100).toString(); } } - - onTextChanged: { - if (activeFocus) { - const val = parseInt(text); - if (!isNaN(val) && val >= 0 && val <= 100) { - Audio.setSourceVolume(val / 100); - } - } - } - onEditingFinished: { + } + + onTextEdited: (text) => { + if (hasFocus) { const val = parseInt(text); - if (isNaN(val) || val < 0 || val > 100) { - text = Math.round(Audio.sourceVolume * 100).toString(); + if (!isNaN(val) && val >= 0 && val <= 100) { + Audio.setSourceVolume(val / 100); } } } + + onEditingFinished: { + const val = parseInt(text); + if (isNaN(val) || val < 0 || val > 100) { + text = Math.round(Audio.sourceVolume * 100).toString(); + } + } } StyledText { @@ -507,7 +453,7 @@ Item { opacity: enabled ? 1 : 0.5 onMoved: { Audio.setSourceVolume(value); - if (!inputVolumeInput.activeFocus) { + if (!inputVolumeInput.hasFocus) { inputVolumeInput.text = Math.round(value * 100).toString(); } } diff --git a/modules/controlcenter/components/SliderInput.qml b/modules/controlcenter/components/SliderInput.qml index 3d7cd4d..7348368 100644 --- a/modules/controlcenter/components/SliderInput.qml +++ b/modules/controlcenter/components/SliderInput.qml @@ -76,79 +76,53 @@ ColumnLayout { Layout.fillWidth: true } - StyledRect { + StyledInputField { + id: inputField Layout.preferredWidth: 70 - implicitHeight: inputField.implicitHeight + Appearance.padding.small * 2 - color: inputHover.containsMouse || inputField.activeFocus - ? Colours.layer(Colours.palette.m3surfaceContainer, 3) - : Colours.layer(Colours.palette.m3surfaceContainer, 2) - radius: Appearance.rounding.small - border.width: 1 - border.color: inputField.activeFocus - ? Colours.palette.m3primary - : Qt.alpha(Colours.palette.m3outline, 0.3) - - Behavior on color { CAnim {} } - Behavior on border.color { CAnim {} } - - MouseArea { - id: inputHover - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.IBeamCursor - acceptedButtons: Qt.NoButton + validator: root.validator + + Component.onCompleted: { + // Initialize text without triggering valueModified signal + text = root.formatValue(root.value); } - - StyledTextField { - id: inputField - anchors.centerIn: parent - width: parent.width - Appearance.padding.normal - horizontalAlignment: TextInput.AlignHCenter - validator: root.validator - - Component.onCompleted: { - // Initialize text without triggering valueModified signal - text = root.formatValue(root.value); - } - - onTextChanged: { - if (activeFocus) { - const val = root.parseValue(text); - if (!isNaN(val)) { - // Validate against validator bounds if available - let isValid = true; - if (root.validator) { - if (root.validator.bottom !== undefined && val < root.validator.bottom) { - isValid = false; - } - if (root.validator.top !== undefined && val > root.validator.top) { - isValid = false; - } + + onTextEdited: (text) => { + if (hasFocus) { + const val = root.parseValue(text); + if (!isNaN(val)) { + // Validate against validator bounds if available + let isValid = true; + if (root.validator) { + if (root.validator.bottom !== undefined && val < root.validator.bottom) { + isValid = false; } - - if (isValid) { - root.valueModified(val); + if (root.validator.top !== undefined && val > root.validator.top) { + isValid = false; } } + + if (isValid) { + root.valueModified(val); + } } } - - onEditingFinished: { - const val = root.parseValue(text); - let isValid = true; - if (root.validator) { - if (root.validator.bottom !== undefined && val < root.validator.bottom) { - isValid = false; - } - if (root.validator.top !== undefined && val > root.validator.top) { - isValid = false; - } + } + + onEditingFinished: { + const val = root.parseValue(text); + let isValid = true; + if (root.validator) { + if (root.validator.bottom !== undefined && val < root.validator.bottom) { + isValid = false; } - - if (isNaN(val) || !isValid) { - text = root.formatValue(root.value); + if (root.validator.top !== undefined && val > root.validator.top) { + isValid = false; } } + + if (isNaN(val) || !isValid) { + text = root.formatValue(root.value); + } } } @@ -181,7 +155,7 @@ ColumnLayout { onValueChanged: { // Update input field text in real-time as slider moves during dragging // Always update when slider value changes (during dragging or external updates) - if (!inputField.activeFocus) { + if (!inputField.hasFocus) { const newValue = root.stepSize > 0 ? Math.round(value / root.stepSize) * root.stepSize : value; inputField.text = root.formatValue(newValue); } @@ -190,7 +164,7 @@ ColumnLayout { onMoved: { const newValue = root.stepSize > 0 ? Math.round(value / root.stepSize) * root.stepSize : value; root.valueModified(newValue); - if (!inputField.activeFocus) { + if (!inputField.hasFocus) { inputField.text = root.formatValue(newValue); } } @@ -199,7 +173,7 @@ ColumnLayout { // Update input field when value changes externally (slider is already bound) onValueChanged: { // Only update if component is initialized to avoid issues during creation - if (root._initialized && !inputField.activeFocus) { + if (root._initialized && !inputField.hasFocus) { inputField.text = root.formatValue(root.value); } } diff --git a/modules/controlcenter/components/controls/SliderInput.qml b/modules/controlcenter/components/controls/SliderInput.qml deleted file mode 100644 index a114f7f..0000000 --- a/modules/controlcenter/components/controls/SliderInput.qml +++ /dev/null @@ -1,179 +0,0 @@ -pragma ComponentBehavior: Bound - -import ".." -import qs.components -import qs.components.controls -import qs.components.effects -import QtQuick -import QtQuick.Layouts - -ColumnLayout { - id: root - - property string label: "" - property real value: 0 - property real from: 0 - property real to: 100 - property real stepSize: 0 - property var validator: null - property string suffix: "" // Optional suffix text (e.g., "×", "px") - property var formatValueFunction: null // Optional custom format function - property var parseValueFunction: null // Optional custom parse function - - function formatValue(val: real): string { - if (formatValueFunction) { - return formatValueFunction(val); - } - // Default format function - if (validator && validator.bottom !== undefined) { - // Check if it's an integer validator - if (validator.top !== undefined && validator.top === Math.floor(validator.top)) { - return Math.round(val).toString(); - } - } - return val.toFixed(1); - } - - function parseValue(text: string): real { - if (parseValueFunction) { - return parseValueFunction(text); - } - // Default parse function - if (validator && validator.bottom !== undefined) { - // Check if it's an integer validator - if (validator.top !== undefined && validator.top === Math.floor(validator.top)) { - return parseInt(text); - } - } - return parseFloat(text); - } - - signal valueChanged(real newValue) - - spacing: Appearance.spacing.small - - RowLayout { - Layout.fillWidth: true - spacing: Appearance.spacing.normal - - StyledText { - visible: root.label !== "" - text: root.label - font.pointSize: Appearance.font.size.normal - } - - Item { - Layout.fillWidth: true - } - - StyledRect { - Layout.preferredWidth: 70 - implicitHeight: inputField.implicitHeight + Appearance.padding.small * 2 - color: inputHover.containsMouse || inputField.activeFocus - ? Colours.layer(Colours.palette.m3surfaceContainer, 3) - : Colours.layer(Colours.palette.m3surfaceContainer, 2) - radius: Appearance.rounding.small - border.width: 1 - border.color: inputField.activeFocus - ? Colours.palette.m3primary - : Qt.alpha(Colours.palette.m3outline, 0.3) - - Behavior on color { CAnim {} } - Behavior on border.color { CAnim {} } - - MouseArea { - id: inputHover - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.IBeamCursor - acceptedButtons: Qt.NoButton - } - - StyledTextField { - id: inputField - anchors.centerIn: parent - width: parent.width - Appearance.padding.normal - horizontalAlignment: TextInput.AlignHCenter - validator: root.validator - - Component.onCompleted: { - text = root.formatValue(root.value); - } - - onTextChanged: { - if (activeFocus) { - const val = root.parseValue(text); - if (!isNaN(val)) { - // Validate against validator bounds if available - let isValid = true; - if (root.validator) { - if (root.validator.bottom !== undefined && val < root.validator.bottom) { - isValid = false; - } - if (root.validator.top !== undefined && val > root.validator.top) { - isValid = false; - } - } - - if (isValid) { - root.valueChanged(val); - } - } - } - } - - onEditingFinished: { - const val = root.parseValue(text); - let isValid = true; - if (root.validator) { - if (root.validator.bottom !== undefined && val < root.validator.bottom) { - isValid = false; - } - if (root.validator.top !== undefined && val > root.validator.top) { - isValid = false; - } - } - - if (isNaN(val) || !isValid) { - text = root.formatValue(root.value); - } - } - } - } - - StyledText { - visible: root.suffix !== "" - text: root.suffix - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.normal - } - } - - StyledSlider { - id: slider - - Layout.fillWidth: true - implicitHeight: Appearance.padding.normal * 3 - - from: root.from - to: root.to - stepSize: root.stepSize - value: root.value - - onMoved: { - const newValue = root.stepSize > 0 ? Math.round(value / root.stepSize) * root.stepSize : value; - root.valueChanged(newValue); - if (!inputField.activeFocus) { - inputField.text = root.formatValue(newValue); - } - } - } - - // Update input field when value changes externally (slider is already bound) - onValueChanged: { - if (!inputField.activeFocus) { - inputField.text = root.formatValue(root.value); - } - } -} - -- cgit v1.2.3-freya