From 8d7bb827ebd8284706577518e2e58d6a61972889 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sun, 9 Nov 2025 22:48:38 -0500 Subject: controlcenter: appearance pane --- .../controlcenter/appearance/AppearancePane.qml | 520 +++++++++++++++++++++ 1 file changed, 520 insertions(+) create mode 100644 modules/controlcenter/appearance/AppearancePane.qml (limited to 'modules/controlcenter/appearance') diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml new file mode 100644 index 0000000..ab00345 --- /dev/null +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -0,0 +1,520 @@ +pragma ComponentBehavior: Bound + +import ".." +import "../../launcher/services" +import qs.components +import qs.components.controls +import qs.components.effects +import qs.components.containers +import qs.components.images +import qs.services +import qs.config +import Caelestia.Models +import Quickshell +import QtQuick +import QtQuick.Layouts + +RowLayout { + id: root + + required property Session session + + anchors.fill: parent + + spacing: 0 + + Item { + Layout.preferredWidth: Math.floor(parent.width * 0.4) + Layout.minimumWidth: 420 + Layout.fillHeight: true + + ColumnLayout { + anchors.fill: parent + anchors.margins: Appearance.padding.large + Appearance.padding.normal + anchors.leftMargin: Appearance.padding.large + anchors.rightMargin: Appearance.padding.large + Appearance.padding.normal / 2 + + spacing: Appearance.spacing.small + + RowLayout { + spacing: Appearance.spacing.smaller + + StyledText { + text: qsTr("Settings") + font.pointSize: Appearance.font.size.large + font.weight: 500 + } + + Item { + Layout.fillWidth: true + } + } + + StyledText { + Layout.topMargin: Appearance.spacing.large + text: qsTr("Theme mode") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } + + StyledText { + text: qsTr("Light or dark theme") + color: Colours.palette.m3outline + } + + StyledRect { + Layout.fillWidth: true + implicitHeight: modeToggle.implicitHeight + Appearance.padding.large * 2 + + radius: Appearance.rounding.normal + color: Colours.tPalette.m3surfaceContainer + + RowLayout { + id: modeToggle + + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.large + + spacing: Appearance.spacing.normal + + StyledText { + Layout.fillWidth: true + text: qsTr("Dark mode") + } + + StyledSwitch { + checked: !Colours.currentLight + onToggled: { + Colours.setMode(checked ? "dark" : "light"); + } + } + } + } + + StyledText { + Layout.topMargin: Appearance.spacing.large + text: qsTr("Color variant") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } + + StyledText { + text: qsTr("Material theme variant") + color: Colours.palette.m3outline + } + + StyledListView { + Layout.fillWidth: true + Layout.fillHeight: true + + model: M3Variants.list + spacing: Appearance.spacing.small / 2 + clip: true + + StyledScrollBar.vertical: StyledScrollBar { + flickable: parent + } + + delegate: StyledRect { + required property var modelData + + anchors.left: parent.left + anchors.right: parent.right + + color: Qt.alpha(Colours.tPalette.m3surfaceContainer, modelData.variant === Schemes.currentVariant ? Colours.tPalette.m3surfaceContainer.a : 0) + radius: Appearance.rounding.normal + border.width: modelData.variant === Schemes.currentVariant ? 1 : 0 + border.color: Colours.palette.m3primary + + StateLayer { + function onClicked(): void { + Quickshell.execDetached(["caelestia", "scheme", "set", "-v", modelData.variant]); + Schemes.reload(); + } + } + + RowLayout { + id: variantRow + + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.normal + + spacing: Appearance.spacing.normal + + MaterialIcon { + text: modelData.icon + font.pointSize: Appearance.font.size.large + fill: modelData.variant === Schemes.currentVariant ? 1 : 0 + } + + StyledText { + Layout.fillWidth: true + text: modelData.name + font.weight: modelData.variant === Schemes.currentVariant ? 500 : 400 + } + + MaterialIcon { + visible: modelData.variant === Schemes.currentVariant + text: "check" + color: Colours.palette.m3primary + font.pointSize: Appearance.font.size.large + } + } + + implicitHeight: variantRow.implicitHeight + Appearance.padding.normal * 2 + } + } + + StyledText { + Layout.topMargin: Appearance.spacing.large + text: qsTr("Color scheme") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } + + StyledText { + text: qsTr("Available color schemes") + color: Colours.palette.m3outline + } + + StyledListView { + Layout.fillWidth: true + Layout.fillHeight: true + + model: Schemes.list + spacing: Appearance.spacing.small / 2 + clip: true + + StyledScrollBar.vertical: StyledScrollBar { + flickable: parent + } + + delegate: StyledRect { + required property var modelData + + anchors.left: parent.left + anchors.right: parent.right + + readonly property string schemeKey: `${modelData.name} ${modelData.flavour}` + readonly property bool isCurrent: schemeKey === Schemes.currentScheme + + color: Qt.alpha(Colours.tPalette.m3surfaceContainer, isCurrent ? Colours.tPalette.m3surfaceContainer.a : 0) + radius: Appearance.rounding.normal + border.width: isCurrent ? 1 : 0 + border.color: Colours.palette.m3primary + + StateLayer { + function onClicked(): void { + Quickshell.execDetached(["caelestia", "scheme", "set", "-n", modelData.name, "-f", modelData.flavour]); + Schemes.reload(); + } + } + + RowLayout { + id: schemeRow + + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.normal + + spacing: Appearance.spacing.normal + + Item { + readonly property real itemHeight: schemeRow.implicitHeight || 50 + Layout.preferredWidth: itemHeight * 0.8 + Layout.preferredHeight: itemHeight * 0.8 + + StyledRect { + id: preview + + anchors.verticalCenter: parent.verticalCenter + + border.width: 1 + border.color: Qt.alpha(`#${modelData.colours?.outline}`, 0.5) + + color: `#${modelData.colours?.surface}` + radius: Appearance.rounding.full + implicitWidth: parent.itemHeight * 0.8 + implicitHeight: parent.itemHeight * 0.8 + + Item { + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.right: parent.right + + implicitWidth: parent.implicitWidth / 2 + clip: true + + StyledRect { + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.right: parent.right + + implicitWidth: preview.implicitWidth + color: `#${modelData.colours?.primary}` + radius: Appearance.rounding.full + } + } + } + } + + ColumnLayout { + Layout.fillWidth: true + spacing: 0 + + StyledText { + text: modelData.name + font.weight: isCurrent ? 500 : 400 + } + + StyledText { + text: modelData.flavour + font.pointSize: Appearance.font.size.small + color: Colours.palette.m3outline + } + } + + MaterialIcon { + visible: isCurrent + text: "check" + color: Colours.palette.m3primary + font.pointSize: Appearance.font.size.large + } + } + + implicitHeight: schemeRow.implicitHeight + Appearance.padding.normal * 2 + } + } + } + + InnerBorder { + leftThickness: 0 + rightThickness: Appearance.padding.normal / 2 + } + } + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + + StyledFlickable { + anchors.fill: parent + anchors.margins: Appearance.padding.large * 2 + + flickableDirection: Flickable.VerticalFlick + contentHeight: contentLayout.implicitHeight + + StyledScrollBar.vertical: StyledScrollBar { + flickable: parent + } + + ColumnLayout { + id: contentLayout + + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + + spacing: Appearance.spacing.normal + + MaterialIcon { + Layout.alignment: Qt.AlignHCenter + text: "palette" + font.pointSize: Appearance.font.size.extraLarge * 3 + font.bold: true + } + + StyledText { + Layout.alignment: Qt.AlignHCenter + text: qsTr("Appearance settings") + font.pointSize: Appearance.font.size.large + font.bold: true + } + + StyledText { + Layout.topMargin: Appearance.spacing.large + Layout.alignment: Qt.AlignHCenter + text: qsTr("Theme mode") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } + + StyledText { + Layout.alignment: Qt.AlignHCenter + text: Colours.currentLight ? qsTr("Light mode") : qsTr("Dark mode") + color: Colours.palette.m3outline + } + + StyledText { + Layout.topMargin: Appearance.spacing.large + Layout.alignment: Qt.AlignHCenter + text: qsTr("Wallpaper") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } + + StyledText { + Layout.alignment: Qt.AlignHCenter + text: qsTr("Select a wallpaper") + color: Colours.palette.m3outline + } + + GridLayout { + Layout.fillWidth: true + Layout.topMargin: Appearance.spacing.normal + Layout.alignment: Qt.AlignHCenter + + columns: Math.max(1, Math.floor(parent.width / 200)) + rowSpacing: Appearance.spacing.normal + columnSpacing: Appearance.spacing.normal + + // Center the grid content + Layout.maximumWidth: { + const cols = columns; + const itemWidth = 180; + const spacing = columnSpacing; + return cols * itemWidth + (cols - 1) * spacing; + } + + Repeater { + model: Wallpapers.list + + delegate: Item { + required property var modelData + + Layout.preferredWidth: 180 + Layout.preferredHeight: 120 + Layout.minimumWidth: 180 + Layout.minimumHeight: 120 + + readonly property bool isCurrent: modelData.path === Wallpapers.actualCurrent + readonly property real imageWidth: Math.max(1, width) + readonly property real imageHeight: Math.max(1, height) + + StateLayer { + radius: Appearance.rounding.normal + + function onClicked(): void { + Wallpapers.setWallpaper(modelData.path); + } + } + + StyledClippingRect { + id: image + + anchors.fill: parent + color: Colours.tPalette.m3surfaceContainer + radius: Appearance.rounding.normal + + border.width: isCurrent ? 2 : 0 + border.color: Colours.palette.m3primary + + CachingImage { + id: cachingImage + + path: modelData.path + anchors.fill: parent + + // Ensure sourceSize is always set to valid dimensions + sourceSize: Qt.size( + Math.max(1, Math.floor(parent.width)), + Math.max(1, Math.floor(parent.height)) + ) + + // Show when ready, hide if fallback is showing + opacity: status === Image.Ready && !fallbackImage.visible ? 1 : 0 + + Behavior on opacity { + NumberAnimation { + duration: 200 + } + } + } + + // Fallback: Direct image load if caching fails or is slow + Image { + id: fallbackImage + + anchors.fill: parent + source: modelData.path + asynchronous: true + fillMode: Image.PreserveAspectCrop + sourceSize: Qt.size( + Math.max(1, Math.floor(parent.width)), + Math.max(1, Math.floor(parent.height)) + ) + + // Show if caching image hasn't loaded after a delay + visible: opacity > 0 + opacity: 0 + + Timer { + id: fallbackTimer + interval: 500 + running: cachingImage.status === Image.Loading || (cachingImage.status !== Image.Ready && cachingImage.status !== Image.Null) + onTriggered: { + if (cachingImage.status !== Image.Ready && fallbackImage.status === Image.Ready) { + fallbackImage.opacity = 1; + } + } + } + + // Also check status changes + onStatusChanged: { + if (status === Image.Ready && cachingImage.status !== Image.Ready) { + Qt.callLater(() => { + if (cachingImage.status !== Image.Ready) { + fallbackImage.opacity = 1; + } + }); + } + } + + Behavior on opacity { + NumberAnimation { + duration: 200 + } + } + } + + MaterialIcon { + anchors.right: parent.right + anchors.top: parent.top + anchors.margins: Appearance.padding.small + + visible: isCurrent + text: "check_circle" + color: Colours.palette.m3primary + font.pointSize: Appearance.font.size.large + } + } + + StyledText { + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + anchors.margins: Appearance.padding.small + + text: modelData.relativePath + font.pointSize: Appearance.font.size.small + color: isCurrent ? Colours.palette.m3primary : Colours.palette.m3onSurface + elide: Text.ElideRight + } + } + } + } + } + } + + InnerBorder { + leftThickness: Appearance.padding.normal / 2 + } + } +} + + -- cgit v1.2.3-freya From d20a5095bb38d843c7f6327a553b9929e229a3b5 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sun, 9 Nov 2025 22:52:21 -0500 Subject: fix: color schemes and variants in appearance pane --- .../controlcenter/appearance/AppearancePane.qml | 42 ++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) (limited to 'modules/controlcenter/appearance') diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index ab00345..b2692c8 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -130,7 +130,25 @@ RowLayout { StateLayer { function onClicked(): void { - Quickshell.execDetached(["caelestia", "scheme", "set", "-v", modelData.variant]); + 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 + onTriggered: { Schemes.reload(); } } @@ -209,7 +227,27 @@ RowLayout { StateLayer { function onClicked(): void { - Quickshell.execDetached(["caelestia", "scheme", "set", "-n", modelData.name, "-f", modelData.flavour]); + 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 + onTriggered: { Schemes.reload(); } } -- cgit v1.2.3-freya From f5ca27c9674d7b205e83b23589b03eccf9379b83 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Mon, 10 Nov 2025 12:05:02 -0500 Subject: controlcenter: polishing appearance panel --- .../controlcenter/appearance/AppearancePane.qml | 81 ++++++++++++---------- 1 file changed, 46 insertions(+), 35 deletions(-) (limited to 'modules/controlcenter/appearance') diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index b2692c8..c8a4522 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -108,6 +108,7 @@ RowLayout { StyledListView { Layout.fillWidth: true Layout.fillHeight: true + Layout.topMargin: 0 model: M3Variants.list spacing: Appearance.spacing.small / 2 @@ -202,6 +203,7 @@ RowLayout { StyledListView { Layout.fillWidth: true Layout.fillHeight: true + Layout.topMargin: 0 model: Schemes.list spacing: Appearance.spacing.small / 2 @@ -262,66 +264,75 @@ RowLayout { spacing: Appearance.spacing.normal - Item { - readonly property real itemHeight: schemeRow.implicitHeight || 50 - Layout.preferredWidth: itemHeight * 0.8 - Layout.preferredHeight: itemHeight * 0.8 + StyledRect { + id: preview - StyledRect { - id: preview + anchors.verticalCenter: parent.verticalCenter - anchors.verticalCenter: parent.verticalCenter + border.width: 1 + border.color: Qt.alpha(`#${modelData.colours?.outline}`, 0.5) - border.width: 1 - border.color: Qt.alpha(`#${modelData.colours?.outline}`, 0.5) + color: `#${modelData.colours?.surface}` + radius: Appearance.rounding.full + implicitWidth: iconPlaceholder.implicitWidth + implicitHeight: iconPlaceholder.implicitWidth - color: `#${modelData.colours?.surface}` - radius: Appearance.rounding.full - implicitWidth: parent.itemHeight * 0.8 - implicitHeight: parent.itemHeight * 0.8 + MaterialIcon { + id: iconPlaceholder + visible: false + text: "circle" + font.pointSize: Appearance.font.size.large + } + + Item { + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.right: parent.right + + implicitWidth: parent.implicitWidth / 2 + clip: true - Item { + StyledRect { anchors.top: parent.top anchors.bottom: parent.bottom anchors.right: parent.right - implicitWidth: parent.implicitWidth / 2 - clip: true - - StyledRect { - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.right: parent.right - - implicitWidth: preview.implicitWidth - color: `#${modelData.colours?.primary}` - radius: Appearance.rounding.full - } + implicitWidth: preview.implicitWidth + color: `#${modelData.colours?.primary}` + radius: Appearance.rounding.full } } } - ColumnLayout { + Column { Layout.fillWidth: true spacing: 0 StyledText { - text: modelData.name - font.weight: isCurrent ? 500 : 400 + text: modelData.flavour ?? "" + font.pointSize: Appearance.font.size.normal } StyledText { - text: modelData.flavour + text: modelData.name ?? "" font.pointSize: Appearance.font.size.small color: Colours.palette.m3outline + + elide: Text.ElideRight + anchors.left: parent.left + anchors.right: parent.right } } - MaterialIcon { - visible: isCurrent - text: "check" - color: Colours.palette.m3primary - font.pointSize: Appearance.font.size.large + Loader { + active: isCurrent + asynchronous: true + + sourceComponent: MaterialIcon { + text: "check" + color: Colours.palette.m3onSurfaceVariant + font.pointSize: Appearance.font.size.large + } } } -- cgit v1.2.3-freya From 058a289a04593a783110eec3d3801afea46e9525 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Tue, 11 Nov 2025 12:42:28 -0500 Subject: controlcenter: appearance panel, more shell.json settings available --- .../controlcenter/appearance/AppearancePane.qml | 617 +++++++++++++++++++-- 1 file changed, 582 insertions(+), 35 deletions(-) (limited to 'modules/controlcenter/appearance') diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index c8a4522..b8e02e1 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -11,6 +11,7 @@ import qs.services import qs.config import Caelestia.Models import Quickshell +import Quickshell.Io import QtQuick import QtQuick.Layouts @@ -19,22 +20,145 @@ RowLayout { required property Session session + // Background settings + property bool desktopClockEnabled: true + property bool backgroundEnabled: true + property bool visualiserEnabled: true + property bool visualiserAutoHide: true + property real visualiserRounding: 1 + property real visualiserSpacing: 1 + anchors.fill: parent spacing: 0 + FileView { + id: configFile + + path: `${Paths.config}/shell.json` + watchChanges: true + + property bool isSaving: false + + onLoaded: { + if (!isSaving) { + try { + const config = JSON.parse(text()); + updateFromConfig(config); + } catch (e) { + console.error("Failed to parse config:", e); + } + } + } + + onFileChanged: { + if (!isSaving) { + try { + const config = JSON.parse(text()); + updateFromConfig(config); + } catch (e) { + console.error("Failed to parse config:", e); + } + } + } + } + + function updateFromConfig(config) { + // Update background settings + if (config.background) { + root.desktopClockEnabled = config.background.desktopClock?.enabled !== false; + root.backgroundEnabled = config.background.enabled !== false; + if (config.background.visualiser) { + root.visualiserEnabled = config.background.visualiser.enabled !== false; + root.visualiserAutoHide = config.background.visualiser.autoHide !== false; + root.visualiserRounding = config.background.visualiser.rounding || 1; + root.visualiserSpacing = config.background.visualiser.spacing || 1; + } + } + } + + Timer { + id: saveTimer + interval: 500 + onTriggered: { + configFile.isSaving = false; + } + } + + function collapseAllSections(exceptSection) { + if (exceptSection !== themeModeSection) themeModeSection.expanded = false; + if (exceptSection !== colorVariantSection) colorVariantSection.expanded = false; + if (exceptSection !== colorSchemeSection) colorSchemeSection.expanded = false; + if (exceptSection !== backgroundSection) backgroundSection.expanded = false; + } + + function saveConfig() { + if (!configFile.loaded) { + console.error("Config file not loaded yet"); + return; + } + + try { + // Set flag to prevent reloading during save + configFile.isSaving = true; + + const config = JSON.parse(configFile.text()); + + // Ensure background object exists + if (!config.background) config.background = {}; + + // Update desktop clock + if (!config.background.desktopClock) config.background.desktopClock = {}; + config.background.desktopClock.enabled = root.desktopClockEnabled; + + // Update background enabled + config.background.enabled = root.backgroundEnabled; + + // Update visualiser + if (!config.background.visualiser) config.background.visualiser = {}; + config.background.visualiser.enabled = root.visualiserEnabled; + config.background.visualiser.autoHide = root.visualiserAutoHide; + config.background.visualiser.rounding = root.visualiserRounding; + config.background.visualiser.spacing = root.visualiserSpacing; + + // Write back to file + const jsonString = JSON.stringify(config, null, 4); + configFile.setText(jsonString); + + // Reset flag after a delay to allow file write to complete + // Use a timer to ensure the file system has time to write + saveTimer.restart(); + } catch (e) { + console.error("Failed to save config:", e); + configFile.isSaving = false; + } + } + Item { Layout.preferredWidth: Math.floor(parent.width * 0.4) Layout.minimumWidth: 420 Layout.fillHeight: true - ColumnLayout { + StyledFlickable { + id: sidebarFlickable anchors.fill: parent - anchors.margins: Appearance.padding.large + Appearance.padding.normal - anchors.leftMargin: Appearance.padding.large - anchors.rightMargin: Appearance.padding.large + Appearance.padding.normal / 2 + flickableDirection: Flickable.VerticalFlick + contentHeight: sidebarLayout.implicitHeight + Appearance.padding.large * 2 - spacing: Appearance.spacing.small + StyledScrollBar.vertical: StyledScrollBar { + flickable: sidebarFlickable + } + + ColumnLayout { + id: sidebarLayout + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + anchors.margins: Appearance.padding.large + Appearance.padding.normal + anchors.leftMargin: Appearance.padding.large + anchors.rightMargin: Appearance.padding.large + Appearance.padding.normal / 2 + + spacing: Appearance.spacing.small RowLayout { spacing: Appearance.spacing.smaller @@ -50,25 +174,75 @@ RowLayout { } } - StyledText { - Layout.topMargin: Appearance.spacing.large - text: qsTr("Theme mode") - font.pointSize: Appearance.font.size.larger - font.weight: 500 - } + Item { + id: themeModeSection + Layout.fillWidth: true + Layout.preferredHeight: themeModeSectionHeader.implicitHeight + property bool expanded: false - StyledText { - text: qsTr("Light or dark theme") - color: Colours.palette.m3outline + ColumnLayout { + id: themeModeSectionHeader + 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: qsTr("Theme mode") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } + + Item { + Layout.fillWidth: true + } + + MaterialIcon { + text: "expand_more" + rotation: themeModeSection.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 { + const wasExpanded = themeModeSection.expanded; + root.collapseAllSections(themeModeSection); + themeModeSection.expanded = !wasExpanded; + } + } + + StyledText { + visible: themeModeSection.expanded + text: qsTr("Light or dark theme") + color: Colours.palette.m3outline + Layout.fillWidth: true + } + } } StyledRect { + visible: themeModeSection.expanded Layout.fillWidth: true - implicitHeight: modeToggle.implicitHeight + Appearance.padding.large * 2 + implicitHeight: themeModeSection.expanded ? modeToggle.implicitHeight + Appearance.padding.large * 2 : 0 radius: Appearance.rounding.normal color: Colours.tPalette.m3surfaceContainer + Behavior on implicitHeight { + Anim {} + } + RowLayout { id: modeToggle @@ -93,23 +267,73 @@ RowLayout { } } - StyledText { - Layout.topMargin: Appearance.spacing.large - text: qsTr("Color variant") - font.pointSize: Appearance.font.size.larger - font.weight: 500 - } + Item { + id: colorVariantSection + Layout.fillWidth: true + Layout.preferredHeight: colorVariantSectionHeader.implicitHeight + property bool expanded: false - StyledText { - text: qsTr("Material theme variant") - color: Colours.palette.m3outline + ColumnLayout { + id: colorVariantSectionHeader + 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: qsTr("Color variant") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } + + Item { + Layout.fillWidth: true + } + + MaterialIcon { + text: "expand_more" + rotation: colorVariantSection.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 { + const wasExpanded = colorVariantSection.expanded; + root.collapseAllSections(colorVariantSection); + colorVariantSection.expanded = !wasExpanded; + } + } + + StyledText { + visible: colorVariantSection.expanded + text: qsTr("Material theme variant") + color: Colours.palette.m3outline + Layout.fillWidth: true + } + } } StyledListView { + visible: colorVariantSection.expanded Layout.fillWidth: true - Layout.fillHeight: true + implicitHeight: colorVariantSection.expanded ? Math.min(400, M3Variants.list.length * 60) : 0 Layout.topMargin: 0 + Behavior on implicitHeight { + Anim {} + } + model: M3Variants.list spacing: Appearance.spacing.small / 2 clip: true @@ -188,23 +412,73 @@ RowLayout { } } - StyledText { - Layout.topMargin: Appearance.spacing.large - text: qsTr("Color scheme") - font.pointSize: Appearance.font.size.larger - font.weight: 500 - } + Item { + id: colorSchemeSection + Layout.fillWidth: true + Layout.preferredHeight: colorSchemeSectionHeader.implicitHeight + property bool expanded: false - StyledText { - text: qsTr("Available color schemes") - color: Colours.palette.m3outline + ColumnLayout { + id: colorSchemeSectionHeader + 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: qsTr("Color scheme") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } + + Item { + Layout.fillWidth: true + } + + MaterialIcon { + text: "expand_more" + rotation: colorSchemeSection.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 { + const wasExpanded = colorSchemeSection.expanded; + root.collapseAllSections(colorSchemeSection); + colorSchemeSection.expanded = !wasExpanded; + } + } + + StyledText { + visible: colorSchemeSection.expanded + text: qsTr("Available color schemes") + color: Colours.palette.m3outline + Layout.fillWidth: true + } + } } StyledListView { + visible: colorSchemeSection.expanded Layout.fillWidth: true - Layout.fillHeight: true + implicitHeight: colorSchemeSection.expanded ? Math.min(400, Schemes.list.length * 80) : 0 Layout.topMargin: 0 + Behavior on implicitHeight { + Anim {} + } + model: Schemes.list spacing: Appearance.spacing.small / 2 clip: true @@ -339,6 +613,279 @@ RowLayout { implicitHeight: schemeRow.implicitHeight + Appearance.padding.normal * 2 } } + + Item { + id: backgroundSection + Layout.fillWidth: true + Layout.preferredHeight: backgroundSectionHeader.implicitHeight + property bool expanded: false + + ColumnLayout { + id: backgroundSectionHeader + 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: qsTr("Background") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } + + Item { + Layout.fillWidth: true + } + + MaterialIcon { + text: "expand_more" + rotation: backgroundSection.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 { + const wasExpanded = backgroundSection.expanded; + root.collapseAllSections(backgroundSection); + backgroundSection.expanded = !wasExpanded; + } + } + } + } + + StyledRect { + visible: backgroundSection.expanded + Layout.fillWidth: true + Layout.topMargin: Appearance.spacing.small / 2 + implicitHeight: backgroundSection.expanded ? desktopClockRow.implicitHeight + Appearance.padding.large * 2 : 0 + radius: Appearance.rounding.normal + color: Colours.tPalette.m3surfaceContainer + + Behavior on implicitHeight { + Anim {} + } + + RowLayout { + id: desktopClockRow + 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("Desktop clock") + } + + StyledSwitch { + checked: root.desktopClockEnabled + onToggled: { + root.desktopClockEnabled = checked; + root.saveConfig(); + } + } + } + } + + StyledRect { + visible: backgroundSection.expanded + Layout.fillWidth: true + Layout.topMargin: Appearance.spacing.small / 2 + implicitHeight: backgroundSection.expanded ? backgroundEnabledRow.implicitHeight + Appearance.padding.large * 2 : 0 + radius: Appearance.rounding.normal + color: Colours.tPalette.m3surfaceContainer + + Behavior on implicitHeight { + Anim {} + } + + RowLayout { + id: backgroundEnabledRow + 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("Background enabled") + } + + StyledSwitch { + checked: root.backgroundEnabled + onToggled: { + root.backgroundEnabled = checked; + root.saveConfig(); + } + } + } + } + + StyledText { + visible: backgroundSection.expanded + Layout.topMargin: Appearance.spacing.normal + text: qsTr("Visualiser") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } + + StyledRect { + visible: backgroundSection.expanded + Layout.fillWidth: true + Layout.topMargin: Appearance.spacing.small / 2 + implicitHeight: backgroundSection.expanded ? visualiserEnabledRow.implicitHeight + Appearance.padding.large * 2 : 0 + radius: Appearance.rounding.normal + color: Colours.tPalette.m3surfaceContainer + + Behavior on implicitHeight { + Anim {} + } + + RowLayout { + id: visualiserEnabledRow + 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("Visualiser enabled") + } + + StyledSwitch { + checked: root.visualiserEnabled + onToggled: { + root.visualiserEnabled = checked; + root.saveConfig(); + } + } + } + } + + StyledRect { + visible: backgroundSection.expanded + Layout.fillWidth: true + Layout.topMargin: Appearance.spacing.small / 2 + implicitHeight: backgroundSection.expanded ? visualiserAutoHideRow.implicitHeight + Appearance.padding.large * 2 : 0 + radius: Appearance.rounding.normal + color: Colours.tPalette.m3surfaceContainer + + Behavior on implicitHeight { + Anim {} + } + + RowLayout { + id: visualiserAutoHideRow + 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("Visualiser auto hide") + } + + StyledSwitch { + checked: root.visualiserAutoHide + onToggled: { + root.visualiserAutoHide = checked; + root.saveConfig(); + } + } + } + } + + StyledRect { + visible: backgroundSection.expanded + Layout.fillWidth: true + Layout.topMargin: Appearance.spacing.small / 2 + implicitHeight: backgroundSection.expanded ? visualiserRoundingRow.implicitHeight + Appearance.padding.large * 2 : 0 + radius: Appearance.rounding.normal + color: Colours.tPalette.m3surfaceContainer + + Behavior on implicitHeight { + Anim {} + } + + RowLayout { + id: visualiserRoundingRow + 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("Visualiser rounding") + } + + CustomSpinBox { + min: 0 + max: 10 + value: Math.round(root.visualiserRounding) + onValueModified: value => { + root.visualiserRounding = value; + root.saveConfig(); + } + } + } + } + + StyledRect { + visible: backgroundSection.expanded + Layout.fillWidth: true + Layout.topMargin: Appearance.spacing.small / 2 + implicitHeight: backgroundSection.expanded ? visualiserSpacingRow.implicitHeight + Appearance.padding.large * 2 : 0 + radius: Appearance.rounding.normal + color: Colours.tPalette.m3surfaceContainer + + Behavior on implicitHeight { + Anim {} + } + + RowLayout { + id: visualiserSpacingRow + 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("Visualiser spacing") + } + + CustomSpinBox { + min: 0 + max: 10 + value: Math.round(root.visualiserSpacing) + onValueModified: value => { + root.visualiserSpacing = value; + root.saveConfig(); + } + } + } + } + } } InnerBorder { -- cgit v1.2.3-freya From c9d13d518eb051b175572e1d0bcf988d0b2555d1 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Tue, 11 Nov 2025 15:11:47 -0500 Subject: controlcenter: apperance panel, mostly finished --- .../controlcenter/appearance/AppearancePane.qml | 76 ++++++++++------------ 1 file changed, 35 insertions(+), 41 deletions(-) (limited to 'modules/controlcenter/appearance') diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index b8e02e1..ffde0c0 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -9,6 +9,7 @@ import qs.components.containers import qs.components.images import qs.services import qs.config +import qs.utils import Caelestia.Models import Quickshell import Quickshell.Io @@ -38,50 +39,45 @@ RowLayout { path: `${Paths.config}/shell.json` watchChanges: true - property bool isSaving: false - onLoaded: { - if (!isSaving) { - try { - const config = JSON.parse(text()); - updateFromConfig(config); - } catch (e) { - console.error("Failed to parse config:", e); - } + try { + const config = JSON.parse(text()); + updateFromConfig(config); + } catch (e) { + console.error("Failed to parse config:", e); } } - onFileChanged: { - if (!isSaving) { - try { - const config = JSON.parse(text()); - updateFromConfig(config); - } catch (e) { - console.error("Failed to parse config:", e); - } - } + onSaveFailed: err => { + console.error("Failed to save config file:", err); } } function updateFromConfig(config) { // Update background settings if (config.background) { - root.desktopClockEnabled = config.background.desktopClock?.enabled !== false; - root.backgroundEnabled = config.background.enabled !== false; + root.desktopClockEnabled = config.background.desktopClock?.enabled !== undefined ? config.background.desktopClock.enabled : false; + root.backgroundEnabled = config.background.enabled !== undefined ? config.background.enabled : true; if (config.background.visualiser) { - root.visualiserEnabled = config.background.visualiser.enabled !== false; - root.visualiserAutoHide = config.background.visualiser.autoHide !== false; - root.visualiserRounding = config.background.visualiser.rounding || 1; - root.visualiserSpacing = config.background.visualiser.spacing || 1; + root.visualiserEnabled = config.background.visualiser.enabled !== undefined ? config.background.visualiser.enabled : false; + root.visualiserAutoHide = config.background.visualiser.autoHide !== undefined ? config.background.visualiser.autoHide : true; + root.visualiserRounding = config.background.visualiser.rounding !== undefined ? config.background.visualiser.rounding : 1; + root.visualiserSpacing = config.background.visualiser.spacing !== undefined ? config.background.visualiser.spacing : 1; + } else { + // Set defaults if visualiser object doesn't exist (matching BackgroundConfig defaults) + root.visualiserEnabled = false; + root.visualiserAutoHide = true; + root.visualiserRounding = 1; + root.visualiserSpacing = 1; } - } - } - - Timer { - id: saveTimer - interval: 500 - onTriggered: { - configFile.isSaving = false; + } else { + // Set defaults if background object doesn't exist (matching BackgroundConfig defaults) + root.desktopClockEnabled = false; + root.backgroundEnabled = true; + root.visualiserEnabled = false; + root.visualiserAutoHide = true; + root.visualiserRounding = 1; + root.visualiserSpacing = 1; } } @@ -99,9 +95,6 @@ RowLayout { } try { - // Set flag to prevent reloading during save - configFile.isSaving = true; - const config = JSON.parse(configFile.text()); // Ensure background object exists @@ -121,16 +114,11 @@ RowLayout { config.background.visualiser.rounding = root.visualiserRounding; config.background.visualiser.spacing = root.visualiserSpacing; - // Write back to file + // Write back to file using setText (same simple approach that worked for taskbar) const jsonString = JSON.stringify(config, null, 4); configFile.setText(jsonString); - - // Reset flag after a delay to allow file write to complete - // Use a timer to ensure the file system has time to write - saveTimer.restart(); } catch (e) { console.error("Failed to save config:", e); - configFile.isSaving = false; } } @@ -690,6 +678,7 @@ RowLayout { } StyledSwitch { + id: desktopClockSwitch checked: root.desktopClockEnabled onToggled: { root.desktopClockEnabled = checked; @@ -725,6 +714,7 @@ RowLayout { } StyledSwitch { + id: backgroundEnabledSwitch checked: root.backgroundEnabled onToggled: { root.backgroundEnabled = checked; @@ -768,6 +758,7 @@ RowLayout { } StyledSwitch { + id: visualiserEnabledSwitch checked: root.visualiserEnabled onToggled: { root.visualiserEnabled = checked; @@ -803,6 +794,7 @@ RowLayout { } StyledSwitch { + id: visualiserAutoHideSwitch checked: root.visualiserAutoHide onToggled: { root.visualiserAutoHide = checked; @@ -838,6 +830,7 @@ RowLayout { } CustomSpinBox { + id: visualiserRoundingSpinBox min: 0 max: 10 value: Math.round(root.visualiserRounding) @@ -875,6 +868,7 @@ RowLayout { } CustomSpinBox { + id: visualiserSpacingSpinBox min: 0 max: 10 value: Math.round(root.visualiserSpacing) -- cgit v1.2.3-freya From 4188d328401b47e07b4918ff250fff6d22128f55 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Tue, 11 Nov 2025 15:50:41 -0500 Subject: controlcenter: appearance panel, now does all shell.json options --- .../controlcenter/appearance/AppearancePane.qml | 821 +++++++++++++++++++++ 1 file changed, 821 insertions(+) (limited to 'modules/controlcenter/appearance') diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index ffde0c0..4bf6d44 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -21,6 +21,19 @@ RowLayout { required property Session session + // Appearance settings + property real animDurationsScale: 1 + property string fontFamilyMaterial: "Material Symbols Rounded" + property string fontFamilyMono: "CaskaydiaCove NF" + property string fontFamilySans: "Rubik" + property real fontSizeScale: 1 + property real paddingScale: 1 + property real roundingScale: 1 + property real spacingScale: 1 + property bool transparencyEnabled: false + property real transparencyBase: 0.85 + property real transparencyLayers: 0.4 + // Background settings property bool desktopClockEnabled: true property bool backgroundEnabled: true @@ -54,6 +67,37 @@ RowLayout { } function updateFromConfig(config) { + // Update appearance settings + if (config.appearance) { + if (config.appearance.anim && config.appearance.anim.durations) { + root.animDurationsScale = config.appearance.anim.durations.scale ?? 1; + } + if (config.appearance.font) { + if (config.appearance.font.family) { + root.fontFamilyMaterial = config.appearance.font.family.material ?? "Material Symbols Rounded"; + root.fontFamilyMono = config.appearance.font.family.mono ?? "CaskaydiaCove NF"; + root.fontFamilySans = config.appearance.font.family.sans ?? "Rubik"; + } + if (config.appearance.font.size) { + root.fontSizeScale = config.appearance.font.size.scale ?? 1; + } + } + if (config.appearance.padding) { + root.paddingScale = config.appearance.padding.scale ?? 1; + } + if (config.appearance.rounding) { + root.roundingScale = config.appearance.rounding.scale ?? 1; + } + if (config.appearance.spacing) { + root.spacingScale = config.appearance.spacing.scale ?? 1; + } + if (config.appearance.transparency) { + root.transparencyEnabled = config.appearance.transparency.enabled ?? false; + root.transparencyBase = config.appearance.transparency.base ?? 0.85; + root.transparencyLayers = config.appearance.transparency.layers ?? 0.4; + } + } + // Update background settings if (config.background) { root.desktopClockEnabled = config.background.desktopClock?.enabled !== undefined ? config.background.desktopClock.enabled : false; @@ -85,6 +129,10 @@ RowLayout { if (exceptSection !== themeModeSection) themeModeSection.expanded = false; if (exceptSection !== colorVariantSection) colorVariantSection.expanded = false; if (exceptSection !== colorSchemeSection) colorSchemeSection.expanded = false; + if (exceptSection !== animationsSection) animationsSection.expanded = false; + if (exceptSection !== fontsSection) fontsSection.expanded = false; + if (exceptSection !== scalesSection) scalesSection.expanded = false; + if (exceptSection !== transparencySection) transparencySection.expanded = false; if (exceptSection !== backgroundSection) backgroundSection.expanded = false; } @@ -97,6 +145,37 @@ RowLayout { try { const config = JSON.parse(configFile.text()); + // Ensure appearance object exists + if (!config.appearance) config.appearance = {}; + + // Update animations + if (!config.appearance.anim) config.appearance.anim = {}; + if (!config.appearance.anim.durations) config.appearance.anim.durations = {}; + config.appearance.anim.durations.scale = root.animDurationsScale; + + // Update fonts + if (!config.appearance.font) config.appearance.font = {}; + if (!config.appearance.font.family) config.appearance.font.family = {}; + config.appearance.font.family.material = root.fontFamilyMaterial; + config.appearance.font.family.mono = root.fontFamilyMono; + config.appearance.font.family.sans = root.fontFamilySans; + if (!config.appearance.font.size) config.appearance.font.size = {}; + config.appearance.font.size.scale = root.fontSizeScale; + + // Update scales + if (!config.appearance.padding) config.appearance.padding = {}; + config.appearance.padding.scale = root.paddingScale; + if (!config.appearance.rounding) config.appearance.rounding = {}; + config.appearance.rounding.scale = root.roundingScale; + if (!config.appearance.spacing) config.appearance.spacing = {}; + config.appearance.spacing.scale = root.spacingScale; + + // Update transparency + if (!config.appearance.transparency) config.appearance.transparency = {}; + config.appearance.transparency.enabled = root.transparencyEnabled; + config.appearance.transparency.base = root.transparencyBase; + config.appearance.transparency.layers = root.transparencyLayers; + // Ensure background object exists if (!config.background) config.background = {}; @@ -602,6 +681,748 @@ RowLayout { } } + Item { + id: animationsSection + Layout.fillWidth: true + Layout.preferredHeight: animationsSectionHeader.implicitHeight + property bool expanded: false + + ColumnLayout { + id: animationsSectionHeader + 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: qsTr("Animations") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } + + Item { + Layout.fillWidth: true + } + + MaterialIcon { + text: "expand_more" + rotation: animationsSection.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 { + const wasExpanded = animationsSection.expanded; + root.collapseAllSections(animationsSection); + animationsSection.expanded = !wasExpanded; + } + } + } + } + + StyledRect { + visible: animationsSection.expanded + Layout.fillWidth: true + Layout.topMargin: Appearance.spacing.small / 2 + implicitHeight: animationsSection.expanded ? animDurationsScaleRow.implicitHeight + Appearance.padding.large * 2 : 0 + radius: Appearance.rounding.normal + color: Colours.tPalette.m3surfaceContainer + + Behavior on implicitHeight { + Anim {} + } + + RowLayout { + id: animDurationsScaleRow + 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("Animation duration scale") + } + + CustomSpinBox { + id: animDurationsScaleSpinBox + min: 0.1 + max: 5 + value: root.animDurationsScale + onValueModified: value => { + root.animDurationsScale = value; + root.saveConfig(); + } + } + } + } + + Item { + id: fontsSection + Layout.fillWidth: true + Layout.preferredHeight: fontsSectionHeader.implicitHeight + property bool expanded: false + + ColumnLayout { + id: fontsSectionHeader + 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: qsTr("Fonts") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } + + Item { + Layout.fillWidth: true + } + + MaterialIcon { + text: "expand_more" + rotation: fontsSection.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 { + const wasExpanded = fontsSection.expanded; + root.collapseAllSections(fontsSection); + fontsSection.expanded = !wasExpanded; + } + } + } + } + + StyledText { + visible: fontsSection.expanded + Layout.topMargin: Appearance.spacing.normal + text: qsTr("Material font family") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } + + StyledListView { + visible: fontsSection.expanded + Layout.fillWidth: true + implicitHeight: fontsSection.expanded ? Math.min(300, Qt.fontFamilies().length * 50) : 0 + Layout.topMargin: 0 + + Behavior on implicitHeight { + Anim {} + } + + model: Qt.fontFamilies() + spacing: Appearance.spacing.small / 2 + clip: true + + StyledScrollBar.vertical: StyledScrollBar { + flickable: parent + } + + delegate: StyledRect { + required property string modelData + + anchors.left: parent.left + anchors.right: parent.right + + readonly property bool isCurrent: modelData === root.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 { + root.fontFamilyMaterial = modelData; + root.saveConfig(); + } + } + + RowLayout { + id: fontFamilyMaterialRow + + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.normal + + spacing: Appearance.spacing.normal + + StyledText { + text: modelData + font.pointSize: Appearance.font.size.normal + } + + Item { + Layout.fillWidth: true + } + + Loader { + active: isCurrent + asynchronous: true + + sourceComponent: MaterialIcon { + text: "check" + color: Colours.palette.m3onSurfaceVariant + font.pointSize: Appearance.font.size.large + } + } + } + + implicitHeight: fontFamilyMaterialRow.implicitHeight + Appearance.padding.normal * 2 + } + } + + StyledText { + visible: fontsSection.expanded + Layout.topMargin: Appearance.spacing.normal + text: qsTr("Monospace font family") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } + + StyledListView { + visible: fontsSection.expanded + Layout.fillWidth: true + implicitHeight: fontsSection.expanded ? Math.min(300, Qt.fontFamilies().length * 50) : 0 + Layout.topMargin: 0 + + Behavior on implicitHeight { + Anim {} + } + + model: Qt.fontFamilies() + spacing: Appearance.spacing.small / 2 + clip: true + + StyledScrollBar.vertical: StyledScrollBar { + flickable: parent + } + + delegate: StyledRect { + required property string modelData + + anchors.left: parent.left + anchors.right: parent.right + + readonly property bool isCurrent: modelData === root.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 { + root.fontFamilyMono = modelData; + root.saveConfig(); + } + } + + RowLayout { + id: fontFamilyMonoRow + + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.normal + + spacing: Appearance.spacing.normal + + StyledText { + text: modelData + font.pointSize: Appearance.font.size.normal + } + + Item { + Layout.fillWidth: true + } + + Loader { + active: isCurrent + asynchronous: true + + sourceComponent: MaterialIcon { + text: "check" + color: Colours.palette.m3onSurfaceVariant + font.pointSize: Appearance.font.size.large + } + } + } + + implicitHeight: fontFamilyMonoRow.implicitHeight + Appearance.padding.normal * 2 + } + } + + StyledText { + visible: fontsSection.expanded + Layout.topMargin: Appearance.spacing.normal + text: qsTr("Sans-serif font family") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } + + StyledListView { + visible: fontsSection.expanded + Layout.fillWidth: true + implicitHeight: fontsSection.expanded ? Math.min(300, Qt.fontFamilies().length * 50) : 0 + Layout.topMargin: 0 + + Behavior on implicitHeight { + Anim {} + } + + model: Qt.fontFamilies() + spacing: Appearance.spacing.small / 2 + clip: true + + StyledScrollBar.vertical: StyledScrollBar { + flickable: parent + } + + delegate: StyledRect { + required property string modelData + + anchors.left: parent.left + anchors.right: parent.right + + readonly property bool isCurrent: modelData === root.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 { + root.fontFamilySans = modelData; + root.saveConfig(); + } + } + + RowLayout { + id: fontFamilySansRow + + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.normal + + spacing: Appearance.spacing.normal + + StyledText { + text: modelData + font.pointSize: Appearance.font.size.normal + } + + Item { + Layout.fillWidth: true + } + + Loader { + active: isCurrent + asynchronous: true + + sourceComponent: MaterialIcon { + text: "check" + color: Colours.palette.m3onSurfaceVariant + font.pointSize: Appearance.font.size.large + } + } + } + + implicitHeight: fontFamilySansRow.implicitHeight + Appearance.padding.normal * 2 + } + } + + StyledRect { + visible: fontsSection.expanded + Layout.fillWidth: true + Layout.topMargin: Appearance.spacing.small / 2 + implicitHeight: fontsSection.expanded ? fontSizeScaleRow.implicitHeight + Appearance.padding.large * 2 : 0 + 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(); + } + } + } + } + + Item { + id: scalesSection + Layout.fillWidth: true + Layout.preferredHeight: scalesSectionHeader.implicitHeight + property bool expanded: false + + ColumnLayout { + id: scalesSectionHeader + 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: qsTr("Scales") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } + + Item { + Layout.fillWidth: true + } + + MaterialIcon { + text: "expand_more" + rotation: scalesSection.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 { + const wasExpanded = scalesSection.expanded; + root.collapseAllSections(scalesSection); + scalesSection.expanded = !wasExpanded; + } + } + } + } + + StyledRect { + visible: scalesSection.expanded + Layout.fillWidth: true + Layout.topMargin: Appearance.spacing.small / 2 + implicitHeight: scalesSection.expanded ? paddingScaleRow.implicitHeight + Appearance.padding.large * 2 : 0 + radius: Appearance.rounding.normal + color: Colours.tPalette.m3surfaceContainer + + Behavior on implicitHeight { + Anim {} + } + + RowLayout { + id: paddingScaleRow + 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("Padding scale") + } + + CustomSpinBox { + id: paddingScaleSpinBox + min: 0.1 + max: 5 + value: root.paddingScale + onValueModified: value => { + root.paddingScale = value; + root.saveConfig(); + } + } + } + } + + StyledRect { + visible: scalesSection.expanded + Layout.fillWidth: true + Layout.topMargin: Appearance.spacing.small / 2 + implicitHeight: scalesSection.expanded ? roundingScaleRow.implicitHeight + Appearance.padding.large * 2 : 0 + radius: Appearance.rounding.normal + color: Colours.tPalette.m3surfaceContainer + + Behavior on implicitHeight { + Anim {} + } + + RowLayout { + id: roundingScaleRow + 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("Rounding scale") + } + + CustomSpinBox { + id: roundingScaleSpinBox + min: 0.1 + max: 5 + value: root.roundingScale + onValueModified: value => { + root.roundingScale = value; + root.saveConfig(); + } + } + } + } + + StyledRect { + visible: scalesSection.expanded + Layout.fillWidth: true + Layout.topMargin: Appearance.spacing.small / 2 + implicitHeight: scalesSection.expanded ? spacingScaleRow.implicitHeight + Appearance.padding.large * 2 : 0 + radius: Appearance.rounding.normal + color: Colours.tPalette.m3surfaceContainer + + Behavior on implicitHeight { + Anim {} + } + + RowLayout { + id: spacingScaleRow + 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("Spacing scale") + } + + CustomSpinBox { + id: spacingScaleSpinBox + min: 0.1 + max: 5 + value: root.spacingScale + onValueModified: value => { + root.spacingScale = value; + root.saveConfig(); + } + } + } + } + + Item { + id: transparencySection + Layout.fillWidth: true + Layout.preferredHeight: transparencySectionHeader.implicitHeight + property bool expanded: false + + ColumnLayout { + id: transparencySectionHeader + 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: qsTr("Transparency") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } + + Item { + Layout.fillWidth: true + } + + MaterialIcon { + text: "expand_more" + rotation: transparencySection.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 { + const wasExpanded = transparencySection.expanded; + root.collapseAllSections(transparencySection); + transparencySection.expanded = !wasExpanded; + } + } + } + } + + StyledRect { + visible: transparencySection.expanded + Layout.fillWidth: true + Layout.topMargin: Appearance.spacing.small / 2 + implicitHeight: transparencySection.expanded ? transparencyEnabledRow.implicitHeight + Appearance.padding.large * 2 : 0 + radius: Appearance.rounding.normal + color: Colours.tPalette.m3surfaceContainer + + Behavior on implicitHeight { + Anim {} + } + + RowLayout { + id: transparencyEnabledRow + 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("Transparency enabled") + } + + StyledSwitch { + id: transparencyEnabledSwitch + checked: root.transparencyEnabled + onToggled: { + root.transparencyEnabled = checked; + root.saveConfig(); + } + } + } + } + + StyledRect { + visible: transparencySection.expanded + Layout.fillWidth: true + Layout.topMargin: Appearance.spacing.small / 2 + implicitHeight: transparencySection.expanded ? transparencyBaseRow.implicitHeight + Appearance.padding.large * 2 : 0 + radius: Appearance.rounding.normal + color: Colours.tPalette.m3surfaceContainer + + Behavior on implicitHeight { + Anim {} + } + + RowLayout { + id: transparencyBaseRow + 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("Transparency base") + } + + CustomSpinBox { + id: transparencyBaseSpinBox + min: 0 + max: 1 + value: root.transparencyBase + onValueModified: value => { + root.transparencyBase = value; + root.saveConfig(); + } + } + } + } + + StyledRect { + visible: transparencySection.expanded + Layout.fillWidth: true + Layout.topMargin: Appearance.spacing.small / 2 + implicitHeight: transparencySection.expanded ? transparencyLayersRow.implicitHeight + Appearance.padding.large * 2 : 0 + radius: Appearance.rounding.normal + color: Colours.tPalette.m3surfaceContainer + + Behavior on implicitHeight { + Anim {} + } + + RowLayout { + id: transparencyLayersRow + 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("Transparency layers") + } + + CustomSpinBox { + id: transparencyLayersSpinBox + min: 0 + max: 1 + value: root.transparencyLayers + onValueModified: value => { + root.transparencyLayers = value; + root.saveConfig(); + } + } + } + } + Item { id: backgroundSection Layout.fillWidth: true -- cgit v1.2.3-freya From e613ab81ccac640cb2bdfee0b48e9e8a61c749a5 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Tue, 11 Nov 2025 15:57:00 -0500 Subject: controlcenter: appearance panel, added border options --- .../controlcenter/appearance/AppearancePane.qml | 140 +++++++++++++++++++++ 1 file changed, 140 insertions(+) (limited to 'modules/controlcenter/appearance') diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index 4bf6d44..d50eb58 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -33,6 +33,8 @@ RowLayout { property bool transparencyEnabled: false property real transparencyBase: 0.85 property real transparencyLayers: 0.4 + property real borderRounding: 1 + property real borderThickness: 1 // Background settings property bool desktopClockEnabled: true @@ -98,6 +100,12 @@ RowLayout { } } + // Update border settings + if (config.border) { + root.borderRounding = config.border.rounding ?? 1; + root.borderThickness = config.border.thickness ?? 1; + } + // Update background settings if (config.background) { root.desktopClockEnabled = config.background.desktopClock?.enabled !== undefined ? config.background.desktopClock.enabled : false; @@ -133,6 +141,7 @@ RowLayout { if (exceptSection !== fontsSection) fontsSection.expanded = false; if (exceptSection !== scalesSection) scalesSection.expanded = false; if (exceptSection !== transparencySection) transparencySection.expanded = false; + if (exceptSection !== borderSection) borderSection.expanded = false; if (exceptSection !== backgroundSection) backgroundSection.expanded = false; } @@ -193,6 +202,11 @@ RowLayout { config.background.visualiser.rounding = root.visualiserRounding; config.background.visualiser.spacing = root.visualiserSpacing; + // Update border + if (!config.border) config.border = {}; + config.border.rounding = root.borderRounding; + config.border.thickness = root.borderThickness; + // Write back to file using setText (same simple approach that worked for taskbar) const jsonString = JSON.stringify(config, null, 4); configFile.setText(jsonString); @@ -1423,6 +1437,132 @@ RowLayout { } } + Item { + id: borderSection + Layout.fillWidth: true + Layout.preferredHeight: borderSectionHeader.implicitHeight + property bool expanded: false + + ColumnLayout { + id: borderSectionHeader + 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: qsTr("Border") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } + + Item { + Layout.fillWidth: true + } + + MaterialIcon { + text: "expand_more" + rotation: borderSection.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 { + const wasExpanded = borderSection.expanded; + root.collapseAllSections(borderSection); + borderSection.expanded = !wasExpanded; + } + } + } + } + + StyledRect { + visible: borderSection.expanded + Layout.fillWidth: true + Layout.topMargin: Appearance.spacing.small / 2 + implicitHeight: borderSection.expanded ? borderRoundingRow.implicitHeight + Appearance.padding.large * 2 : 0 + radius: Appearance.rounding.normal + color: Colours.tPalette.m3surfaceContainer + + Behavior on implicitHeight { + Anim {} + } + + RowLayout { + id: borderRoundingRow + 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("Border rounding") + } + + CustomSpinBox { + id: borderRoundingSpinBox + min: 0.1 + max: 5 + value: root.borderRounding + onValueModified: value => { + root.borderRounding = value; + root.saveConfig(); + } + } + } + } + + StyledRect { + visible: borderSection.expanded + Layout.fillWidth: true + Layout.topMargin: Appearance.spacing.small / 2 + implicitHeight: borderSection.expanded ? borderThicknessRow.implicitHeight + Appearance.padding.large * 2 : 0 + radius: Appearance.rounding.normal + color: Colours.tPalette.m3surfaceContainer + + Behavior on implicitHeight { + Anim {} + } + + RowLayout { + id: borderThicknessRow + 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("Border thickness") + } + + CustomSpinBox { + id: borderThicknessSpinBox + min: 0.1 + max: 5 + value: root.borderThickness + onValueModified: value => { + root.borderThickness = value; + root.saveConfig(); + } + } + } + } + Item { id: backgroundSection Layout.fillWidth: true -- cgit v1.2.3-freya From cb6eaaa25a61b0e14a3d2f120e4cbfef6551a1d0 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Tue, 11 Nov 2025 16:22:14 -0500 Subject: controlcenter: desktop selector in appearance panel font styling --- .../controlcenter/appearance/AppearancePane.qml | 70 ++++++++++++++++++++-- 1 file changed, 65 insertions(+), 5 deletions(-) (limited to 'modules/controlcenter/appearance') diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index d50eb58..e3600ed 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -2045,16 +2045,76 @@ RowLayout { } } + // Gradient overlay for filename with rounded bottom corners + Rectangle { + id: filenameOverlay + anchors.left: parent.left + 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; + } + } + StyledText { + id: filenameText anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom - anchors.margins: Appearance.padding.small + anchors.leftMargin: Appearance.padding.normal + anchors.rightMargin: Appearance.padding.normal + anchors.bottomMargin: Appearance.padding.normal + + readonly property string fileName: { + const path = modelData.relativePath || ""; + const parts = path.split("/"); + return parts.length > 0 ? parts[parts.length - 1] : path; + } - text: modelData.relativePath - font.pointSize: Appearance.font.size.small - color: isCurrent ? Colours.palette.m3primary : Colours.palette.m3onSurface - elide: Text.ElideRight + text: fileName + font.pointSize: Appearance.font.size.smaller + font.weight: 500 + 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; + } } } } -- cgit v1.2.3-freya 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 + .../controlcenter/appearance/AppearancePane.qml | 1872 ++++++-------------- modules/controlcenter/ethernet/EthernetList.qml | 77 +- modules/controlcenter/network/NetworkList.qml | 81 +- modules/controlcenter/taskbar/TaskbarPane.qml | 1013 +++-------- 8 files changed, 1014 insertions(+), 2294 deletions(-) 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 'modules/controlcenter/appearance') 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 + } + } +} + diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index e3600ed..cfe5b56 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -255,1588 +255,770 @@ RowLayout { } } - Item { + CollapsibleSection { id: themeModeSection - Layout.fillWidth: true - Layout.preferredHeight: themeModeSectionHeader.implicitHeight - property bool expanded: false - - ColumnLayout { - id: themeModeSectionHeader - 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: qsTr("Theme mode") - font.pointSize: Appearance.font.size.larger - font.weight: 500 - } - - Item { - Layout.fillWidth: true - } - - MaterialIcon { - text: "expand_more" - rotation: themeModeSection.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 { - const wasExpanded = themeModeSection.expanded; - root.collapseAllSections(themeModeSection); - themeModeSection.expanded = !wasExpanded; - } - } - - StyledText { - visible: themeModeSection.expanded - text: qsTr("Light or dark theme") - color: Colours.palette.m3outline - Layout.fillWidth: true - } - } - } - - StyledRect { - visible: themeModeSection.expanded - Layout.fillWidth: true - implicitHeight: themeModeSection.expanded ? modeToggle.implicitHeight + Appearance.padding.large * 2 : 0 - - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - - Behavior on implicitHeight { - Anim {} + title: qsTr("Theme mode") + description: qsTr("Light or dark theme") + onToggleRequested: { + root.collapseAllSections(themeModeSection); } - RowLayout { - id: modeToggle - - 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: modeToggle.implicitHeight + Appearance.padding.large * 2 - StyledText { - Layout.fillWidth: true - text: qsTr("Dark mode") - } + radius: Appearance.rounding.normal + color: Colours.tPalette.m3surfaceContainer - StyledSwitch { - checked: !Colours.currentLight - onToggled: { - Colours.setMode(checked ? "dark" : "light"); - } + Behavior on implicitHeight { + Anim {} } - } - } - Item { - id: colorVariantSection - Layout.fillWidth: true - Layout.preferredHeight: colorVariantSectionHeader.implicitHeight - property bool expanded: false + RowLayout { + id: modeToggle - ColumnLayout { - id: colorVariantSectionHeader - anchors.left: parent.left - anchors.right: parent.right - spacing: Appearance.spacing.small + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.large - RowLayout { - Layout.fillWidth: true - spacing: Appearance.spacing.small + spacing: Appearance.spacing.normal StyledText { - Layout.topMargin: Appearance.spacing.large - text: qsTr("Color variant") - font.pointSize: Appearance.font.size.larger - font.weight: 500 - } - - Item { Layout.fillWidth: true + text: qsTr("Dark mode") } - MaterialIcon { - text: "expand_more" - rotation: colorVariantSection.expanded ? 180 : 0 - color: Colours.palette.m3onSurface - Behavior on rotation { - Anim {} + StyledSwitch { + checked: !Colours.currentLight + onToggled: { + Colours.setMode(checked ? "dark" : "light"); } } } - - StateLayer { - anchors.fill: parent - anchors.leftMargin: -Appearance.padding.normal - anchors.rightMargin: -Appearance.padding.normal - function onClicked(): void { - const wasExpanded = colorVariantSection.expanded; - root.collapseAllSections(colorVariantSection); - colorVariantSection.expanded = !wasExpanded; - } - } - - StyledText { - visible: colorVariantSection.expanded - text: qsTr("Material theme variant") - color: Colours.palette.m3outline - Layout.fillWidth: true - } } } - StyledListView { - visible: colorVariantSection.expanded - Layout.fillWidth: true - implicitHeight: colorVariantSection.expanded ? Math.min(400, M3Variants.list.length * 60) : 0 - Layout.topMargin: 0 - - Behavior on implicitHeight { - Anim {} + CollapsibleSection { + id: colorVariantSection + title: qsTr("Color variant") + description: qsTr("Material theme variant") + onToggleRequested: { + root.collapseAllSections(colorVariantSection); } - model: M3Variants.list - spacing: Appearance.spacing.small / 2 - clip: true - - StyledScrollBar.vertical: StyledScrollBar { - flickable: parent - } + StyledListView { + Layout.fillWidth: true + implicitHeight: colorVariantSection.expanded ? Math.min(400, M3Variants.list.length * 60) : 0 - delegate: StyledRect { - required property var modelData + Behavior on implicitHeight { + Anim {} + } - anchors.left: parent.left - anchors.right: parent.right + model: M3Variants.list + spacing: Appearance.spacing.small / 2 + clip: 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 - - 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 - onTriggered: { - Schemes.reload(); - } + StyledScrollBar.vertical: StyledScrollBar { + flickable: parent } - RowLayout { - id: variantRow + delegate: StyledRect { + required property var modelData anchors.left: parent.left anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.normal - spacing: Appearance.spacing.normal - - MaterialIcon { - text: modelData.icon - font.pointSize: Appearance.font.size.large - fill: modelData.variant === Schemes.currentVariant ? 1 : 0 - } + 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 - StyledText { - Layout.fillWidth: true - text: modelData.name - font.weight: modelData.variant === Schemes.currentVariant ? 500 : 400 + 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(); + }); + } } - - MaterialIcon { - visible: modelData.variant === Schemes.currentVariant - text: "check" - color: Colours.palette.m3primary - font.pointSize: Appearance.font.size.large + + Timer { + id: reloadTimer + interval: 300 + onTriggered: { + Schemes.reload(); + } } - } - implicitHeight: variantRow.implicitHeight + Appearance.padding.normal * 2 - } - } + RowLayout { + id: variantRow - Item { - id: colorSchemeSection - Layout.fillWidth: true - Layout.preferredHeight: colorSchemeSectionHeader.implicitHeight - property bool expanded: false - - ColumnLayout { - id: colorSchemeSectionHeader - anchors.left: parent.left - anchors.right: parent.right - spacing: Appearance.spacing.small + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.normal - RowLayout { - Layout.fillWidth: true - spacing: Appearance.spacing.small + spacing: Appearance.spacing.normal - StyledText { - Layout.topMargin: Appearance.spacing.large - text: qsTr("Color scheme") - font.pointSize: Appearance.font.size.larger - font.weight: 500 - } - - Item { - Layout.fillWidth: true - } + MaterialIcon { + text: modelData.icon + font.pointSize: Appearance.font.size.large + fill: modelData.variant === Schemes.currentVariant ? 1 : 0 + } - MaterialIcon { - text: "expand_more" - rotation: colorSchemeSection.expanded ? 180 : 0 - color: Colours.palette.m3onSurface - Behavior on rotation { - Anim {} + StyledText { + Layout.fillWidth: true + text: modelData.name + font.weight: modelData.variant === Schemes.currentVariant ? 500 : 400 } - } - } - StateLayer { - anchors.fill: parent - anchors.leftMargin: -Appearance.padding.normal - anchors.rightMargin: -Appearance.padding.normal - function onClicked(): void { - const wasExpanded = colorSchemeSection.expanded; - root.collapseAllSections(colorSchemeSection); - colorSchemeSection.expanded = !wasExpanded; + MaterialIcon { + visible: modelData.variant === Schemes.currentVariant + text: "check" + color: Colours.palette.m3primary + font.pointSize: Appearance.font.size.large + } } - } - StyledText { - visible: colorSchemeSection.expanded - text: qsTr("Available color schemes") - color: Colours.palette.m3outline - Layout.fillWidth: true + implicitHeight: variantRow.implicitHeight + Appearance.padding.normal * 2 } } } - StyledListView { - visible: colorSchemeSection.expanded - Layout.fillWidth: true - implicitHeight: colorSchemeSection.expanded ? Math.min(400, Schemes.list.length * 80) : 0 - Layout.topMargin: 0 - - Behavior on implicitHeight { - Anim {} - } - - model: Schemes.list - spacing: Appearance.spacing.small / 2 - clip: true - - StyledScrollBar.vertical: StyledScrollBar { - flickable: parent + CollapsibleSection { + id: colorSchemeSection + title: qsTr("Color scheme") + description: qsTr("Available color schemes") + onToggleRequested: { + root.collapseAllSections(colorSchemeSection); } - delegate: StyledRect { - required property var modelData + StyledListView { + Layout.fillWidth: true + implicitHeight: colorSchemeSection.expanded ? Math.min(400, Schemes.list.length * 80) : 0 - anchors.left: parent.left - anchors.right: parent.right + Behavior on implicitHeight { + Anim {} + } - readonly property string schemeKey: `${modelData.name} ${modelData.flavour}` - readonly property bool isCurrent: schemeKey === Schemes.currentScheme + model: Schemes.list + spacing: Appearance.spacing.small / 2 + clip: true - 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}`; - - // 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 - onTriggered: { - Schemes.reload(); - } + StyledScrollBar.vertical: StyledScrollBar { + flickable: parent } - RowLayout { - id: schemeRow + delegate: StyledRect { + required property var modelData anchors.left: parent.left anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.normal - spacing: Appearance.spacing.normal + readonly property string schemeKey: `${modelData.name} ${modelData.flavour}` + readonly property bool isCurrent: schemeKey === Schemes.currentScheme + + color: Qt.alpha(Colours.tPalette.m3surfaceContainer, isCurrent ? Colours.tPalette.m3surfaceContainer.a : 0) + radius: Appearance.rounding.normal + border.width: isCurrent ? 1 : 0 + border.color: Colours.palette.m3primary + + StateLayer { + function onClicked(): void { + 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 + onTriggered: { + Schemes.reload(); + } + } - StyledRect { - id: preview + RowLayout { + id: schemeRow + anchors.left: parent.left + anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.normal - border.width: 1 - border.color: Qt.alpha(`#${modelData.colours?.outline}`, 0.5) + spacing: Appearance.spacing.normal - color: `#${modelData.colours?.surface}` - radius: Appearance.rounding.full - implicitWidth: iconPlaceholder.implicitWidth - implicitHeight: iconPlaceholder.implicitWidth + StyledRect { + id: preview - MaterialIcon { - id: iconPlaceholder - visible: false - text: "circle" - font.pointSize: Appearance.font.size.large - } + anchors.verticalCenter: parent.verticalCenter - Item { - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.right: parent.right + border.width: 1 + border.color: Qt.alpha(`#${modelData.colours?.outline}`, 0.5) - implicitWidth: parent.implicitWidth / 2 - clip: true + color: `#${modelData.colours?.surface}` + radius: Appearance.rounding.full + implicitWidth: iconPlaceholder.implicitWidth + implicitHeight: iconPlaceholder.implicitWidth - StyledRect { + MaterialIcon { + id: iconPlaceholder + visible: false + text: "circle" + font.pointSize: Appearance.font.size.large + } + + 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 - Column { - Layout.fillWidth: true - spacing: 0 + StyledRect { + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.right: parent.right - StyledText { - text: modelData.flavour ?? "" - font.pointSize: Appearance.font.size.normal + implicitWidth: preview.implicitWidth + color: `#${modelData.colours?.primary}` + radius: Appearance.rounding.full + } + } } - StyledText { - text: modelData.name ?? "" - font.pointSize: Appearance.font.size.small - color: Colours.palette.m3outline + Column { + Layout.fillWidth: true + spacing: 0 - elide: Text.ElideRight - anchors.left: parent.left - anchors.right: parent.right - } - } + StyledText { + text: modelData.flavour ?? "" + font.pointSize: Appearance.font.size.normal + } - Loader { - active: isCurrent - asynchronous: true + StyledText { + text: modelData.name ?? "" + font.pointSize: Appearance.font.size.small + color: Colours.palette.m3outline - sourceComponent: MaterialIcon { - text: "check" - color: Colours.palette.m3onSurfaceVariant - font.pointSize: Appearance.font.size.large + elide: Text.ElideRight + anchors.left: parent.left + anchors.right: parent.right + } } - } - } - - implicitHeight: schemeRow.implicitHeight + Appearance.padding.normal * 2 - } - } - Item { - id: animationsSection - Layout.fillWidth: true - Layout.preferredHeight: animationsSectionHeader.implicitHeight - property bool expanded: false - - ColumnLayout { - id: animationsSectionHeader - anchors.left: parent.left - anchors.right: parent.right - spacing: Appearance.spacing.small - - RowLayout { - Layout.fillWidth: true - spacing: Appearance.spacing.small + Loader { + active: isCurrent + asynchronous: true - StyledText { - Layout.topMargin: Appearance.spacing.large - text: qsTr("Animations") - font.pointSize: Appearance.font.size.larger - font.weight: 500 - } - - Item { - Layout.fillWidth: true - } - - MaterialIcon { - text: "expand_more" - rotation: animationsSection.expanded ? 180 : 0 - color: Colours.palette.m3onSurface - Behavior on rotation { - Anim {} + sourceComponent: MaterialIcon { + text: "check" + color: Colours.palette.m3onSurfaceVariant + font.pointSize: Appearance.font.size.large + } } } - } - StateLayer { - anchors.fill: parent - anchors.leftMargin: -Appearance.padding.normal - anchors.rightMargin: -Appearance.padding.normal - function onClicked(): void { - const wasExpanded = animationsSection.expanded; - root.collapseAllSections(animationsSection); - animationsSection.expanded = !wasExpanded; - } + implicitHeight: schemeRow.implicitHeight + Appearance.padding.normal * 2 } } } - StyledRect { - visible: animationsSection.expanded - Layout.fillWidth: true - Layout.topMargin: Appearance.spacing.small / 2 - implicitHeight: animationsSection.expanded ? animDurationsScaleRow.implicitHeight + Appearance.padding.large * 2 : 0 - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - - Behavior on implicitHeight { - Anim {} + CollapsibleSection { + id: animationsSection + title: qsTr("Animations") + onToggleRequested: { + root.collapseAllSections(animationsSection); } - RowLayout { - id: animDurationsScaleRow - 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("Animation duration scale") - } - - CustomSpinBox { - id: animDurationsScaleSpinBox - min: 0.1 - max: 5 - value: root.animDurationsScale - onValueModified: value => { - root.animDurationsScale = value; - root.saveConfig(); - } + SpinBoxRow { + label: qsTr("Animation duration scale") + min: 0.1 + max: 5 + value: root.animDurationsScale + onValueModified: value => { + root.animDurationsScale = value; + root.saveConfig(); } } } - Item { + CollapsibleSection { id: fontsSection - Layout.fillWidth: true - Layout.preferredHeight: fontsSectionHeader.implicitHeight - property bool expanded: false - - ColumnLayout { - id: fontsSectionHeader - 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: qsTr("Fonts") - font.pointSize: Appearance.font.size.larger - font.weight: 500 - } - - Item { - Layout.fillWidth: true - } - - MaterialIcon { - text: "expand_more" - rotation: fontsSection.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 { - const wasExpanded = fontsSection.expanded; - root.collapseAllSections(fontsSection); - fontsSection.expanded = !wasExpanded; - } - } + title: qsTr("Fonts") + onToggleRequested: { + root.collapseAllSections(fontsSection); } - } - StyledText { - visible: fontsSection.expanded - Layout.topMargin: Appearance.spacing.normal - text: qsTr("Material font family") - font.pointSize: Appearance.font.size.larger - font.weight: 500 - } - - StyledListView { - visible: fontsSection.expanded - Layout.fillWidth: true - implicitHeight: fontsSection.expanded ? Math.min(300, Qt.fontFamilies().length * 50) : 0 - Layout.topMargin: 0 - - Behavior on implicitHeight { - Anim {} - } - - model: Qt.fontFamilies() - spacing: Appearance.spacing.small / 2 - clip: true - - StyledScrollBar.vertical: StyledScrollBar { - flickable: parent + StyledText { + Layout.topMargin: Appearance.spacing.normal + text: qsTr("Material font family") + font.pointSize: Appearance.font.size.larger + font.weight: 500 } - delegate: StyledRect { - required property string modelData + StyledListView { + Layout.fillWidth: true + implicitHeight: fontsSection.expanded ? Math.min(300, Qt.fontFamilies().length * 50) : 0 - anchors.left: parent.left - anchors.right: parent.right + Behavior on implicitHeight { + Anim {} + } - readonly property bool isCurrent: modelData === root.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 + model: Qt.fontFamilies() + spacing: Appearance.spacing.small / 2 + clip: true - StateLayer { - function onClicked(): void { - root.fontFamilyMaterial = modelData; - root.saveConfig(); - } + StyledScrollBar.vertical: StyledScrollBar { + flickable: parent } - RowLayout { - id: fontFamilyMaterialRow + delegate: StyledRect { + required property string modelData anchors.left: parent.left anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.normal - spacing: Appearance.spacing.normal - - StyledText { - text: modelData - font.pointSize: Appearance.font.size.normal - } - - Item { - Layout.fillWidth: true - } + readonly property bool isCurrent: modelData === root.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 - Loader { - active: isCurrent - asynchronous: true - - sourceComponent: MaterialIcon { - text: "check" - color: Colours.palette.m3onSurfaceVariant - font.pointSize: Appearance.font.size.large + StateLayer { + function onClicked(): void { + root.fontFamilyMaterial = modelData; + root.saveConfig(); } } - } - - implicitHeight: fontFamilyMaterialRow.implicitHeight + Appearance.padding.normal * 2 - } - } - - StyledText { - visible: fontsSection.expanded - Layout.topMargin: Appearance.spacing.normal - text: qsTr("Monospace font family") - font.pointSize: Appearance.font.size.larger - font.weight: 500 - } - - StyledListView { - visible: fontsSection.expanded - Layout.fillWidth: true - implicitHeight: fontsSection.expanded ? Math.min(300, Qt.fontFamilies().length * 50) : 0 - Layout.topMargin: 0 - - Behavior on implicitHeight { - Anim {} - } - - model: Qt.fontFamilies() - spacing: Appearance.spacing.small / 2 - clip: true - - StyledScrollBar.vertical: StyledScrollBar { - flickable: parent - } - - delegate: StyledRect { - required property string modelData - - anchors.left: parent.left - anchors.right: parent.right - - readonly property bool isCurrent: modelData === root.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 { - root.fontFamilyMono = modelData; - root.saveConfig(); - } - } - RowLayout { - id: fontFamilyMonoRow + 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: fontFamilyMonoRow.implicitHeight + Appearance.padding.normal * 2 - } - } - - StyledText { - visible: fontsSection.expanded - Layout.topMargin: Appearance.spacing.normal - text: qsTr("Sans-serif font family") - font.pointSize: Appearance.font.size.larger - font.weight: 500 - } - - StyledListView { - visible: fontsSection.expanded - Layout.fillWidth: true - implicitHeight: fontsSection.expanded ? Math.min(300, Qt.fontFamilies().length * 50) : 0 - Layout.topMargin: 0 - Behavior on implicitHeight { - Anim {} + implicitHeight: fontFamilyMaterialRow.implicitHeight + Appearance.padding.normal * 2 + } } - model: Qt.fontFamilies() - spacing: Appearance.spacing.small / 2 - clip: true - - StyledScrollBar.vertical: StyledScrollBar { - flickable: parent + StyledText { + Layout.topMargin: Appearance.spacing.normal + text: qsTr("Monospace font family") + font.pointSize: Appearance.font.size.larger + font.weight: 500 } - delegate: StyledRect { - required property string modelData + StyledListView { + Layout.fillWidth: true + implicitHeight: fontsSection.expanded ? Math.min(300, Qt.fontFamilies().length * 50) : 0 - anchors.left: parent.left - anchors.right: parent.right + Behavior on implicitHeight { + Anim {} + } - readonly property bool isCurrent: modelData === root.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 + model: Qt.fontFamilies() + spacing: Appearance.spacing.small / 2 + clip: true - StateLayer { - function onClicked(): void { - root.fontFamilySans = modelData; - root.saveConfig(); - } + StyledScrollBar.vertical: StyledScrollBar { + flickable: parent } - RowLayout { - id: fontFamilySansRow + delegate: StyledRect { + required property string modelData anchors.left: parent.left anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.normal - spacing: Appearance.spacing.normal - - StyledText { - text: modelData - font.pointSize: Appearance.font.size.normal - } - - Item { - Layout.fillWidth: true - } + readonly property bool isCurrent: modelData === root.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 - Loader { - active: isCurrent - asynchronous: true - - sourceComponent: MaterialIcon { - text: "check" - color: Colours.palette.m3onSurfaceVariant - font.pointSize: Appearance.font.size.large + StateLayer { + function onClicked(): void { + root.fontFamilyMono = modelData; + root.saveConfig(); } } - } - - implicitHeight: fontFamilySansRow.implicitHeight + Appearance.padding.normal * 2 - } - } - StyledRect { - visible: fontsSection.expanded - Layout.fillWidth: true - Layout.topMargin: Appearance.spacing.small / 2 - implicitHeight: fontsSection.expanded ? fontSizeScaleRow.implicitHeight + Appearance.padding.large * 2 : 0 - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer + RowLayout { + id: fontFamilyMonoRow - 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(); - } - } - } - } - - Item { - id: scalesSection - Layout.fillWidth: true - Layout.preferredHeight: scalesSectionHeader.implicitHeight - property bool expanded: false + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.normal - ColumnLayout { - id: scalesSectionHeader - anchors.left: parent.left - anchors.right: parent.right - spacing: Appearance.spacing.small + spacing: Appearance.spacing.normal - RowLayout { - Layout.fillWidth: true - spacing: Appearance.spacing.small + StyledText { + text: modelData + font.pointSize: Appearance.font.size.normal + } - StyledText { - Layout.topMargin: Appearance.spacing.large - text: qsTr("Scales") - font.pointSize: Appearance.font.size.larger - font.weight: 500 - } + Item { + Layout.fillWidth: true + } - Item { - Layout.fillWidth: true - } + Loader { + active: isCurrent + asynchronous: true - MaterialIcon { - text: "expand_more" - rotation: scalesSection.expanded ? 180 : 0 - color: Colours.palette.m3onSurface - Behavior on rotation { - Anim {} + sourceComponent: MaterialIcon { + text: "check" + color: Colours.palette.m3onSurfaceVariant + font.pointSize: Appearance.font.size.large + } } } - } - StateLayer { - anchors.fill: parent - anchors.leftMargin: -Appearance.padding.normal - anchors.rightMargin: -Appearance.padding.normal - function onClicked(): void { - const wasExpanded = scalesSection.expanded; - root.collapseAllSections(scalesSection); - scalesSection.expanded = !wasExpanded; - } + implicitHeight: fontFamilyMonoRow.implicitHeight + Appearance.padding.normal * 2 } } - } - StyledRect { - visible: scalesSection.expanded - Layout.fillWidth: true - Layout.topMargin: Appearance.spacing.small / 2 - implicitHeight: scalesSection.expanded ? paddingScaleRow.implicitHeight + Appearance.padding.large * 2 : 0 - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - - Behavior on implicitHeight { - Anim {} + StyledText { + Layout.topMargin: Appearance.spacing.normal + text: qsTr("Sans-serif font family") + font.pointSize: Appearance.font.size.larger + font.weight: 500 } - RowLayout { - id: paddingScaleRow - 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("Padding scale") - } + StyledListView { + Layout.fillWidth: true + implicitHeight: fontsSection.expanded ? Math.min(300, Qt.fontFamilies().length * 50) : 0 - CustomSpinBox { - id: paddingScaleSpinBox - min: 0.1 - max: 5 - value: root.paddingScale - onValueModified: value => { - root.paddingScale = value; - root.saveConfig(); - } + Behavior on implicitHeight { + Anim {} } - } - } - StyledRect { - visible: scalesSection.expanded - Layout.fillWidth: true - Layout.topMargin: Appearance.spacing.small / 2 - implicitHeight: scalesSection.expanded ? roundingScaleRow.implicitHeight + Appearance.padding.large * 2 : 0 - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - - Behavior on implicitHeight { - Anim {} - } + model: Qt.fontFamilies() + spacing: Appearance.spacing.small / 2 + clip: true - RowLayout { - id: roundingScaleRow - 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("Rounding scale") + StyledScrollBar.vertical: StyledScrollBar { + flickable: parent } - CustomSpinBox { - id: roundingScaleSpinBox - min: 0.1 - max: 5 - value: root.roundingScale - onValueModified: value => { - root.roundingScale = value; - root.saveConfig(); - } - } - } - } + delegate: StyledRect { + required property string modelData - StyledRect { - visible: scalesSection.expanded - Layout.fillWidth: true - Layout.topMargin: Appearance.spacing.small / 2 - implicitHeight: scalesSection.expanded ? spacingScaleRow.implicitHeight + Appearance.padding.large * 2 : 0 - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - - Behavior on implicitHeight { - Anim {} - } + anchors.left: parent.left + anchors.right: parent.right - RowLayout { - id: spacingScaleRow - 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("Spacing scale") - } + readonly property bool isCurrent: modelData === root.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 - CustomSpinBox { - id: spacingScaleSpinBox - min: 0.1 - max: 5 - value: root.spacingScale - onValueModified: value => { - root.spacingScale = value; - root.saveConfig(); + StateLayer { + function onClicked(): void { + root.fontFamilySans = modelData; + root.saveConfig(); + } } - } - } - } - - Item { - id: transparencySection - Layout.fillWidth: true - Layout.preferredHeight: transparencySectionHeader.implicitHeight - property bool expanded: false - ColumnLayout { - id: transparencySectionHeader - anchors.left: parent.left - anchors.right: parent.right - spacing: Appearance.spacing.small + RowLayout { + id: fontFamilySansRow - RowLayout { - Layout.fillWidth: true - spacing: Appearance.spacing.small + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.normal - StyledText { - Layout.topMargin: Appearance.spacing.large - text: qsTr("Transparency") - font.pointSize: Appearance.font.size.larger - font.weight: 500 - } + spacing: Appearance.spacing.normal - Item { - Layout.fillWidth: true - } - - MaterialIcon { - text: "expand_more" - rotation: transparencySection.expanded ? 180 : 0 - color: Colours.palette.m3onSurface - Behavior on rotation { - Anim {} + StyledText { + text: modelData + font.pointSize: Appearance.font.size.normal } - } - } - StateLayer { - anchors.fill: parent - anchors.leftMargin: -Appearance.padding.normal - anchors.rightMargin: -Appearance.padding.normal - function onClicked(): void { - const wasExpanded = transparencySection.expanded; - root.collapseAllSections(transparencySection); - transparencySection.expanded = !wasExpanded; - } - } - } - } - - StyledRect { - visible: transparencySection.expanded - Layout.fillWidth: true - Layout.topMargin: Appearance.spacing.small / 2 - implicitHeight: transparencySection.expanded ? transparencyEnabledRow.implicitHeight + Appearance.padding.large * 2 : 0 - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - - Behavior on implicitHeight { - Anim {} - } + Item { + Layout.fillWidth: true + } - RowLayout { - id: transparencyEnabledRow - 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("Transparency enabled") - } + Loader { + active: isCurrent + asynchronous: true - StyledSwitch { - id: transparencyEnabledSwitch - checked: root.transparencyEnabled - onToggled: { - root.transparencyEnabled = checked; - root.saveConfig(); + sourceComponent: MaterialIcon { + text: "check" + color: Colours.palette.m3onSurfaceVariant + font.pointSize: Appearance.font.size.large + } + } } - } - } - } - - StyledRect { - visible: transparencySection.expanded - Layout.fillWidth: true - Layout.topMargin: Appearance.spacing.small / 2 - implicitHeight: transparencySection.expanded ? transparencyBaseRow.implicitHeight + Appearance.padding.large * 2 : 0 - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - Behavior on implicitHeight { - Anim {} - } - - RowLayout { - id: transparencyBaseRow - 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("Transparency base") + implicitHeight: fontFamilySansRow.implicitHeight + Appearance.padding.normal * 2 } - - CustomSpinBox { - id: transparencyBaseSpinBox - min: 0 - max: 1 - value: root.transparencyBase - onValueModified: value => { - root.transparencyBase = value; - root.saveConfig(); - } - } - } - } - - StyledRect { - visible: transparencySection.expanded - Layout.fillWidth: true - Layout.topMargin: Appearance.spacing.small / 2 - implicitHeight: transparencySection.expanded ? transparencyLayersRow.implicitHeight + Appearance.padding.large * 2 : 0 - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - - Behavior on implicitHeight { - Anim {} } - RowLayout { - id: transparencyLayersRow - 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("Transparency layers") - } + StyledRect { + Layout.fillWidth: true + implicitHeight: fontSizeScaleRow.implicitHeight + Appearance.padding.large * 2 + radius: Appearance.rounding.normal + color: Colours.tPalette.m3surfaceContainer - CustomSpinBox { - id: transparencyLayersSpinBox - min: 0 - max: 1 - value: root.transparencyLayers - onValueModified: value => { - root.transparencyLayers = value; - root.saveConfig(); - } + Behavior on implicitHeight { + Anim {} } - } - } - - Item { - id: borderSection - Layout.fillWidth: true - Layout.preferredHeight: borderSectionHeader.implicitHeight - property bool expanded: false - - ColumnLayout { - id: borderSectionHeader - anchors.left: parent.left - anchors.right: parent.right - spacing: Appearance.spacing.small RowLayout { - Layout.fillWidth: true - spacing: Appearance.spacing.small + 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.topMargin: Appearance.spacing.large - text: qsTr("Border") - font.pointSize: Appearance.font.size.larger - font.weight: 500 - } - - Item { Layout.fillWidth: true + text: qsTr("Font size scale") } - MaterialIcon { - text: "expand_more" - rotation: borderSection.expanded ? 180 : 0 - color: Colours.palette.m3onSurface - Behavior on rotation { - Anim {} + CustomSpinBox { + id: fontSizeScaleSpinBox + min: 0.1 + max: 5 + value: root.fontSizeScale + onValueModified: value => { + root.fontSizeScale = value; + root.saveConfig(); } } } - - StateLayer { - anchors.fill: parent - anchors.leftMargin: -Appearance.padding.normal - anchors.rightMargin: -Appearance.padding.normal - function onClicked(): void { - const wasExpanded = borderSection.expanded; - root.collapseAllSections(borderSection); - borderSection.expanded = !wasExpanded; - } - } } } - StyledRect { - visible: borderSection.expanded - Layout.fillWidth: true - Layout.topMargin: Appearance.spacing.small / 2 - implicitHeight: borderSection.expanded ? borderRoundingRow.implicitHeight + Appearance.padding.large * 2 : 0 - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - - Behavior on implicitHeight { - Anim {} + CollapsibleSection { + id: scalesSection + title: qsTr("Scales") + onToggleRequested: { + root.collapseAllSections(scalesSection); } - RowLayout { - id: borderRoundingRow - 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("Border rounding") + SpinBoxRow { + label: qsTr("Padding scale") + min: 0.1 + max: 5 + value: root.paddingScale + onValueModified: value => { + root.paddingScale = value; + root.saveConfig(); } - - CustomSpinBox { - id: borderRoundingSpinBox - min: 0.1 - max: 5 - value: root.borderRounding - onValueModified: value => { - root.borderRounding = value; - root.saveConfig(); - } - } - } - } - - StyledRect { - visible: borderSection.expanded - Layout.fillWidth: true - Layout.topMargin: Appearance.spacing.small / 2 - implicitHeight: borderSection.expanded ? borderThicknessRow.implicitHeight + Appearance.padding.large * 2 : 0 - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - - Behavior on implicitHeight { - Anim {} } - RowLayout { - id: borderThicknessRow - 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("Border thickness") - } - - CustomSpinBox { - id: borderThicknessSpinBox - min: 0.1 - max: 5 - value: root.borderThickness - onValueModified: value => { - root.borderThickness = value; - root.saveConfig(); - } + SpinBoxRow { + label: qsTr("Rounding scale") + min: 0.1 + max: 5 + value: root.roundingScale + onValueModified: value => { + root.roundingScale = value; + root.saveConfig(); } } - } - - Item { - id: backgroundSection - Layout.fillWidth: true - Layout.preferredHeight: backgroundSectionHeader.implicitHeight - property bool expanded: false - - ColumnLayout { - id: backgroundSectionHeader - 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: qsTr("Background") - font.pointSize: Appearance.font.size.larger - font.weight: 500 - } - - Item { - Layout.fillWidth: true - } - MaterialIcon { - text: "expand_more" - rotation: backgroundSection.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 { - const wasExpanded = backgroundSection.expanded; - root.collapseAllSections(backgroundSection); - backgroundSection.expanded = !wasExpanded; - } + SpinBoxRow { + label: qsTr("Spacing scale") + min: 0.1 + max: 5 + value: root.spacingScale + onValueModified: value => { + root.spacingScale = value; + root.saveConfig(); } } } - StyledRect { - visible: backgroundSection.expanded - Layout.fillWidth: true - Layout.topMargin: Appearance.spacing.small / 2 - implicitHeight: backgroundSection.expanded ? desktopClockRow.implicitHeight + Appearance.padding.large * 2 : 0 - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - - Behavior on implicitHeight { - Anim {} + CollapsibleSection { + id: transparencySection + title: qsTr("Transparency") + onToggleRequested: { + root.collapseAllSections(transparencySection); } - RowLayout { - id: desktopClockRow - 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("Desktop clock") + SwitchRow { + label: qsTr("Transparency enabled") + checked: root.transparencyEnabled + onToggled: checked => { + root.transparencyEnabled = checked; + root.saveConfig(); } - - StyledSwitch { - id: desktopClockSwitch - checked: root.desktopClockEnabled - onToggled: { - root.desktopClockEnabled = checked; - root.saveConfig(); - } - } - } - } - - StyledRect { - visible: backgroundSection.expanded - Layout.fillWidth: true - Layout.topMargin: Appearance.spacing.small / 2 - implicitHeight: backgroundSection.expanded ? backgroundEnabledRow.implicitHeight + Appearance.padding.large * 2 : 0 - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - - Behavior on implicitHeight { - Anim {} } - RowLayout { - id: backgroundEnabledRow - 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("Background enabled") + SpinBoxRow { + label: qsTr("Transparency base") + min: 0 + max: 1 + value: root.transparencyBase + onValueModified: value => { + root.transparencyBase = value; + root.saveConfig(); } + } - StyledSwitch { - id: backgroundEnabledSwitch - checked: root.backgroundEnabled - onToggled: { - root.backgroundEnabled = checked; - root.saveConfig(); - } + SpinBoxRow { + label: qsTr("Transparency layers") + min: 0 + max: 1 + value: root.transparencyLayers + onValueModified: value => { + root.transparencyLayers = value; + root.saveConfig(); } } } - StyledText { - visible: backgroundSection.expanded - Layout.topMargin: Appearance.spacing.normal - text: qsTr("Visualiser") - font.pointSize: Appearance.font.size.larger - font.weight: 500 - } - - StyledRect { - visible: backgroundSection.expanded - Layout.fillWidth: true - Layout.topMargin: Appearance.spacing.small / 2 - implicitHeight: backgroundSection.expanded ? visualiserEnabledRow.implicitHeight + Appearance.padding.large * 2 : 0 - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - - Behavior on implicitHeight { - Anim {} + CollapsibleSection { + id: borderSection + title: qsTr("Border") + onToggleRequested: { + root.collapseAllSections(borderSection); } - RowLayout { - id: visualiserEnabledRow - 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("Visualiser enabled") + SpinBoxRow { + label: qsTr("Border rounding") + min: 0.1 + max: 5 + value: root.borderRounding + onValueModified: value => { + root.borderRounding = value; + root.saveConfig(); } + } - StyledSwitch { - id: visualiserEnabledSwitch - checked: root.visualiserEnabled - onToggled: { - root.visualiserEnabled = checked; - root.saveConfig(); - } + SpinBoxRow { + label: qsTr("Border thickness") + min: 0.1 + max: 5 + value: root.borderThickness + onValueModified: value => { + root.borderThickness = value; + root.saveConfig(); } } } - StyledRect { - visible: backgroundSection.expanded - Layout.fillWidth: true - Layout.topMargin: Appearance.spacing.small / 2 - implicitHeight: backgroundSection.expanded ? visualiserAutoHideRow.implicitHeight + Appearance.padding.large * 2 : 0 - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - - Behavior on implicitHeight { - Anim {} + CollapsibleSection { + id: backgroundSection + title: qsTr("Background") + onToggleRequested: { + root.collapseAllSections(backgroundSection); } - RowLayout { - id: visualiserAutoHideRow - 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("Visualiser auto hide") + SwitchRow { + label: qsTr("Desktop clock") + checked: root.desktopClockEnabled + onToggled: checked => { + root.desktopClockEnabled = checked; + root.saveConfig(); } + } - StyledSwitch { - id: visualiserAutoHideSwitch - checked: root.visualiserAutoHide - onToggled: { - root.visualiserAutoHide = checked; - root.saveConfig(); - } + SwitchRow { + label: qsTr("Background enabled") + checked: root.backgroundEnabled + onToggled: checked => { + root.backgroundEnabled = checked; + root.saveConfig(); } } - } - StyledRect { - visible: backgroundSection.expanded - Layout.fillWidth: true - Layout.topMargin: Appearance.spacing.small / 2 - implicitHeight: backgroundSection.expanded ? visualiserRoundingRow.implicitHeight + Appearance.padding.large * 2 : 0 - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - - Behavior on implicitHeight { - Anim {} + StyledText { + Layout.topMargin: Appearance.spacing.normal + text: qsTr("Visualiser") + font.pointSize: Appearance.font.size.larger + font.weight: 500 } - RowLayout { - id: visualiserRoundingRow - 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("Visualiser rounding") - } - - CustomSpinBox { - id: visualiserRoundingSpinBox - min: 0 - max: 10 - value: Math.round(root.visualiserRounding) - onValueModified: value => { - root.visualiserRounding = value; - root.saveConfig(); - } + SwitchRow { + label: qsTr("Visualiser enabled") + checked: root.visualiserEnabled + onToggled: checked => { + root.visualiserEnabled = checked; + root.saveConfig(); } } - } - - StyledRect { - visible: backgroundSection.expanded - Layout.fillWidth: true - Layout.topMargin: Appearance.spacing.small / 2 - implicitHeight: backgroundSection.expanded ? visualiserSpacingRow.implicitHeight + Appearance.padding.large * 2 : 0 - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - Behavior on implicitHeight { - Anim {} + SwitchRow { + label: qsTr("Visualiser auto hide") + checked: root.visualiserAutoHide + onToggled: checked => { + root.visualiserAutoHide = checked; + root.saveConfig(); + } } - RowLayout { - id: visualiserSpacingRow - 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("Visualiser spacing") + SpinBoxRow { + label: qsTr("Visualiser rounding") + min: 0 + max: 10 + value: Math.round(root.visualiserRounding) + onValueModified: value => { + root.visualiserRounding = value; + root.saveConfig(); } + } - CustomSpinBox { - id: visualiserSpacingSpinBox - min: 0 - max: 10 - value: Math.round(root.visualiserSpacing) - onValueModified: value => { - root.visualiserSpacing = value; - root.saveConfig(); - } + SpinBoxRow { + label: qsTr("Visualiser spacing") + min: 0 + max: 10 + value: Math.round(root.visualiserSpacing) + onValueModified: value => { + root.visualiserSpacing = value; + root.saveConfig(); } } } diff --git a/modules/controlcenter/ethernet/EthernetList.qml b/modules/controlcenter/ethernet/EthernetList.qml index ff7cee2..8b04c09 100644 --- a/modules/controlcenter/ethernet/EthernetList.qml +++ b/modules/controlcenter/ethernet/EthernetList.qml @@ -34,7 +34,7 @@ ColumnLayout { icon: "settings" accent: "Primary" - function onClicked(): void { + onClicked: { if (root.session.ethernet.active) root.session.ethernet.active = null; else { @@ -166,81 +166,6 @@ ColumnLayout { implicitHeight: rowLayout.implicitHeight + Appearance.padding.normal * 2 } } - - component ToggleButton: StyledRect { - id: toggleBtn - - required property bool toggled - property string icon - property string label - property string accent: "Secondary" - - function onClicked(): void { - } - - Layout.preferredWidth: implicitWidth + (toggleStateLayer.pressed ? Appearance.padding.normal * 2 : toggled ? Appearance.padding.small * 2 : 0) - implicitWidth: toggleBtnInner.implicitWidth + Appearance.padding.large * 2 - implicitHeight: toggleBtnIcon.implicitHeight + Appearance.padding.normal * 2 - - radius: toggled || toggleStateLayer.pressed ? Appearance.rounding.small : Math.min(width, height) / 2 * Math.min(1, Appearance.rounding.scale) - color: toggled ? Colours.palette[`m3${accent.toLowerCase()}`] : Colours.palette[`m3${accent.toLowerCase()}Container`] - - StateLayer { - id: toggleStateLayer - - color: toggleBtn.toggled ? Colours.palette[`m3on${toggleBtn.accent}`] : Colours.palette[`m3on${toggleBtn.accent}Container`] - - function onClicked(): void { - toggleBtn.onClicked(); - } - } - - RowLayout { - id: toggleBtnInner - - anchors.centerIn: parent - spacing: Appearance.spacing.normal - - MaterialIcon { - id: toggleBtnIcon - - visible: !!text - fill: toggleBtn.toggled ? 1 : 0 - text: toggleBtn.icon - color: toggleBtn.toggled ? Colours.palette[`m3on${toggleBtn.accent}`] : Colours.palette[`m3on${toggleBtn.accent}Container`] - font.pointSize: Appearance.font.size.large - - Behavior on fill { - Anim {} - } - } - - Loader { - asynchronous: true - active: !!toggleBtn.label - visible: active - - sourceComponent: StyledText { - text: toggleBtn.label - color: toggleBtn.toggled ? Colours.palette[`m3on${toggleBtn.accent}`] : Colours.palette[`m3on${toggleBtn.accent}Container`] - } - } - } - - Behavior on radius { - Anim { - duration: Appearance.anim.durations.expressiveFastSpatial - easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial - } - } - - Behavior on Layout.preferredWidth { - Anim { - duration: Appearance.anim.durations.expressiveFastSpatial - easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial - } - } - } } diff --git a/modules/controlcenter/network/NetworkList.qml b/modules/controlcenter/network/NetworkList.qml index 60bd857..09d7352 100644 --- a/modules/controlcenter/network/NetworkList.qml +++ b/modules/controlcenter/network/NetworkList.qml @@ -34,7 +34,7 @@ ColumnLayout { icon: "wifi" accent: "Tertiary" - function onClicked(): void { + onClicked: { Network.toggleWifi(); } } @@ -44,7 +44,7 @@ ColumnLayout { icon: "wifi_find" accent: "Secondary" - function onClicked(): void { + onClicked: { Network.rescanWifi(); } } @@ -54,7 +54,7 @@ ColumnLayout { icon: "settings" accent: "Primary" - function onClicked(): void { + onClicked: { if (root.session.network.active) root.session.network.active = null; else { @@ -224,79 +224,4 @@ ColumnLayout { implicitHeight: rowLayout.implicitHeight + Appearance.padding.normal * 2 } } - - component ToggleButton: StyledRect { - id: toggleBtn - - required property bool toggled - property string icon - property string label - property string accent: "Secondary" - - function onClicked(): void { - } - - Layout.preferredWidth: implicitWidth + (toggleStateLayer.pressed ? Appearance.padding.normal * 2 : toggled ? Appearance.padding.small * 2 : 0) - implicitWidth: toggleBtnInner.implicitWidth + Appearance.padding.large * 2 - implicitHeight: toggleBtnIcon.implicitHeight + Appearance.padding.normal * 2 - - radius: toggled || toggleStateLayer.pressed ? Appearance.rounding.small : Math.min(width, height) / 2 * Math.min(1, Appearance.rounding.scale) - color: toggled ? Colours.palette[`m3${accent.toLowerCase()}`] : Colours.palette[`m3${accent.toLowerCase()}Container`] - - StateLayer { - id: toggleStateLayer - - color: toggleBtn.toggled ? Colours.palette[`m3on${toggleBtn.accent}`] : Colours.palette[`m3on${toggleBtn.accent}Container`] - - function onClicked(): void { - toggleBtn.onClicked(); - } - } - - RowLayout { - id: toggleBtnInner - - anchors.centerIn: parent - spacing: Appearance.spacing.normal - - MaterialIcon { - id: toggleBtnIcon - - visible: !!text - fill: toggleBtn.toggled ? 1 : 0 - text: toggleBtn.icon - color: toggleBtn.toggled ? Colours.palette[`m3on${toggleBtn.accent}`] : Colours.palette[`m3on${toggleBtn.accent}Container`] - font.pointSize: Appearance.font.size.large - - Behavior on fill { - Anim {} - } - } - - Loader { - asynchronous: true - active: !!toggleBtn.label - visible: active - - sourceComponent: StyledText { - text: toggleBtn.label - color: toggleBtn.toggled ? Colours.palette[`m3on${toggleBtn.accent}`] : Colours.palette[`m3on${toggleBtn.accent}Container`] - } - } - } - - Behavior on radius { - Anim { - duration: Appearance.anim.durations.expressiveFastSpatial - easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial - } - } - - Behavior on Layout.preferredWidth { - Anim { - duration: Appearance.anim.durations.expressiveFastSpatial - easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial - } - } - } } diff --git a/modules/controlcenter/taskbar/TaskbarPane.qml b/modules/controlcenter/taskbar/TaskbarPane.qml index 22414c7..2bb50d8 100644 --- a/modules/controlcenter/taskbar/TaskbarPane.qml +++ b/modules/controlcenter/taskbar/TaskbarPane.qml @@ -239,73 +239,12 @@ RowLayout { } } - Item { + CollapsibleSection { id: clockSection - Layout.fillWidth: true - Layout.preferredHeight: clockSectionHeader.implicitHeight - property bool expanded: false - - ColumnLayout { - id: clockSectionHeader - 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: qsTr("Clock") - font.pointSize: Appearance.font.size.larger - font.weight: 500 - } - - Item { - Layout.fillWidth: true - } - - MaterialIcon { - text: "expand_more" - rotation: clockSection.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 { - const wasExpanded = clockSection.expanded; - root.collapseAllSections(clockSection); - clockSection.expanded = !wasExpanded; - } - } - - StyledText { - visible: clockSection.expanded - text: qsTr("Clock display settings") - color: Colours.palette.m3outline - Layout.fillWidth: true - } - } - } - - StyledRect { - Layout.fillWidth: true - visible: clockSection.expanded - implicitHeight: clockSection.expanded ? clockRow.implicitHeight + Appearance.padding.large * 2 : 0 - - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - - Behavior on implicitHeight { - Anim {} + title: qsTr("Clock") + description: qsTr("Clock display settings") + onToggleRequested: { + root.collapseAllSections(clockSection); } RowLayout { @@ -333,835 +272,319 @@ RowLayout { } } - Item { + CollapsibleSection { id: barBehaviorSection - Layout.fillWidth: true - Layout.preferredHeight: barBehaviorSectionHeader.implicitHeight - property bool expanded: false - - ColumnLayout { - id: barBehaviorSectionHeader - 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: qsTr("Bar Behavior") - font.pointSize: Appearance.font.size.larger - font.weight: 500 - } - - Item { - Layout.fillWidth: true - } - - MaterialIcon { - text: "expand_more" - rotation: barBehaviorSection.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 { - const wasExpanded = barBehaviorSection.expanded; - root.collapseAllSections(barBehaviorSection); - barBehaviorSection.expanded = !wasExpanded; - } - } + title: qsTr("Bar Behavior") + onToggleRequested: { + root.collapseAllSections(barBehaviorSection); } - } - - StyledRect { - visible: barBehaviorSection.expanded - Layout.fillWidth: true - Layout.topMargin: Appearance.spacing.small / 2 - implicitHeight: barBehaviorSection.expanded ? persistentRow.implicitHeight + Appearance.padding.large * 2 : 0 - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - - Behavior on implicitHeight { - Anim {} - } - - RowLayout { - id: persistentRow - 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("Persistent") - } - StyledSwitch { - checked: root.persistent - onToggled: { - root.persistent = checked; - root.saveConfig(); - } + SwitchRow { + label: qsTr("Persistent") + checked: root.persistent + onToggled: checked => { + root.persistent = checked; + root.saveConfig(); } } - } - - StyledRect { - visible: barBehaviorSection.expanded - Layout.fillWidth: true - Layout.topMargin: Appearance.spacing.small / 2 - implicitHeight: barBehaviorSection.expanded ? showOnHoverRow.implicitHeight + Appearance.padding.large * 2 : 0 - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - - Behavior on implicitHeight { - Anim {} - } - - RowLayout { - id: showOnHoverRow - 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 on hover") - } - StyledSwitch { - checked: root.showOnHover - onToggled: { - root.showOnHover = checked; - root.saveConfig(); - } + SwitchRow { + label: qsTr("Show on hover") + checked: root.showOnHover + onToggled: checked => { + root.showOnHover = checked; + root.saveConfig(); } } - } - - StyledRect { - visible: barBehaviorSection.expanded - Layout.fillWidth: true - Layout.topMargin: Appearance.spacing.small / 2 - implicitHeight: barBehaviorSection.expanded ? dragThresholdRow.implicitHeight + Appearance.padding.large * 2 : 0 - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - - Behavior on implicitHeight { - Anim {} - } - RowLayout { - id: dragThresholdRow - 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("Drag threshold") - } - - CustomSpinBox { - min: 0 - max: 100 - value: root.dragThreshold - onValueModified: value => { - root.dragThreshold = value; - root.saveConfig(); - } + SpinBoxRow { + label: qsTr("Drag threshold") + min: 0 + max: 100 + value: root.dragThreshold + onValueModified: value => { + root.dragThreshold = value; + root.saveConfig(); } } } - Item { + CollapsibleSection { id: statusIconsSection - Layout.fillWidth: true - Layout.preferredHeight: statusIconsSectionHeader.implicitHeight - property bool expanded: false - - ColumnLayout { - id: statusIconsSectionHeader - 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: qsTr("Status Icons") - font.pointSize: Appearance.font.size.larger - font.weight: 500 - } - - Item { - Layout.fillWidth: true - } - - MaterialIcon { - text: "expand_more" - rotation: statusIconsSection.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 { - const wasExpanded = statusIconsSection.expanded; - root.collapseAllSections(statusIconsSection); - statusIconsSection.expanded = !wasExpanded; - } - } + title: qsTr("Status Icons") + onToggleRequested: { + root.collapseAllSections(statusIconsSection); } - } - - StyledRect { - visible: statusIconsSection.expanded - Layout.fillWidth: true - Layout.topMargin: Appearance.spacing.small / 2 - implicitHeight: statusIconsSection.expanded ? showAudioRow.implicitHeight + Appearance.padding.large * 2 : 0 - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - - Behavior on implicitHeight { - Anim {} - } - - RowLayout { - id: showAudioRow - 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 audio") - } - StyledSwitch { - checked: root.showAudio - onToggled: { - root.showAudio = checked; - root.saveConfig(); - } + SwitchRow { + label: qsTr("Show audio") + checked: root.showAudio + onToggled: checked => { + root.showAudio = checked; + root.saveConfig(); } } - } - - StyledRect { - visible: statusIconsSection.expanded - Layout.fillWidth: true - Layout.topMargin: Appearance.spacing.small / 2 - implicitHeight: statusIconsSection.expanded ? showMicrophoneRow.implicitHeight + Appearance.padding.large * 2 : 0 - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - - Behavior on implicitHeight { - Anim {} - } - RowLayout { - id: showMicrophoneRow - 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 microphone") + SwitchRow { + label: qsTr("Show microphone") + checked: root.showMicrophone + onToggled: checked => { + root.showMicrophone = checked; + root.saveConfig(); } - - StyledSwitch { - checked: root.showMicrophone - onToggled: { - root.showMicrophone = checked; - root.saveConfig(); - } - } - } - } - - StyledRect { - visible: statusIconsSection.expanded - Layout.fillWidth: true - Layout.topMargin: Appearance.spacing.small / 2 - implicitHeight: statusIconsSection.expanded ? showKbLayoutRow.implicitHeight + Appearance.padding.large * 2 : 0 - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - - Behavior on implicitHeight { - Anim {} } - RowLayout { - id: showKbLayoutRow - 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 keyboard layout") + SwitchRow { + label: qsTr("Show keyboard layout") + checked: root.showKbLayout + onToggled: checked => { + root.showKbLayout = checked; + root.saveConfig(); } + } - StyledSwitch { - checked: root.showKbLayout - onToggled: { - root.showKbLayout = checked; - root.saveConfig(); - } + SwitchRow { + label: qsTr("Show network") + checked: root.showNetwork + onToggled: checked => { + root.showNetwork = checked; + root.saveConfig(); } } - } - StyledRect { - visible: statusIconsSection.expanded - Layout.fillWidth: true - Layout.topMargin: Appearance.spacing.small / 2 - implicitHeight: statusIconsSection.expanded ? showNetworkRow.implicitHeight + Appearance.padding.large * 2 : 0 - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - - Behavior on implicitHeight { - Anim {} + SwitchRow { + label: qsTr("Show bluetooth") + checked: root.showBluetooth + onToggled: checked => { + root.showBluetooth = checked; + root.saveConfig(); + } } - RowLayout { - id: showNetworkRow - 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 network") + SwitchRow { + label: qsTr("Show battery") + checked: root.showBattery + onToggled: checked => { + root.showBattery = checked; + root.saveConfig(); } + } - StyledSwitch { - checked: root.showNetwork - onToggled: { - root.showNetwork = checked; - root.saveConfig(); - } + SwitchRow { + label: qsTr("Show lock status") + checked: root.showLockStatus + onToggled: checked => { + root.showLockStatus = checked; + root.saveConfig(); } } } - StyledRect { - visible: statusIconsSection.expanded - Layout.fillWidth: true - Layout.topMargin: Appearance.spacing.small / 2 - implicitHeight: statusIconsSection.expanded ? showBluetoothRow.implicitHeight + Appearance.padding.large * 2 : 0 - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - - Behavior on implicitHeight { - Anim {} + CollapsibleSection { + id: traySettingsSection + title: qsTr("Tray Settings") + onToggleRequested: { + root.collapseAllSections(traySettingsSection); } - RowLayout { - id: showBluetoothRow - 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 bluetooth") + SwitchRow { + label: qsTr("Background") + checked: root.trayBackground + onToggled: checked => { + root.trayBackground = checked; + root.saveConfig(); } - - StyledSwitch { - checked: root.showBluetooth - onToggled: { - root.showBluetooth = checked; - root.saveConfig(); - } - } - } - } - - StyledRect { - visible: statusIconsSection.expanded - Layout.fillWidth: true - Layout.topMargin: Appearance.spacing.small / 2 - implicitHeight: statusIconsSection.expanded ? showBatteryRow.implicitHeight + Appearance.padding.large * 2 : 0 - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - - Behavior on implicitHeight { - Anim {} } - RowLayout { - id: showBatteryRow - 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 battery") + SwitchRow { + label: qsTr("Compact") + checked: root.trayCompact + onToggled: checked => { + root.trayCompact = checked; + root.saveConfig(); } + } - StyledSwitch { - checked: root.showBattery - onToggled: { - root.showBattery = checked; - root.saveConfig(); - } + SwitchRow { + label: qsTr("Recolour") + checked: root.trayRecolour + onToggled: checked => { + root.trayRecolour = checked; + root.saveConfig(); } } } - StyledRect { - visible: statusIconsSection.expanded - Layout.fillWidth: true - Layout.topMargin: Appearance.spacing.small / 2 - implicitHeight: statusIconsSection.expanded ? showLockStatusRow.implicitHeight + Appearance.padding.large * 2 : 0 - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - - Behavior on implicitHeight { - Anim {} + CollapsibleSection { + id: workspacesSection + title: qsTr("Workspaces") + onToggleRequested: { + root.collapseAllSections(workspacesSection); } - RowLayout { - id: showLockStatusRow - 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 lock status") - } + StyledRect { + Layout.fillWidth: true + implicitHeight: workspacesShownRow.implicitHeight + Appearance.padding.large * 2 + radius: Appearance.rounding.normal + color: Colours.tPalette.m3surfaceContainer - StyledSwitch { - checked: root.showLockStatus - onToggled: { - root.showLockStatus = checked; - root.saveConfig(); - } + Behavior on implicitHeight { + Anim {} } - } - } - - Item { - id: traySettingsSection - Layout.fillWidth: true - Layout.preferredHeight: traySettingsSectionHeader.implicitHeight - property bool expanded: false - - ColumnLayout { - id: traySettingsSectionHeader - anchors.left: parent.left - anchors.right: parent.right - spacing: Appearance.spacing.small RowLayout { - Layout.fillWidth: true - spacing: Appearance.spacing.small + id: workspacesShownRow + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.large + spacing: Appearance.spacing.normal StyledText { - Layout.topMargin: Appearance.spacing.large - text: qsTr("Tray Settings") - font.pointSize: Appearance.font.size.larger - font.weight: 500 - } - - Item { Layout.fillWidth: true + text: qsTr("Shown") } - MaterialIcon { - text: "expand_more" - rotation: traySettingsSection.expanded ? 180 : 0 - color: Colours.palette.m3onSurface - Behavior on rotation { - Anim {} + CustomSpinBox { + min: 1 + max: 20 + value: root.workspacesShown + onValueModified: value => { + root.workspacesShown = value; + root.saveConfig(); } } } - - StateLayer { - anchors.fill: parent - anchors.leftMargin: -Appearance.padding.normal - anchors.rightMargin: -Appearance.padding.normal - function onClicked(): void { - const wasExpanded = traySettingsSection.expanded; - root.collapseAllSections(traySettingsSection); - traySettingsSection.expanded = !wasExpanded; - } - } } - } - - StyledRect { - visible: traySettingsSection.expanded - Layout.fillWidth: true - Layout.topMargin: Appearance.spacing.small / 2 - implicitHeight: traySettingsSection.expanded ? trayBackgroundRow.implicitHeight + Appearance.padding.large * 2 : 0 - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - - Behavior on implicitHeight { - Anim {} - } - - RowLayout { - id: trayBackgroundRow - 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("Background") - } + StyledRect { + Layout.fillWidth: true + implicitHeight: workspacesActiveIndicatorRow.implicitHeight + Appearance.padding.large * 2 + radius: Appearance.rounding.normal + color: Colours.tPalette.m3surfaceContainer - StyledSwitch { - checked: root.trayBackground - onToggled: { - root.trayBackground = checked; - root.saveConfig(); - } + Behavior on implicitHeight { + Anim {} } - } - } - - StyledRect { - visible: traySettingsSection.expanded - Layout.fillWidth: true - Layout.topMargin: Appearance.spacing.small / 2 - implicitHeight: traySettingsSection.expanded ? trayCompactRow.implicitHeight + Appearance.padding.large * 2 : 0 - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - Behavior on implicitHeight { - Anim {} - } - - RowLayout { - id: trayCompactRow - 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("Compact") - } + RowLayout { + id: workspacesActiveIndicatorRow + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.large + spacing: Appearance.spacing.normal - StyledSwitch { - checked: root.trayCompact - onToggled: { - root.trayCompact = checked; - root.saveConfig(); + StyledText { + Layout.fillWidth: true + text: qsTr("Active indicator") } - } - } - } - - StyledRect { - visible: traySettingsSection.expanded - Layout.fillWidth: true - Layout.topMargin: Appearance.spacing.small / 2 - implicitHeight: traySettingsSection.expanded ? trayRecolourRow.implicitHeight + Appearance.padding.large * 2 : 0 - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - - Behavior on implicitHeight { - Anim {} - } - RowLayout { - id: trayRecolourRow - 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("Recolour") - } - - StyledSwitch { - checked: root.trayRecolour - onToggled: { - root.trayRecolour = checked; - root.saveConfig(); + StyledSwitch { + checked: root.workspacesActiveIndicator + onToggled: { + root.workspacesActiveIndicator = checked; + root.saveConfig(); + } } } } - } - Item { - id: workspacesSection - Layout.fillWidth: true - Layout.preferredHeight: workspacesSectionHeader.implicitHeight - property bool expanded: false + StyledRect { + Layout.fillWidth: true + implicitHeight: workspacesOccupiedBgRow.implicitHeight + Appearance.padding.large * 2 + radius: Appearance.rounding.normal + color: Colours.tPalette.m3surfaceContainer - ColumnLayout { - id: workspacesSectionHeader - anchors.left: parent.left - anchors.right: parent.right - spacing: Appearance.spacing.small + Behavior on implicitHeight { + Anim {} + } RowLayout { - Layout.fillWidth: true - spacing: Appearance.spacing.small + id: workspacesOccupiedBgRow + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.large + spacing: Appearance.spacing.normal StyledText { - Layout.topMargin: Appearance.spacing.large - text: qsTr("Workspaces") - font.pointSize: Appearance.font.size.larger - font.weight: 500 - } - - Item { Layout.fillWidth: true + text: qsTr("Occupied background") } - MaterialIcon { - text: "expand_more" - rotation: workspacesSection.expanded ? 180 : 0 - color: Colours.palette.m3onSurface - Behavior on rotation { - Anim {} + StyledSwitch { + checked: root.workspacesOccupiedBg + onToggled: { + root.workspacesOccupiedBg = checked; + root.saveConfig(); } } } - - StateLayer { - anchors.fill: parent - anchors.leftMargin: -Appearance.padding.normal - anchors.rightMargin: -Appearance.padding.normal - function onClicked(): void { - const wasExpanded = workspacesSection.expanded; - root.collapseAllSections(workspacesSection); - workspacesSection.expanded = !wasExpanded; - } - } } - } - StyledRect { - visible: workspacesSection.expanded - Layout.fillWidth: true - Layout.topMargin: Appearance.spacing.small / 2 - implicitHeight: workspacesSection.expanded ? workspacesShownRow.implicitHeight + Appearance.padding.large * 2 : 0 - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - - Behavior on implicitHeight { - Anim {} - } - - RowLayout { - id: workspacesShownRow - 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("Shown") - } + StyledRect { + Layout.fillWidth: true + implicitHeight: workspacesShowWindowsRow.implicitHeight + Appearance.padding.large * 2 + radius: Appearance.rounding.normal + color: Colours.tPalette.m3surfaceContainer - CustomSpinBox { - min: 1 - max: 20 - value: root.workspacesShown - onValueModified: value => { - root.workspacesShown = value; - root.saveConfig(); - } + Behavior on implicitHeight { + Anim {} } - } - } - StyledRect { - visible: workspacesSection.expanded - Layout.fillWidth: true - Layout.topMargin: Appearance.spacing.small / 2 - implicitHeight: workspacesSection.expanded ? workspacesActiveIndicatorRow.implicitHeight + Appearance.padding.large * 2 : 0 - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - - 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 - - StyledText { - Layout.fillWidth: true - text: qsTr("Active indicator") - } + RowLayout { + id: workspacesShowWindowsRow + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.large + spacing: Appearance.spacing.normal - StyledSwitch { - checked: root.workspacesActiveIndicator - onToggled: { - root.workspacesActiveIndicator = checked; - root.saveConfig(); + StyledText { + Layout.fillWidth: true + text: qsTr("Show windows") } - } - } - } - - StyledRect { - visible: workspacesSection.expanded - Layout.fillWidth: true - Layout.topMargin: Appearance.spacing.small / 2 - implicitHeight: workspacesSection.expanded ? workspacesOccupiedBgRow.implicitHeight + Appearance.padding.large * 2 : 0 - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - - Behavior on implicitHeight { - Anim {} - } - - RowLayout { - id: workspacesOccupiedBgRow - 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("Occupied background") - } - StyledSwitch { - checked: root.workspacesOccupiedBg - onToggled: { - root.workspacesOccupiedBg = checked; - root.saveConfig(); + StyledSwitch { + checked: root.workspacesShowWindows + onToggled: { + root.workspacesShowWindows = checked; + root.saveConfig(); + } } } } - } - StyledRect { - visible: workspacesSection.expanded - Layout.fillWidth: true - Layout.topMargin: Appearance.spacing.small / 2 - implicitHeight: workspacesSection.expanded ? workspacesShowWindowsRow.implicitHeight + Appearance.padding.large * 2 : 0 - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - - Behavior on implicitHeight { - Anim {} - } - - 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") - } + StyledRect { + Layout.fillWidth: true + implicitHeight: workspacesPerMonitorRow.implicitHeight + Appearance.padding.large * 2 + radius: Appearance.rounding.normal + color: Colours.tPalette.m3surfaceContainer - StyledSwitch { - checked: root.workspacesShowWindows - onToggled: { - root.workspacesShowWindows = checked; - root.saveConfig(); - } + Behavior on implicitHeight { + Anim {} } - } - } - StyledRect { - visible: workspacesSection.expanded - Layout.fillWidth: true - Layout.topMargin: Appearance.spacing.small / 2 - implicitHeight: workspacesSection.expanded ? workspacesPerMonitorRow.implicitHeight + Appearance.padding.large * 2 : 0 - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - - Behavior on implicitHeight { - Anim {} - } - - RowLayout { - id: workspacesPerMonitorRow - 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("Per monitor workspaces") - } + StyledText { + Layout.fillWidth: true + text: qsTr("Per monitor workspaces") + } - StyledSwitch { - checked: root.workspacesPerMonitor - onToggled: { - root.workspacesPerMonitor = checked; - root.saveConfig(); + StyledSwitch { + checked: root.workspacesPerMonitor + onToggled: { + root.workspacesPerMonitor = checked; + root.saveConfig(); + } } } } -- 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 'modules/controlcenter/appearance') 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 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 'modules/controlcenter/appearance') 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 'modules/controlcenter/appearance') 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 97e1bde96b698cb85cd663c8d4343a6b40da34a3 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Thu, 13 Nov 2025 22:21:29 -0500 Subject: controlcenter: appearance pane tweaks --- .../controlcenter/appearance/AppearancePane.qml | 35 +++++++--------------- 1 file changed, 11 insertions(+), 24 deletions(-) (limited to 'modules/controlcenter/appearance') diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index e4d5892..efd67e9 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -997,47 +997,34 @@ RowLayout { font.bold: true } - StyledText { - Layout.topMargin: Appearance.spacing.large - Layout.alignment: Qt.AlignHCenter - text: qsTr("Theme mode") - font.pointSize: Appearance.font.size.larger - font.weight: 500 - } - - StyledText { - Layout.alignment: Qt.AlignHCenter - text: Colours.currentLight ? qsTr("Light mode") : qsTr("Dark mode") - color: Colours.palette.m3outline - } - StyledText { Layout.topMargin: Appearance.spacing.large Layout.alignment: Qt.AlignHCenter text: qsTr("Wallpaper") - font.pointSize: Appearance.font.size.larger - font.weight: 500 + font.pointSize: Appearance.font.size.extraLarge + font.weight: 600 } StyledText { Layout.alignment: Qt.AlignHCenter text: qsTr("Select a wallpaper") - color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.normal + color: Colours.palette.m3onSurfaceVariant } GridLayout { Layout.fillWidth: true - Layout.topMargin: Appearance.spacing.normal + Layout.topMargin: Appearance.spacing.large Layout.alignment: Qt.AlignHCenter - columns: Math.max(1, Math.floor(parent.width / 200)) + columns: Math.max(2, Math.floor(parent.width / 200)) rowSpacing: Appearance.spacing.normal columnSpacing: Appearance.spacing.normal // Center the grid content Layout.maximumWidth: { const cols = columns; - const itemWidth = 180; + const itemWidth = 200; const spacing = columnSpacing; return cols * itemWidth + (cols - 1) * spacing; } @@ -1048,10 +1035,10 @@ RowLayout { delegate: Item { required property var modelData - Layout.preferredWidth: 180 - Layout.preferredHeight: 120 - Layout.minimumWidth: 180 - Layout.minimumHeight: 120 + Layout.preferredWidth: 200 + Layout.preferredHeight: 140 + Layout.minimumWidth: 200 + Layout.minimumHeight: 140 readonly property bool isCurrent: modelData.path === Wallpapers.actualCurrent readonly property real imageWidth: Math.max(1, width) -- cgit v1.2.3-freya From 9de5e790a075d2f1d74113147bfad72b357d1215 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Fri, 14 Nov 2025 16:42:23 -0500 Subject: controlcenter: minor tidying (capitalization and filename) --- .../controlcenter/appearance/AppearancePane.qml | 2 +- modules/controlcenter/audio/AudioPane.qml | 2 +- modules/controlcenter/bluetooth/Settings.qml | 2 +- modules/controlcenter/network/NetworkSettings.qml | 106 +++++++++++++++++++++ modules/controlcenter/network/NetworkingPane.qml | 2 +- .../controlcenter/network/NetworkingSettings.qml | 106 --------------------- modules/controlcenter/taskbar/TaskbarPane.qml | 2 +- 7 files changed, 111 insertions(+), 111 deletions(-) create mode 100644 modules/controlcenter/network/NetworkSettings.qml delete mode 100644 modules/controlcenter/network/NetworkingSettings.qml (limited to 'modules/controlcenter/appearance') diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index efd67e9..719e9b3 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -992,7 +992,7 @@ RowLayout { StyledText { Layout.alignment: Qt.AlignHCenter - text: qsTr("Appearance settings") + text: qsTr("Appearance Settings") font.pointSize: Appearance.font.size.large font.bold: true } diff --git a/modules/controlcenter/audio/AudioPane.qml b/modules/controlcenter/audio/AudioPane.qml index 732bb89..6f5a1f4 100644 --- a/modules/controlcenter/audio/AudioPane.qml +++ b/modules/controlcenter/audio/AudioPane.qml @@ -256,7 +256,7 @@ RowLayout { StyledText { Layout.alignment: Qt.AlignHCenter - text: qsTr("Audio settings") + text: qsTr("Audio Settings") font.pointSize: Appearance.font.size.large font.bold: true } diff --git a/modules/controlcenter/bluetooth/Settings.qml b/modules/controlcenter/bluetooth/Settings.qml index fb493ff..fd33af9 100644 --- a/modules/controlcenter/bluetooth/Settings.qml +++ b/modules/controlcenter/bluetooth/Settings.qml @@ -26,7 +26,7 @@ ColumnLayout { StyledText { Layout.alignment: Qt.AlignHCenter - text: qsTr("Bluetooth settings") + text: qsTr("Bluetooth Settings") font.pointSize: Appearance.font.size.large font.bold: true } diff --git a/modules/controlcenter/network/NetworkSettings.qml b/modules/controlcenter/network/NetworkSettings.qml new file mode 100644 index 0000000..75a7660 --- /dev/null +++ b/modules/controlcenter/network/NetworkSettings.qml @@ -0,0 +1,106 @@ +pragma ComponentBehavior: Bound + +import ".." +import qs.components +import qs.components.controls +import qs.components.effects +import qs.services +import qs.config +import QtQuick +import QtQuick.Layouts + +ColumnLayout { + id: root + + required property Session session + + spacing: Appearance.spacing.normal + + MaterialIcon { + Layout.alignment: Qt.AlignHCenter + text: "router" + font.pointSize: Appearance.font.size.extraLarge * 3 + font.bold: true + } + + StyledText { + Layout.alignment: Qt.AlignHCenter + text: qsTr("Network Settings") + font.pointSize: Appearance.font.size.large + font.bold: true + } + + SectionHeader { + Layout.topMargin: Appearance.spacing.large + title: qsTr("Ethernet") + description: qsTr("Ethernet device information") + } + + SectionContainer { + contentSpacing: Appearance.spacing.small / 2 + + PropertyRow { + label: qsTr("Total devices") + value: qsTr("%1").arg(Nmcli.ethernetDevices.length) + } + + PropertyRow { + showTopMargin: true + label: qsTr("Connected devices") + value: qsTr("%1").arg(Nmcli.ethernetDevices.filter(d => d.connected).length) + } + } + + SectionHeader { + Layout.topMargin: Appearance.spacing.large + title: qsTr("Wireless") + description: qsTr("WiFi network settings") + } + + SectionContainer { + ToggleRow { + label: qsTr("WiFi enabled") + checked: Nmcli.wifiEnabled + toggle.onToggled: { + Nmcli.enableWifi(checked); + } + } + } + + SectionHeader { + Layout.topMargin: Appearance.spacing.large + title: qsTr("Current connection") + description: qsTr("Active network connection information") + } + + SectionContainer { + contentSpacing: Appearance.spacing.small / 2 + + PropertyRow { + label: qsTr("Network") + value: Nmcli.active ? Nmcli.active.ssid : (Nmcli.activeEthernet ? Nmcli.activeEthernet.interface : qsTr("Not connected")) + } + + PropertyRow { + showTopMargin: true + visible: Nmcli.active !== null + label: qsTr("Signal strength") + value: Nmcli.active ? qsTr("%1%").arg(Nmcli.active.strength) : qsTr("N/A") + } + + PropertyRow { + showTopMargin: true + visible: Nmcli.active !== null + label: qsTr("Security") + value: Nmcli.active ? (Nmcli.active.isSecure ? qsTr("Secured") : qsTr("Open")) : qsTr("N/A") + } + + PropertyRow { + showTopMargin: true + visible: Nmcli.active !== null + label: qsTr("Frequency") + value: Nmcli.active ? qsTr("%1 MHz").arg(Nmcli.active.frequency) : qsTr("N/A") + } + } +} + diff --git a/modules/controlcenter/network/NetworkingPane.qml b/modules/controlcenter/network/NetworkingPane.qml index c5928de..823c19a 100644 --- a/modules/controlcenter/network/NetworkingPane.qml +++ b/modules/controlcenter/network/NetworkingPane.qml @@ -478,7 +478,7 @@ RowLayout { flickableDirection: Flickable.VerticalFlick contentHeight: settingsInner.height - NetworkingSettings { + NetworkSettings { id: settingsInner anchors.left: parent.left diff --git a/modules/controlcenter/network/NetworkingSettings.qml b/modules/controlcenter/network/NetworkingSettings.qml deleted file mode 100644 index 2475fed..0000000 --- a/modules/controlcenter/network/NetworkingSettings.qml +++ /dev/null @@ -1,106 +0,0 @@ -pragma ComponentBehavior: Bound - -import ".." -import qs.components -import qs.components.controls -import qs.components.effects -import qs.services -import qs.config -import QtQuick -import QtQuick.Layouts - -ColumnLayout { - id: root - - required property Session session - - spacing: Appearance.spacing.normal - - MaterialIcon { - Layout.alignment: Qt.AlignHCenter - text: "router" - font.pointSize: Appearance.font.size.extraLarge * 3 - font.bold: true - } - - StyledText { - Layout.alignment: Qt.AlignHCenter - text: qsTr("Networking settings") - font.pointSize: Appearance.font.size.large - font.bold: true - } - - SectionHeader { - Layout.topMargin: Appearance.spacing.large - title: qsTr("Ethernet") - description: qsTr("Ethernet device information") - } - - SectionContainer { - contentSpacing: Appearance.spacing.small / 2 - - PropertyRow { - label: qsTr("Total devices") - value: qsTr("%1").arg(Nmcli.ethernetDevices.length) - } - - PropertyRow { - showTopMargin: true - label: qsTr("Connected devices") - value: qsTr("%1").arg(Nmcli.ethernetDevices.filter(d => d.connected).length) - } - } - - SectionHeader { - Layout.topMargin: Appearance.spacing.large - title: qsTr("Wireless") - description: qsTr("WiFi network settings") - } - - SectionContainer { - ToggleRow { - label: qsTr("WiFi enabled") - checked: Nmcli.wifiEnabled - toggle.onToggled: { - Nmcli.enableWifi(checked); - } - } - } - - SectionHeader { - Layout.topMargin: Appearance.spacing.large - title: qsTr("Current connection") - description: qsTr("Active network connection information") - } - - SectionContainer { - contentSpacing: Appearance.spacing.small / 2 - - PropertyRow { - label: qsTr("Network") - value: Nmcli.active ? Nmcli.active.ssid : (Nmcli.activeEthernet ? Nmcli.activeEthernet.interface : qsTr("Not connected")) - } - - PropertyRow { - showTopMargin: true - visible: Nmcli.active !== null - label: qsTr("Signal strength") - value: Nmcli.active ? qsTr("%1%").arg(Nmcli.active.strength) : qsTr("N/A") - } - - PropertyRow { - showTopMargin: true - visible: Nmcli.active !== null - label: qsTr("Security") - value: Nmcli.active ? (Nmcli.active.isSecure ? qsTr("Secured") : qsTr("Open")) : qsTr("N/A") - } - - PropertyRow { - showTopMargin: true - visible: Nmcli.active !== null - label: qsTr("Frequency") - value: Nmcli.active ? qsTr("%1 MHz").arg(Nmcli.active.frequency) : qsTr("N/A") - } - } -} - diff --git a/modules/controlcenter/taskbar/TaskbarPane.qml b/modules/controlcenter/taskbar/TaskbarPane.qml index cf52fd3..0dcc152 100644 --- a/modules/controlcenter/taskbar/TaskbarPane.qml +++ b/modules/controlcenter/taskbar/TaskbarPane.qml @@ -631,7 +631,7 @@ RowLayout { StyledText { Layout.alignment: Qt.AlignHCenter - text: qsTr("Taskbar settings") + text: qsTr("Taskbar Settings") font.pointSize: Appearance.font.size.large font.bold: true } -- 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 'modules/controlcenter/appearance') 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 20e07b2a13036335c9890fe67708d2ab2292a4b4 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sat, 15 Nov 2025 00:29:42 -0500 Subject: controlcenter: clip to all styledflickable --- modules/controlcenter/Panes.qml | 2 ++ modules/controlcenter/appearance/AppearancePane.qml | 2 ++ modules/controlcenter/audio/AudioPane.qml | 1 + modules/controlcenter/bluetooth/BtPane.qml | 3 +++ modules/controlcenter/bluetooth/Details.qml | 1 + modules/controlcenter/ethernet/EthernetDetails.qml | 1 + modules/controlcenter/ethernet/EthernetPane.qml | 4 +++- modules/controlcenter/launcher/LauncherPane.qml | 1 + modules/controlcenter/network/NetworkingPane.qml | 5 ++++- modules/controlcenter/network/WirelessDetails.qml | 1 + modules/controlcenter/network/WirelessPane.qml | 1 + modules/controlcenter/taskbar/TaskbarPane.qml | 2 ++ 12 files changed, 22 insertions(+), 2 deletions(-) (limited to 'modules/controlcenter/appearance') diff --git a/modules/controlcenter/Panes.qml b/modules/controlcenter/Panes.qml index 8a46088..52ad7f2 100644 --- a/modules/controlcenter/Panes.qml +++ b/modules/controlcenter/Panes.qml @@ -19,12 +19,14 @@ ClippingRectangle { required property Session session color: "transparent" + clip: true ColumnLayout { id: layout spacing: 0 y: -root.session.activeIndex * root.height + clip: true Pane { index: 0 diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index 5ddcb06..fd75ff4 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -225,6 +225,7 @@ RowLayout { anchors.fill: parent flickableDirection: Flickable.VerticalFlick contentHeight: sidebarLayout.implicitHeight + Appearance.padding.large * 2 + clip: true StyledScrollBar.vertical: StyledScrollBar { flickable: sidebarFlickable @@ -965,6 +966,7 @@ RowLayout { flickableDirection: Flickable.VerticalFlick contentHeight: contentLayout.implicitHeight + clip: true StyledScrollBar.vertical: StyledScrollBar { flickable: parent diff --git a/modules/controlcenter/audio/AudioPane.qml b/modules/controlcenter/audio/AudioPane.qml index 8c990a6..0a7f602 100644 --- a/modules/controlcenter/audio/AudioPane.qml +++ b/modules/controlcenter/audio/AudioPane.qml @@ -31,6 +31,7 @@ RowLayout { anchors.rightMargin: Appearance.padding.large + Appearance.padding.normal / 2 flickableDirection: Flickable.VerticalFlick contentHeight: leftContent.height + clip: true ColumnLayout { id: leftContent diff --git a/modules/controlcenter/bluetooth/BtPane.qml b/modules/controlcenter/bluetooth/BtPane.qml index 96dc002..40cf356 100644 --- a/modules/controlcenter/bluetooth/BtPane.qml +++ b/modules/controlcenter/bluetooth/BtPane.qml @@ -50,6 +50,7 @@ RowLayout { radius: rightBorder.innerRadius color: "transparent" + clip: true Loader { id: loader @@ -59,6 +60,7 @@ RowLayout { anchors.fill: parent anchors.margins: Appearance.padding.large * 2 + clip: true asynchronous: true sourceComponent: pane ? details : settings @@ -106,6 +108,7 @@ RowLayout { StyledFlickable { flickableDirection: Flickable.VerticalFlick contentHeight: settingsInner.height + clip: true Settings { id: settingsInner diff --git a/modules/controlcenter/bluetooth/Details.qml b/modules/controlcenter/bluetooth/Details.qml index 104f673..cbccd9b 100644 --- a/modules/controlcenter/bluetooth/Details.qml +++ b/modules/controlcenter/bluetooth/Details.qml @@ -22,6 +22,7 @@ Item { anchors.fill: parent flickableDirection: Flickable.VerticalFlick + clip: true contentHeight: layout.height ColumnLayout { diff --git a/modules/controlcenter/ethernet/EthernetDetails.qml b/modules/controlcenter/ethernet/EthernetDetails.qml index 68510da..6a01a8b 100644 --- a/modules/controlcenter/ethernet/EthernetDetails.qml +++ b/modules/controlcenter/ethernet/EthernetDetails.qml @@ -34,6 +34,7 @@ Item { anchors.fill: parent flickableDirection: Flickable.VerticalFlick + clip: true contentHeight: layout.height ColumnLayout { diff --git a/modules/controlcenter/ethernet/EthernetPane.qml b/modules/controlcenter/ethernet/EthernetPane.qml index b3ff317..05d0b1b 100644 --- a/modules/controlcenter/ethernet/EthernetPane.qml +++ b/modules/controlcenter/ethernet/EthernetPane.qml @@ -50,6 +50,7 @@ RowLayout { radius: rightBorder.innerRadius color: "transparent" + clip: true Loader { id: loader @@ -64,7 +65,7 @@ RowLayout { scale: 1 transformOrigin: Item.Center - clip: false + clip: true asynchronous: true sourceComponent: pane ? details : settings @@ -120,6 +121,7 @@ RowLayout { StyledFlickable { flickableDirection: Flickable.VerticalFlick contentHeight: settingsInner.height + clip: true EthernetSettings { id: settingsInner diff --git a/modules/controlcenter/launcher/LauncherPane.qml b/modules/controlcenter/launcher/LauncherPane.qml index 0645860..01ed72e 100644 --- a/modules/controlcenter/launcher/LauncherPane.qml +++ b/modules/controlcenter/launcher/LauncherPane.qml @@ -421,6 +421,7 @@ RowLayout { anchors.fill: parent flickableDirection: Flickable.VerticalFlick contentHeight: debugLayout.implicitHeight + clip: true StyledScrollBar.vertical: StyledScrollBar { flickable: parent diff --git a/modules/controlcenter/network/NetworkingPane.qml b/modules/controlcenter/network/NetworkingPane.qml index 620e6f7..2faa40a 100644 --- a/modules/controlcenter/network/NetworkingPane.qml +++ b/modules/controlcenter/network/NetworkingPane.qml @@ -36,6 +36,7 @@ RowLayout { anchors.rightMargin: Appearance.padding.large + Appearance.padding.normal / 2 flickableDirection: Flickable.VerticalFlick contentHeight: leftContent.height + clip: true ColumnLayout { id: leftContent @@ -399,6 +400,7 @@ RowLayout { radius: rightBorder.innerRadius color: "transparent" + clip: true // Right pane - networking details/settings Loader { @@ -416,7 +418,7 @@ RowLayout { scale: 1 transformOrigin: Item.Center - clip: false + clip: true asynchronous: true sourceComponent: pane ? (ethernetPane ? ethernetDetails : wirelessDetails) : settings @@ -472,6 +474,7 @@ RowLayout { StyledFlickable { flickableDirection: Flickable.VerticalFlick contentHeight: settingsInner.height + clip: true NetworkSettings { id: settingsInner diff --git a/modules/controlcenter/network/WirelessDetails.qml b/modules/controlcenter/network/WirelessDetails.qml index 334c4b3..f41451a 100644 --- a/modules/controlcenter/network/WirelessDetails.qml +++ b/modules/controlcenter/network/WirelessDetails.qml @@ -102,6 +102,7 @@ Item { anchors.fill: parent flickableDirection: Flickable.VerticalFlick + clip: true contentHeight: layout.height ColumnLayout { diff --git a/modules/controlcenter/network/WirelessPane.qml b/modules/controlcenter/network/WirelessPane.qml index 9d48729..67a00f7 100644 --- a/modules/controlcenter/network/WirelessPane.qml +++ b/modules/controlcenter/network/WirelessPane.qml @@ -120,6 +120,7 @@ RowLayout { StyledFlickable { flickableDirection: Flickable.VerticalFlick contentHeight: settingsInner.height + clip: true WirelessSettings { id: settingsInner diff --git a/modules/controlcenter/taskbar/TaskbarPane.qml b/modules/controlcenter/taskbar/TaskbarPane.qml index ee47683..72e9a80 100644 --- a/modules/controlcenter/taskbar/TaskbarPane.qml +++ b/modules/controlcenter/taskbar/TaskbarPane.qml @@ -209,6 +209,7 @@ RowLayout { anchors.fill: parent flickableDirection: Flickable.VerticalFlick contentHeight: sidebarLayout.implicitHeight + Appearance.padding.large * 2 + clip: true StyledScrollBar.vertical: StyledScrollBar { flickable: sidebarFlickable @@ -608,6 +609,7 @@ RowLayout { flickableDirection: Flickable.VerticalFlick contentHeight: contentLayout.implicitHeight + clip: true StyledScrollBar.vertical: StyledScrollBar { flickable: parent -- cgit v1.2.3-freya From f29f0a3805239976778c1492cf846d4f614e660c Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sat, 15 Nov 2025 01:36:57 -0500 Subject: controlcenter: removed FileView and now all use singleton Config --- .../controlcenter/appearance/AppearancePane.qml | 225 +++++---------------- modules/controlcenter/launcher/LauncherPane.qml | 77 +++---- modules/controlcenter/taskbar/TaskbarPane.qml | 196 ++++++------------ 3 files changed, 138 insertions(+), 360 deletions(-) (limited to 'modules/controlcenter/appearance') diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index fd75ff4..6f77d25 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -12,7 +12,6 @@ import qs.config import qs.utils import Caelestia.Models import Quickshell -import Quickshell.Io import QtQuick import QtQuick.Layouts @@ -22,117 +21,32 @@ RowLayout { required property Session session // Appearance settings - property real animDurationsScale: 1 - property string fontFamilyMaterial: "Material Symbols Rounded" - property string fontFamilyMono: "CaskaydiaCove NF" - property string fontFamilySans: "Rubik" - property real fontSizeScale: 1 - property real paddingScale: 1 - property real roundingScale: 1 - property real spacingScale: 1 - property bool transparencyEnabled: false - property real transparencyBase: 0.85 - property real transparencyLayers: 0.4 - property real borderRounding: 1 - property real borderThickness: 1 + property real animDurationsScale: Config.appearance.anim.durations.scale ?? 1 + property string fontFamilyMaterial: Config.appearance.font.family.material ?? "Material Symbols Rounded" + property string fontFamilyMono: Config.appearance.font.family.mono ?? "CaskaydiaCove NF" + property string fontFamilySans: Config.appearance.font.family.sans ?? "Rubik" + property real fontSizeScale: Config.appearance.font.size.scale ?? 1 + property real paddingScale: Config.appearance.padding.scale ?? 1 + property real roundingScale: Config.appearance.rounding.scale ?? 1 + property real spacingScale: Config.appearance.spacing.scale ?? 1 + property bool transparencyEnabled: Config.appearance.transparency.enabled ?? false + property real transparencyBase: Config.appearance.transparency.base ?? 0.85 + property real transparencyLayers: Config.appearance.transparency.layers ?? 0.4 + property real borderRounding: Config.border.rounding ?? 1 + property real borderThickness: Config.border.thickness ?? 1 // Background settings - property bool desktopClockEnabled: true - property bool backgroundEnabled: true - property bool visualiserEnabled: true - property bool visualiserAutoHide: true - property real visualiserRounding: 1 - property real visualiserSpacing: 1 + property bool desktopClockEnabled: Config.background.desktopClock.enabled ?? false + property bool backgroundEnabled: Config.background.enabled ?? true + property bool visualiserEnabled: Config.background.visualiser.enabled ?? false + property bool visualiserAutoHide: Config.background.visualiser.autoHide ?? true + property real visualiserRounding: Config.background.visualiser.rounding ?? 1 + property real visualiserSpacing: Config.background.visualiser.spacing ?? 1 anchors.fill: parent spacing: 0 - FileView { - id: configFile - - path: `${Paths.config}/shell.json` - watchChanges: true - - onLoaded: { - try { - const config = JSON.parse(text()); - updateFromConfig(config); - } catch (e) { - console.error("Failed to parse config:", e); - } - } - - onSaveFailed: err => { - console.error("Failed to save config file:", err); - } - } - - function updateFromConfig(config) { - // Update appearance settings - if (config.appearance) { - if (config.appearance.anim && config.appearance.anim.durations) { - root.animDurationsScale = config.appearance.anim.durations.scale ?? 1; - } - if (config.appearance.font) { - if (config.appearance.font.family) { - root.fontFamilyMaterial = config.appearance.font.family.material ?? "Material Symbols Rounded"; - root.fontFamilyMono = config.appearance.font.family.mono ?? "CaskaydiaCove NF"; - root.fontFamilySans = config.appearance.font.family.sans ?? "Rubik"; - } - if (config.appearance.font.size) { - root.fontSizeScale = config.appearance.font.size.scale ?? 1; - } - } - if (config.appearance.padding) { - root.paddingScale = config.appearance.padding.scale ?? 1; - } - if (config.appearance.rounding) { - root.roundingScale = config.appearance.rounding.scale ?? 1; - } - if (config.appearance.spacing) { - root.spacingScale = config.appearance.spacing.scale ?? 1; - } - if (config.appearance.transparency) { - root.transparencyEnabled = config.appearance.transparency.enabled ?? false; - root.transparencyBase = config.appearance.transparency.base ?? 0.85; - root.transparencyLayers = config.appearance.transparency.layers ?? 0.4; - } - } - - // Update border settings - if (config.border) { - root.borderRounding = config.border.rounding ?? 1; - root.borderThickness = config.border.thickness ?? 1; - } - - // Update background settings - if (config.background) { - root.desktopClockEnabled = config.background.desktopClock?.enabled !== undefined ? config.background.desktopClock.enabled : false; - root.backgroundEnabled = config.background.enabled !== undefined ? config.background.enabled : true; - if (config.background.visualiser) { - root.visualiserEnabled = config.background.visualiser.enabled !== undefined ? config.background.visualiser.enabled : false; - root.visualiserAutoHide = config.background.visualiser.autoHide !== undefined ? config.background.visualiser.autoHide : true; - root.visualiserRounding = config.background.visualiser.rounding !== undefined ? config.background.visualiser.rounding : 1; - root.visualiserSpacing = config.background.visualiser.spacing !== undefined ? config.background.visualiser.spacing : 1; - } else { - // Set defaults if visualiser object doesn't exist (matching BackgroundConfig defaults) - root.visualiserEnabled = false; - root.visualiserAutoHide = true; - root.visualiserRounding = 1; - root.visualiserSpacing = 1; - } - } else { - // Set defaults if background object doesn't exist (matching BackgroundConfig defaults) - root.desktopClockEnabled = false; - root.backgroundEnabled = true; - root.visualiserEnabled = false; - root.visualiserAutoHide = true; - root.visualiserRounding = 1; - root.visualiserSpacing = 1; - } - } - function collapseAllSections(exceptSection) { if (exceptSection !== themeModeSection) themeModeSection.expanded = false; if (exceptSection !== colorVariantSection) colorVariantSection.expanded = false; @@ -146,73 +60,40 @@ RowLayout { } function saveConfig() { - if (!configFile.loaded) { - console.error("Config file not loaded yet"); - return; - } - - try { - const config = JSON.parse(configFile.text()); - - // Ensure appearance object exists - if (!config.appearance) config.appearance = {}; - - // Update animations - if (!config.appearance.anim) config.appearance.anim = {}; - if (!config.appearance.anim.durations) config.appearance.anim.durations = {}; - config.appearance.anim.durations.scale = root.animDurationsScale; - - // Update fonts - if (!config.appearance.font) config.appearance.font = {}; - if (!config.appearance.font.family) config.appearance.font.family = {}; - config.appearance.font.family.material = root.fontFamilyMaterial; - config.appearance.font.family.mono = root.fontFamilyMono; - config.appearance.font.family.sans = root.fontFamilySans; - if (!config.appearance.font.size) config.appearance.font.size = {}; - config.appearance.font.size.scale = root.fontSizeScale; - - // Update scales - if (!config.appearance.padding) config.appearance.padding = {}; - config.appearance.padding.scale = root.paddingScale; - if (!config.appearance.rounding) config.appearance.rounding = {}; - config.appearance.rounding.scale = root.roundingScale; - if (!config.appearance.spacing) config.appearance.spacing = {}; - config.appearance.spacing.scale = root.spacingScale; - - // Update transparency - if (!config.appearance.transparency) config.appearance.transparency = {}; - config.appearance.transparency.enabled = root.transparencyEnabled; - config.appearance.transparency.base = root.transparencyBase; - config.appearance.transparency.layers = root.transparencyLayers; - - // Ensure background object exists - if (!config.background) config.background = {}; - - // Update desktop clock - if (!config.background.desktopClock) config.background.desktopClock = {}; - config.background.desktopClock.enabled = root.desktopClockEnabled; - - // Update background enabled - config.background.enabled = root.backgroundEnabled; - - // Update visualiser - if (!config.background.visualiser) config.background.visualiser = {}; - config.background.visualiser.enabled = root.visualiserEnabled; - config.background.visualiser.autoHide = root.visualiserAutoHide; - config.background.visualiser.rounding = root.visualiserRounding; - config.background.visualiser.spacing = root.visualiserSpacing; - - // Update border - if (!config.border) config.border = {}; - config.border.rounding = root.borderRounding; - config.border.thickness = root.borderThickness; - - // Write back to file using setText (same simple approach that worked for taskbar) - const jsonString = JSON.stringify(config, null, 4); - configFile.setText(jsonString); - } catch (e) { - console.error("Failed to save config:", e); - } + // Update animations + Config.appearance.anim.durations.scale = root.animDurationsScale; + + // Update fonts + Config.appearance.font.family.material = root.fontFamilyMaterial; + Config.appearance.font.family.mono = root.fontFamilyMono; + Config.appearance.font.family.sans = root.fontFamilySans; + Config.appearance.font.size.scale = root.fontSizeScale; + + // Update scales + Config.appearance.padding.scale = root.paddingScale; + Config.appearance.rounding.scale = root.roundingScale; + Config.appearance.spacing.scale = root.spacingScale; + + // Update transparency + Config.appearance.transparency.enabled = root.transparencyEnabled; + Config.appearance.transparency.base = root.transparencyBase; + Config.appearance.transparency.layers = root.transparencyLayers; + + // Update desktop clock + Config.background.desktopClock.enabled = root.desktopClockEnabled; + + // Update background enabled + Config.background.enabled = root.backgroundEnabled; + + // Update visualiser + Config.background.visualiser.enabled = root.visualiserEnabled; + Config.background.visualiser.autoHide = root.visualiserAutoHide; + Config.background.visualiser.rounding = root.visualiserRounding; + Config.background.visualiser.spacing = root.visualiserSpacing; + + // Update border + Config.border.rounding = root.borderRounding; + Config.border.thickness = root.borderThickness; } Item { diff --git a/modules/controlcenter/launcher/LauncherPane.qml b/modules/controlcenter/launcher/LauncherPane.qml index 0a1175f..d585c32 100644 --- a/modules/controlcenter/launcher/LauncherPane.qml +++ b/modules/controlcenter/launcher/LauncherPane.qml @@ -11,7 +11,6 @@ import qs.config import qs.utils import Caelestia import Quickshell -import Quickshell.Io import Quickshell.Widgets import QtQuick import QtQuick.Layouts @@ -29,74 +28,46 @@ RowLayout { spacing: 0 - FileView { - id: configFile - - path: `${Paths.config}/shell.json` - watchChanges: true - - onLoaded: { - try { - const config = JSON.parse(text()); - updateToggleState(); - } catch (e) { - console.error("Failed to parse config:", e); - } - } - } - function updateToggleState() { - if (!root.selectedApp || !configFile.loaded) { + if (!root.selectedApp) { root.hideFromLauncherChecked = false; return; } - try { - const config = JSON.parse(configFile.text()); - const appId = root.selectedApp.id || root.selectedApp.entry?.id; + const appId = root.selectedApp.id || root.selectedApp.entry?.id; - if (config.launcher && config.launcher.hiddenApps) { - root.hideFromLauncherChecked = config.launcher.hiddenApps.includes(appId); - } else { - root.hideFromLauncherChecked = false; - } - } catch (e) { - console.error("Failed to update toggle state:", e); + if (Config.launcher.hiddenApps && Config.launcher.hiddenApps.length > 0) { + root.hideFromLauncherChecked = Config.launcher.hiddenApps.includes(appId); + } else { + root.hideFromLauncherChecked = false; } } function saveHiddenApps(isHidden) { - if (!configFile.loaded || !root.selectedApp) { + if (!root.selectedApp) { return; } - try { - const config = JSON.parse(configFile.text()); - const appId = root.selectedApp.id || root.selectedApp.entry?.id; - - if (!config.launcher) config.launcher = {}; - if (!config.launcher.hiddenApps) config.launcher.hiddenApps = []; + const appId = root.selectedApp.id || root.selectedApp.entry?.id; - const hiddenApps = config.launcher.hiddenApps; + // Create a new array to ensure change detection + const hiddenApps = Config.launcher.hiddenApps ? [...Config.launcher.hiddenApps] : []; - if (isHidden) { - // Add to hiddenApps if not already there - if (!hiddenApps.includes(appId)) { - hiddenApps.push(appId); - } - } else { - // Remove from hiddenApps - const index = hiddenApps.indexOf(appId); - if (index !== -1) { - hiddenApps.splice(index, 1); - } + if (isHidden) { + // Add to hiddenApps if not already there + if (!hiddenApps.includes(appId)) { + hiddenApps.push(appId); + } + } else { + // Remove from hiddenApps + const index = hiddenApps.indexOf(appId); + if (index !== -1) { + hiddenApps.splice(index, 1); } - - const jsonString = JSON.stringify(config, null, 4); - configFile.setText(jsonString); - } catch (e) { - console.error("Failed to save config:", e); } + + // Update Config to trigger save + Config.launcher.hiddenApps = hiddenApps; } onSelectedAppChanged: { @@ -437,7 +408,7 @@ RowLayout { visible: root.selectedApp !== null label: qsTr("Hide from launcher") checked: root.hideFromLauncherChecked - enabled: root.selectedApp !== null && configFile.loaded + enabled: root.selectedApp !== null onToggled: checked => { root.hideFromLauncherChecked = checked; root.saveHiddenApps(checked); diff --git a/modules/controlcenter/taskbar/TaskbarPane.qml b/modules/controlcenter/taskbar/TaskbarPane.qml index 72e9a80..5385ab7 100644 --- a/modules/controlcenter/taskbar/TaskbarPane.qml +++ b/modules/controlcenter/taskbar/TaskbarPane.qml @@ -9,7 +9,6 @@ import qs.services import qs.config import qs.utils import Quickshell -import Quickshell.Io import QtQuick import QtQuick.Layouts @@ -19,171 +18,98 @@ RowLayout { required property Session session // Bar Behavior - property bool persistent: true - property bool showOnHover: true - property int dragThreshold: 20 + property bool persistent: Config.bar.persistent ?? true + property bool showOnHover: Config.bar.showOnHover ?? true + property int dragThreshold: Config.bar.dragThreshold ?? 20 // Status Icons - property bool showAudio: true - property bool showMicrophone: true - property bool showKbLayout: false - property bool showNetwork: true - property bool showBluetooth: true - property bool showBattery: true - property bool showLockStatus: true + property bool showAudio: Config.bar.status.showAudio ?? true + property bool showMicrophone: Config.bar.status.showMicrophone ?? true + property bool showKbLayout: Config.bar.status.showKbLayout ?? false + property bool showNetwork: Config.bar.status.showNetwork ?? true + property bool showBluetooth: Config.bar.status.showBluetooth ?? true + property bool showBattery: Config.bar.status.showBattery ?? true + property bool showLockStatus: Config.bar.status.showLockStatus ?? true // Tray Settings - property bool trayBackground: false - property bool trayCompact: false - property bool trayRecolour: false + property bool trayBackground: Config.bar.tray.background ?? false + property bool trayCompact: Config.bar.tray.compact ?? false + property bool trayRecolour: Config.bar.tray.recolour ?? false // Workspaces - property int workspacesShown: 5 - property bool workspacesActiveIndicator: true - property bool workspacesOccupiedBg: false - property bool workspacesShowWindows: false - property bool workspacesPerMonitor: true + property int workspacesShown: Config.bar.workspaces.shown ?? 5 + property bool workspacesActiveIndicator: Config.bar.workspaces.activeIndicator ?? true + property bool workspacesOccupiedBg: Config.bar.workspaces.occupiedBg ?? false + property bool workspacesShowWindows: Config.bar.workspaces.showWindows ?? false + property bool workspacesPerMonitor: Config.bar.workspaces.perMonitorWorkspaces ?? true anchors.fill: parent spacing: 0 - FileView { - id: configFile - - path: `${Paths.config}/shell.json` - watchChanges: true - - onLoaded: { - try { - const config = JSON.parse(text()); - updateFromConfig(config); - } catch (e) { - console.error("Failed to parse config:", e); - } - } - } - - function updateFromConfig(config) { + Component.onCompleted: { // Update clock toggle - if (config.bar && config.bar.clock) { - clockShowIconSwitch.checked = config.bar.clock.showIcon !== false; - } + clockShowIconSwitch.checked = Config.bar.clock.showIcon ?? true; // Update entries - if (config.bar && config.bar.entries) { + if (Config.bar.entries) { entriesModel.clear(); - for (const entry of config.bar.entries) { + for (let i = 0; i < Config.bar.entries.length; i++) { + const entry = Config.bar.entries[i]; entriesModel.append({ id: entry.id, enabled: entry.enabled !== false }); } } + } + + function saveConfig(entryIndex, entryEnabled) { + // Update clock setting + Config.bar.clock.showIcon = clockShowIconSwitch.checked; // Update bar behavior - if (config.bar) { - root.persistent = config.bar.persistent !== false; - root.showOnHover = config.bar.showOnHover !== false; - root.dragThreshold = config.bar.dragThreshold || 20; - } + Config.bar.persistent = root.persistent; + Config.bar.showOnHover = root.showOnHover; + Config.bar.dragThreshold = root.dragThreshold; // Update status icons - if (config.bar && config.bar.status) { - root.showAudio = config.bar.status.showAudio !== false; - root.showMicrophone = config.bar.status.showMicrophone !== false; - root.showKbLayout = config.bar.status.showKbLayout === true; - root.showNetwork = config.bar.status.showNetwork !== false; - root.showBluetooth = config.bar.status.showBluetooth !== false; - root.showBattery = config.bar.status.showBattery !== false; - root.showLockStatus = config.bar.status.showLockStatus !== false; - } + Config.bar.status.showAudio = root.showAudio; + Config.bar.status.showMicrophone = root.showMicrophone; + Config.bar.status.showKbLayout = root.showKbLayout; + Config.bar.status.showNetwork = root.showNetwork; + Config.bar.status.showBluetooth = root.showBluetooth; + Config.bar.status.showBattery = root.showBattery; + Config.bar.status.showLockStatus = root.showLockStatus; // Update tray settings - if (config.bar && config.bar.tray) { - root.trayBackground = config.bar.tray.background === true; - root.trayCompact = config.bar.tray.compact === true; - root.trayRecolour = config.bar.tray.recolour === true; - } + Config.bar.tray.background = root.trayBackground; + Config.bar.tray.compact = root.trayCompact; + Config.bar.tray.recolour = root.trayRecolour; // Update workspaces - if (config.bar && config.bar.workspaces) { - root.workspacesShown = config.bar.workspaces.shown || 5; - root.workspacesActiveIndicator = config.bar.workspaces.activeIndicator !== false; - root.workspacesOccupiedBg = config.bar.workspaces.occupiedBg === true; - root.workspacesShowWindows = config.bar.workspaces.showWindows === true; - root.workspacesPerMonitor = config.bar.workspaces.perMonitorWorkspaces !== false; - } - } - - function saveConfig(entryIndex, entryEnabled) { - 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; - - // Update bar behavior - config.bar.persistent = root.persistent; - config.bar.showOnHover = root.showOnHover; - config.bar.dragThreshold = root.dragThreshold; - - // Update status icons - if (!config.bar.status) config.bar.status = {}; - config.bar.status.showAudio = root.showAudio; - config.bar.status.showMicrophone = root.showMicrophone; - config.bar.status.showKbLayout = root.showKbLayout; - config.bar.status.showNetwork = root.showNetwork; - config.bar.status.showBluetooth = root.showBluetooth; - config.bar.status.showBattery = root.showBattery; - config.bar.status.showLockStatus = root.showLockStatus; - - // Update tray settings - if (!config.bar.tray) config.bar.tray = {}; - config.bar.tray.background = root.trayBackground; - config.bar.tray.compact = root.trayCompact; - config.bar.tray.recolour = root.trayRecolour; - - // Update workspaces - if (!config.bar.workspaces) config.bar.workspaces = {}; - config.bar.workspaces.shown = root.workspacesShown; - config.bar.workspaces.activeIndicator = root.workspacesActiveIndicator; - config.bar.workspaces.occupiedBg = root.workspacesOccupiedBg; - config.bar.workspaces.showWindows = root.workspacesShowWindows; - config.bar.workspaces.perMonitorWorkspaces = root.workspacesPerMonitor; - - // Update 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) - // Otherwise use the value from the model - let enabled = entry.enabled; - if (entryIndex !== undefined && i === entryIndex) { - enabled = entryEnabled; - } - config.bar.entries.push({ - id: entry.id, - enabled: enabled - }); + Config.bar.workspaces.shown = root.workspacesShown; + Config.bar.workspaces.activeIndicator = root.workspacesActiveIndicator; + Config.bar.workspaces.occupiedBg = root.workspacesOccupiedBg; + Config.bar.workspaces.showWindows = root.workspacesShowWindows; + Config.bar.workspaces.perMonitorWorkspaces = root.workspacesPerMonitor; + + // Update entries from the model (same approach as clock - use provided value if available) + const entries = []; + for (let i = 0; i < entriesModel.count; i++) { + const entry = entriesModel.get(i); + // If this is the entry being updated, use the provided value (same as clock toggle reads from switch) + // Otherwise use the value from the model + let enabled = entry.enabled; + if (entryIndex !== undefined && i === entryIndex) { + enabled = entryEnabled; } - - // Write back to file using setText (same simple approach that worked for clock) - const jsonString = JSON.stringify(config, null, 4); - configFile.setText(jsonString); - } catch (e) { - console.error("Failed to save config:", e); + entries.push({ + id: entry.id, + enabled: enabled + }); } + Config.bar.entries = entries; } ListModel { -- cgit v1.2.3-freya From 266cffe3421b4619ee10205a20058ab4c52def9e Mon Sep 17 00:00:00 2001 From: 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> Date: Sat, 15 Nov 2025 17:43:03 +1100 Subject: controlcenter: fix wallpaper gradient overlay --- .../controlcenter/appearance/AppearancePane.qml | 1184 ++++++++++---------- 1 file changed, 596 insertions(+), 588 deletions(-) (limited to 'modules/controlcenter/appearance') diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index 6f77d25..1e81205 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -48,15 +48,24 @@ RowLayout { spacing: 0 function collapseAllSections(exceptSection) { - if (exceptSection !== themeModeSection) themeModeSection.expanded = false; - if (exceptSection !== colorVariantSection) colorVariantSection.expanded = false; - if (exceptSection !== colorSchemeSection) colorSchemeSection.expanded = false; - if (exceptSection !== animationsSection) animationsSection.expanded = false; - if (exceptSection !== fontsSection) fontsSection.expanded = false; - if (exceptSection !== scalesSection) scalesSection.expanded = false; - if (exceptSection !== transparencySection) transparencySection.expanded = false; - if (exceptSection !== borderSection) borderSection.expanded = false; - if (exceptSection !== backgroundSection) backgroundSection.expanded = false; + if (exceptSection !== themeModeSection) + themeModeSection.expanded = false; + if (exceptSection !== colorVariantSection) + colorVariantSection.expanded = false; + if (exceptSection !== colorSchemeSection) + colorSchemeSection.expanded = false; + if (exceptSection !== animationsSection) + animationsSection.expanded = false; + if (exceptSection !== fontsSection) + fontsSection.expanded = false; + if (exceptSection !== scalesSection) + scalesSection.expanded = false; + if (exceptSection !== transparencySection) + transparencySection.expanded = false; + if (exceptSection !== borderSection) + borderSection.expanded = false; + if (exceptSection !== backgroundSection) + backgroundSection.expanded = false; } function saveConfig() { @@ -123,712 +132,712 @@ RowLayout { spacing: Appearance.spacing.small - RowLayout { - spacing: Appearance.spacing.smaller + RowLayout { + spacing: Appearance.spacing.smaller - StyledText { - text: qsTr("Settings") - font.pointSize: Appearance.font.size.large - font.weight: 500 - } + StyledText { + text: qsTr("Settings") + font.pointSize: Appearance.font.size.large + font.weight: 500 + } - Item { - Layout.fillWidth: true + Item { + Layout.fillWidth: true + } } - } - CollapsibleSection { - id: themeModeSection - title: qsTr("Theme mode") - description: qsTr("Light or dark theme") - onToggleRequested: { - root.collapseAllSections(themeModeSection); - } + CollapsibleSection { + id: themeModeSection + title: qsTr("Theme mode") + description: qsTr("Light or dark theme") + onToggleRequested: { + root.collapseAllSections(themeModeSection); + } - SwitchRow { - label: qsTr("Dark mode") - checked: !Colours.currentLight - onToggled: checked => { - Colours.setMode(checked ? "dark" : "light"); + SwitchRow { + label: qsTr("Dark mode") + checked: !Colours.currentLight + onToggled: checked => { + Colours.setMode(checked ? "dark" : "light"); + } } } - } - CollapsibleSection { - id: colorVariantSection - title: qsTr("Color variant") - description: qsTr("Material theme variant") - onToggleRequested: { - root.collapseAllSections(colorVariantSection); - } + CollapsibleSection { + id: colorVariantSection + title: qsTr("Color variant") + description: qsTr("Material theme variant") + onToggleRequested: { + root.collapseAllSections(colorVariantSection); + } - StyledListView { - Layout.fillWidth: true - implicitHeight: colorVariantSection.expanded ? Math.min(400, M3Variants.list.length * 60) : 0 + StyledListView { + Layout.fillWidth: true + implicitHeight: colorVariantSection.expanded ? Math.min(400, M3Variants.list.length * 60) : 0 - model: M3Variants.list - spacing: Appearance.spacing.small / 2 - clip: true + model: M3Variants.list + spacing: Appearance.spacing.small / 2 + clip: true - StyledScrollBar.vertical: StyledScrollBar { - flickable: parent - } + StyledScrollBar.vertical: StyledScrollBar { + flickable: parent + } - delegate: StyledRect { - required property var modelData + delegate: StyledRect { + required property var modelData - width: parent ? parent.width : 0 + width: parent ? parent.width : 0 - 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 + } } } - } - CollapsibleSection { - id: colorSchemeSection - title: qsTr("Color scheme") - description: qsTr("Available color schemes") - onToggleRequested: { - root.collapseAllSections(colorSchemeSection); - } + CollapsibleSection { + id: colorSchemeSection + title: qsTr("Color scheme") + description: qsTr("Available color schemes") + onToggleRequested: { + root.collapseAllSections(colorSchemeSection); + } - StyledListView { - Layout.fillWidth: true - implicitHeight: colorSchemeSection.expanded ? Math.min(400, Schemes.list.length * 80) : 0 + StyledListView { + Layout.fillWidth: true + implicitHeight: colorSchemeSection.expanded ? Math.min(400, Schemes.list.length * 80) : 0 - model: Schemes.list - spacing: Appearance.spacing.small / 2 - clip: true + model: Schemes.list + spacing: Appearance.spacing.small / 2 + clip: true - StyledScrollBar.vertical: StyledScrollBar { - flickable: parent - } + StyledScrollBar.vertical: StyledScrollBar { + flickable: parent + } - delegate: StyledRect { - required property var modelData + delegate: StyledRect { + required property var modelData - width: parent ? parent.width : 0 + width: parent ? parent.width : 0 - 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 - spacing: Appearance.spacing.normal + RowLayout { + id: schemeRow - 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 + } } } - } - - CollapsibleSection { - id: animationsSection - title: qsTr("Animations") - onToggleRequested: { - root.collapseAllSections(animationsSection); - } - SpinBoxRow { - label: qsTr("Animation duration scale") - min: 0.1 - max: 5 - value: root.animDurationsScale - onValueModified: value => { - root.animDurationsScale = value; - root.saveConfig(); + CollapsibleSection { + id: animationsSection + title: qsTr("Animations") + onToggleRequested: { + root.collapseAllSections(animationsSection); } - } - } - - CollapsibleSection { - id: fontsSection - title: qsTr("Fonts") - onToggleRequested: { - root.collapseAllSections(fontsSection); - } - StyledText { - Layout.topMargin: Appearance.spacing.normal - text: qsTr("Material font family") - font.pointSize: Appearance.font.size.larger - font.weight: 500 + SpinBoxRow { + label: qsTr("Animation duration scale") + min: 0.1 + max: 5 + value: root.animDurationsScale + onValueModified: value => { + root.animDurationsScale = value; + root.saveConfig(); + } + } } - StyledListView { - 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 + CollapsibleSection { + id: fontsSection + title: qsTr("Fonts") + onToggleRequested: { + root.collapseAllSections(fontsSection); } - delegate: StyledRect { - required property string modelData + StyledText { + Layout.topMargin: Appearance.spacing.normal + text: qsTr("Material font family") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } - anchors.left: parent.left - anchors.right: parent.right + StyledListView { + Layout.fillWidth: true + implicitHeight: fontsSection.expanded ? Math.min(300, Qt.fontFamilies().length * 50) : 0 - readonly property bool isCurrent: modelData === root.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 + model: Qt.fontFamilies() + spacing: Appearance.spacing.small / 2 + clip: true - StateLayer { - function onClicked(): void { - root.fontFamilyMaterial = modelData; - root.saveConfig(); - } + StyledScrollBar.vertical: StyledScrollBar { + flickable: parent } - RowLayout { - id: fontFamilyMaterialRow + delegate: StyledRect { + required property string modelData anchors.left: parent.left anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.normal - spacing: Appearance.spacing.normal + readonly property bool isCurrent: modelData === root.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 - StyledText { - text: modelData - font.pointSize: Appearance.font.size.normal + StateLayer { + function onClicked(): void { + root.fontFamilyMaterial = modelData; + root.saveConfig(); + } } - Item { - Layout.fillWidth: true - } + RowLayout { + id: fontFamilyMaterialRow - Loader { - active: isCurrent - asynchronous: true + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.normal - sourceComponent: MaterialIcon { - text: "check" - color: Colours.palette.m3onSurfaceVariant - font.pointSize: Appearance.font.size.large - } - } - } + spacing: Appearance.spacing.normal - implicitHeight: fontFamilyMaterialRow.implicitHeight + Appearance.padding.normal * 2 - } - } + StyledText { + text: modelData + font.pointSize: Appearance.font.size.normal + } - StyledText { - Layout.topMargin: Appearance.spacing.normal - text: qsTr("Monospace font family") - font.pointSize: Appearance.font.size.larger - font.weight: 500 - } + Item { + Layout.fillWidth: true + } - StyledListView { - Layout.fillWidth: true - implicitHeight: fontsSection.expanded ? Math.min(300, Qt.fontFamilies().length * 50) : 0 + Loader { + active: isCurrent + asynchronous: true - model: Qt.fontFamilies() - spacing: Appearance.spacing.small / 2 - clip: true + sourceComponent: MaterialIcon { + text: "check" + color: Colours.palette.m3onSurfaceVariant + font.pointSize: Appearance.font.size.large + } + } + } - StyledScrollBar.vertical: StyledScrollBar { - flickable: parent + implicitHeight: fontFamilyMaterialRow.implicitHeight + Appearance.padding.normal * 2 + } } - delegate: StyledRect { - required property string modelData + StyledText { + Layout.topMargin: Appearance.spacing.normal + text: qsTr("Monospace font family") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } - anchors.left: parent.left - anchors.right: parent.right + StyledListView { + Layout.fillWidth: true + implicitHeight: fontsSection.expanded ? Math.min(300, Qt.fontFamilies().length * 50) : 0 - readonly property bool isCurrent: modelData === root.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 + model: Qt.fontFamilies() + spacing: Appearance.spacing.small / 2 + clip: true - StateLayer { - function onClicked(): void { - root.fontFamilyMono = modelData; - root.saveConfig(); - } + StyledScrollBar.vertical: StyledScrollBar { + flickable: parent } - RowLayout { - id: fontFamilyMonoRow + delegate: StyledRect { + required property string modelData anchors.left: parent.left anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.normal - spacing: Appearance.spacing.normal + readonly property bool isCurrent: modelData === root.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 - StyledText { - text: modelData - font.pointSize: Appearance.font.size.normal + StateLayer { + function onClicked(): void { + root.fontFamilyMono = modelData; + root.saveConfig(); + } } - Item { - Layout.fillWidth: true - } + RowLayout { + id: fontFamilyMonoRow - Loader { - active: isCurrent - asynchronous: true + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.normal - sourceComponent: MaterialIcon { - text: "check" - color: Colours.palette.m3onSurfaceVariant - font.pointSize: Appearance.font.size.large - } - } - } + spacing: Appearance.spacing.normal - implicitHeight: fontFamilyMonoRow.implicitHeight + Appearance.padding.normal * 2 - } - } + StyledText { + text: modelData + font.pointSize: Appearance.font.size.normal + } - StyledText { - Layout.topMargin: Appearance.spacing.normal - text: qsTr("Sans-serif font family") - font.pointSize: Appearance.font.size.larger - font.weight: 500 - } + Item { + Layout.fillWidth: true + } - StyledListView { - Layout.fillWidth: true - implicitHeight: fontsSection.expanded ? Math.min(300, Qt.fontFamilies().length * 50) : 0 + Loader { + active: isCurrent + asynchronous: true - model: Qt.fontFamilies() - spacing: Appearance.spacing.small / 2 - clip: true + sourceComponent: MaterialIcon { + text: "check" + color: Colours.palette.m3onSurfaceVariant + font.pointSize: Appearance.font.size.large + } + } + } - StyledScrollBar.vertical: StyledScrollBar { - flickable: parent + implicitHeight: fontFamilyMonoRow.implicitHeight + Appearance.padding.normal * 2 + } } - delegate: StyledRect { - required property string modelData + StyledText { + Layout.topMargin: Appearance.spacing.normal + text: qsTr("Sans-serif font family") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } - anchors.left: parent.left - anchors.right: parent.right + StyledListView { + Layout.fillWidth: true + implicitHeight: fontsSection.expanded ? Math.min(300, Qt.fontFamilies().length * 50) : 0 - readonly property bool isCurrent: modelData === root.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 + model: Qt.fontFamilies() + spacing: Appearance.spacing.small / 2 + clip: true - StateLayer { - function onClicked(): void { - root.fontFamilySans = modelData; - root.saveConfig(); - } + StyledScrollBar.vertical: StyledScrollBar { + flickable: parent } - RowLayout { - id: fontFamilySansRow + delegate: StyledRect { + required property string modelData anchors.left: parent.left anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.normal - spacing: Appearance.spacing.normal + readonly property bool isCurrent: modelData === root.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 - StyledText { - text: modelData - font.pointSize: Appearance.font.size.normal + StateLayer { + function onClicked(): void { + root.fontFamilySans = modelData; + root.saveConfig(); + } } - Item { - Layout.fillWidth: true - } + RowLayout { + id: fontFamilySansRow + + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.normal - Loader { - active: isCurrent - asynchronous: true + spacing: Appearance.spacing.normal - sourceComponent: MaterialIcon { - text: "check" - color: Colours.palette.m3onSurfaceVariant - font.pointSize: Appearance.font.size.large + StyledText { + text: modelData + font.pointSize: Appearance.font.size.normal + } + + Item { + Layout.fillWidth: true + } + + Loader { + active: isCurrent + asynchronous: true + + 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 + } } - } - SpinBoxRow { - label: qsTr("Font size scale") - 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(); + } } } - } - CollapsibleSection { - id: scalesSection - title: qsTr("Scales") - onToggleRequested: { - root.collapseAllSections(scalesSection); - } + CollapsibleSection { + id: scalesSection + title: qsTr("Scales") + onToggleRequested: { + root.collapseAllSections(scalesSection); + } - SpinBoxRow { - label: qsTr("Padding scale") - min: 0.1 - max: 5 - value: root.paddingScale - onValueModified: value => { - root.paddingScale = value; - root.saveConfig(); + SpinBoxRow { + label: qsTr("Padding scale") + min: 0.1 + max: 5 + value: root.paddingScale + onValueModified: value => { + root.paddingScale = value; + root.saveConfig(); + } } - } - SpinBoxRow { - label: qsTr("Rounding scale") - min: 0.1 - max: 5 - value: root.roundingScale - onValueModified: value => { - root.roundingScale = value; - root.saveConfig(); + SpinBoxRow { + label: qsTr("Rounding scale") + min: 0.1 + max: 5 + value: root.roundingScale + onValueModified: value => { + root.roundingScale = value; + root.saveConfig(); + } } - } - SpinBoxRow { - label: qsTr("Spacing scale") - min: 0.1 - max: 5 - value: root.spacingScale - onValueModified: value => { - root.spacingScale = value; - root.saveConfig(); + SpinBoxRow { + label: qsTr("Spacing scale") + min: 0.1 + max: 5 + value: root.spacingScale + onValueModified: value => { + root.spacingScale = value; + root.saveConfig(); + } } } - } - CollapsibleSection { - id: transparencySection - title: qsTr("Transparency") - onToggleRequested: { - root.collapseAllSections(transparencySection); - } + CollapsibleSection { + id: transparencySection + title: qsTr("Transparency") + onToggleRequested: { + root.collapseAllSections(transparencySection); + } - SwitchRow { - label: qsTr("Transparency enabled") - checked: root.transparencyEnabled - onToggled: checked => { - root.transparencyEnabled = checked; - root.saveConfig(); + SwitchRow { + label: qsTr("Transparency enabled") + checked: root.transparencyEnabled + onToggled: checked => { + root.transparencyEnabled = checked; + root.saveConfig(); + } } - } - SpinBoxRow { - label: qsTr("Transparency base") - min: 0 - max: 1 - value: root.transparencyBase - onValueModified: value => { - root.transparencyBase = value; - root.saveConfig(); + SpinBoxRow { + label: qsTr("Transparency base") + min: 0 + max: 1 + value: root.transparencyBase + onValueModified: value => { + root.transparencyBase = value; + root.saveConfig(); + } } - } - SpinBoxRow { - label: qsTr("Transparency layers") - min: 0 - max: 1 - value: root.transparencyLayers - onValueModified: value => { - root.transparencyLayers = value; - root.saveConfig(); + SpinBoxRow { + label: qsTr("Transparency layers") + min: 0 + max: 1 + value: root.transparencyLayers + onValueModified: value => { + root.transparencyLayers = value; + root.saveConfig(); + } } } - } - CollapsibleSection { - id: borderSection - title: qsTr("Border") - onToggleRequested: { - root.collapseAllSections(borderSection); - } + CollapsibleSection { + id: borderSection + title: qsTr("Border") + onToggleRequested: { + root.collapseAllSections(borderSection); + } - SpinBoxRow { - label: qsTr("Border rounding") - min: 0.1 - max: 5 - value: root.borderRounding - onValueModified: value => { - root.borderRounding = value; - root.saveConfig(); + SpinBoxRow { + label: qsTr("Border rounding") + min: 0.1 + max: 5 + value: root.borderRounding + onValueModified: value => { + root.borderRounding = value; + root.saveConfig(); + } } - } - SpinBoxRow { - label: qsTr("Border thickness") - min: 0.1 - max: 5 - value: root.borderThickness - onValueModified: value => { - root.borderThickness = value; - root.saveConfig(); + SpinBoxRow { + label: qsTr("Border thickness") + min: 0.1 + max: 5 + value: root.borderThickness + onValueModified: value => { + root.borderThickness = value; + root.saveConfig(); + } } } - } - CollapsibleSection { - id: backgroundSection - title: qsTr("Background") - onToggleRequested: { - root.collapseAllSections(backgroundSection); - } + CollapsibleSection { + id: backgroundSection + title: qsTr("Background") + onToggleRequested: { + root.collapseAllSections(backgroundSection); + } - SwitchRow { - label: qsTr("Desktop clock") - checked: root.desktopClockEnabled - onToggled: checked => { - root.desktopClockEnabled = checked; - root.saveConfig(); + SwitchRow { + label: qsTr("Desktop clock") + checked: root.desktopClockEnabled + onToggled: checked => { + root.desktopClockEnabled = checked; + root.saveConfig(); + } } - } - SwitchRow { - label: qsTr("Background enabled") - checked: root.backgroundEnabled - onToggled: checked => { - root.backgroundEnabled = checked; - root.saveConfig(); + SwitchRow { + label: qsTr("Background enabled") + checked: root.backgroundEnabled + onToggled: checked => { + root.backgroundEnabled = checked; + root.saveConfig(); + } } - } - StyledText { - Layout.topMargin: Appearance.spacing.normal - text: qsTr("Visualiser") - font.pointSize: Appearance.font.size.larger - font.weight: 500 - } + StyledText { + Layout.topMargin: Appearance.spacing.normal + text: qsTr("Visualiser") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } - SwitchRow { - label: qsTr("Visualiser enabled") - checked: root.visualiserEnabled - onToggled: checked => { - root.visualiserEnabled = checked; - root.saveConfig(); + SwitchRow { + label: qsTr("Visualiser enabled") + checked: root.visualiserEnabled + onToggled: checked => { + root.visualiserEnabled = checked; + root.saveConfig(); + } } - } - SwitchRow { - label: qsTr("Visualiser auto hide") - checked: root.visualiserAutoHide - onToggled: checked => { - root.visualiserAutoHide = checked; - root.saveConfig(); + SwitchRow { + label: qsTr("Visualiser auto hide") + checked: root.visualiserAutoHide + onToggled: checked => { + root.visualiserAutoHide = checked; + root.saveConfig(); + } } - } - SpinBoxRow { - label: qsTr("Visualiser rounding") - min: 0 - max: 10 - value: Math.round(root.visualiserRounding) - onValueModified: value => { - root.visualiserRounding = value; - root.saveConfig(); + SpinBoxRow { + label: qsTr("Visualiser rounding") + min: 0 + max: 10 + value: Math.round(root.visualiserRounding) + onValueModified: value => { + root.visualiserRounding = value; + root.saveConfig(); + } } - } - SpinBoxRow { - label: qsTr("Visualiser spacing") - min: 0 - max: 10 - value: Math.round(root.visualiserSpacing) - onValueModified: value => { - root.visualiserSpacing = value; - root.saveConfig(); + SpinBoxRow { + label: qsTr("Visualiser spacing") + min: 0 + max: 10 + value: Math.round(root.visualiserSpacing) + onValueModified: value => { + root.visualiserSpacing = value; + root.saveConfig(); + } } } } - } } InnerBorder { @@ -862,34 +871,34 @@ RowLayout { spacing: Appearance.spacing.normal - MaterialIcon { - Layout.alignment: Qt.AlignHCenter - text: "palette" - font.pointSize: Appearance.font.size.extraLarge * 3 - font.bold: true - } + MaterialIcon { + Layout.alignment: Qt.AlignHCenter + text: "palette" + font.pointSize: Appearance.font.size.extraLarge * 3 + font.bold: true + } - StyledText { - Layout.alignment: Qt.AlignHCenter - text: qsTr("Appearance Settings") - font.pointSize: Appearance.font.size.large - font.bold: true - } + StyledText { + Layout.alignment: Qt.AlignHCenter + text: qsTr("Appearance Settings") + font.pointSize: Appearance.font.size.large + font.bold: true + } - StyledText { - Layout.topMargin: Appearance.spacing.large - Layout.alignment: Qt.AlignHCenter - text: qsTr("Wallpaper") - font.pointSize: Appearance.font.size.extraLarge - font.weight: 600 - } + StyledText { + Layout.topMargin: Appearance.spacing.large + Layout.alignment: Qt.AlignHCenter + text: qsTr("Wallpaper") + font.pointSize: Appearance.font.size.extraLarge + font.weight: 600 + } - StyledText { - Layout.alignment: Qt.AlignHCenter - text: qsTr("Select a wallpaper") - font.pointSize: Appearance.font.size.normal - color: Colours.palette.m3onSurfaceVariant - } + StyledText { + Layout.alignment: Qt.AlignHCenter + text: qsTr("Select a wallpaper") + font.pointSize: Appearance.font.size.normal + color: Colours.palette.m3onSurfaceVariant + } GridLayout { Layout.fillWidth: true @@ -948,10 +957,7 @@ RowLayout { 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)) - ) + 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 @@ -971,10 +977,7 @@ RowLayout { source: modelData.path asynchronous: true fillMode: Image.PreserveAspectCrop - sourceSize: Qt.size( - Math.max(1, Math.floor(parent.width)), - Math.max(1, Math.floor(parent.height)) - ) + sourceSize: Qt.size(Math.max(1, Math.floor(parent.width)), Math.max(1, Math.floor(parent.height))) // Show if caching image hasn't loaded after a delay visible: opacity > 0 @@ -1019,24 +1022,35 @@ RowLayout { color: Colours.palette.m3primary font.pointSize: Appearance.font.size.large } - } - // Gradient overlay for filename with rounded bottom corners - Rectangle { - id: filenameOverlay - anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: parent.bottom - height: filenameText.implicitHeight + Appearance.padding.normal * 2 + // Gradient overlay for filename with rounded bottom corners + Rectangle { + id: filenameOverlay - // Match the parent's rounded corners at the bottom - radius: Appearance.rounding.normal + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + + implicitHeight: filenameText.implicitHeight + Appearance.padding.normal * 2 - 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) } + 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 @@ -1075,10 +1089,6 @@ RowLayout { 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 { @@ -1103,5 +1113,3 @@ RowLayout { } } } - - -- cgit v1.2.3-freya From f79fd9328ce01bde921775a0301a3a6969deaa34 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sat, 15 Nov 2025 10:25:45 -0500 Subject: controlcenter: fix edge to edge in panels --- .../controlcenter/appearance/AppearancePane.qml | 12 ++++++----- modules/controlcenter/audio/AudioPane.qml | 18 ++++++++++------ modules/controlcenter/bluetooth/BtPane.qml | 3 --- modules/controlcenter/launcher/LauncherPane.qml | 14 +++++++++--- modules/controlcenter/network/NetworkingPane.qml | 25 +++++++++------------- modules/controlcenter/taskbar/TaskbarPane.qml | 12 ++++++----- 6 files changed, 46 insertions(+), 38 deletions(-) (limited to 'modules/controlcenter/appearance') diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index 1e81205..35b479a 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -114,8 +114,7 @@ RowLayout { id: sidebarFlickable anchors.fill: parent flickableDirection: Flickable.VerticalFlick - contentHeight: sidebarLayout.implicitHeight + Appearance.padding.large * 2 - clip: true + contentHeight: sidebarLayout.height StyledScrollBar.vertical: StyledScrollBar { flickable: sidebarFlickable @@ -852,11 +851,12 @@ RowLayout { StyledFlickable { anchors.fill: parent - anchors.margins: Appearance.padding.large * 2 + anchors.margins: Appearance.padding.normal + anchors.leftMargin: 0 + anchors.rightMargin: Appearance.padding.normal / 2 flickableDirection: Flickable.VerticalFlick - contentHeight: contentLayout.implicitHeight - clip: true + contentHeight: contentLayout.height StyledScrollBar.vertical: StyledScrollBar { flickable: parent @@ -868,6 +868,8 @@ RowLayout { anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top + anchors.leftMargin: Appearance.padding.large * 2 + anchors.rightMargin: Appearance.padding.large * 2 spacing: Appearance.spacing.normal diff --git a/modules/controlcenter/audio/AudioPane.qml b/modules/controlcenter/audio/AudioPane.qml index b4a0bf1..1f8677e 100644 --- a/modules/controlcenter/audio/AudioPane.qml +++ b/modules/controlcenter/audio/AudioPane.qml @@ -31,7 +31,10 @@ RowLayout { anchors.rightMargin: Appearance.padding.large + Appearance.padding.normal / 2 flickableDirection: Flickable.VerticalFlick contentHeight: leftContent.height - clip: true + + StyledScrollBar.vertical: StyledScrollBar { + flickable: parent + } ColumnLayout { id: leftContent @@ -225,17 +228,16 @@ RowLayout { Layout.fillHeight: true StyledFlickable { - id: rightFlickable - anchors.fill: parent - anchors.margins: Appearance.padding.large * 2 + anchors.margins: Appearance.padding.normal + anchors.leftMargin: 0 + anchors.rightMargin: Appearance.padding.normal / 2 flickableDirection: Flickable.VerticalFlick - contentHeight: contentLayout.implicitHeight - clip: true + contentHeight: contentLayout.height StyledScrollBar.vertical: StyledScrollBar { - flickable: rightFlickable + flickable: parent } ColumnLayout { @@ -244,6 +246,8 @@ RowLayout { anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top + anchors.leftMargin: Appearance.padding.large * 2 + anchors.rightMargin: Appearance.padding.large * 2 spacing: Appearance.spacing.normal diff --git a/modules/controlcenter/bluetooth/BtPane.qml b/modules/controlcenter/bluetooth/BtPane.qml index 40cf356..96dc002 100644 --- a/modules/controlcenter/bluetooth/BtPane.qml +++ b/modules/controlcenter/bluetooth/BtPane.qml @@ -50,7 +50,6 @@ RowLayout { radius: rightBorder.innerRadius color: "transparent" - clip: true Loader { id: loader @@ -60,7 +59,6 @@ RowLayout { anchors.fill: parent anchors.margins: Appearance.padding.large * 2 - clip: true asynchronous: true sourceComponent: pane ? details : settings @@ -108,7 +106,6 @@ RowLayout { StyledFlickable { flickableDirection: Flickable.VerticalFlick contentHeight: settingsInner.height - clip: true Settings { id: settingsInner diff --git a/modules/controlcenter/launcher/LauncherPane.qml b/modules/controlcenter/launcher/LauncherPane.qml index d585c32..1869e18 100644 --- a/modules/controlcenter/launcher/LauncherPane.qml +++ b/modules/controlcenter/launcher/LauncherPane.qml @@ -153,6 +153,7 @@ RowLayout { anchors.margins: Appearance.padding.large + Appearance.padding.normal anchors.leftMargin: Appearance.padding.large anchors.rightMargin: Appearance.padding.large + Appearance.padding.normal / 2 + anchors.bottomMargin: 0 spacing: Appearance.spacing.small @@ -335,12 +336,16 @@ RowLayout { ColumnLayout { anchors.fill: parent - anchors.margins: Appearance.padding.large * 2 + anchors.margins: Appearance.padding.normal + anchors.leftMargin: 0 + anchors.rightMargin: Appearance.padding.normal / 2 spacing: Appearance.spacing.normal Item { Layout.alignment: Qt.AlignHCenter + Layout.leftMargin: Appearance.padding.large * 2 + Layout.rightMargin: Appearance.padding.large * 2 implicitWidth: iconLoader.implicitWidth implicitHeight: iconLoader.implicitHeight @@ -376,6 +381,8 @@ RowLayout { StyledText { Layout.alignment: Qt.AlignHCenter + Layout.leftMargin: Appearance.padding.large * 2 + Layout.rightMargin: Appearance.padding.large * 2 text: root.selectedApp ? (root.selectedApp.name || root.selectedApp.entry?.name || qsTr("Application Details")) : qsTr("Launcher Applications") font.pointSize: Appearance.font.size.large font.bold: true @@ -385,12 +392,13 @@ RowLayout { Layout.fillWidth: true Layout.fillHeight: true Layout.topMargin: Appearance.spacing.large + Layout.leftMargin: Appearance.padding.large * 2 + Layout.rightMargin: Appearance.padding.large * 2 StyledFlickable { anchors.fill: parent flickableDirection: Flickable.VerticalFlick - contentHeight: debugLayout.implicitHeight - clip: true + contentHeight: debugLayout.height StyledScrollBar.vertical: StyledScrollBar { flickable: parent diff --git a/modules/controlcenter/network/NetworkingPane.qml b/modules/controlcenter/network/NetworkingPane.qml index 74e0034..c404af0 100644 --- a/modules/controlcenter/network/NetworkingPane.qml +++ b/modules/controlcenter/network/NetworkingPane.qml @@ -38,12 +38,8 @@ Item { id: leftFlickable anchors.fill: parent - anchors.margins: Appearance.padding.large + Appearance.padding.normal - anchors.leftMargin: Appearance.padding.large - anchors.rightMargin: Appearance.padding.large + Appearance.padding.normal / 2 flickableDirection: Flickable.VerticalFlick contentHeight: leftContent.height - clip: true StyledScrollBar.vertical: StyledScrollBar { flickable: leftFlickable @@ -54,6 +50,10 @@ Item { anchors.left: parent.left anchors.right: parent.right + anchors.top: parent.top + anchors.margins: Appearance.padding.large + Appearance.padding.normal + anchors.leftMargin: Appearance.padding.large + anchors.rightMargin: Appearance.padding.large + Appearance.padding.normal / 2 spacing: Appearance.spacing.normal // Settings header above the collapsible sections @@ -404,7 +404,6 @@ Item { radius: rightBorder.innerRadius color: "transparent" - clip: true // Right pane - networking details/settings Loader { @@ -415,14 +414,17 @@ Item { property var pane: ethernetPane || wirelessPane property string paneId: ethernetPane ? (ethernetPane.interface || "") : (wirelessPane ? (wirelessPane.ssid || wirelessPane.bssid || "") : "") - anchors.fill: parent - anchors.margins: Appearance.padding.large * 2 + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.leftMargin: Appearance.padding.large * 2 + anchors.rightMargin: Appearance.padding.large * 2 opacity: 1 scale: 1 transformOrigin: Item.Center - clip: true asynchronous: true sourceComponent: pane ? (ethernetPane ? ethernetDetails : wirelessDetails) : settings @@ -476,15 +478,8 @@ Item { id: settings StyledFlickable { - id: settingsFlickable - flickableDirection: Flickable.VerticalFlick contentHeight: settingsInner.height - clip: true - - StyledScrollBar.vertical: StyledScrollBar { - flickable: settingsFlickable - } NetworkSettings { id: settingsInner diff --git a/modules/controlcenter/taskbar/TaskbarPane.qml b/modules/controlcenter/taskbar/TaskbarPane.qml index 5385ab7..9ec9e2a 100644 --- a/modules/controlcenter/taskbar/TaskbarPane.qml +++ b/modules/controlcenter/taskbar/TaskbarPane.qml @@ -134,8 +134,7 @@ RowLayout { id: sidebarFlickable anchors.fill: parent flickableDirection: Flickable.VerticalFlick - contentHeight: sidebarLayout.implicitHeight + Appearance.padding.large * 2 - clip: true + contentHeight: sidebarLayout.height StyledScrollBar.vertical: StyledScrollBar { flickable: sidebarFlickable @@ -531,11 +530,12 @@ RowLayout { StyledFlickable { anchors.fill: parent - anchors.margins: Appearance.padding.large * 2 + anchors.margins: Appearance.padding.normal + anchors.leftMargin: 0 + anchors.rightMargin: Appearance.padding.normal / 2 flickableDirection: Flickable.VerticalFlick - contentHeight: contentLayout.implicitHeight - clip: true + contentHeight: contentLayout.height StyledScrollBar.vertical: StyledScrollBar { flickable: parent @@ -547,6 +547,8 @@ RowLayout { anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top + anchors.leftMargin: Appearance.padding.large * 2 + anchors.rightMargin: Appearance.padding.large * 2 spacing: Appearance.spacing.normal -- cgit v1.2.3-freya From f3a0fcc715a8b2fd2a588b5fa13301033380a2ed Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sat, 15 Nov 2025 11:08:03 -0500 Subject: controlcenter: correcting margin inconsistencies between panels --- modules/controlcenter/appearance/AppearancePane.qml | 1 + modules/controlcenter/audio/AudioPane.qml | 1 + modules/controlcenter/launcher/LauncherPane.qml | 1 + modules/controlcenter/taskbar/TaskbarPane.qml | 1 + 4 files changed, 4 insertions(+) (limited to 'modules/controlcenter/appearance') diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index 35b479a..c9d62a0 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -870,6 +870,7 @@ RowLayout { anchors.top: parent.top anchors.leftMargin: Appearance.padding.large * 2 anchors.rightMargin: Appearance.padding.large * 2 + anchors.topMargin: Appearance.padding.large * 2 spacing: Appearance.spacing.normal diff --git a/modules/controlcenter/audio/AudioPane.qml b/modules/controlcenter/audio/AudioPane.qml index 1f8677e..add7078 100644 --- a/modules/controlcenter/audio/AudioPane.qml +++ b/modules/controlcenter/audio/AudioPane.qml @@ -248,6 +248,7 @@ RowLayout { anchors.top: parent.top anchors.leftMargin: Appearance.padding.large * 2 anchors.rightMargin: Appearance.padding.large * 2 + anchors.topMargin: Appearance.padding.large * 2 spacing: Appearance.spacing.normal diff --git a/modules/controlcenter/launcher/LauncherPane.qml b/modules/controlcenter/launcher/LauncherPane.qml index 1869e18..8ddccb4 100644 --- a/modules/controlcenter/launcher/LauncherPane.qml +++ b/modules/controlcenter/launcher/LauncherPane.qml @@ -346,6 +346,7 @@ RowLayout { Layout.alignment: Qt.AlignHCenter Layout.leftMargin: Appearance.padding.large * 2 Layout.rightMargin: Appearance.padding.large * 2 + Layout.topMargin: Appearance.padding.large * 2 implicitWidth: iconLoader.implicitWidth implicitHeight: iconLoader.implicitHeight diff --git a/modules/controlcenter/taskbar/TaskbarPane.qml b/modules/controlcenter/taskbar/TaskbarPane.qml index 9ec9e2a..107091e 100644 --- a/modules/controlcenter/taskbar/TaskbarPane.qml +++ b/modules/controlcenter/taskbar/TaskbarPane.qml @@ -549,6 +549,7 @@ RowLayout { anchors.top: parent.top anchors.leftMargin: Appearance.padding.large * 2 anchors.rightMargin: Appearance.padding.large * 2 + anchors.topMargin: Appearance.padding.large * 2 spacing: Appearance.spacing.normal -- cgit v1.2.3-freya From 546eaa7d4260e9c0f918cfb2a4853d289d807e94 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sat, 15 Nov 2025 11:13:11 -0500 Subject: controlcenter: appearance transparency values now use sliders --- .../controlcenter/appearance/AppearancePane.qml | 98 ++++++++++++++++++---- 1 file changed, 82 insertions(+), 16 deletions(-) (limited to 'modules/controlcenter/appearance') diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index c9d62a0..d78ab21 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -711,25 +711,91 @@ RowLayout { } } - SpinBoxRow { - label: qsTr("Transparency base") - min: 0 - max: 1 - value: root.transparencyBase - onValueModified: value => { - root.transparencyBase = value; - root.saveConfig(); + SectionContainer { + contentSpacing: Appearance.spacing.normal + + ColumnLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.small + + RowLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.normal + + StyledText { + text: qsTr("Transparency base") + font.pointSize: Appearance.font.size.normal + } + + Item { + Layout.fillWidth: true + } + + StyledText { + text: qsTr("%1%").arg(Math.round(root.transparencyBase * 100)) + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.normal + } + } + + StyledSlider { + id: baseSlider + + Layout.fillWidth: true + implicitHeight: Appearance.padding.normal * 3 + + from: 0 + to: 100 + value: root.transparencyBase * 100 + onMoved: { + root.transparencyBase = baseSlider.value / 100; + root.saveConfig(); + } + } } } - SpinBoxRow { - label: qsTr("Transparency layers") - min: 0 - max: 1 - value: root.transparencyLayers - onValueModified: value => { - root.transparencyLayers = value; - root.saveConfig(); + SectionContainer { + contentSpacing: Appearance.spacing.normal + + ColumnLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.small + + RowLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.normal + + StyledText { + text: qsTr("Transparency layers") + font.pointSize: Appearance.font.size.normal + } + + Item { + Layout.fillWidth: true + } + + StyledText { + text: qsTr("%1%").arg(Math.round(root.transparencyLayers * 100)) + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.normal + } + } + + StyledSlider { + id: layersSlider + + Layout.fillWidth: true + implicitHeight: Appearance.padding.normal * 3 + + from: 0 + to: 100 + value: root.transparencyLayers * 100 + onMoved: { + root.transparencyLayers = layersSlider.value / 100; + root.saveConfig(); + } + } } } } -- 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 'modules/controlcenter/appearance') 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 b0006f2f1146c14f4a8d719d6a268ffce1fed0de Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sat, 15 Nov 2025 16:44:11 -0500 Subject: controlcenter: corrected all panels edge-to-edge containers --- .../controlcenter/appearance/AppearancePane.qml | 295 ++++++++++++--------- modules/controlcenter/audio/AudioPane.qml | 124 ++++++--- modules/controlcenter/bluetooth/BtPane.qml | 37 ++- modules/controlcenter/launcher/LauncherPane.qml | 87 ++++-- modules/controlcenter/network/NetworkingPane.qml | 63 +++-- modules/controlcenter/taskbar/TaskbarPane.qml | 155 ++++++----- 6 files changed, 491 insertions(+), 270 deletions(-) (limited to 'modules/controlcenter/appearance') diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index 6781cf0..ba95977 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -12,6 +12,7 @@ import qs.config import qs.utils import Caelestia.Models import Quickshell +import Quickshell.Widgets import QtQuick import QtQuick.Layouts @@ -47,26 +48,6 @@ RowLayout { spacing: 0 - function collapseAllSections(exceptSection) { - if (exceptSection !== themeModeSection) - themeModeSection.expanded = false; - if (exceptSection !== colorVariantSection) - colorVariantSection.expanded = false; - if (exceptSection !== colorSchemeSection) - colorSchemeSection.expanded = false; - if (exceptSection !== animationsSection) - animationsSection.expanded = false; - if (exceptSection !== fontsSection) - fontsSection.expanded = false; - if (exceptSection !== scalesSection) - scalesSection.expanded = false; - if (exceptSection !== transparencySection) - transparencySection.expanded = false; - if (exceptSection !== borderSection) - borderSection.expanded = false; - if (exceptSection !== backgroundSection) - backgroundSection.expanded = false; - } function saveConfig() { // Update animations @@ -110,26 +91,72 @@ RowLayout { Layout.minimumWidth: 420 Layout.fillHeight: true - StyledFlickable { - id: sidebarFlickable + ClippingRectangle { + id: leftAppearanceClippingRect anchors.fill: parent - flickableDirection: Flickable.VerticalFlick - contentHeight: sidebarLayout.height - - StyledScrollBar.vertical: StyledScrollBar { - flickable: sidebarFlickable - } + anchors.margins: Appearance.padding.normal + anchors.leftMargin: 0 + anchors.rightMargin: Appearance.padding.normal / 2 + radius: leftAppearanceBorder.innerRadius + color: "transparent" - ColumnLayout { - id: sidebarLayout - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top + Loader { + id: leftAppearanceLoader + anchors.fill: parent anchors.margins: Appearance.padding.large + Appearance.padding.normal anchors.leftMargin: Appearance.padding.large anchors.rightMargin: Appearance.padding.large + Appearance.padding.normal / 2 + asynchronous: true + sourceComponent: appearanceLeftContentComponent + property var rootPane: root + } + } + + InnerBorder { + id: leftAppearanceBorder + leftThickness: 0 + rightThickness: Appearance.padding.normal / 2 + } + + Component { + id: appearanceLeftContentComponent + + StyledFlickable { + id: sidebarFlickable + readonly property var rootPane: leftAppearanceLoader.rootPane + flickableDirection: Flickable.VerticalFlick + contentHeight: sidebarLayout.height + + function collapseAllSections(exceptSection) { + if (exceptSection !== themeModeSection) + themeModeSection.expanded = false; + if (exceptSection !== colorVariantSection) + colorVariantSection.expanded = false; + if (exceptSection !== colorSchemeSection) + colorSchemeSection.expanded = false; + if (exceptSection !== animationsSection) + animationsSection.expanded = false; + if (exceptSection !== fontsSection) + fontsSection.expanded = false; + if (exceptSection !== scalesSection) + scalesSection.expanded = false; + if (exceptSection !== transparencySection) + transparencySection.expanded = false; + if (exceptSection !== borderSection) + borderSection.expanded = false; + if (exceptSection !== backgroundSection) + backgroundSection.expanded = false; + } - spacing: Appearance.spacing.small + StyledScrollBar.vertical: StyledScrollBar { + flickable: sidebarFlickable + } + + ColumnLayout { + id: sidebarLayout + anchors.left: parent.left + anchors.right: parent.right + spacing: Appearance.spacing.small RowLayout { spacing: Appearance.spacing.smaller @@ -150,7 +177,7 @@ RowLayout { title: qsTr("Theme mode") description: qsTr("Light or dark theme") onToggleRequested: { - root.collapseAllSections(themeModeSection); + sidebarFlickable.collapseAllSections(themeModeSection); } SwitchRow { @@ -167,7 +194,7 @@ RowLayout { title: qsTr("Color variant") description: qsTr("Material theme variant") onToggleRequested: { - root.collapseAllSections(colorVariantSection); + sidebarFlickable.collapseAllSections(colorVariantSection); } StyledListView { @@ -257,7 +284,7 @@ RowLayout { title: qsTr("Color scheme") description: qsTr("Available color schemes") onToggleRequested: { - root.collapseAllSections(colorSchemeSection); + sidebarFlickable.collapseAllSections(colorSchemeSection); } StyledListView { @@ -401,7 +428,7 @@ RowLayout { id: animationsSection title: qsTr("Animations") onToggleRequested: { - root.collapseAllSections(animationsSection); + sidebarFlickable.collapseAllSections(animationsSection); } SpinBoxRow { @@ -409,10 +436,10 @@ RowLayout { min: 0.1 max: 5 step: 0.1 - value: root.animDurationsScale + value: rootPane.animDurationsScale onValueModified: value => { - root.animDurationsScale = value; - root.saveConfig(); + rootPane.animDurationsScale = value; + rootPane.saveConfig(); } } } @@ -421,7 +448,7 @@ RowLayout { id: fontsSection title: qsTr("Fonts") onToggleRequested: { - root.collapseAllSections(fontsSection); + sidebarFlickable.collapseAllSections(fontsSection); } StyledText { @@ -449,7 +476,7 @@ RowLayout { anchors.left: parent.left anchors.right: parent.right - readonly property bool isCurrent: modelData === root.fontFamilyMaterial + 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 @@ -457,8 +484,8 @@ RowLayout { StateLayer { function onClicked(): void { - root.fontFamilyMaterial = modelData; - root.saveConfig(); + rootPane.fontFamilyMaterial = modelData; + rootPane.saveConfig(); } } @@ -522,7 +549,7 @@ RowLayout { anchors.left: parent.left anchors.right: parent.right - readonly property bool isCurrent: modelData === root.fontFamilyMono + 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 @@ -530,8 +557,8 @@ RowLayout { StateLayer { function onClicked(): void { - root.fontFamilyMono = modelData; - root.saveConfig(); + rootPane.fontFamilyMono = modelData; + rootPane.saveConfig(); } } @@ -595,7 +622,7 @@ RowLayout { anchors.left: parent.left anchors.right: parent.right - readonly property bool isCurrent: modelData === root.fontFamilySans + 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 @@ -603,8 +630,8 @@ RowLayout { StateLayer { function onClicked(): void { - root.fontFamilySans = modelData; - root.saveConfig(); + rootPane.fontFamilySans = modelData; + rootPane.saveConfig(); } } @@ -648,10 +675,10 @@ RowLayout { min: 0.1 max: 5 step: 0.1 - value: root.fontSizeScale + value: rootPane.fontSizeScale onValueModified: value => { - root.fontSizeScale = value; - root.saveConfig(); + rootPane.fontSizeScale = value; + rootPane.saveConfig(); } } } @@ -660,7 +687,7 @@ RowLayout { id: scalesSection title: qsTr("Scales") onToggleRequested: { - root.collapseAllSections(scalesSection); + sidebarFlickable.collapseAllSections(scalesSection); } SpinBoxRow { @@ -668,10 +695,10 @@ RowLayout { min: 0.1 max: 5 step: 0.1 - value: root.paddingScale + value: rootPane.paddingScale onValueModified: value => { - root.paddingScale = value; - root.saveConfig(); + rootPane.paddingScale = value; + rootPane.saveConfig(); } } @@ -680,10 +707,10 @@ RowLayout { min: 0.1 max: 5 step: 0.1 - value: root.roundingScale + value: rootPane.roundingScale onValueModified: value => { - root.roundingScale = value; - root.saveConfig(); + rootPane.roundingScale = value; + rootPane.saveConfig(); } } @@ -692,10 +719,10 @@ RowLayout { min: 0.1 max: 5 step: 0.1 - value: root.spacingScale + value: rootPane.spacingScale onValueModified: value => { - root.spacingScale = value; - root.saveConfig(); + rootPane.spacingScale = value; + rootPane.saveConfig(); } } } @@ -704,15 +731,15 @@ RowLayout { id: transparencySection title: qsTr("Transparency") onToggleRequested: { - root.collapseAllSections(transparencySection); + sidebarFlickable.collapseAllSections(transparencySection); } SwitchRow { label: qsTr("Transparency enabled") - checked: root.transparencyEnabled + checked: rootPane.transparencyEnabled onToggled: checked => { - root.transparencyEnabled = checked; - root.saveConfig(); + rootPane.transparencyEnabled = checked; + rootPane.saveConfig(); } } @@ -737,7 +764,7 @@ RowLayout { } StyledText { - text: qsTr("%1%").arg(Math.round(root.transparencyBase * 100)) + text: qsTr("%1%").arg(Math.round(rootPane.transparencyBase * 100)) color: Colours.palette.m3outline font.pointSize: Appearance.font.size.normal } @@ -751,10 +778,10 @@ RowLayout { from: 0 to: 100 - value: root.transparencyBase * 100 + value: rootPane.transparencyBase * 100 onMoved: { - root.transparencyBase = baseSlider.value / 100; - root.saveConfig(); + rootPane.transparencyBase = baseSlider.value / 100; + rootPane.saveConfig(); } } } @@ -781,7 +808,7 @@ RowLayout { } StyledText { - text: qsTr("%1%").arg(Math.round(root.transparencyLayers * 100)) + text: qsTr("%1%").arg(Math.round(rootPane.transparencyLayers * 100)) color: Colours.palette.m3outline font.pointSize: Appearance.font.size.normal } @@ -795,10 +822,10 @@ RowLayout { from: 0 to: 100 - value: root.transparencyLayers * 100 + value: rootPane.transparencyLayers * 100 onMoved: { - root.transparencyLayers = layersSlider.value / 100; - root.saveConfig(); + rootPane.transparencyLayers = layersSlider.value / 100; + rootPane.saveConfig(); } } } @@ -809,7 +836,7 @@ RowLayout { id: borderSection title: qsTr("Border") onToggleRequested: { - root.collapseAllSections(borderSection); + sidebarFlickable.collapseAllSections(borderSection); } SpinBoxRow { @@ -817,10 +844,10 @@ RowLayout { min: 0.1 max: 100 step: 0.1 - value: root.borderRounding + value: rootPane.borderRounding onValueModified: value => { - root.borderRounding = value; - root.saveConfig(); + rootPane.borderRounding = value; + rootPane.saveConfig(); } } @@ -829,10 +856,10 @@ RowLayout { min: 0.1 max: 100 step: 0.1 - value: root.borderThickness + value: rootPane.borderThickness onValueModified: value => { - root.borderThickness = value; - root.saveConfig(); + rootPane.borderThickness = value; + rootPane.saveConfig(); } } } @@ -841,24 +868,24 @@ RowLayout { id: backgroundSection title: qsTr("Background") onToggleRequested: { - root.collapseAllSections(backgroundSection); + sidebarFlickable.collapseAllSections(backgroundSection); } SwitchRow { label: qsTr("Desktop clock") - checked: root.desktopClockEnabled + checked: rootPane.desktopClockEnabled onToggled: checked => { - root.desktopClockEnabled = checked; - root.saveConfig(); + rootPane.desktopClockEnabled = checked; + rootPane.saveConfig(); } } SwitchRow { label: qsTr("Background enabled") - checked: root.backgroundEnabled + checked: rootPane.backgroundEnabled onToggled: checked => { - root.backgroundEnabled = checked; - root.saveConfig(); + rootPane.backgroundEnabled = checked; + rootPane.saveConfig(); } } @@ -871,19 +898,19 @@ RowLayout { SwitchRow { label: qsTr("Visualiser enabled") - checked: root.visualiserEnabled + checked: rootPane.visualiserEnabled onToggled: checked => { - root.visualiserEnabled = checked; - root.saveConfig(); + rootPane.visualiserEnabled = checked; + rootPane.saveConfig(); } } SwitchRow { label: qsTr("Visualiser auto hide") - checked: root.visualiserAutoHide + checked: rootPane.visualiserAutoHide onToggled: checked => { - root.visualiserAutoHide = checked; - root.saveConfig(); + rootPane.visualiserAutoHide = checked; + rootPane.saveConfig(); } } @@ -891,10 +918,10 @@ RowLayout { label: qsTr("Visualiser rounding") min: 0 max: 10 - value: Math.round(root.visualiserRounding) + value: Math.round(rootPane.visualiserRounding) onValueModified: value => { - root.visualiserRounding = value; - root.saveConfig(); + rootPane.visualiserRounding = value; + rootPane.saveConfig(); } } @@ -902,19 +929,15 @@ RowLayout { label: qsTr("Visualiser spacing") min: 0 max: 10 - value: Math.round(root.visualiserSpacing) + value: Math.round(rootPane.visualiserSpacing) onValueModified: value => { - root.visualiserSpacing = value; - root.saveConfig(); + rootPane.visualiserSpacing = value; + rootPane.saveConfig(); } } } } - } - - InnerBorder { - leftThickness: 0 - rightThickness: Appearance.padding.normal / 2 + } } } @@ -922,30 +945,53 @@ RowLayout { Layout.fillWidth: true Layout.fillHeight: true - StyledFlickable { + ClippingRectangle { + id: rightAppearanceClippingRect anchors.fill: parent anchors.margins: Appearance.padding.normal anchors.leftMargin: 0 anchors.rightMargin: Appearance.padding.normal / 2 + radius: rightAppearanceBorder.innerRadius + color: "transparent" - flickableDirection: Flickable.VerticalFlick - contentHeight: contentLayout.height - - StyledScrollBar.vertical: StyledScrollBar { - flickable: parent + Loader { + id: rightAppearanceLoader + anchors.fill: parent + anchors.topMargin: Appearance.padding.large * 2 + anchors.bottomMargin: Appearance.padding.large * 2 + anchors.leftMargin: 0 + anchors.rightMargin: 0 + asynchronous: true + sourceComponent: appearanceRightContentComponent + property var rootPane: root } + } - ColumnLayout { - id: contentLayout + InnerBorder { + id: rightAppearanceBorder + leftThickness: Appearance.padding.normal / 2 + } - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - anchors.leftMargin: Appearance.padding.large * 2 - anchors.rightMargin: Appearance.padding.large * 2 - anchors.topMargin: Appearance.padding.large * 2 + Component { + id: appearanceRightContentComponent - spacing: Appearance.spacing.normal + StyledFlickable { + id: rightAppearanceFlickable + flickableDirection: Flickable.VerticalFlick + contentHeight: contentLayout.height + + StyledScrollBar.vertical: StyledScrollBar { + flickable: rightAppearanceFlickable + } + + ColumnLayout { + id: contentLayout + + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: Appearance.padding.large * 2 + anchors.rightMargin: Appearance.padding.large * 2 + spacing: Appearance.spacing.normal MaterialIcon { Layout.alignment: Qt.AlignHCenter @@ -1182,10 +1228,7 @@ RowLayout { } } } - } - - InnerBorder { - leftThickness: Appearance.padding.normal / 2 + } } } } diff --git a/modules/controlcenter/audio/AudioPane.qml b/modules/controlcenter/audio/AudioPane.qml index add7078..005de3a 100644 --- a/modules/controlcenter/audio/AudioPane.qml +++ b/modules/controlcenter/audio/AudioPane.qml @@ -7,6 +7,7 @@ import qs.components.effects import qs.components.containers import qs.services import qs.config +import Quickshell.Widgets import QtQuick import QtQuick.Layouts @@ -20,28 +21,58 @@ RowLayout { spacing: 0 Item { + id: leftAudioItem Layout.preferredWidth: Math.floor(parent.width * 0.4) Layout.minimumWidth: 420 Layout.fillHeight: true - StyledFlickable { + ClippingRectangle { + id: leftAudioClippingRect anchors.fill: parent - anchors.margins: Appearance.padding.large + Appearance.padding.normal - anchors.leftMargin: Appearance.padding.large - anchors.rightMargin: Appearance.padding.large + Appearance.padding.normal / 2 - flickableDirection: Flickable.VerticalFlick - contentHeight: leftContent.height - - StyledScrollBar.vertical: StyledScrollBar { - flickable: parent + anchors.margins: Appearance.padding.normal + anchors.leftMargin: 0 + anchors.rightMargin: Appearance.padding.normal / 2 + + radius: leftAudioBorder.innerRadius + color: "transparent" + + Loader { + id: leftAudioLoader + + anchors.fill: parent + anchors.margins: Appearance.padding.large + Appearance.padding.normal + anchors.leftMargin: Appearance.padding.large + anchors.rightMargin: Appearance.padding.large + Appearance.padding.normal / 2 + + asynchronous: true + sourceComponent: audioLeftContentComponent } + } + + InnerBorder { + id: leftAudioBorder + leftThickness: 0 + rightThickness: Appearance.padding.normal / 2 + } + + Component { + id: audioLeftContentComponent + + StyledFlickable { + id: leftAudioFlickable + flickableDirection: Flickable.VerticalFlick + contentHeight: leftContent.height - ColumnLayout { - id: leftContent + StyledScrollBar.vertical: StyledScrollBar { + flickable: leftAudioFlickable + } + + 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 // Settings header above the collapsible sections RowLayout { @@ -215,42 +246,64 @@ RowLayout { } } } - } - - InnerBorder { - leftThickness: 0 - rightThickness: Appearance.padding.normal / 2 + } } } Item { + id: rightAudioItem Layout.fillWidth: true Layout.fillHeight: true - StyledFlickable { + ClippingRectangle { + id: rightAudioClippingRect anchors.fill: parent anchors.margins: Appearance.padding.normal anchors.leftMargin: 0 anchors.rightMargin: Appearance.padding.normal / 2 - flickableDirection: Flickable.VerticalFlick - contentHeight: contentLayout.height + radius: rightAudioBorder.innerRadius + color: "transparent" + + Loader { + id: rightAudioLoader + + anchors.fill: parent + anchors.topMargin: Appearance.padding.large * 2 + anchors.bottomMargin: Appearance.padding.large * 2 + anchors.leftMargin: 0 + anchors.rightMargin: 0 - StyledScrollBar.vertical: StyledScrollBar { - flickable: parent + asynchronous: true + sourceComponent: audioRightContentComponent } + } + + InnerBorder { + id: rightAudioBorder + leftThickness: Appearance.padding.normal / 2 + } - ColumnLayout { - id: contentLayout + Component { + id: audioRightContentComponent - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - anchors.leftMargin: Appearance.padding.large * 2 - anchors.rightMargin: Appearance.padding.large * 2 - anchors.topMargin: Appearance.padding.large * 2 + StyledFlickable { + id: rightAudioFlickable + flickableDirection: Flickable.VerticalFlick + contentHeight: contentLayout.height + + StyledScrollBar.vertical: StyledScrollBar { + flickable: rightAudioFlickable + } + + ColumnLayout { + id: contentLayout - spacing: Appearance.spacing.normal + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: Appearance.padding.large * 2 + anchors.rightMargin: Appearance.padding.large * 2 + spacing: Appearance.spacing.normal ConnectionHeader { icon: "volume_up" @@ -397,10 +450,7 @@ RowLayout { } } } - } - - InnerBorder { - leftThickness: Appearance.padding.normal / 2 + } } } } \ No newline at end of file diff --git a/modules/controlcenter/bluetooth/BtPane.qml b/modules/controlcenter/bluetooth/BtPane.qml index 96dc002..32d2c0d 100644 --- a/modules/controlcenter/bluetooth/BtPane.qml +++ b/modules/controlcenter/bluetooth/BtPane.qml @@ -19,30 +19,57 @@ RowLayout { spacing: 0 Item { + id: leftBtItem Layout.preferredWidth: Math.floor(parent.width * 0.4) Layout.minimumWidth: 420 Layout.fillHeight: true - DeviceList { + ClippingRectangle { + id: leftBtClippingRect anchors.fill: parent - anchors.margins: Appearance.padding.large + Appearance.padding.normal - anchors.leftMargin: Appearance.padding.large - anchors.rightMargin: Appearance.padding.large + Appearance.padding.normal / 2 + anchors.margins: Appearance.padding.normal + anchors.leftMargin: 0 + anchors.rightMargin: Appearance.padding.normal / 2 + + radius: leftBtBorder.innerRadius + color: "transparent" + + Loader { + id: leftBtLoader + + anchors.fill: parent + anchors.margins: Appearance.padding.large + Appearance.padding.normal + anchors.leftMargin: Appearance.padding.large + anchors.rightMargin: Appearance.padding.large + Appearance.padding.normal / 2 - session: root.session + asynchronous: true + sourceComponent: btDeviceListComponent + } } InnerBorder { + id: leftBtBorder leftThickness: 0 rightThickness: Appearance.padding.normal / 2 } + + Component { + id: btDeviceListComponent + + DeviceList { + anchors.fill: parent + session: root.session + } + } } Item { + id: rightBtItem Layout.fillWidth: true Layout.fillHeight: true ClippingRectangle { + id: btClippingRect anchors.fill: parent anchors.margins: Appearance.padding.normal anchors.leftMargin: 0 diff --git a/modules/controlcenter/launcher/LauncherPane.qml b/modules/controlcenter/launcher/LauncherPane.qml index 8ddccb4..12abc1e 100644 --- a/modules/controlcenter/launcher/LauncherPane.qml +++ b/modules/controlcenter/launcher/LauncherPane.qml @@ -144,20 +144,50 @@ RowLayout { } Item { + id: leftLauncherItem Layout.preferredWidth: Math.floor(parent.width * 0.4) Layout.minimumWidth: 420 Layout.fillHeight: true - ColumnLayout { + ClippingRectangle { + id: leftLauncherClippingRect anchors.fill: parent - anchors.margins: Appearance.padding.large + Appearance.padding.normal - anchors.leftMargin: Appearance.padding.large - anchors.rightMargin: Appearance.padding.large + Appearance.padding.normal / 2 - anchors.bottomMargin: 0 + anchors.margins: Appearance.padding.normal + anchors.leftMargin: 0 + anchors.rightMargin: Appearance.padding.normal / 2 + + radius: leftLauncherBorder.innerRadius + color: "transparent" + + Loader { + id: leftLauncherLoader - spacing: Appearance.spacing.small + anchors.fill: parent + anchors.margins: Appearance.padding.large + Appearance.padding.normal + anchors.leftMargin: Appearance.padding.large + anchors.rightMargin: Appearance.padding.large + Appearance.padding.normal / 2 + anchors.bottomMargin: 0 - RowLayout { + asynchronous: true + sourceComponent: leftContentComponent + } + } + + InnerBorder { + id: leftLauncherBorder + leftThickness: 0 + rightThickness: Appearance.padding.normal / 2 + } + + Component { + id: leftContentComponent + + ColumnLayout { + anchors.fill: parent + + spacing: Appearance.spacing.small + + RowLayout { spacing: Appearance.spacing.smaller StyledText { @@ -268,6 +298,7 @@ RowLayout { } StyledListView { + id: appsListView Layout.fillWidth: true Layout.fillHeight: true @@ -323,26 +354,50 @@ RowLayout { } } } - - InnerBorder { - leftThickness: 0 - rightThickness: Appearance.padding.normal / 2 } } Item { + id: rightLauncherItem Layout.fillWidth: true Layout.fillHeight: true - ColumnLayout { + ClippingRectangle { + id: rightLauncherClippingRect anchors.fill: parent anchors.margins: Appearance.padding.normal anchors.leftMargin: 0 anchors.rightMargin: Appearance.padding.normal / 2 - spacing: Appearance.spacing.normal + radius: rightLauncherBorder.innerRadius + color: "transparent" - Item { + Loader { + id: rightLauncherLoader + + anchors.fill: parent + anchors.margins: Appearance.padding.large * 2 + + asynchronous: true + sourceComponent: rightContentComponent + } + } + + InnerBorder { + id: rightLauncherBorder + + leftThickness: Appearance.padding.normal / 2 + } + + Component { + id: rightContentComponent + + ColumnLayout { + anchors.fill: parent + + spacing: Appearance.spacing.normal + + Item { Layout.alignment: Qt.AlignHCenter Layout.leftMargin: Appearance.padding.large * 2 Layout.rightMargin: Appearance.padding.large * 2 @@ -397,6 +452,7 @@ RowLayout { Layout.rightMargin: Appearance.padding.large * 2 StyledFlickable { + id: detailsFlickable anchors.fill: parent flickableDirection: Flickable.VerticalFlick contentHeight: debugLayout.height @@ -428,9 +484,6 @@ RowLayout { } } } - - InnerBorder { - leftThickness: Appearance.padding.normal / 2 } } } diff --git a/modules/controlcenter/network/NetworkingPane.qml b/modules/controlcenter/network/NetworkingPane.qml index ada29dc..c3621bb 100644 --- a/modules/controlcenter/network/NetworkingPane.qml +++ b/modules/controlcenter/network/NetworkingPane.qml @@ -29,32 +29,59 @@ Item { spacing: 0 Item { + id: leftNetworkItem Layout.preferredWidth: Math.floor(parent.width * 0.4) Layout.minimumWidth: 420 Layout.fillHeight: true - // Left pane - networking list with collapsible sections - StyledFlickable { - id: leftFlickable - + ClippingRectangle { + id: leftNetworkClippingRect anchors.fill: parent - flickableDirection: Flickable.VerticalFlick - contentHeight: leftContent.height + anchors.margins: Appearance.padding.normal + anchors.leftMargin: 0 + anchors.rightMargin: Appearance.padding.normal / 2 - StyledScrollBar.vertical: StyledScrollBar { - flickable: leftFlickable - } + radius: leftNetworkBorder.innerRadius + color: "transparent" - ColumnLayout { - id: leftContent + Loader { + id: leftNetworkLoader - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top + anchors.fill: parent anchors.margins: Appearance.padding.large + Appearance.padding.normal anchors.leftMargin: Appearance.padding.large anchors.rightMargin: Appearance.padding.large + Appearance.padding.normal / 2 - spacing: Appearance.spacing.normal + + asynchronous: true + sourceComponent: networkListComponent + } + } + + InnerBorder { + id: leftNetworkBorder + leftThickness: 0 + rightThickness: Appearance.padding.normal / 2 + } + + Component { + id: networkListComponent + + StyledFlickable { + id: leftFlickable + + flickableDirection: Flickable.VerticalFlick + contentHeight: leftContent.height + + StyledScrollBar.vertical: StyledScrollBar { + flickable: leftFlickable + } + + ColumnLayout { + id: leftContent + + anchors.left: parent.left + anchors.right: parent.right + spacing: Appearance.spacing.normal // Settings header above the collapsible sections RowLayout { @@ -385,18 +412,16 @@ Item { } } } - - InnerBorder { - leftThickness: 0 - rightThickness: Appearance.padding.normal / 2 } } Item { + id: rightNetworkItem Layout.fillWidth: true Layout.fillHeight: true ClippingRectangle { + id: networkClippingRect anchors.fill: parent anchors.margins: Appearance.padding.normal anchors.leftMargin: 0 diff --git a/modules/controlcenter/taskbar/TaskbarPane.qml b/modules/controlcenter/taskbar/TaskbarPane.qml index 107091e..85e5275 100644 --- a/modules/controlcenter/taskbar/TaskbarPane.qml +++ b/modules/controlcenter/taskbar/TaskbarPane.qml @@ -9,6 +9,7 @@ import qs.services import qs.config import qs.utils import Quickshell +import Quickshell.Widgets import QtQuick import QtQuick.Layouts @@ -17,6 +18,9 @@ RowLayout { required property Session session + // Clock + property bool clockShowIcon: Config.bar.clock.showIcon ?? true + // Bar Behavior property bool persistent: Config.bar.persistent ?? true property bool showOnHover: Config.bar.showOnHover ?? true @@ -48,9 +52,6 @@ RowLayout { spacing: 0 Component.onCompleted: { - // Update clock toggle - clockShowIconSwitch.checked = Config.bar.clock.showIcon ?? true; - // Update entries if (Config.bar.entries) { entriesModel.clear(); @@ -66,7 +67,7 @@ RowLayout { function saveConfig(entryIndex, entryEnabled) { // Update clock setting - Config.bar.clock.showIcon = clockShowIconSwitch.checked; + Config.bar.clock.showIcon = root.clockShowIcon; // Update bar behavior Config.bar.persistent = root.persistent; @@ -116,42 +117,62 @@ RowLayout { id: entriesModel } - - function collapseAllSections(exceptSection) { - if (exceptSection !== clockSection) clockSection.expanded = false; - if (exceptSection !== barBehaviorSection) barBehaviorSection.expanded = false; - if (exceptSection !== statusIconsSection) statusIconsSection.expanded = false; - if (exceptSection !== traySettingsSection) traySettingsSection.expanded = false; - if (exceptSection !== workspacesSection) workspacesSection.expanded = false; - } - Item { + id: leftTaskbarItem Layout.preferredWidth: Math.floor(parent.width * 0.4) Layout.minimumWidth: 420 Layout.fillHeight: true - StyledFlickable { - id: sidebarFlickable + ClippingRectangle { + id: leftTaskbarClippingRect anchors.fill: parent - flickableDirection: Flickable.VerticalFlick - contentHeight: sidebarLayout.height + anchors.margins: Appearance.padding.normal + anchors.leftMargin: 0 + anchors.rightMargin: Appearance.padding.normal / 2 - StyledScrollBar.vertical: StyledScrollBar { - flickable: sidebarFlickable - } + radius: leftTaskbarBorder.innerRadius + color: "transparent" + + Loader { + id: leftTaskbarLoader - ColumnLayout { - id: sidebarLayout - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top + anchors.fill: parent anchors.margins: Appearance.padding.large + Appearance.padding.normal anchors.leftMargin: Appearance.padding.large anchors.rightMargin: Appearance.padding.large + Appearance.padding.normal / 2 - spacing: Appearance.spacing.small + asynchronous: true + sourceComponent: leftTaskbarContentComponent + } + } - RowLayout { + InnerBorder { + id: leftTaskbarBorder + leftThickness: 0 + rightThickness: Appearance.padding.normal / 2 + } + + Component { + id: leftTaskbarContentComponent + + StyledFlickable { + id: sidebarFlickable + flickableDirection: Flickable.VerticalFlick + contentHeight: sidebarLayout.height + + StyledScrollBar.vertical: StyledScrollBar { + flickable: sidebarFlickable + } + + ColumnLayout { + id: sidebarLayout + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + + spacing: Appearance.spacing.small + + RowLayout { spacing: Appearance.spacing.smaller StyledText { @@ -169,9 +190,6 @@ RowLayout { id: clockSection title: qsTr("Clock") description: qsTr("Clock display settings") - onToggleRequested: { - root.collapseAllSections(clockSection); - } RowLayout { id: clockRow @@ -190,8 +208,9 @@ RowLayout { StyledSwitch { id: clockShowIconSwitch - checked: true + checked: root.clockShowIcon onToggled: { + root.clockShowIcon = checked; root.saveConfig(); } } @@ -201,9 +220,6 @@ RowLayout { CollapsibleSection { id: barBehaviorSection title: qsTr("Bar Behavior") - onToggleRequested: { - root.collapseAllSections(barBehaviorSection); - } SwitchRow { label: qsTr("Persistent") @@ -238,9 +254,6 @@ RowLayout { CollapsibleSection { id: statusIconsSection title: qsTr("Status Icons") - onToggleRequested: { - root.collapseAllSections(statusIconsSection); - } SwitchRow { label: qsTr("Show audio") @@ -309,9 +322,6 @@ RowLayout { CollapsibleSection { id: traySettingsSection title: qsTr("Tray Settings") - onToggleRequested: { - root.collapseAllSections(traySettingsSection); - } SwitchRow { label: qsTr("Background") @@ -344,9 +354,6 @@ RowLayout { CollapsibleSection { id: workspacesSection title: qsTr("Workspaces") - onToggleRequested: { - root.collapseAllSections(workspacesSection); - } StyledRect { Layout.fillWidth: true @@ -517,43 +524,62 @@ RowLayout { } } } - - InnerBorder { - leftThickness: 0 - rightThickness: Appearance.padding.normal / 2 } } Item { + id: rightTaskbarItem Layout.fillWidth: true Layout.fillHeight: true - StyledFlickable { + ClippingRectangle { + id: rightTaskbarClippingRect anchors.fill: parent anchors.margins: Appearance.padding.normal anchors.leftMargin: 0 anchors.rightMargin: Appearance.padding.normal / 2 - flickableDirection: Flickable.VerticalFlick - contentHeight: contentLayout.height + radius: rightTaskbarBorder.innerRadius + color: "transparent" + + Loader { + id: rightTaskbarLoader - StyledScrollBar.vertical: StyledScrollBar { - flickable: parent + anchors.fill: parent + anchors.margins: Appearance.padding.large * 2 + + asynchronous: true + sourceComponent: rightTaskbarContentComponent } + } + + InnerBorder { + id: rightTaskbarBorder + + leftThickness: Appearance.padding.normal / 2 + } + + Component { + id: rightTaskbarContentComponent + + StyledFlickable { + flickableDirection: Flickable.VerticalFlick + contentHeight: contentLayout.height - ColumnLayout { - id: contentLayout + StyledScrollBar.vertical: StyledScrollBar { + flickable: parent + } + + ColumnLayout { + id: contentLayout - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - anchors.leftMargin: Appearance.padding.large * 2 - anchors.rightMargin: Appearance.padding.large * 2 - anchors.topMargin: Appearance.padding.large * 2 + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top - spacing: Appearance.spacing.normal + spacing: Appearance.spacing.normal - MaterialIcon { + MaterialIcon { Layout.alignment: Qt.AlignHCenter text: "task_alt" font.pointSize: Appearance.font.size.extraLarge * 3 @@ -577,7 +603,7 @@ RowLayout { StyledText { Layout.alignment: Qt.AlignHCenter - text: clockShowIconSwitch.checked ? qsTr("Clock icon enabled") : qsTr("Clock icon disabled") + text: root.clockShowIcon ? qsTr("Clock icon enabled") : qsTr("Clock icon disabled") color: Colours.palette.m3outline } @@ -597,9 +623,6 @@ RowLayout { } } - - InnerBorder { - leftThickness: Appearance.padding.normal / 2 } } } -- cgit v1.2.3-freya From 7023a72ef1ad21b96aae1bbf820f2181df837f9b Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sat, 15 Nov 2025 17:07:27 -0500 Subject: controlcenter: corrected wallpaper flicker issue --- .../controlcenter/appearance/AppearancePane.qml | 79 +++++++++++----------- modules/launcher/items/WallpaperItem.qml | 1 + shell | 1 + 3 files changed, 42 insertions(+), 39 deletions(-) create mode 160000 shell (limited to 'modules/controlcenter/appearance') diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index ba95977..baf49cf 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -1049,10 +1049,10 @@ RowLayout { Layout.preferredHeight: 140 Layout.minimumWidth: 200 Layout.minimumHeight: 140 + Layout.maximumWidth: 200 + Layout.maximumHeight: 140 readonly property bool isCurrent: modelData.path === Wallpapers.actualCurrent - readonly property real imageWidth: Math.max(1, width) - readonly property real imageHeight: Math.max(1, height) StateLayer { radius: Appearance.rounding.normal @@ -1069,71 +1069,72 @@ RowLayout { color: Colours.tPalette.m3surfaceContainer radius: Appearance.rounding.normal - border.width: isCurrent ? 2 : 0 - border.color: Colours.palette.m3primary - CachingImage { id: cachingImage path: modelData.path anchors.fill: parent + cache: true + visible: opacity > 0 - // 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 + // Show when ready + opacity: status === Image.Ready ? 1 : 0 Behavior on opacity { NumberAnimation { - duration: 200 + duration: 150 + easing.type: Easing.OutQuad } } } - // Fallback: Direct image load if caching fails or is slow + // Fallback image for when caching fails Image { id: fallbackImage anchors.fill: parent - source: modelData.path + source: fallbackTimer.triggered && cachingImage.status !== Image.Ready ? modelData.path : "" asynchronous: true fillMode: Image.PreserveAspectCrop - sourceSize: Qt.size(Math.max(1, Math.floor(parent.width)), Math.max(1, Math.floor(parent.height))) - - // Show if caching image hasn't loaded after a delay + cache: true visible: opacity > 0 - opacity: 0 - - Timer { - id: fallbackTimer - interval: 500 - running: cachingImage.status === Image.Loading || (cachingImage.status !== Image.Ready && cachingImage.status !== Image.Null) - onTriggered: { - if (cachingImage.status !== Image.Ready && fallbackImage.status === Image.Ready) { - fallbackImage.opacity = 1; - } - } - } - // Also check status changes - onStatusChanged: { - if (status === Image.Ready && cachingImage.status !== Image.Ready) { - Qt.callLater(() => { - if (cachingImage.status !== Image.Ready) { - fallbackImage.opacity = 1; - } - }); - } - } + opacity: status === Image.Ready && cachingImage.status !== Image.Ready ? 1 : 0 Behavior on opacity { NumberAnimation { - duration: 200 + duration: 150 + easing.type: Easing.OutQuad } } } + // Timer to trigger fallback only if caching hasn't loaded + Timer { + id: fallbackTimer + + property bool triggered: false + interval: 800 + running: cachingImage.status === Image.Loading || cachingImage.status === Image.Null + onTriggered: triggered = true + } + } + + // Border overlay that doesn't affect image size + Rectangle { + anchors.fill: parent + color: "transparent" + radius: Appearance.rounding.normal + border.width: isCurrent ? 2 : 0 + border.color: Colours.palette.m3primary + + Behavior on border.width { + NumberAnimation { + duration: 150 + easing.type: Easing.OutQuad + } + } + MaterialIcon { anchors.right: parent.right anchors.top: parent.top diff --git a/modules/launcher/items/WallpaperItem.qml b/modules/launcher/items/WallpaperItem.qml index 1128bad..9fdac3f 100644 --- a/modules/launcher/items/WallpaperItem.qml +++ b/modules/launcher/items/WallpaperItem.qml @@ -67,6 +67,7 @@ Item { CachingImage { path: root.modelData.path smooth: !root.PathView.view.moving + cache: true anchors.fill: parent } diff --git a/shell b/shell new file mode 160000 index 0000000..442b49e --- /dev/null +++ b/shell @@ -0,0 +1 @@ +Subproject commit 442b49e0e1975d51efcef90d10566c90dedbf7e1 -- cgit v1.2.3-freya From ec8da25d194eb6c9d0898870e52b061853907583 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sat, 15 Nov 2025 19:07:30 -0500 Subject: controlcenter: revamped and added more sliders --- .../controlcenter/appearance/AppearancePane.qml | 1043 ++++++++++++++++++-- modules/controlcenter/audio/AudioPane.qml | 156 ++- modules/controlcenter/taskbar/TaskbarPane.qml | 104 +- 3 files changed, 1199 insertions(+), 104 deletions(-) (limited to 'modules/controlcenter/appearance') diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index baf49cf..ea81fa7 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -431,15 +431,102 @@ RowLayout { sidebarFlickable.collapseAllSections(animationsSection); } - SpinBoxRow { - label: qsTr("Animation duration scale") - min: 0.1 - max: 5 - step: 0.1 - value: rootPane.animDurationsScale - onValueModified: value => { - rootPane.animDurationsScale = value; - rootPane.saveConfig(); + SectionContainer { + contentSpacing: Appearance.spacing.normal + + ColumnLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.small + + RowLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.normal + + StyledText { + text: qsTr("Animation duration scale") + font.pointSize: Appearance.font.size.normal + } + + Item { + Layout.fillWidth: true + } + + StyledRect { + Layout.preferredWidth: 70 + implicitHeight: animDurationsInput.implicitHeight + Appearance.padding.small * 2 + color: animDurationsInputHover.containsMouse || animDurationsInput.activeFocus + ? Colours.layer(Colours.palette.m3surfaceContainer, 3) + : Colours.layer(Colours.palette.m3surfaceContainer, 2) + radius: Appearance.rounding.small + border.width: 1 + border.color: animDurationsInput.activeFocus + ? Colours.palette.m3primary + : Qt.alpha(Colours.palette.m3outline, 0.3) + + Behavior on color { CAnim {} } + Behavior on border.color { CAnim {} } + + MouseArea { + id: animDurationsInputHover + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.IBeamCursor + acceptedButtons: Qt.NoButton + } + + StyledTextField { + id: animDurationsInput + anchors.centerIn: parent + width: parent.width - Appearance.padding.normal + horizontalAlignment: TextInput.AlignHCenter + validator: DoubleValidator { bottom: 0.1; top: 5.0 } + + Component.onCompleted: { + text = (rootPane.animDurationsScale).toFixed(1); + } + + onTextChanged: { + if (activeFocus) { + const val = parseFloat(text); + if (!isNaN(val) && val >= 0.1 && val <= 5.0) { + rootPane.animDurationsScale = val; + rootPane.saveConfig(); + } + } + } + onEditingFinished: { + const val = parseFloat(text); + if (isNaN(val) || val < 0.1 || val > 5.0) { + text = (rootPane.animDurationsScale).toFixed(1); + } + } + } + } + + StyledText { + text: "×" + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.normal + } + } + + StyledSlider { + id: animDurationsSlider + + Layout.fillWidth: true + implicitHeight: Appearance.padding.normal * 3 + + from: 0.1 + to: 5.0 + value: rootPane.animDurationsScale + onMoved: { + rootPane.animDurationsScale = animDurationsSlider.value; + if (!animDurationsInput.activeFocus) { + animDurationsInput.text = (animDurationsSlider.value).toFixed(1); + } + rootPane.saveConfig(); + } + } } } } @@ -473,8 +560,7 @@ RowLayout { delegate: StyledRect { required property string modelData - anchors.left: parent.left - anchors.right: parent.right + width: parent ? parent.width : 0 readonly property bool isCurrent: modelData === rootPane.fontFamilyMaterial color: Qt.alpha(Colours.tPalette.m3surfaceContainer, isCurrent ? Colours.tPalette.m3surfaceContainer.a : 0) @@ -546,8 +632,7 @@ RowLayout { delegate: StyledRect { required property string modelData - anchors.left: parent.left - anchors.right: parent.right + width: parent ? parent.width : 0 readonly property bool isCurrent: modelData === rootPane.fontFamilyMono color: Qt.alpha(Colours.tPalette.m3surfaceContainer, isCurrent ? Colours.tPalette.m3surfaceContainer.a : 0) @@ -619,8 +704,7 @@ RowLayout { delegate: StyledRect { required property string modelData - anchors.left: parent.left - anchors.right: parent.right + width: parent ? parent.width : 0 readonly property bool isCurrent: modelData === rootPane.fontFamilySans color: Qt.alpha(Colours.tPalette.m3surfaceContainer, isCurrent ? Colours.tPalette.m3surfaceContainer.a : 0) @@ -670,15 +754,102 @@ RowLayout { } } - SpinBoxRow { - label: qsTr("Font size scale") - min: 0.1 - max: 5 - step: 0.1 - value: rootPane.fontSizeScale - onValueModified: value => { - rootPane.fontSizeScale = value; - rootPane.saveConfig(); + SectionContainer { + contentSpacing: Appearance.spacing.normal + + ColumnLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.small + + RowLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.normal + + StyledText { + text: qsTr("Font size scale") + font.pointSize: Appearance.font.size.normal + } + + Item { + Layout.fillWidth: true + } + + StyledRect { + Layout.preferredWidth: 70 + implicitHeight: fontSizeInput.implicitHeight + Appearance.padding.small * 2 + color: fontSizeInputHover.containsMouse || fontSizeInput.activeFocus + ? Colours.layer(Colours.palette.m3surfaceContainer, 3) + : Colours.layer(Colours.palette.m3surfaceContainer, 2) + radius: Appearance.rounding.small + border.width: 1 + border.color: fontSizeInput.activeFocus + ? Colours.palette.m3primary + : Qt.alpha(Colours.palette.m3outline, 0.3) + + Behavior on color { CAnim {} } + Behavior on border.color { CAnim {} } + + MouseArea { + id: fontSizeInputHover + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.IBeamCursor + acceptedButtons: Qt.NoButton + } + + StyledTextField { + id: fontSizeInput + anchors.centerIn: parent + width: parent.width - Appearance.padding.normal + horizontalAlignment: TextInput.AlignHCenter + validator: DoubleValidator { bottom: 0.1; top: 5.0 } + + Component.onCompleted: { + text = (rootPane.fontSizeScale).toFixed(1); + } + + onTextChanged: { + if (activeFocus) { + const val = parseFloat(text); + if (!isNaN(val) && val >= 0.1 && val <= 5.0) { + rootPane.fontSizeScale = val; + rootPane.saveConfig(); + } + } + } + onEditingFinished: { + const val = parseFloat(text); + if (isNaN(val) || val < 0.1 || val > 5.0) { + text = (rootPane.fontSizeScale).toFixed(1); + } + } + } + } + + StyledText { + text: "×" + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.normal + } + } + + StyledSlider { + id: fontSizeSlider + + Layout.fillWidth: true + implicitHeight: Appearance.padding.normal * 3 + + from: 0.1 + to: 5.0 + value: rootPane.fontSizeScale + onMoved: { + rootPane.fontSizeScale = fontSizeSlider.value; + if (!fontSizeInput.activeFocus) { + fontSizeInput.text = (fontSizeSlider.value).toFixed(1); + } + rootPane.saveConfig(); + } + } } } } @@ -690,39 +861,300 @@ RowLayout { sidebarFlickable.collapseAllSections(scalesSection); } - SpinBoxRow { - label: qsTr("Padding scale") - min: 0.1 - max: 5 - step: 0.1 - value: rootPane.paddingScale - onValueModified: value => { - rootPane.paddingScale = value; - rootPane.saveConfig(); + SectionContainer { + contentSpacing: Appearance.spacing.normal + + ColumnLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.small + + RowLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.normal + + StyledText { + text: qsTr("Padding scale") + font.pointSize: Appearance.font.size.normal + } + + Item { + Layout.fillWidth: true + } + + StyledRect { + Layout.preferredWidth: 70 + implicitHeight: paddingInput.implicitHeight + Appearance.padding.small * 2 + color: paddingInputHover.containsMouse || paddingInput.activeFocus + ? Colours.layer(Colours.palette.m3surfaceContainer, 3) + : Colours.layer(Colours.palette.m3surfaceContainer, 2) + radius: Appearance.rounding.small + border.width: 1 + border.color: paddingInput.activeFocus + ? Colours.palette.m3primary + : Qt.alpha(Colours.palette.m3outline, 0.3) + + Behavior on color { CAnim {} } + Behavior on border.color { CAnim {} } + + MouseArea { + id: paddingInputHover + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.IBeamCursor + acceptedButtons: Qt.NoButton + } + + StyledTextField { + id: paddingInput + anchors.centerIn: parent + width: parent.width - Appearance.padding.normal + horizontalAlignment: TextInput.AlignHCenter + validator: DoubleValidator { bottom: 0.1; top: 5.0 } + + Component.onCompleted: { + text = (rootPane.paddingScale).toFixed(1); + } + + onTextChanged: { + if (activeFocus) { + const val = parseFloat(text); + if (!isNaN(val) && val >= 0.1 && val <= 5.0) { + rootPane.paddingScale = val; + rootPane.saveConfig(); + } + } + } + onEditingFinished: { + const val = parseFloat(text); + if (isNaN(val) || val < 0.1 || val > 5.0) { + text = (rootPane.paddingScale).toFixed(1); + } + } + } + } + + StyledText { + text: "×" + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.normal + } + } + + StyledSlider { + id: paddingSlider + + Layout.fillWidth: true + implicitHeight: Appearance.padding.normal * 3 + + from: 0.1 + to: 5.0 + value: rootPane.paddingScale + onMoved: { + rootPane.paddingScale = paddingSlider.value; + if (!paddingInput.activeFocus) { + paddingInput.text = (paddingSlider.value).toFixed(1); + } + rootPane.saveConfig(); + } + } } } - SpinBoxRow { - label: qsTr("Rounding scale") - min: 0.1 - max: 5 - step: 0.1 - value: rootPane.roundingScale - onValueModified: value => { - rootPane.roundingScale = value; - rootPane.saveConfig(); + SectionContainer { + contentSpacing: Appearance.spacing.normal + + ColumnLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.small + + RowLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.normal + + StyledText { + text: qsTr("Rounding scale") + font.pointSize: Appearance.font.size.normal + } + + Item { + Layout.fillWidth: true + } + + StyledRect { + Layout.preferredWidth: 70 + implicitHeight: roundingInput.implicitHeight + Appearance.padding.small * 2 + color: roundingInputHover.containsMouse || roundingInput.activeFocus + ? Colours.layer(Colours.palette.m3surfaceContainer, 3) + : Colours.layer(Colours.palette.m3surfaceContainer, 2) + radius: Appearance.rounding.small + border.width: 1 + border.color: roundingInput.activeFocus + ? Colours.palette.m3primary + : Qt.alpha(Colours.palette.m3outline, 0.3) + + Behavior on color { CAnim {} } + Behavior on border.color { CAnim {} } + + MouseArea { + id: roundingInputHover + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.IBeamCursor + acceptedButtons: Qt.NoButton + } + + StyledTextField { + id: roundingInput + anchors.centerIn: parent + width: parent.width - Appearance.padding.normal + horizontalAlignment: TextInput.AlignHCenter + validator: DoubleValidator { bottom: 0.1; top: 5.0 } + + Component.onCompleted: { + text = (rootPane.roundingScale).toFixed(1); + } + + onTextChanged: { + if (activeFocus) { + const val = parseFloat(text); + if (!isNaN(val) && val >= 0.1 && val <= 5.0) { + rootPane.roundingScale = val; + rootPane.saveConfig(); + } + } + } + onEditingFinished: { + const val = parseFloat(text); + if (isNaN(val) || val < 0.1 || val > 5.0) { + text = (rootPane.roundingScale).toFixed(1); + } + } + } + } + + StyledText { + text: "×" + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.normal + } + } + + StyledSlider { + id: roundingSlider + + Layout.fillWidth: true + implicitHeight: Appearance.padding.normal * 3 + + from: 0.1 + to: 5.0 + value: rootPane.roundingScale + onMoved: { + rootPane.roundingScale = roundingSlider.value; + if (!roundingInput.activeFocus) { + roundingInput.text = (roundingSlider.value).toFixed(1); + } + rootPane.saveConfig(); + } + } } } - SpinBoxRow { - label: qsTr("Spacing scale") - min: 0.1 - max: 5 - step: 0.1 - value: rootPane.spacingScale - onValueModified: value => { - rootPane.spacingScale = value; - rootPane.saveConfig(); + SectionContainer { + contentSpacing: Appearance.spacing.normal + + ColumnLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.small + + RowLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.normal + + StyledText { + text: qsTr("Spacing scale") + font.pointSize: Appearance.font.size.normal + } + + Item { + Layout.fillWidth: true + } + + StyledRect { + Layout.preferredWidth: 70 + implicitHeight: spacingInput.implicitHeight + Appearance.padding.small * 2 + color: spacingInputHover.containsMouse || spacingInput.activeFocus + ? Colours.layer(Colours.palette.m3surfaceContainer, 3) + : Colours.layer(Colours.palette.m3surfaceContainer, 2) + radius: Appearance.rounding.small + border.width: 1 + border.color: spacingInput.activeFocus + ? Colours.palette.m3primary + : Qt.alpha(Colours.palette.m3outline, 0.3) + + Behavior on color { CAnim {} } + Behavior on border.color { CAnim {} } + + MouseArea { + id: spacingInputHover + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.IBeamCursor + acceptedButtons: Qt.NoButton + } + + StyledTextField { + id: spacingInput + anchors.centerIn: parent + width: parent.width - Appearance.padding.normal + horizontalAlignment: TextInput.AlignHCenter + validator: DoubleValidator { bottom: 0.1; top: 5.0 } + + Component.onCompleted: { + text = (rootPane.spacingScale).toFixed(1); + } + + onTextChanged: { + if (activeFocus) { + const val = parseFloat(text); + if (!isNaN(val) && val >= 0.1 && val <= 5.0) { + rootPane.spacingScale = val; + rootPane.saveConfig(); + } + } + } + onEditingFinished: { + const val = parseFloat(text); + if (isNaN(val) || val < 0.1 || val > 5.0) { + text = (rootPane.spacingScale).toFixed(1); + } + } + } + } + + StyledText { + text: "×" + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.normal + } + } + + StyledSlider { + id: spacingSlider + + Layout.fillWidth: true + implicitHeight: Appearance.padding.normal * 3 + + from: 0.1 + to: 5.0 + value: rootPane.spacingScale + onMoved: { + rootPane.spacingScale = spacingSlider.value; + if (!spacingInput.activeFocus) { + spacingInput.text = (spacingSlider.value).toFixed(1); + } + rootPane.saveConfig(); + } + } } } } @@ -763,8 +1195,60 @@ RowLayout { Layout.fillWidth: true } + StyledRect { + Layout.preferredWidth: 70 + implicitHeight: transparencyBaseInput.implicitHeight + Appearance.padding.small * 2 + color: transparencyBaseInputHover.containsMouse || transparencyBaseInput.activeFocus + ? Colours.layer(Colours.palette.m3surfaceContainer, 3) + : Colours.layer(Colours.palette.m3surfaceContainer, 2) + radius: Appearance.rounding.small + border.width: 1 + border.color: transparencyBaseInput.activeFocus + ? Colours.palette.m3primary + : Qt.alpha(Colours.palette.m3outline, 0.3) + + Behavior on color { CAnim {} } + Behavior on border.color { CAnim {} } + + MouseArea { + id: transparencyBaseInputHover + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.IBeamCursor + acceptedButtons: Qt.NoButton + } + + StyledTextField { + id: transparencyBaseInput + anchors.centerIn: parent + width: parent.width - Appearance.padding.normal + horizontalAlignment: TextInput.AlignHCenter + validator: IntValidator { bottom: 0; top: 100 } + + Component.onCompleted: { + text = Math.round(rootPane.transparencyBase * 100).toString(); + } + + onTextChanged: { + if (activeFocus) { + const val = parseInt(text); + if (!isNaN(val) && val >= 0 && val <= 100) { + rootPane.transparencyBase = val / 100; + rootPane.saveConfig(); + } + } + } + onEditingFinished: { + const val = parseInt(text); + if (isNaN(val) || val < 0 || val > 100) { + text = Math.round(rootPane.transparencyBase * 100).toString(); + } + } + } + } + StyledText { - text: qsTr("%1%").arg(Math.round(rootPane.transparencyBase * 100)) + text: "%" color: Colours.palette.m3outline font.pointSize: Appearance.font.size.normal } @@ -781,6 +1265,9 @@ RowLayout { value: rootPane.transparencyBase * 100 onMoved: { rootPane.transparencyBase = baseSlider.value / 100; + if (!transparencyBaseInput.activeFocus) { + transparencyBaseInput.text = Math.round(baseSlider.value).toString(); + } rootPane.saveConfig(); } } @@ -807,8 +1294,60 @@ RowLayout { Layout.fillWidth: true } + StyledRect { + Layout.preferredWidth: 70 + implicitHeight: transparencyLayersInput.implicitHeight + Appearance.padding.small * 2 + color: transparencyLayersInputHover.containsMouse || transparencyLayersInput.activeFocus + ? Colours.layer(Colours.palette.m3surfaceContainer, 3) + : Colours.layer(Colours.palette.m3surfaceContainer, 2) + radius: Appearance.rounding.small + border.width: 1 + border.color: transparencyLayersInput.activeFocus + ? Colours.palette.m3primary + : Qt.alpha(Colours.palette.m3outline, 0.3) + + Behavior on color { CAnim {} } + Behavior on border.color { CAnim {} } + + MouseArea { + id: transparencyLayersInputHover + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.IBeamCursor + acceptedButtons: Qt.NoButton + } + + StyledTextField { + id: transparencyLayersInput + anchors.centerIn: parent + width: parent.width - Appearance.padding.normal + horizontalAlignment: TextInput.AlignHCenter + validator: IntValidator { bottom: 0; top: 100 } + + Component.onCompleted: { + text = Math.round(rootPane.transparencyLayers * 100).toString(); + } + + onTextChanged: { + if (activeFocus) { + const val = parseInt(text); + if (!isNaN(val) && val >= 0 && val <= 100) { + rootPane.transparencyLayers = val / 100; + rootPane.saveConfig(); + } + } + } + onEditingFinished: { + const val = parseInt(text); + if (isNaN(val) || val < 0 || val > 100) { + text = Math.round(rootPane.transparencyLayers * 100).toString(); + } + } + } + } + StyledText { - text: qsTr("%1%").arg(Math.round(rootPane.transparencyLayers * 100)) + text: "%" color: Colours.palette.m3outline font.pointSize: Appearance.font.size.normal } @@ -825,6 +1364,9 @@ RowLayout { value: rootPane.transparencyLayers * 100 onMoved: { rootPane.transparencyLayers = layersSlider.value / 100; + if (!transparencyLayersInput.activeFocus) { + transparencyLayersInput.text = Math.round(layersSlider.value).toString(); + } rootPane.saveConfig(); } } @@ -839,27 +1381,189 @@ RowLayout { sidebarFlickable.collapseAllSections(borderSection); } - SpinBoxRow { - label: qsTr("Border rounding") - min: 0.1 - max: 100 - step: 0.1 - value: rootPane.borderRounding - onValueModified: value => { - rootPane.borderRounding = value; - rootPane.saveConfig(); + SectionContainer { + contentSpacing: Appearance.spacing.normal + + ColumnLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.small + + RowLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.normal + + StyledText { + text: qsTr("Border rounding") + font.pointSize: Appearance.font.size.normal + } + + Item { + Layout.fillWidth: true + } + + StyledRect { + Layout.preferredWidth: 70 + implicitHeight: borderRoundingInput.implicitHeight + Appearance.padding.small * 2 + color: borderRoundingInputHover.containsMouse || borderRoundingInput.activeFocus + ? Colours.layer(Colours.palette.m3surfaceContainer, 3) + : Colours.layer(Colours.palette.m3surfaceContainer, 2) + radius: Appearance.rounding.small + border.width: 1 + border.color: borderRoundingInput.activeFocus + ? Colours.palette.m3primary + : Qt.alpha(Colours.palette.m3outline, 0.3) + + Behavior on color { CAnim {} } + Behavior on border.color { CAnim {} } + + MouseArea { + id: borderRoundingInputHover + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.IBeamCursor + acceptedButtons: Qt.NoButton + } + + StyledTextField { + id: borderRoundingInput + anchors.centerIn: parent + width: parent.width - Appearance.padding.normal + horizontalAlignment: TextInput.AlignHCenter + validator: DoubleValidator { bottom: 0.1; top: 100 } + + Component.onCompleted: { + text = (rootPane.borderRounding).toFixed(1); + } + + onTextChanged: { + if (activeFocus) { + const val = parseFloat(text); + if (!isNaN(val) && val >= 0.1 && val <= 100) { + rootPane.borderRounding = val; + rootPane.saveConfig(); + } + } + } + onEditingFinished: { + const val = parseFloat(text); + if (isNaN(val) || val < 0.1 || val > 100) { + text = (rootPane.borderRounding).toFixed(1); + } + } + } + } + } + + StyledSlider { + id: borderRoundingSlider + + Layout.fillWidth: true + implicitHeight: Appearance.padding.normal * 3 + + from: 0.1 + to: 100 + value: rootPane.borderRounding + onMoved: { + rootPane.borderRounding = borderRoundingSlider.value; + if (!borderRoundingInput.activeFocus) { + borderRoundingInput.text = (borderRoundingSlider.value).toFixed(1); + } + rootPane.saveConfig(); + } + } } } - SpinBoxRow { - label: qsTr("Border thickness") - min: 0.1 - max: 100 - step: 0.1 - value: rootPane.borderThickness - onValueModified: value => { - rootPane.borderThickness = value; - rootPane.saveConfig(); + SectionContainer { + contentSpacing: Appearance.spacing.normal + + ColumnLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.small + + RowLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.normal + + StyledText { + text: qsTr("Border thickness") + font.pointSize: Appearance.font.size.normal + } + + Item { + Layout.fillWidth: true + } + + StyledRect { + Layout.preferredWidth: 70 + implicitHeight: borderThicknessInput.implicitHeight + Appearance.padding.small * 2 + color: borderThicknessInputHover.containsMouse || borderThicknessInput.activeFocus + ? Colours.layer(Colours.palette.m3surfaceContainer, 3) + : Colours.layer(Colours.palette.m3surfaceContainer, 2) + radius: Appearance.rounding.small + border.width: 1 + border.color: borderThicknessInput.activeFocus + ? Colours.palette.m3primary + : Qt.alpha(Colours.palette.m3outline, 0.3) + + Behavior on color { CAnim {} } + Behavior on border.color { CAnim {} } + + MouseArea { + id: borderThicknessInputHover + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.IBeamCursor + acceptedButtons: Qt.NoButton + } + + StyledTextField { + id: borderThicknessInput + anchors.centerIn: parent + width: parent.width - Appearance.padding.normal + horizontalAlignment: TextInput.AlignHCenter + validator: DoubleValidator { bottom: 0.1; top: 100 } + + Component.onCompleted: { + text = (rootPane.borderThickness).toFixed(1); + } + + onTextChanged: { + if (activeFocus) { + const val = parseFloat(text); + if (!isNaN(val) && val >= 0.1 && val <= 100) { + rootPane.borderThickness = val; + rootPane.saveConfig(); + } + } + } + onEditingFinished: { + const val = parseFloat(text); + if (isNaN(val) || val < 0.1 || val > 100) { + text = (rootPane.borderThickness).toFixed(1); + } + } + } + } + } + + StyledSlider { + id: borderThicknessSlider + + Layout.fillWidth: true + implicitHeight: Appearance.padding.normal * 3 + + from: 0.1 + to: 100 + value: rootPane.borderThickness + onMoved: { + rootPane.borderThickness = borderThicknessSlider.value; + if (!borderThicknessInput.activeFocus) { + borderThicknessInput.text = (borderThicknessSlider.value).toFixed(1); + } + rootPane.saveConfig(); + } + } } } } @@ -914,25 +1618,190 @@ RowLayout { } } - SpinBoxRow { - label: qsTr("Visualiser rounding") - min: 0 - max: 10 - value: Math.round(rootPane.visualiserRounding) - onValueModified: value => { - rootPane.visualiserRounding = value; - rootPane.saveConfig(); + SectionContainer { + contentSpacing: Appearance.spacing.normal + + ColumnLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.small + + RowLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.normal + + StyledText { + text: qsTr("Visualiser rounding") + font.pointSize: Appearance.font.size.normal + } + + Item { + Layout.fillWidth: true + } + + StyledRect { + Layout.preferredWidth: 70 + implicitHeight: visualiserRoundingInput.implicitHeight + Appearance.padding.small * 2 + color: visualiserRoundingInputHover.containsMouse || visualiserRoundingInput.activeFocus + ? Colours.layer(Colours.palette.m3surfaceContainer, 3) + : Colours.layer(Colours.palette.m3surfaceContainer, 2) + radius: Appearance.rounding.small + border.width: 1 + border.color: visualiserRoundingInput.activeFocus + ? Colours.palette.m3primary + : Qt.alpha(Colours.palette.m3outline, 0.3) + + Behavior on color { CAnim {} } + Behavior on border.color { CAnim {} } + + MouseArea { + id: visualiserRoundingInputHover + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.IBeamCursor + acceptedButtons: Qt.NoButton + } + + StyledTextField { + id: visualiserRoundingInput + anchors.centerIn: parent + width: parent.width - Appearance.padding.normal + horizontalAlignment: TextInput.AlignHCenter + validator: IntValidator { bottom: 0; top: 10 } + + Component.onCompleted: { + text = Math.round(rootPane.visualiserRounding).toString(); + } + + onTextChanged: { + if (activeFocus) { + const val = parseInt(text); + if (!isNaN(val) && val >= 0 && val <= 10) { + rootPane.visualiserRounding = val; + rootPane.saveConfig(); + } + } + } + onEditingFinished: { + const val = parseInt(text); + if (isNaN(val) || val < 0 || val > 10) { + text = Math.round(rootPane.visualiserRounding).toString(); + } + } + } + } + } + + StyledSlider { + id: visualiserRoundingSlider + + Layout.fillWidth: true + implicitHeight: Appearance.padding.normal * 3 + + from: 0 + to: 10 + stepSize: 1 + value: rootPane.visualiserRounding + onMoved: { + rootPane.visualiserRounding = Math.round(visualiserRoundingSlider.value); + if (!visualiserRoundingInput.activeFocus) { + visualiserRoundingInput.text = Math.round(visualiserRoundingSlider.value).toString(); + } + rootPane.saveConfig(); + } + } } } - SpinBoxRow { - label: qsTr("Visualiser spacing") - min: 0 - max: 10 - value: Math.round(rootPane.visualiserSpacing) - onValueModified: value => { - rootPane.visualiserSpacing = value; - rootPane.saveConfig(); + SectionContainer { + contentSpacing: Appearance.spacing.normal + + ColumnLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.small + + RowLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.normal + + StyledText { + text: qsTr("Visualiser spacing") + font.pointSize: Appearance.font.size.normal + } + + Item { + Layout.fillWidth: true + } + + StyledRect { + Layout.preferredWidth: 70 + implicitHeight: visualiserSpacingInput.implicitHeight + Appearance.padding.small * 2 + color: visualiserSpacingInputHover.containsMouse || visualiserSpacingInput.activeFocus + ? Colours.layer(Colours.palette.m3surfaceContainer, 3) + : Colours.layer(Colours.palette.m3surfaceContainer, 2) + radius: Appearance.rounding.small + border.width: 1 + border.color: visualiserSpacingInput.activeFocus + ? Colours.palette.m3primary + : Qt.alpha(Colours.palette.m3outline, 0.3) + + Behavior on color { CAnim {} } + Behavior on border.color { CAnim {} } + + MouseArea { + id: visualiserSpacingInputHover + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.IBeamCursor + acceptedButtons: Qt.NoButton + } + + StyledTextField { + id: visualiserSpacingInput + anchors.centerIn: parent + width: parent.width - Appearance.padding.normal + horizontalAlignment: TextInput.AlignHCenter + validator: DoubleValidator { bottom: 0; top: 2 } + + Component.onCompleted: { + text = (rootPane.visualiserSpacing).toFixed(1); + } + + onTextChanged: { + if (activeFocus) { + const val = parseFloat(text); + if (!isNaN(val) && val >= 0 && val <= 2) { + rootPane.visualiserSpacing = val; + rootPane.saveConfig(); + } + } + } + onEditingFinished: { + const val = parseFloat(text); + if (isNaN(val) || val < 0 || val > 2) { + text = (rootPane.visualiserSpacing).toFixed(1); + } + } + } + } + } + + StyledSlider { + id: visualiserSpacingSlider + + Layout.fillWidth: true + implicitHeight: Appearance.padding.normal * 3 + + from: 0 + to: 2 + value: rootPane.visualiserSpacing + onMoved: { + rootPane.visualiserSpacing = visualiserSpacingSlider.value; + if (!visualiserSpacingInput.activeFocus) { + visualiserSpacingInput.text = (visualiserSpacingSlider.value).toFixed(1); + } + rootPane.saveConfig(); + } + } } } } diff --git a/modules/controlcenter/audio/AudioPane.qml b/modules/controlcenter/audio/AudioPane.qml index 1c0c770..3440a2f 100644 --- a/modules/controlcenter/audio/AudioPane.qml +++ b/modules/controlcenter/audio/AudioPane.qml @@ -336,11 +336,74 @@ RowLayout { Layout.fillWidth: true } + StyledRect { + 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) + 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 + } + + 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(); + } + } + } + + onTextChanged: { + if (activeFocus) { + const val = parseInt(text); + 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 { - text: Audio.muted ? qsTr("Muted") : qsTr("%1%").arg(Math.round(Audio.volume * 100)) - color: Audio.muted ? Colours.palette.m3primary : Colours.palette.m3outline + text: "%" + color: Colours.palette.m3outline font.pointSize: Appearance.font.size.normal - font.weight: 500 + opacity: Audio.muted ? 0.5 : 1 } StyledRect { @@ -362,20 +425,26 @@ RowLayout { id: muteIcon anchors.centerIn: parent - text: Audio.muted ? "volume_off" : "volume_mute" + text: Audio.muted ? "volume_off" : "volume_up" color: Audio.muted ? Colours.palette.m3onSecondary : Colours.palette.m3onSecondaryContainer } } } StyledSlider { + id: outputVolumeSlider Layout.fillWidth: true implicitHeight: Appearance.padding.normal * 3 value: Audio.volume enabled: !Audio.muted opacity: enabled ? 1 : 0.5 - onMoved: Audio.setVolume(value) + onMoved: { + Audio.setVolume(value); + if (!outputVolumeInput.activeFocus) { + outputVolumeInput.text = Math.round(value * 100).toString(); + } + } } } } @@ -406,11 +475,74 @@ RowLayout { Layout.fillWidth: true } + StyledRect { + 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) + 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 + } + + 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(); + } + } + } + + onTextChanged: { + if (activeFocus) { + const val = parseInt(text); + 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 { - text: Audio.sourceMuted ? qsTr("Muted") : qsTr("%1%").arg(Math.round(Audio.sourceVolume * 100)) - color: Audio.sourceMuted ? Colours.palette.m3primary : Colours.palette.m3outline + text: "%" + color: Colours.palette.m3outline font.pointSize: Appearance.font.size.normal - font.weight: 500 + opacity: Audio.sourceMuted ? 0.5 : 1 } StyledRect { @@ -439,13 +571,19 @@ RowLayout { } StyledSlider { + id: inputVolumeSlider Layout.fillWidth: true implicitHeight: Appearance.padding.normal * 3 value: Audio.sourceVolume enabled: !Audio.sourceMuted opacity: enabled ? 1 : 0.5 - onMoved: Audio.setSourceVolume(value) + onMoved: { + Audio.setSourceVolume(value); + if (!inputVolumeInput.activeFocus) { + inputVolumeInput.text = Math.round(value * 100).toString(); + } + } } } } diff --git a/modules/controlcenter/taskbar/TaskbarPane.qml b/modules/controlcenter/taskbar/TaskbarPane.qml index 85e5275..6d9761b 100644 --- a/modules/controlcenter/taskbar/TaskbarPane.qml +++ b/modules/controlcenter/taskbar/TaskbarPane.qml @@ -239,14 +239,102 @@ RowLayout { } } - SpinBoxRow { - label: qsTr("Drag threshold") - min: 0 - max: 100 - value: root.dragThreshold - onValueModified: value => { - root.dragThreshold = value; - root.saveConfig(); + SectionContainer { + contentSpacing: Appearance.spacing.normal + + ColumnLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.small + + RowLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.normal + + 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 5be8d68ae7bd1f58ed10979a9b1c7cf055519fc6 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sat, 15 Nov 2025 19:26:14 -0500 Subject: controlcenter: increased animation on fade-in (wallpaper and applications) --- .../controlcenter/appearance/AppearancePane.qml | 44 +++++++++++----------- modules/controlcenter/launcher/LauncherPane.qml | 2 +- 2 files changed, 23 insertions(+), 23 deletions(-) (limited to 'modules/controlcenter/appearance') diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index ea81fa7..388fc88 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -518,13 +518,13 @@ RowLayout { from: 0.1 to: 5.0 - value: rootPane.animDurationsScale + value: rootPane.animDurationsScale onMoved: { rootPane.animDurationsScale = animDurationsSlider.value; if (!animDurationsInput.activeFocus) { animDurationsInput.text = (animDurationsSlider.value).toFixed(1); } - rootPane.saveConfig(); + rootPane.saveConfig(); } } } @@ -841,13 +841,13 @@ RowLayout { from: 0.1 to: 5.0 - value: rootPane.fontSizeScale + value: rootPane.fontSizeScale onMoved: { rootPane.fontSizeScale = fontSizeSlider.value; if (!fontSizeInput.activeFocus) { fontSizeInput.text = (fontSizeSlider.value).toFixed(1); } - rootPane.saveConfig(); + rootPane.saveConfig(); } } } @@ -948,13 +948,13 @@ RowLayout { from: 0.1 to: 5.0 - value: rootPane.paddingScale + value: rootPane.paddingScale onMoved: { rootPane.paddingScale = paddingSlider.value; if (!paddingInput.activeFocus) { paddingInput.text = (paddingSlider.value).toFixed(1); } - rootPane.saveConfig(); + rootPane.saveConfig(); } } } @@ -1047,13 +1047,13 @@ RowLayout { from: 0.1 to: 5.0 - value: rootPane.roundingScale + value: rootPane.roundingScale onMoved: { rootPane.roundingScale = roundingSlider.value; if (!roundingInput.activeFocus) { roundingInput.text = (roundingSlider.value).toFixed(1); } - rootPane.saveConfig(); + rootPane.saveConfig(); } } } @@ -1146,13 +1146,13 @@ RowLayout { from: 0.1 to: 5.0 - value: rootPane.spacingScale + value: rootPane.spacingScale onMoved: { rootPane.spacingScale = spacingSlider.value; if (!spacingInput.activeFocus) { spacingInput.text = (spacingSlider.value).toFixed(1); } - rootPane.saveConfig(); + rootPane.saveConfig(); } } } @@ -1462,13 +1462,13 @@ RowLayout { from: 0.1 to: 100 - value: rootPane.borderRounding + value: rootPane.borderRounding onMoved: { rootPane.borderRounding = borderRoundingSlider.value; if (!borderRoundingInput.activeFocus) { borderRoundingInput.text = (borderRoundingSlider.value).toFixed(1); } - rootPane.saveConfig(); + rootPane.saveConfig(); } } } @@ -1555,13 +1555,13 @@ RowLayout { from: 0.1 to: 100 - value: rootPane.borderThickness + value: rootPane.borderThickness onMoved: { rootPane.borderThickness = borderThicknessSlider.value; if (!borderThicknessInput.activeFocus) { borderThicknessInput.text = (borderThicknessSlider.value).toFixed(1); } - rootPane.saveConfig(); + rootPane.saveConfig(); } } } @@ -1677,7 +1677,7 @@ RowLayout { const val = parseInt(text); if (!isNaN(val) && val >= 0 && val <= 10) { rootPane.visualiserRounding = val; - rootPane.saveConfig(); + rootPane.saveConfig(); } } } @@ -1706,11 +1706,11 @@ RowLayout { if (!visualiserRoundingInput.activeFocus) { visualiserRoundingInput.text = Math.round(visualiserRoundingSlider.value).toString(); } - rootPane.saveConfig(); - } - } + rootPane.saveConfig(); } } + } + } SectionContainer { contentSpacing: Appearance.spacing.normal @@ -1951,7 +1951,7 @@ RowLayout { Behavior on opacity { NumberAnimation { - duration: 150 + duration: 1000 easing.type: Easing.OutQuad } } @@ -1972,7 +1972,7 @@ RowLayout { Behavior on opacity { NumberAnimation { - duration: 150 + duration: 1000 easing.type: Easing.OutQuad } } @@ -2049,7 +2049,7 @@ RowLayout { Behavior on opacity { NumberAnimation { - duration: 200 + duration: 1000 easing.type: Easing.OutCubic } } @@ -2085,7 +2085,7 @@ RowLayout { Behavior on opacity { NumberAnimation { - duration: 200 + duration: 1000 easing.type: Easing.OutCubic } } diff --git a/modules/controlcenter/launcher/LauncherPane.qml b/modules/controlcenter/launcher/LauncherPane.qml index a6f6bd6..a7e122e 100644 --- a/modules/controlcenter/launcher/LauncherPane.qml +++ b/modules/controlcenter/launcher/LauncherPane.qml @@ -324,7 +324,7 @@ RowLayout { Behavior on opacity { NumberAnimation { - duration: 200 + duration: 1000 easing.type: Easing.OutCubic } } -- cgit v1.2.3-freya From cc74308800af5146791e953ab25a270fec50fdf3 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sat, 15 Nov 2025 19:40:19 -0500 Subject: config: added save to singleton, updated relevant settings panes --- config/Config.qml | 381 ++++++++++++++++++++- .../controlcenter/appearance/AppearancePane.qml | 3 + 2 files changed, 382 insertions(+), 2 deletions(-) (limited to 'modules/controlcenter/appearance') diff --git a/config/Config.qml b/config/Config.qml index 267a184..3756043 100644 --- a/config/Config.qml +++ b/config/Config.qml @@ -4,6 +4,7 @@ import qs.utils import Caelestia import Quickshell import Quickshell.Io +import QtQuick Singleton { id: root @@ -26,16 +27,392 @@ Singleton { property alias services: adapter.services property alias paths: adapter.paths + // Public save function - call this to persist config changes + function save(): void { + saveTimer.restart(); + } + ElapsedTimer { id: timer } + Timer { + id: saveTimer + + interval: 500 + onTriggered: { + try { + // Parse current config to preserve structure and comments if possible + let config = {}; + try { + config = JSON.parse(fileView.text()); + } catch (e) { + // If parsing fails, start with empty object + config = {}; + } + + // Update config with current values + config = serializeConfig(); + + // Save to file with pretty printing + fileView.setText(JSON.stringify(config, null, 2)); + } catch (e) { + Toaster.toast(qsTr("Failed to serialize config"), e.message, "settings_alert", Toast.Error); + } + } + } + + // Helper function to serialize the config object + function serializeConfig(): var { + return { + appearance: serializeAppearance(), + general: serializeGeneral(), + background: serializeBackground(), + bar: serializeBar(), + border: serializeBorder(), + dashboard: serializeDashboard(), + controlCenter: serializeControlCenter(), + launcher: serializeLauncher(), + notifs: serializeNotifs(), + osd: serializeOsd(), + session: serializeSession(), + winfo: serializeWinfo(), + lock: serializeLock(), + utilities: serializeUtilities(), + sidebar: serializeSidebar(), + services: serializeServices(), + paths: serializePaths() + }; + } + + function serializeAppearance(): var { + return { + rounding: { scale: appearance.rounding.scale }, + spacing: { scale: appearance.spacing.scale }, + padding: { scale: appearance.padding.scale }, + font: { + family: { + sans: appearance.font.family.sans, + mono: appearance.font.family.mono, + material: appearance.font.family.material, + clock: appearance.font.family.clock + }, + size: { scale: appearance.font.size.scale } + }, + anim: { + durations: { scale: appearance.anim.durations.scale } + }, + transparency: { + enabled: appearance.transparency.enabled, + base: appearance.transparency.base, + layers: appearance.transparency.layers + } + }; + } + + function serializeGeneral(): var { + return { + apps: { + terminal: general.apps.terminal, + audio: general.apps.audio, + playback: general.apps.playback, + explorer: general.apps.explorer + }, + idle: { + lockBeforeSleep: general.idle.lockBeforeSleep, + inhibitWhenAudio: general.idle.inhibitWhenAudio, + timeouts: general.idle.timeouts + }, + battery: { + warnLevels: general.battery.warnLevels, + criticalLevel: general.battery.criticalLevel + } + }; + } + + function serializeBackground(): var { + return { + enabled: background.enabled, + desktopClock: { + enabled: background.desktopClock.enabled + }, + visualiser: { + enabled: background.visualiser.enabled, + autoHide: background.visualiser.autoHide, + blur: background.visualiser.blur, + rounding: background.visualiser.rounding, + spacing: background.visualiser.spacing + } + }; + } + + function serializeBar(): var { + return { + persistent: bar.persistent, + showOnHover: bar.showOnHover, + dragThreshold: bar.dragThreshold, + scrollActions: { + workspaces: bar.scrollActions.workspaces, + volume: bar.scrollActions.volume, + brightness: bar.scrollActions.brightness + }, + popouts: { + activeWindow: bar.popouts.activeWindow, + tray: bar.popouts.tray, + statusIcons: bar.popouts.statusIcons + }, + workspaces: { + shown: bar.workspaces.shown, + activeIndicator: bar.workspaces.activeIndicator, + occupiedBg: bar.workspaces.occupiedBg, + showWindows: bar.workspaces.showWindows, + showWindowsOnSpecialWorkspaces: bar.workspaces.showWindowsOnSpecialWorkspaces, + activeTrail: bar.workspaces.activeTrail, + perMonitorWorkspaces: bar.workspaces.perMonitorWorkspaces, + label: bar.workspaces.label, + occupiedLabel: bar.workspaces.occupiedLabel, + activeLabel: bar.workspaces.activeLabel, + capitalisation: bar.workspaces.capitalisation, + specialWorkspaceIcons: bar.workspaces.specialWorkspaceIcons + }, + tray: { + background: bar.tray.background, + recolour: bar.tray.recolour, + compact: bar.tray.compact, + iconSubs: bar.tray.iconSubs + }, + status: { + showAudio: bar.status.showAudio, + showMicrophone: bar.status.showMicrophone, + showKbLayout: bar.status.showKbLayout, + showNetwork: bar.status.showNetwork, + showBluetooth: bar.status.showBluetooth, + showBattery: bar.status.showBattery, + showLockStatus: bar.status.showLockStatus + }, + clock: { + showIcon: bar.clock.showIcon + }, + sizes: { + innerWidth: bar.sizes.innerWidth, + windowPreviewSize: bar.sizes.windowPreviewSize, + trayMenuWidth: bar.sizes.trayMenuWidth, + batteryWidth: bar.sizes.batteryWidth, + networkWidth: bar.sizes.networkWidth + }, + entries: bar.entries + }; + } + + function serializeBorder(): var { + return { + thickness: border.thickness, + rounding: border.rounding + }; + } + + function serializeDashboard(): var { + return { + enabled: dashboard.enabled, + showOnHover: dashboard.showOnHover, + mediaUpdateInterval: dashboard.mediaUpdateInterval, + dragThreshold: dashboard.dragThreshold, + sizes: { + tabIndicatorHeight: dashboard.sizes.tabIndicatorHeight, + tabIndicatorSpacing: dashboard.sizes.tabIndicatorSpacing, + infoWidth: dashboard.sizes.infoWidth, + infoIconSize: dashboard.sizes.infoIconSize, + dateTimeWidth: dashboard.sizes.dateTimeWidth, + mediaWidth: dashboard.sizes.mediaWidth, + mediaProgressSweep: dashboard.sizes.mediaProgressSweep, + mediaProgressThickness: dashboard.sizes.mediaProgressThickness, + resourceProgessThickness: dashboard.sizes.resourceProgessThickness, + weatherWidth: dashboard.sizes.weatherWidth, + mediaCoverArtSize: dashboard.sizes.mediaCoverArtSize, + mediaVisualiserSize: dashboard.sizes.mediaVisualiserSize, + resourceSize: dashboard.sizes.resourceSize + } + }; + } + + function serializeControlCenter(): var { + return { + sizes: { + heightMult: controlCenter.sizes.heightMult, + ratio: controlCenter.sizes.ratio + } + }; + } + + function serializeLauncher(): var { + return { + enabled: launcher.enabled, + showOnHover: launcher.showOnHover, + maxShown: launcher.maxShown, + maxWallpapers: launcher.maxWallpapers, + specialPrefix: launcher.specialPrefix, + actionPrefix: launcher.actionPrefix, + enableDangerousActions: launcher.enableDangerousActions, + dragThreshold: launcher.dragThreshold, + vimKeybinds: launcher.vimKeybinds, + hiddenApps: launcher.hiddenApps, + useFuzzy: { + apps: launcher.useFuzzy.apps, + actions: launcher.useFuzzy.actions, + schemes: launcher.useFuzzy.schemes, + variants: launcher.useFuzzy.variants, + wallpapers: launcher.useFuzzy.wallpapers + }, + sizes: { + itemWidth: launcher.sizes.itemWidth, + itemHeight: launcher.sizes.itemHeight, + wallpaperWidth: launcher.sizes.wallpaperWidth, + wallpaperHeight: launcher.sizes.wallpaperHeight + }, + actions: launcher.actions + }; + } + + function serializeNotifs(): var { + return { + expire: notifs.expire, + defaultExpireTimeout: notifs.defaultExpireTimeout, + clearThreshold: notifs.clearThreshold, + expandThreshold: notifs.expandThreshold, + actionOnClick: notifs.actionOnClick, + groupPreviewNum: notifs.groupPreviewNum, + sizes: { + width: notifs.sizes.width, + image: notifs.sizes.image, + badge: notifs.sizes.badge + } + }; + } + + function serializeOsd(): var { + return { + enabled: osd.enabled, + hideDelay: osd.hideDelay, + enableBrightness: osd.enableBrightness, + enableMicrophone: osd.enableMicrophone, + sizes: { + sliderWidth: osd.sizes.sliderWidth, + sliderHeight: osd.sizes.sliderHeight + } + }; + } + + function serializeSession(): var { + return { + enabled: session.enabled, + dragThreshold: session.dragThreshold, + vimKeybinds: session.vimKeybinds, + commands: { + logout: session.commands.logout, + shutdown: session.commands.shutdown, + hibernate: session.commands.hibernate, + reboot: session.commands.reboot + }, + sizes: { + button: session.sizes.button + } + }; + } + + function serializeWinfo(): var { + return { + sizes: { + heightMult: winfo.sizes.heightMult, + detailsWidth: winfo.sizes.detailsWidth + } + }; + } + + function serializeLock(): var { + return { + recolourLogo: lock.recolourLogo, + enableFprint: lock.enableFprint, + maxFprintTries: lock.maxFprintTries, + sizes: { + heightMult: lock.sizes.heightMult, + ratio: lock.sizes.ratio, + centerWidth: lock.sizes.centerWidth + } + }; + } + + function serializeUtilities(): var { + return { + enabled: utilities.enabled, + maxToasts: utilities.maxToasts, + sizes: { + width: utilities.sizes.width, + toastWidth: utilities.sizes.toastWidth + }, + toasts: { + configLoaded: utilities.toasts.configLoaded, + chargingChanged: utilities.toasts.chargingChanged, + gameModeChanged: utilities.toasts.gameModeChanged, + dndChanged: utilities.toasts.dndChanged, + audioOutputChanged: utilities.toasts.audioOutputChanged, + audioInputChanged: utilities.toasts.audioInputChanged, + capsLockChanged: utilities.toasts.capsLockChanged, + numLockChanged: utilities.toasts.numLockChanged, + kbLayoutChanged: utilities.toasts.kbLayoutChanged, + vpnChanged: utilities.toasts.vpnChanged, + nowPlaying: utilities.toasts.nowPlaying + }, + vpn: { + enabled: utilities.vpn.enabled, + provider: utilities.vpn.provider + } + }; + } + + function serializeSidebar(): var { + return { + enabled: sidebar.enabled, + dragThreshold: sidebar.dragThreshold, + sizes: { + width: sidebar.sizes.width + } + }; + } + + function serializeServices(): var { + return { + weatherLocation: services.weatherLocation, + useFahrenheit: services.useFahrenheit, + useTwelveHourClock: services.useTwelveHourClock, + gpuType: services.gpuType, + visualiserBars: services.visualiserBars, + audioIncrement: services.audioIncrement, + maxVolume: services.maxVolume, + smartScheme: services.smartScheme, + defaultPlayer: services.defaultPlayer, + playerAliases: services.playerAliases + }; + } + + function serializePaths(): var { + return { + wallpaperDir: paths.wallpaperDir, + sessionGif: paths.sessionGif, + mediaGif: paths.mediaGif + }; + } + FileView { + id: fileView + path: `${Paths.config}/shell.json` watchChanges: true onFileChanged: { - timer.restart(); - reload(); + // Prevent reload loop - don't reload if we just saved + if (!saveTimer.running) { + timer.restart(); + reload(); + } } onLoaded: { try { diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index 388fc88..22e5580 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -84,6 +84,9 @@ RowLayout { // Update border Config.border.rounding = root.borderRounding; Config.border.thickness = root.borderThickness; + + // Persist changes to disk + Config.save(); } Item { -- cgit v1.2.3-freya From f2e9f60344a2185d244c8eda962bb2f4e9917be0 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sat, 15 Nov 2025 21:05:50 -0500 Subject: controlcenter: max padding now 2 --- modules/controlcenter/appearance/AppearancePane.qml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'modules/controlcenter/appearance') diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index 22e5580..37397ae 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -912,7 +912,7 @@ RowLayout { anchors.centerIn: parent width: parent.width - Appearance.padding.normal horizontalAlignment: TextInput.AlignHCenter - validator: DoubleValidator { bottom: 0.1; top: 5.0 } + validator: DoubleValidator { bottom: 0.1; top: 2.0 } Component.onCompleted: { text = (rootPane.paddingScale).toFixed(1); @@ -921,7 +921,7 @@ RowLayout { onTextChanged: { if (activeFocus) { const val = parseFloat(text); - if (!isNaN(val) && val >= 0.1 && val <= 5.0) { + if (!isNaN(val) && val >= 0.1 && val <= 2.0) { rootPane.paddingScale = val; rootPane.saveConfig(); } @@ -929,7 +929,7 @@ RowLayout { } onEditingFinished: { const val = parseFloat(text); - if (isNaN(val) || val < 0.1 || val > 5.0) { + if (isNaN(val) || val < 0.1 || val > 2.0) { text = (rootPane.paddingScale).toFixed(1); } } @@ -950,7 +950,7 @@ RowLayout { implicitHeight: Appearance.padding.normal * 3 from: 0.1 - to: 5.0 + to: 2.0 value: rootPane.paddingScale onMoved: { rootPane.paddingScale = paddingSlider.value; -- 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 'modules/controlcenter/appearance') 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 699d7ef7fcabe7e38243143a3f1aab7fa55ad8c0 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sat, 15 Nov 2025 21:24:48 -0500 Subject: controlcenter: padding and spacing scale min-max --- modules/controlcenter/appearance/AppearancePane.qml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'modules/controlcenter/appearance') diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index 459f4c1..1e4ff0d 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -892,7 +892,7 @@ RowLayout { anchors.centerIn: parent width: parent.width - Appearance.padding.normal horizontalAlignment: TextInput.AlignHCenter - validator: DoubleValidator { bottom: 0.1; top: 2.0 } + validator: DoubleValidator { bottom: 0.5; top: 2.0 } Component.onCompleted: { text = (rootPane.paddingScale).toFixed(1); @@ -901,7 +901,7 @@ RowLayout { onTextChanged: { if (activeFocus) { const val = parseFloat(text); - if (!isNaN(val) && val >= 0.1 && val <= 2.0) { + if (!isNaN(val) && val >= 0.5 && val <= 2.0) { rootPane.paddingScale = val; rootPane.saveConfig(); } @@ -909,7 +909,7 @@ RowLayout { } onEditingFinished: { const val = parseFloat(text); - if (isNaN(val) || val < 0.1 || val > 2.0) { + if (isNaN(val) || val < 0.5 || val > 2.0) { text = (rootPane.paddingScale).toFixed(1); } } @@ -929,7 +929,7 @@ RowLayout { Layout.fillWidth: true implicitHeight: Appearance.padding.normal * 3 - from: 0.1 + from: 0.5 to: 2.0 value: rootPane.paddingScale onMoved: { @@ -1090,7 +1090,7 @@ RowLayout { anchors.centerIn: parent width: parent.width - Appearance.padding.normal horizontalAlignment: TextInput.AlignHCenter - validator: DoubleValidator { bottom: 0.1; top: 5.0 } + validator: DoubleValidator { bottom: 0.1; top: 2.0 } Component.onCompleted: { text = (rootPane.spacingScale).toFixed(1); @@ -1099,7 +1099,7 @@ RowLayout { onTextChanged: { if (activeFocus) { const val = parseFloat(text); - if (!isNaN(val) && val >= 0.1 && val <= 5.0) { + if (!isNaN(val) && val >= 0.1 && val <= 2.0) { rootPane.spacingScale = val; rootPane.saveConfig(); } @@ -1107,7 +1107,7 @@ RowLayout { } onEditingFinished: { const val = parseFloat(text); - if (isNaN(val) || val < 0.1 || val > 5.0) { + if (isNaN(val) || val < 0.1 || val > 2.0) { text = (rootPane.spacingScale).toFixed(1); } } @@ -1128,7 +1128,7 @@ RowLayout { implicitHeight: Appearance.padding.normal * 3 from: 0.1 - to: 5.0 + to: 2.0 value: rootPane.spacingScale onMoved: { rootPane.spacingScale = spacingSlider.value; -- cgit v1.2.3-freya From 420a19bf31c6c97b66f6333e41fa37b9278cc6c5 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sat, 15 Nov 2025 21:27:38 -0500 Subject: controlcenter: gradient rectangle around wallpapers fix --- modules/controlcenter/appearance/AppearancePane.qml | 2 ++ 1 file changed, 2 insertions(+) (limited to 'modules/controlcenter/appearance') diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index 1e4ff0d..38d335d 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -2008,6 +2008,8 @@ RowLayout { implicitHeight: filenameText.implicitHeight + Appearance.padding.normal * 2 + radius: Appearance.rounding.normal + gradient: Gradient { GradientStop { position: 0.0 -- cgit v1.2.3-freya From 7d839be3e5c42a06e48c5e2c7808d1579c84e07c Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sat, 15 Nov 2025 21:43:09 -0500 Subject: controlcenter: fonts performance fix (virtual listview) --- .../controlcenter/appearance/AppearancePane.qml | 550 +++++++++++---------- 1 file changed, 276 insertions(+), 274 deletions(-) (limited to 'modules/controlcenter/appearance') diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index 38d335d..279c362 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -540,64 +540,65 @@ RowLayout { font.weight: 500 } - ColumnLayout { + StyledListView { Layout.fillWidth: true + Layout.preferredHeight: Math.min(contentHeight, 300) + + clip: true spacing: Appearance.spacing.small / 2 + model: Qt.fontFamilies() - Repeater { - model: Qt.fontFamilies() + delegate: StyledRect { + required property string modelData + required property int index - delegate: StyledRect { - required property string modelData + width: ListView.view.width - 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 } } @@ -608,64 +609,65 @@ RowLayout { font.weight: 500 } - ColumnLayout { + StyledListView { Layout.fillWidth: true + Layout.preferredHeight: Math.min(contentHeight, 300) + + clip: true spacing: Appearance.spacing.small / 2 + model: Qt.fontFamilies() - Repeater { - model: Qt.fontFamilies() + delegate: StyledRect { + required property string modelData + required property int index - delegate: StyledRect { - required property string modelData + width: ListView.view.width - 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 } } @@ -676,64 +678,65 @@ RowLayout { font.weight: 500 } - ColumnLayout { + StyledListView { Layout.fillWidth: true + Layout.preferredHeight: Math.min(contentHeight, 300) + + clip: true spacing: Appearance.spacing.small / 2 + model: Qt.fontFamilies() - Repeater { - model: Qt.fontFamilies() + delegate: StyledRect { + required property string modelData + required property int index - delegate: StyledRect { - required property string modelData + width: ListView.view.width - 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 } } @@ -1874,210 +1877,209 @@ RowLayout { color: Colours.palette.m3onSurfaceVariant } - GridLayout { + GridView { + id: wallpaperGrid Layout.fillWidth: true Layout.topMargin: Appearance.spacing.large + Layout.preferredHeight: Math.min(600, Math.ceil(count / Math.floor(width / cellWidth)) * cellHeight) Layout.alignment: Qt.AlignHCenter - columns: Math.max(2, Math.floor(parent.width / 200)) - rowSpacing: Appearance.spacing.normal - columnSpacing: Appearance.spacing.normal + cellWidth: 200 + Appearance.spacing.normal + cellHeight: 140 + Appearance.spacing.normal + + model: Wallpapers.list + clip: true - // Center the grid content - Layout.maximumWidth: { - const cols = columns; - const itemWidth = 200; - const spacing = columnSpacing; - return cols * itemWidth + (cols - 1) * spacing; + // Enable caching for better performance + cacheBuffer: cellHeight * 2 + + StyledScrollBar.vertical: StyledScrollBar { + flickable: wallpaperGrid } - Repeater { - model: Wallpapers.list + delegate: Item { + required property var modelData - delegate: Item { - required property var modelData + width: 200 + height: 140 - Layout.preferredWidth: 200 - Layout.preferredHeight: 140 - Layout.minimumWidth: 200 - Layout.minimumHeight: 140 - Layout.maximumWidth: 200 - Layout.maximumHeight: 140 + // Center in cell + x: (wallpaperGrid.cellWidth - width) / 2 + y: (wallpaperGrid.cellHeight - height) / 2 - readonly property bool isCurrent: modelData.path === Wallpapers.actualCurrent + readonly property bool isCurrent: modelData.path === Wallpapers.actualCurrent - StateLayer { - radius: Appearance.rounding.normal + StateLayer { + radius: Appearance.rounding.normal - function onClicked(): void { - Wallpapers.setWallpaper(modelData.path); - } + function onClicked(): void { + Wallpapers.setWallpaper(modelData.path); } + } - StyledClippingRect { - id: image + StyledClippingRect { + id: image - anchors.fill: parent - color: Colours.tPalette.m3surfaceContainer - radius: Appearance.rounding.normal + anchors.fill: parent + color: Colours.tPalette.m3surfaceContainer + radius: Appearance.rounding.normal - CachingImage { - id: cachingImage + CachingImage { + id: cachingImage - path: modelData.path - anchors.fill: parent - cache: true - visible: opacity > 0 + path: modelData.path + anchors.fill: parent + cache: true + visible: opacity > 0 - // Show when ready - opacity: status === Image.Ready ? 1 : 0 + // Show when ready + opacity: status === Image.Ready ? 1 : 0 - Behavior on opacity { - NumberAnimation { - duration: 1000 - easing.type: Easing.OutQuad - } + Behavior on opacity { + NumberAnimation { + duration: 1000 + easing.type: Easing.OutQuad } } + } - // Fallback image for when caching fails - Image { - id: fallbackImage + // Fallback image for when caching fails + Image { + id: fallbackImage - anchors.fill: parent - source: fallbackTimer.triggered && cachingImage.status !== Image.Ready ? modelData.path : "" - asynchronous: true - fillMode: Image.PreserveAspectCrop - cache: true - visible: opacity > 0 + anchors.fill: parent + source: fallbackTimer.triggered && cachingImage.status !== Image.Ready ? modelData.path : "" + asynchronous: true + fillMode: Image.PreserveAspectCrop + cache: true + visible: opacity > 0 - opacity: status === Image.Ready && cachingImage.status !== Image.Ready ? 1 : 0 + opacity: status === Image.Ready && cachingImage.status !== Image.Ready ? 1 : 0 - Behavior on opacity { - NumberAnimation { - duration: 1000 - easing.type: Easing.OutQuad - } + Behavior on opacity { + NumberAnimation { + duration: 1000 + easing.type: Easing.OutQuad } } + } - // Timer to trigger fallback only if caching hasn't loaded - Timer { - id: fallbackTimer + // Timer to trigger fallback only if caching hasn't loaded + Timer { + id: fallbackTimer - property bool triggered: false - interval: 800 - running: cachingImage.status === Image.Loading || cachingImage.status === Image.Null - onTriggered: triggered = true - } + property bool triggered: false + interval: 800 + running: cachingImage.status === Image.Loading || cachingImage.status === Image.Null + onTriggered: triggered = true } + } - // Border overlay that doesn't affect image size - Rectangle { - anchors.fill: parent - color: "transparent" - radius: Appearance.rounding.normal - border.width: isCurrent ? 2 : 0 - border.color: Colours.palette.m3primary + // Border overlay that doesn't affect image size + Rectangle { + anchors.fill: parent + color: "transparent" + radius: Appearance.rounding.normal + border.width: isCurrent ? 2 : 0 + border.color: Colours.palette.m3primary - Behavior on border.width { - NumberAnimation { - duration: 150 - easing.type: Easing.OutQuad - } + Behavior on border.width { + NumberAnimation { + duration: 150 + easing.type: Easing.OutQuad } + } - MaterialIcon { - anchors.right: parent.right - anchors.top: parent.top - anchors.margins: Appearance.padding.small + MaterialIcon { + anchors.right: parent.right + anchors.top: parent.top + anchors.margins: Appearance.padding.small - visible: isCurrent - text: "check_circle" - color: Colours.palette.m3primary - font.pointSize: Appearance.font.size.large - } + visible: isCurrent + text: "check_circle" + color: Colours.palette.m3primary + font.pointSize: Appearance.font.size.large + } - // Gradient overlay for filename with rounded bottom corners - Rectangle { - id: filenameOverlay + // Gradient overlay for filename with rounded bottom corners + Rectangle { + id: filenameOverlay - anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom - implicitHeight: filenameText.implicitHeight + Appearance.padding.normal * 2 + implicitHeight: filenameText.implicitHeight + Appearance.padding.normal * 2 - radius: Appearance.rounding.normal + 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) - } + gradient: Gradient { + GradientStop { + position: 0.0 + color: Qt.rgba(0, 0, 0, 0) } - } - - opacity: 0 - - Behavior on opacity { - NumberAnimation { - duration: 1000 - easing.type: Easing.OutCubic + 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 - Component.onCompleted: { - opacity = 1; + Behavior on opacity { + NumberAnimation { + duration: 1000 + easing.type: Easing.OutCubic } } - StyledText { - id: filenameText - anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: parent.bottom - anchors.leftMargin: Appearance.padding.normal - anchors.rightMargin: Appearance.padding.normal - anchors.bottomMargin: Appearance.padding.normal + Component.onCompleted: { + opacity = 1; + } + } - readonly property string fileName: { - const path = modelData.relativePath || ""; - const parts = path.split("/"); - return parts.length > 0 ? parts[parts.length - 1] : path; - } + StyledText { + id: filenameText + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + anchors.leftMargin: Appearance.padding.normal + anchors.rightMargin: Appearance.padding.normal + anchors.bottomMargin: Appearance.padding.normal + + readonly property string fileName: { + const path = modelData.relativePath || ""; + const parts = path.split("/"); + return parts.length > 0 ? parts[parts.length - 1] : path; + } - text: fileName - font.pointSize: Appearance.font.size.smaller - font.weight: 500 - color: isCurrent ? Colours.palette.m3primary : "#FFFFFF" - elide: Text.ElideMiddle - maximumLineCount: 1 + text: fileName + font.pointSize: Appearance.font.size.smaller + font.weight: 500 + color: isCurrent ? Colours.palette.m3primary : "#FFFFFF" + elide: Text.ElideMiddle + maximumLineCount: 1 - opacity: 0 + opacity: 0 - Behavior on opacity { - NumberAnimation { - duration: 1000 - easing.type: Easing.OutCubic - } + Behavior on opacity { + NumberAnimation { + duration: 1000 + easing.type: Easing.OutCubic } + } - Component.onCompleted: { - opacity = 1; - } + Component.onCompleted: { + opacity = 1; } } } -- cgit v1.2.3-freya From c3d9fb8dc0c0aeb9b1a0f47d9e2e770af42daa1b Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sat, 15 Nov 2025 22:03:27 -0500 Subject: controlcenter: wallpaper gradient/outline 1px fix --- modules/controlcenter/appearance/AppearancePane.qml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'modules/controlcenter/appearance') diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index 279c362..b94a1e2 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -1923,6 +1923,7 @@ RowLayout { anchors.fill: parent color: Colours.tPalette.m3surfaceContainer radius: Appearance.rounding.normal + antialiasing: true CachingImage { id: cachingImage @@ -1931,6 +1932,7 @@ RowLayout { anchors.fill: parent cache: true visible: opacity > 0 + antialiasing: true // Show when ready opacity: status === Image.Ready ? 1 : 0 @@ -1953,6 +1955,7 @@ RowLayout { fillMode: Image.PreserveAspectCrop cache: true visible: opacity > 0 + antialiasing: true opacity: status === Image.Ready && cachingImage.status !== Image.Ready ? 1 : 0 @@ -1982,6 +1985,7 @@ RowLayout { radius: Appearance.rounding.normal border.width: isCurrent ? 2 : 0 border.color: Colours.palette.m3primary + antialiasing: true Behavior on border.width { NumberAnimation { @@ -2019,16 +2023,16 @@ RowLayout { color: Qt.rgba(0, 0, 0, 0) } GradientStop { - position: 0.3 - color: Qt.rgba(0, 0, 0, 0.3) + position: 0.4 + color: Qt.rgba(0, 0, 0, 0.2) } GradientStop { - position: 0.7 - color: Qt.rgba(0, 0, 0, 0.75) + position: 0.8 + color: Qt.rgba(0, 0, 0, 0.5) } GradientStop { position: 1.0 - color: Qt.rgba(0, 0, 0, 0.85) + color: Qt.rgba(0, 0, 0, 0.6) } } } -- cgit v1.2.3-freya From ce3902d8f34922401d7bec542c6116091aa2cf58 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sat, 15 Nov 2025 23:41:31 -0500 Subject: controlcenter: wallpaper selector --- .../controlcenter/appearance/AppearancePane.qml | 130 +++++++++++++++------ 1 file changed, 94 insertions(+), 36 deletions(-) (limited to 'modules/controlcenter/appearance') diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index b94a1e2..11eb2b0 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -1812,10 +1812,7 @@ RowLayout { Loader { id: rightAppearanceLoader anchors.fill: parent - anchors.topMargin: Appearance.padding.large * 2 - anchors.bottomMargin: Appearance.padding.large * 2 - anchors.leftMargin: 0 - anchors.rightMargin: 0 + anchors.margins: Appearance.padding.large * 2 asynchronous: true sourceComponent: appearanceRightContentComponent property var rootPane: root @@ -1844,12 +1841,12 @@ RowLayout { anchors.left: parent.left anchors.right: parent.right - anchors.leftMargin: Appearance.padding.large * 2 - anchors.rightMargin: Appearance.padding.large * 2 + anchors.top: parent.top spacing: Appearance.spacing.normal MaterialIcon { - Layout.alignment: Qt.AlignHCenter + Layout.alignment: Qt.AlignHCenter | Qt.AlignTop + Layout.topMargin: 0 text: "palette" font.pointSize: Appearance.font.size.extraLarge * 3 font.bold: true @@ -1877,39 +1874,53 @@ RowLayout { color: Colours.palette.m3onSurfaceVariant } - GridView { - id: wallpaperGrid + Item { Layout.fillWidth: true Layout.topMargin: Appearance.spacing.large - Layout.preferredHeight: Math.min(600, Math.ceil(count / Math.floor(width / cellWidth)) * cellHeight) - Layout.alignment: Qt.AlignHCenter - - cellWidth: 200 + Appearance.spacing.normal - cellHeight: 140 + Appearance.spacing.normal + Layout.preferredHeight: wallpaperGrid.Layout.preferredHeight + + GridView { + id: wallpaperGrid + anchors.fill: parent + + readonly property int minCellWidth: 200 + Appearance.spacing.normal + readonly property int columnsCount: Math.max(1, Math.floor(parent.width / minCellWidth)) + + Layout.preferredHeight: Math.ceil(count / columnsCount) * cellHeight + height: Layout.preferredHeight + + // Distribute width evenly across columns + cellWidth: width / columnsCount + cellHeight: 140 + Appearance.spacing.normal + + leftMargin: 0 + rightMargin: 0 + topMargin: 0 + bottomMargin: 0 - model: Wallpapers.list - clip: true + model: Wallpapers.list + clip: true + + // Disable GridView's own scrolling - let parent handle it + interactive: false // Enable caching for better performance cacheBuffer: cellHeight * 2 - StyledScrollBar.vertical: StyledScrollBar { - flickable: wallpaperGrid - } - delegate: Item { required property var modelData - width: 200 - height: 140 - - // Center in cell - x: (wallpaperGrid.cellWidth - width) / 2 - y: (wallpaperGrid.cellHeight - height) / 2 + width: wallpaperGrid.cellWidth + height: wallpaperGrid.cellHeight readonly property bool isCurrent: modelData.path === Wallpapers.actualCurrent StateLayer { + anchors.fill: parent + anchors.leftMargin: Appearance.spacing.normal / 2 + anchors.rightMargin: Appearance.spacing.normal / 2 + anchors.topMargin: Appearance.spacing.normal / 2 + anchors.bottomMargin: Appearance.spacing.normal / 2 radius: Appearance.rounding.normal function onClicked(): void { @@ -1921,6 +1932,10 @@ RowLayout { id: image anchors.fill: parent + anchors.leftMargin: Appearance.spacing.normal / 2 + anchors.rightMargin: Appearance.spacing.normal / 2 + anchors.topMargin: Appearance.spacing.normal / 2 + anchors.bottomMargin: Appearance.spacing.normal / 2 color: Colours.tPalette.m3surfaceContainer radius: Appearance.rounding.normal antialiasing: true @@ -1930,6 +1945,7 @@ RowLayout { path: modelData.path anchors.fill: parent + fillMode: Image.PreserveAspectCrop cache: true visible: opacity > 0 antialiasing: true @@ -1981,6 +1997,10 @@ RowLayout { // Border overlay that doesn't affect image size Rectangle { anchors.fill: parent + anchors.leftMargin: Appearance.spacing.normal / 2 + anchors.rightMargin: Appearance.spacing.normal / 2 + anchors.topMargin: Appearance.spacing.normal / 2 + anchors.bottomMargin: Appearance.spacing.normal / 2 color: "transparent" radius: Appearance.rounding.normal border.width: isCurrent ? 2 : 0 @@ -2012,27 +2032,63 @@ RowLayout { anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom + anchors.leftMargin: isCurrent ? 2 : 0 + anchors.rightMargin: isCurrent ? 2 : 0 + anchors.bottomMargin: isCurrent ? 2 : 0 - implicitHeight: filenameText.implicitHeight + Appearance.padding.normal * 2 + implicitHeight: filenameText.implicitHeight + Appearance.padding.normal * 1.5 - radius: Appearance.rounding.normal + // Only round bottom corners + topLeftRadius: 0 + topRightRadius: 0 + bottomLeftRadius: Appearance.rounding.normal + bottomRightRadius: Appearance.rounding.normal + + Behavior on anchors.leftMargin { + NumberAnimation { + duration: 150 + easing.type: Easing.OutQuad + } + } + + Behavior on anchors.rightMargin { + NumberAnimation { + duration: 150 + easing.type: Easing.OutQuad + } + } + + Behavior on anchors.bottomMargin { + NumberAnimation { + duration: 150 + easing.type: Easing.OutQuad + } + } gradient: Gradient { GradientStop { position: 0.0 - color: Qt.rgba(0, 0, 0, 0) + color: Qt.rgba(Colours.palette.m3surfaceContainer.r, + Colours.palette.m3surfaceContainer.g, + Colours.palette.m3surfaceContainer.b, 0) } GradientStop { - position: 0.4 - color: Qt.rgba(0, 0, 0, 0.2) + position: 0.3 + color: Qt.rgba(Colours.palette.m3surfaceContainer.r, + Colours.palette.m3surfaceContainer.g, + Colours.palette.m3surfaceContainer.b, 0.7) } GradientStop { - position: 0.8 - color: Qt.rgba(0, 0, 0, 0.5) + position: 0.6 + color: Qt.rgba(Colours.palette.m3surfaceContainer.r, + Colours.palette.m3surfaceContainer.g, + Colours.palette.m3surfaceContainer.b, 0.9) } GradientStop { position: 1.0 - color: Qt.rgba(0, 0, 0, 0.6) + color: Qt.rgba(Colours.palette.m3surfaceContainer.r, + Colours.palette.m3surfaceContainer.g, + Colours.palette.m3surfaceContainer.b, 0.95) } } } @@ -2069,9 +2125,10 @@ RowLayout { text: fileName font.pointSize: Appearance.font.size.smaller font.weight: 500 - color: isCurrent ? Colours.palette.m3primary : "#FFFFFF" + color: isCurrent ? Colours.palette.m3primary : Colours.palette.m3onSurface elide: Text.ElideMiddle maximumLineCount: 1 + horizontalAlignment: Text.AlignHCenter opacity: 0 @@ -2088,8 +2145,9 @@ RowLayout { } } } - } + } } } } + } } -- cgit v1.2.3-freya From 63d680404f82a80a820a0273409bd52ee3cf2b00 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sun, 16 Nov 2025 08:56:38 -0500 Subject: controlcenter: font slider min-max --- modules/controlcenter/appearance/AppearancePane.qml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'modules/controlcenter/appearance') diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index 11eb2b0..e91448e 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -788,7 +788,7 @@ RowLayout { anchors.centerIn: parent width: parent.width - Appearance.padding.normal horizontalAlignment: TextInput.AlignHCenter - validator: DoubleValidator { bottom: 0.1; top: 5.0 } + validator: DoubleValidator { bottom: 0.7; top: 1.5 } Component.onCompleted: { text = (rootPane.fontSizeScale).toFixed(1); @@ -797,7 +797,7 @@ RowLayout { onTextChanged: { if (activeFocus) { const val = parseFloat(text); - if (!isNaN(val) && val >= 0.1 && val <= 5.0) { + if (!isNaN(val) && val >= 0.7 && val <= 1.5) { rootPane.fontSizeScale = val; rootPane.saveConfig(); } @@ -805,7 +805,7 @@ RowLayout { } onEditingFinished: { const val = parseFloat(text); - if (isNaN(val) || val < 0.1 || val > 5.0) { + if (isNaN(val) || val < 0.7 || val > 1.5) { text = (rootPane.fontSizeScale).toFixed(1); } } @@ -825,8 +825,8 @@ RowLayout { Layout.fillWidth: true implicitHeight: Appearance.padding.normal * 3 - from: 0.1 - to: 5.0 + from: 0.7 + to: 1.5 value: rootPane.fontSizeScale onMoved: { rootPane.fontSizeScale = fontSizeSlider.value; -- cgit v1.2.3-freya From 95615e64b77ea19f099ab0339f9c074362705ccf Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sun, 16 Nov 2025 09:25:56 -0500 Subject: controlcenter: wallpaper selector now spacing scale aware --- modules/controlcenter/appearance/AppearancePane.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'modules/controlcenter/appearance') diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index e91448e..45f622f 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -2112,8 +2112,8 @@ RowLayout { anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom - anchors.leftMargin: Appearance.padding.normal - anchors.rightMargin: Appearance.padding.normal + anchors.leftMargin: Appearance.padding.normal + Appearance.spacing.normal / 2 + anchors.rightMargin: Appearance.padding.normal + Appearance.spacing.normal / 2 anchors.bottomMargin: Appearance.padding.normal readonly property string fileName: { -- cgit v1.2.3-freya From 1ce742283836e8133054bdddfbd0852b62ac6c7b Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sun, 16 Nov 2025 12:36:50 -0500 Subject: controlcenter: 1px issue and radii uniform issues fixed --- .../controlcenter/appearance/AppearancePane.qml | 137 +++++++++------------ 1 file changed, 58 insertions(+), 79 deletions(-) (limited to 'modules/controlcenter/appearance') diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index 45f622f..afc792b 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -1899,7 +1899,6 @@ RowLayout { bottomMargin: 0 model: Wallpapers.list - clip: true // Disable GridView's own scrolling - let parent handle it interactive: false @@ -1914,14 +1913,16 @@ RowLayout { height: wallpaperGrid.cellHeight readonly property bool isCurrent: modelData.path === Wallpapers.actualCurrent + readonly property real itemMargin: Appearance.spacing.normal / 2 + readonly property real itemRadius: Appearance.rounding.normal StateLayer { anchors.fill: parent - anchors.leftMargin: Appearance.spacing.normal / 2 - anchors.rightMargin: Appearance.spacing.normal / 2 - anchors.topMargin: Appearance.spacing.normal / 2 - anchors.bottomMargin: Appearance.spacing.normal / 2 - radius: Appearance.rounding.normal + anchors.leftMargin: itemMargin + anchors.rightMargin: itemMargin + anchors.topMargin: itemMargin + anchors.bottomMargin: itemMargin + radius: itemRadius function onClicked(): void { Wallpapers.setWallpaper(modelData.path); @@ -1932,13 +1933,15 @@ RowLayout { id: image anchors.fill: parent - anchors.leftMargin: Appearance.spacing.normal / 2 - anchors.rightMargin: Appearance.spacing.normal / 2 - anchors.topMargin: Appearance.spacing.normal / 2 - anchors.bottomMargin: Appearance.spacing.normal / 2 + anchors.leftMargin: itemMargin + anchors.rightMargin: itemMargin + anchors.topMargin: itemMargin + anchors.bottomMargin: itemMargin color: Colours.tPalette.m3surfaceContainer - radius: Appearance.rounding.normal + radius: itemRadius antialiasing: true + layer.enabled: true + layer.smooth: true CachingImage { id: cachingImage @@ -1949,6 +1952,7 @@ RowLayout { cache: true visible: opacity > 0 antialiasing: true + smooth: true // Show when ready opacity: status === Image.Ready ? 1 : 0 @@ -1972,6 +1976,7 @@ RowLayout { cache: true visible: opacity > 0 antialiasing: true + smooth: true opacity: status === Image.Ready && cachingImage.status !== Image.Ready ? 1 : 0 @@ -1992,79 +1997,20 @@ RowLayout { running: cachingImage.status === Image.Loading || cachingImage.status === Image.Null onTriggered: triggered = true } - } - // Border overlay that doesn't affect image size - Rectangle { - anchors.fill: parent - anchors.leftMargin: Appearance.spacing.normal / 2 - anchors.rightMargin: Appearance.spacing.normal / 2 - anchors.topMargin: Appearance.spacing.normal / 2 - anchors.bottomMargin: Appearance.spacing.normal / 2 - color: "transparent" - radius: Appearance.rounding.normal - border.width: isCurrent ? 2 : 0 - border.color: Colours.palette.m3primary - antialiasing: true - - Behavior on border.width { - NumberAnimation { - duration: 150 - easing.type: Easing.OutQuad - } - } - - MaterialIcon { - anchors.right: parent.right - anchors.top: parent.top - anchors.margins: Appearance.padding.small - - visible: isCurrent - text: "check_circle" - color: Colours.palette.m3primary - font.pointSize: Appearance.font.size.large - } - - // Gradient overlay for filename with rounded bottom corners + // Gradient overlay for filename - positioned inside image container for perfect alignment Rectangle { id: filenameOverlay anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom - anchors.leftMargin: isCurrent ? 2 : 0 - anchors.rightMargin: isCurrent ? 2 : 0 - anchors.bottomMargin: isCurrent ? 2 : 0 implicitHeight: filenameText.implicitHeight + Appearance.padding.normal * 1.5 - // Only round bottom corners - topLeftRadius: 0 - topRightRadius: 0 - bottomLeftRadius: Appearance.rounding.normal - bottomRightRadius: Appearance.rounding.normal - - Behavior on anchors.leftMargin { - NumberAnimation { - duration: 150 - easing.type: Easing.OutQuad - } - } - - Behavior on anchors.rightMargin { - NumberAnimation { - duration: 150 - easing.type: Easing.OutQuad - } - } + // No rounded corners - clipped by parent's rounded corners + radius: 0 - Behavior on anchors.bottomMargin { - NumberAnimation { - duration: 150 - easing.type: Easing.OutQuad - } - } - gradient: Gradient { GradientStop { position: 0.0 @@ -2091,19 +2037,52 @@ RowLayout { Colours.palette.m3surfaceContainer.b, 0.95) } } + + opacity: 0 + + Behavior on opacity { + NumberAnimation { + duration: 1000 + easing.type: Easing.OutCubic + } + } + + Component.onCompleted: { + opacity = 1; + } } + } - opacity: 0 + // Border overlay that doesn't affect image size + Rectangle { + anchors.fill: parent + anchors.leftMargin: itemMargin + anchors.rightMargin: itemMargin + anchors.topMargin: itemMargin + anchors.bottomMargin: itemMargin + color: "transparent" + radius: itemRadius + border.width + border.width: isCurrent ? 2 : 0 + border.color: Colours.palette.m3primary + antialiasing: true + smooth: true - Behavior on opacity { + Behavior on border.width { NumberAnimation { - duration: 1000 - easing.type: Easing.OutCubic + duration: 150 + easing.type: Easing.OutQuad } } - Component.onCompleted: { - opacity = 1; + MaterialIcon { + anchors.right: parent.right + anchors.top: parent.top + anchors.margins: Appearance.padding.small + + visible: isCurrent + text: "check_circle" + color: Colours.palette.m3primary + font.pointSize: Appearance.font.size.large } } -- cgit v1.2.3-freya From 76029cd8a08086102adcb57585c32fc4ed5e0db0 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sun, 16 Nov 2025 13:04:03 -0500 Subject: controlcenter: font subsections collapsiblesections --- .../controlcenter/appearance/AppearancePane.qml | 297 ++++++++++----------- 1 file changed, 147 insertions(+), 150 deletions(-) (limited to 'modules/controlcenter/appearance') diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index afc792b..13ebf55 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -533,210 +533,207 @@ RowLayout { sidebarFlickable.collapseAllSections(fontsSection); } - StyledText { - Layout.topMargin: Appearance.spacing.normal - text: qsTr("Material font family") - font.pointSize: Appearance.font.size.larger - font.weight: 500 - } + CollapsibleSection { + id: materialFontSection + title: qsTr("Material font family") + expanded: true - StyledListView { - Layout.fillWidth: true - Layout.preferredHeight: Math.min(contentHeight, 300) - - clip: true - spacing: Appearance.spacing.small / 2 - model: Qt.fontFamilies() + StyledListView { + Layout.fillWidth: true + Layout.preferredHeight: Math.min(contentHeight, 300) + + clip: true + spacing: Appearance.spacing.small / 2 + model: Qt.fontFamilies() - delegate: StyledRect { - required property string modelData - required property int index + delegate: StyledRect { + required property string modelData + required property int index - width: ListView.view.width + width: ListView.view.width - 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 + } } } - StyledText { - Layout.topMargin: Appearance.spacing.normal - text: qsTr("Monospace font family") - font.pointSize: Appearance.font.size.larger - font.weight: 500 - } + CollapsibleSection { + id: monoFontSection + title: qsTr("Monospace font family") + expanded: false - StyledListView { - Layout.fillWidth: true - Layout.preferredHeight: Math.min(contentHeight, 300) - - clip: true - spacing: Appearance.spacing.small / 2 - model: Qt.fontFamilies() + StyledListView { + Layout.fillWidth: true + Layout.preferredHeight: Math.min(contentHeight, 300) + + clip: true + spacing: Appearance.spacing.small / 2 + model: Qt.fontFamilies() - delegate: StyledRect { - required property string modelData - required property int index + delegate: StyledRect { + required property string modelData + required property int index - width: ListView.view.width + width: ListView.view.width - 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 + } } } - StyledText { - Layout.topMargin: Appearance.spacing.normal - text: qsTr("Sans-serif font family") - font.pointSize: Appearance.font.size.larger - font.weight: 500 - } + CollapsibleSection { + id: sansFontSection + title: qsTr("Sans-serif font family") + expanded: false - StyledListView { - Layout.fillWidth: true - Layout.preferredHeight: Math.min(contentHeight, 300) - - clip: true - spacing: Appearance.spacing.small / 2 - model: Qt.fontFamilies() + StyledListView { + Layout.fillWidth: true + Layout.preferredHeight: Math.min(contentHeight, 300) + + clip: true + spacing: Appearance.spacing.small / 2 + model: Qt.fontFamilies() - delegate: StyledRect { - required property string modelData - required property int index + delegate: StyledRect { + required property string modelData + required property int index - width: ListView.view.width + width: ListView.view.width - 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 59c906d6508ccbc6f4731f222bb56a9a5e9f5345 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sun, 16 Nov 2025 13:09:09 -0500 Subject: controlcenter: appearance pane async loader --- modules/bar/components/Settings.qml | 2 +- modules/bar/components/SettingsIcon.qml | 2 +- .../controlcenter/appearance/AppearancePane.qml | 303 +++++++++++---------- modules/utilities/cards/Toggles.qml | 2 +- 4 files changed, 165 insertions(+), 144 deletions(-) (limited to 'modules/controlcenter/appearance') diff --git a/modules/bar/components/Settings.qml b/modules/bar/components/Settings.qml index 0eceea7..7cd18be 100644 --- a/modules/bar/components/Settings.qml +++ b/modules/bar/components/Settings.qml @@ -22,7 +22,7 @@ Item { function onClicked(): void { WindowFactory.create(null, { - active: "appearance" + active: "network" }); } } diff --git a/modules/bar/components/SettingsIcon.qml b/modules/bar/components/SettingsIcon.qml index 0eceea7..7cd18be 100644 --- a/modules/bar/components/SettingsIcon.qml +++ b/modules/bar/components/SettingsIcon.qml @@ -22,7 +22,7 @@ Item { function onClicked(): void { WindowFactory.create(null, { - active: "appearance" + active: "network" }); } } diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index 13ebf55..09cb04f 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -538,65 +538,72 @@ RowLayout { title: qsTr("Material font family") expanded: true - StyledListView { + Loader { Layout.fillWidth: true - Layout.preferredHeight: Math.min(contentHeight, 300) - - clip: true - spacing: Appearance.spacing.small / 2 - model: Qt.fontFamilies() - - delegate: StyledRect { - required property string modelData - required property int index - - width: ListView.view.width - - 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(); + Layout.preferredHeight: item ? Math.min(item.contentHeight, 300) : 0 + asynchronous: true + active: materialFontSection.expanded + + sourceComponent: StyledListView { + id: materialFontList + property alias contentHeight: materialFontList.contentHeight + + clip: true + spacing: Appearance.spacing.small / 2 + model: Qt.fontFamilies() + + delegate: StyledRect { + required property string modelData + required property int index + + width: ListView.view.width + + 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(); + } } - } - 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 + } } } } @@ -606,65 +613,72 @@ RowLayout { title: qsTr("Monospace font family") expanded: false - StyledListView { + Loader { Layout.fillWidth: true - Layout.preferredHeight: Math.min(contentHeight, 300) - - clip: true - spacing: Appearance.spacing.small / 2 - model: Qt.fontFamilies() - - delegate: StyledRect { - required property string modelData - required property int index - - width: ListView.view.width - - 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(); + Layout.preferredHeight: item ? Math.min(item.contentHeight, 300) : 0 + asynchronous: true + active: monoFontSection.expanded + + sourceComponent: StyledListView { + id: monoFontList + property alias contentHeight: monoFontList.contentHeight + + clip: true + spacing: Appearance.spacing.small / 2 + model: Qt.fontFamilies() + + delegate: StyledRect { + required property string modelData + required property int index + + width: ListView.view.width + + 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(); + } } - } - 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 + } } } } @@ -674,65 +688,72 @@ RowLayout { title: qsTr("Sans-serif font family") expanded: false - StyledListView { + Loader { Layout.fillWidth: true - Layout.preferredHeight: Math.min(contentHeight, 300) - - clip: true - spacing: Appearance.spacing.small / 2 - model: Qt.fontFamilies() - - delegate: StyledRect { - required property string modelData - required property int index - - width: ListView.view.width - - 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(); + Layout.preferredHeight: item ? Math.min(item.contentHeight, 300) : 0 + asynchronous: true + active: sansFontSection.expanded + + sourceComponent: StyledListView { + id: sansFontList + property alias contentHeight: sansFontList.contentHeight + + clip: true + spacing: Appearance.spacing.small / 2 + model: Qt.fontFamilies() + + delegate: StyledRect { + required property string modelData + required property int index + + width: ListView.view.width + + 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(); + } } - } - 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 + } } } } diff --git a/modules/utilities/cards/Toggles.qml b/modules/utilities/cards/Toggles.qml index 71f4d1d..51e991e 100644 --- a/modules/utilities/cards/Toggles.qml +++ b/modules/utilities/cards/Toggles.qml @@ -68,7 +68,7 @@ StyledRect { toggle: false onClicked: { root.visibilities.utilities = false; - root.popouts.detach("appearance"); + root.popouts.detach("network"); } } -- cgit v1.2.3-freya From f77323b136a3773266a1140a17e5f7b4cb3f26e7 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sun, 16 Nov 2025 13:21:59 -0500 Subject: controlcenter: lazyloading wallpaper --- .../controlcenter/appearance/AppearancePane.qml | 69 +++++++++++++--------- 1 file changed, 42 insertions(+), 27 deletions(-) (limited to 'modules/controlcenter/appearance') diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index 09cb04f..fb67ec3 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -1895,34 +1895,47 @@ RowLayout { Item { Layout.fillWidth: true Layout.topMargin: Appearance.spacing.large - Layout.preferredHeight: wallpaperGrid.Layout.preferredHeight + Layout.preferredHeight: wallpaperLoader.item ? wallpaperLoader.item.layoutPreferredHeight : 0 - GridView { - id: wallpaperGrid + Loader { + id: wallpaperLoader anchors.fill: parent - - readonly property int minCellWidth: 200 + Appearance.spacing.normal - readonly property int columnsCount: Math.max(1, Math.floor(parent.width / minCellWidth)) - - Layout.preferredHeight: Math.ceil(count / columnsCount) * cellHeight - height: Layout.preferredHeight - - // Distribute width evenly across columns - cellWidth: width / columnsCount - cellHeight: 140 + Appearance.spacing.normal - - leftMargin: 0 - rightMargin: 0 - topMargin: 0 - bottomMargin: 0 - - model: Wallpapers.list - - // Disable GridView's own scrolling - let parent handle it - interactive: false - - // Enable caching for better performance - cacheBuffer: cellHeight * 2 + asynchronous: true + active: { + // Lazy load: only activate when right pane is loaded + // This defers heavy wallpaper list loading until the right pane is visible + return rightAppearanceLoader.item !== null; + } + + sourceComponent: Item { + property alias layoutPreferredHeight: wallpaperGrid.layoutPreferredHeight + + GridView { + id: wallpaperGrid + anchors.fill: parent + + readonly property int minCellWidth: 200 + Appearance.spacing.normal + readonly property int columnsCount: Math.max(1, Math.floor(parent.width / minCellWidth)) + + readonly property int layoutPreferredHeight: Math.ceil(count / columnsCount) * cellHeight + height: layoutPreferredHeight + + // Distribute width evenly across columns + cellWidth: width / columnsCount + cellHeight: 140 + Appearance.spacing.normal + + leftMargin: 0 + rightMargin: 0 + topMargin: 0 + bottomMargin: 0 + + model: Wallpapers.list + + // Disable GridView's own scrolling - let parent handle it + interactive: false + + // Enable caching for better performance + cacheBuffer: cellHeight * 2 delegate: Item { required property var modelData @@ -2141,7 +2154,9 @@ RowLayout { } } } - } + } + } + } } } } -- cgit v1.2.3-freya From 07637da8f6ad4a28670d9eeb70f83ae8889c0a17 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sun, 16 Nov 2025 19:31:22 -0500 Subject: controlcenter: removed accordion auto-close on sections --- .../controlcenter/appearance/AppearancePane.qml | 47 ---------------------- 1 file changed, 47 deletions(-) (limited to 'modules/controlcenter/appearance') diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index fb67ec3..5f70abb 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -130,26 +130,6 @@ RowLayout { flickableDirection: Flickable.VerticalFlick contentHeight: sidebarLayout.height - function collapseAllSections(exceptSection) { - if (exceptSection !== themeModeSection) - themeModeSection.expanded = false; - if (exceptSection !== colorVariantSection) - colorVariantSection.expanded = false; - if (exceptSection !== colorSchemeSection) - colorSchemeSection.expanded = false; - if (exceptSection !== animationsSection) - animationsSection.expanded = false; - if (exceptSection !== fontsSection) - fontsSection.expanded = false; - if (exceptSection !== scalesSection) - scalesSection.expanded = false; - if (exceptSection !== transparencySection) - transparencySection.expanded = false; - if (exceptSection !== borderSection) - borderSection.expanded = false; - if (exceptSection !== backgroundSection) - backgroundSection.expanded = false; - } StyledScrollBar.vertical: StyledScrollBar { flickable: sidebarFlickable @@ -179,9 +159,6 @@ RowLayout { id: themeModeSection title: qsTr("Theme mode") description: qsTr("Light or dark theme") - onToggleRequested: { - sidebarFlickable.collapseAllSections(themeModeSection); - } SwitchRow { label: qsTr("Dark mode") @@ -196,9 +173,6 @@ RowLayout { id: colorVariantSection title: qsTr("Color variant") description: qsTr("Material theme variant") - onToggleRequested: { - sidebarFlickable.collapseAllSections(colorVariantSection); - } ColumnLayout { Layout.fillWidth: true @@ -282,9 +256,6 @@ RowLayout { id: colorSchemeSection title: qsTr("Color scheme") description: qsTr("Available color schemes") - onToggleRequested: { - sidebarFlickable.collapseAllSections(colorSchemeSection); - } ColumnLayout { Layout.fillWidth: true @@ -422,9 +393,6 @@ RowLayout { CollapsibleSection { id: animationsSection title: qsTr("Animations") - onToggleRequested: { - sidebarFlickable.collapseAllSections(animationsSection); - } SectionContainer { contentSpacing: Appearance.spacing.normal @@ -529,9 +497,6 @@ RowLayout { CollapsibleSection { id: fontsSection title: qsTr("Fonts") - onToggleRequested: { - sidebarFlickable.collapseAllSections(fontsSection); - } CollapsibleSection { id: materialFontSection @@ -861,9 +826,6 @@ RowLayout { CollapsibleSection { id: scalesSection title: qsTr("Scales") - onToggleRequested: { - sidebarFlickable.collapseAllSections(scalesSection); - } SectionContainer { contentSpacing: Appearance.spacing.normal @@ -1166,9 +1128,6 @@ RowLayout { CollapsibleSection { id: transparencySection title: qsTr("Transparency") - onToggleRequested: { - sidebarFlickable.collapseAllSections(transparencySection); - } SwitchRow { label: qsTr("Transparency enabled") @@ -1381,9 +1340,6 @@ RowLayout { CollapsibleSection { id: borderSection title: qsTr("Border") - onToggleRequested: { - sidebarFlickable.collapseAllSections(borderSection); - } SectionContainer { contentSpacing: Appearance.spacing.normal @@ -1575,9 +1531,6 @@ RowLayout { CollapsibleSection { id: backgroundSection title: qsTr("Background") - onToggleRequested: { - sidebarFlickable.collapseAllSections(backgroundSection); - } SwitchRow { label: qsTr("Desktop clock") -- cgit v1.2.3-freya From 8981ab8806609496360d11cf34384fc337368ff7 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sun, 16 Nov 2025 19:36:05 -0500 Subject: controlcenter: added collapse/expand all to apperaance and taskbar --- .../controlcenter/appearance/AppearancePane.qml | 29 +++++++++++++++ modules/controlcenter/taskbar/TaskbarPane.qml | 41 ++++++++++++++++------ 2 files changed, 60 insertions(+), 10 deletions(-) (limited to 'modules/controlcenter/appearance') diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index 5f70abb..2b3bbce 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -141,6 +141,17 @@ RowLayout { anchors.right: parent.right spacing: Appearance.spacing.small + readonly property bool allSectionsExpanded: + themeModeSection.expanded && + colorVariantSection.expanded && + colorSchemeSection.expanded && + animationsSection.expanded && + fontsSection.expanded && + scalesSection.expanded && + transparencySection.expanded && + borderSection.expanded && + backgroundSection.expanded + RowLayout { spacing: Appearance.spacing.smaller @@ -153,6 +164,24 @@ RowLayout { Item { Layout.fillWidth: true } + + IconButton { + icon: sidebarLayout.allSectionsExpanded ? "unfold_less" : "unfold_more" + type: IconButton.Text + label.animate: true + onClicked: { + const shouldExpand = !sidebarLayout.allSectionsExpanded; + themeModeSection.expanded = shouldExpand; + colorVariantSection.expanded = shouldExpand; + colorSchemeSection.expanded = shouldExpand; + animationsSection.expanded = shouldExpand; + fontsSection.expanded = shouldExpand; + scalesSection.expanded = shouldExpand; + transparencySection.expanded = shouldExpand; + borderSection.expanded = shouldExpand; + backgroundSection.expanded = shouldExpand; + } + } } CollapsibleSection { diff --git a/modules/controlcenter/taskbar/TaskbarPane.qml b/modules/controlcenter/taskbar/TaskbarPane.qml index 8d0e5a0..c731acc 100644 --- a/modules/controlcenter/taskbar/TaskbarPane.qml +++ b/modules/controlcenter/taskbar/TaskbarPane.qml @@ -175,19 +175,40 @@ RowLayout { spacing: Appearance.spacing.small + readonly property bool allSectionsExpanded: + clockSection.expanded && + barBehaviorSection.expanded && + statusIconsSection.expanded && + traySettingsSection.expanded && + workspacesSection.expanded + RowLayout { - spacing: Appearance.spacing.smaller + spacing: Appearance.spacing.smaller - StyledText { - text: qsTr("Settings") - font.pointSize: Appearance.font.size.large - font.weight: 500 - } + StyledText { + text: qsTr("Settings") + font.pointSize: Appearance.font.size.large + font.weight: 500 + } - Item { - Layout.fillWidth: true - } - } + Item { + Layout.fillWidth: true + } + + IconButton { + icon: sidebarLayout.allSectionsExpanded ? "unfold_less" : "unfold_more" + type: IconButton.Text + label.animate: true + onClicked: { + const shouldExpand = !sidebarLayout.allSectionsExpanded; + clockSection.expanded = shouldExpand; + barBehaviorSection.expanded = shouldExpand; + statusIconsSection.expanded = shouldExpand; + traySettingsSection.expanded = shouldExpand; + workspacesSection.expanded = shouldExpand; + } + } + } CollapsibleSection { id: clockSection -- cgit v1.2.3-freya From 57bb5cede133be96f848fea9ffe5bc9deb1a6d91 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sun, 16 Nov 2025 21:40:47 -0500 Subject: controlcenter: lazyloading wallpapers FIXED --- .../controlcenter/appearance/AppearancePane.qml | 300 ++++++++++++++++++--- 1 file changed, 265 insertions(+), 35 deletions(-) (limited to 'modules/controlcenter/appearance') diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index 2b3bbce..51e4cdf 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -50,42 +50,32 @@ RowLayout { function saveConfig() { - // Update animations Config.appearance.anim.durations.scale = root.animDurationsScale; - // Update fonts Config.appearance.font.family.material = root.fontFamilyMaterial; Config.appearance.font.family.mono = root.fontFamilyMono; Config.appearance.font.family.sans = root.fontFamilySans; Config.appearance.font.size.scale = root.fontSizeScale; - // Update scales Config.appearance.padding.scale = root.paddingScale; Config.appearance.rounding.scale = root.roundingScale; Config.appearance.spacing.scale = root.spacingScale; - // Update transparency Config.appearance.transparency.enabled = root.transparencyEnabled; Config.appearance.transparency.base = root.transparencyBase; Config.appearance.transparency.layers = root.transparencyLayers; - // Update desktop clock Config.background.desktopClock.enabled = root.desktopClockEnabled; - - // Update background enabled Config.background.enabled = root.backgroundEnabled; - // Update visualiser Config.background.visualiser.enabled = root.visualiserEnabled; Config.background.visualiser.autoHide = root.visualiserAutoHide; Config.background.visualiser.rounding = root.visualiserRounding; Config.background.visualiser.spacing = root.visualiserSpacing; - // Update border Config.border.rounding = root.borderRounding; Config.border.thickness = root.borderThickness; - // Persist changes to disk Config.save(); } @@ -224,13 +214,11 @@ RowLayout { function onClicked(): void { const variant = modelData.variant; - // Optimistic update - set immediately + // Optimistic update - set immediately for responsive UI Schemes.currentVariant = variant; - - // Execute the command Quickshell.execDetached(["caelestia", "scheme", "set", "-v", variant]); - // Reload after a delay to confirm + // Reload after a delay to confirm changes Qt.callLater(() => { reloadTimer.restart(); }); @@ -312,13 +300,11 @@ RowLayout { const flavour = modelData.flavour; const schemeKey = `${name} ${flavour}`; - // Optimistic update - set immediately + // Optimistic update - set immediately for responsive UI Schemes.currentScheme = schemeKey; - - // Execute the command Quickshell.execDetached(["caelestia", "scheme", "set", "-n", name, "-f", flavour]); - // Reload after a delay to confirm + // Reload after a delay to confirm changes Qt.callLater(() => { reloadTimer.restart(); }); @@ -1816,6 +1802,12 @@ RowLayout { asynchronous: true sourceComponent: appearanceRightContentComponent property var rootPane: root + + onStatusChanged: { + if (status === Loader.Error) { + console.error("[AppearancePane] Right appearance loader error!"); + } + } } } @@ -1884,25 +1876,99 @@ RowLayout { anchors.fill: parent asynchronous: true active: { - // Lazy load: only activate when right pane is loaded - // This defers heavy wallpaper list loading until the right pane is visible - return rightAppearanceLoader.item !== null; + // Lazy load: only activate when: + // 1. Right pane is loaded AND + // 2. Appearance pane is active (index 3) or adjacent (for smooth transitions) + // This prevents loading all wallpapers when control center opens but appearance pane isn't visible + const isActive = root.session.activeIndex === 3; + const isAdjacent = Math.abs(root.session.activeIndex - 3) === 1; + const shouldActivate = rightAppearanceLoader.item !== null && (isActive || isAdjacent); + return shouldActivate; + } + + onStatusChanged: { + if (status === Loader.Error) { + console.error("[AppearancePane] Wallpaper loader error!"); + } } sourceComponent: Item { + id: wallpaperGridContainer property alias layoutPreferredHeight: wallpaperGrid.layoutPreferredHeight + // Find and store reference to parent Flickable for scroll monitoring + property var parentFlickable: { + let item = parent; + while (item) { + if (item.flickableDirection !== undefined) { + return item; + } + item = item.parent; + } + return null; + } + + // Lazy loading model: loads one image at a time, only when touching bottom + // This prevents GridView from creating all delegates at once + QtObject { + id: lazyModel + + property var sourceList: null + property int loadedCount: 0 // Total items available to load + property int visibleCount: 0 // Items actually exposed to GridView (only visible + buffer) + property int totalCount: 0 + + function initialize(list) { + sourceList = list; + totalCount = list ? list.length : 0; + // Start with enough items to fill the initial viewport (~3 rows) + const initialRows = 3; + const cols = wallpaperGrid.columnsCount > 0 ? wallpaperGrid.columnsCount : 3; + const initialCount = Math.min(initialRows * cols, totalCount); + loadedCount = initialCount; + visibleCount = initialCount; + } + + function loadOneRow() { + if (loadedCount < totalCount) { + const cols = wallpaperGrid.columnsCount > 0 ? wallpaperGrid.columnsCount : 1; + const itemsToLoad = Math.min(cols, totalCount - loadedCount); + loadedCount += itemsToLoad; + } + } + + function updateVisibleCount(neededCount) { + // Always round up to complete rows to avoid incomplete rows in the grid + const cols = wallpaperGrid.columnsCount > 0 ? wallpaperGrid.columnsCount : 1; + const maxVisible = Math.min(neededCount, loadedCount); + const rows = Math.ceil(maxVisible / cols); + const newVisibleCount = Math.min(rows * cols, loadedCount); + + if (newVisibleCount > visibleCount) { + visibleCount = newVisibleCount; + } + } + } + GridView { id: wallpaperGrid anchors.fill: parent + property int _delegateCount: 0 + readonly property int minCellWidth: 200 + Appearance.spacing.normal readonly property int columnsCount: Math.max(1, Math.floor(parent.width / minCellWidth)) - readonly property int layoutPreferredHeight: Math.ceil(count / columnsCount) * cellHeight - height: layoutPreferredHeight + // Height based on visible items only - prevents GridView from creating all delegates + readonly property int layoutPreferredHeight: { + if (!lazyModel || lazyModel.visibleCount === 0 || columnsCount === 0) { + return 0; + } + const calculated = Math.ceil(lazyModel.visibleCount / columnsCount) * cellHeight; + return calculated; + } - // Distribute width evenly across columns + height: layoutPreferredHeight cellWidth: width / columnsCount cellHeight: 140 + Appearance.spacing.normal @@ -1911,13 +1977,178 @@ RowLayout { topMargin: 0 bottomMargin: 0 - model: Wallpapers.list + // Use ListModel for incremental updates to prevent flashing when new items are added + ListModel { + id: wallpaperListModel + } + + model: wallpaperListModel + + Connections { + target: lazyModel + function onVisibleCountChanged(): void { + if (!lazyModel || !lazyModel.sourceList) return; + + const newCount = lazyModel.visibleCount; + const currentCount = wallpaperListModel.count; + + // Only append new items - never remove or replace existing ones + if (newCount > currentCount) { + const flickable = wallpaperGridContainer.parentFlickable; + const oldScrollY = flickable ? flickable.contentY : 0; + + for (let i = currentCount; i < newCount; i++) { + wallpaperListModel.append({modelData: lazyModel.sourceList[i]}); + } + + // Preserve scroll position after model update + if (flickable) { + Qt.callLater(function() { + if (Math.abs(flickable.contentY - oldScrollY) < 1) { + flickable.contentY = oldScrollY; + } + }); + } + } + } + } + + Component.onCompleted: { + Qt.callLater(function() { + const isActive = root.session.activeIndex === 3; + if (width > 0 && parent && parent.visible && isActive && Wallpapers.list) { + lazyModel.initialize(Wallpapers.list); + wallpaperListModel.clear(); + for (let i = 0; i < lazyModel.visibleCount; i++) { + wallpaperListModel.append({modelData: lazyModel.sourceList[i]}); + } + } + }); + } - // Disable GridView's own scrolling - let parent handle it + Connections { + target: root.session + function onActiveIndexChanged(): void { + const isActive = root.session.activeIndex === 3; + if (isActive && width > 0 && !lazyModel.sourceList && parent && parent.visible && Wallpapers.list) { + lazyModel.initialize(Wallpapers.list); + wallpaperListModel.clear(); + for (let i = 0; i < lazyModel.visibleCount; i++) { + wallpaperListModel.append({modelData: lazyModel.sourceList[i]}); + } + } + } + } + + onWidthChanged: { + const isActive = root.session.activeIndex === 3; + if (width > 0 && !lazyModel.sourceList && parent && parent.visible && isActive && Wallpapers.list) { + lazyModel.initialize(Wallpapers.list); + wallpaperListModel.clear(); + for (let i = 0; i < lazyModel.visibleCount; i++) { + wallpaperListModel.append({modelData: lazyModel.sourceList[i]}); + } + } + } + + // Force true lazy loading: only create delegates for visible items + displayMarginBeginning: 0 + displayMarginEnd: 0 + cacheBuffer: 0 + + // Debounce expansion to avoid too frequent checks + property bool _expansionInProgress: false + + Connections { + target: wallpaperGridContainer.parentFlickable + function onContentYChanged(): void { + if (!lazyModel || !lazyModel.sourceList || lazyModel.loadedCount >= lazyModel.totalCount || wallpaperGrid._expansionInProgress) { + return; + } + + const flickable = wallpaperGridContainer.parentFlickable; + if (!flickable) return; + + const gridY = wallpaperGridContainer.y; + const scrollY = flickable.contentY; + const viewportHeight = flickable.height; + + const topY = scrollY - gridY; + const bottomY = scrollY + viewportHeight - gridY; + + if (bottomY < 0) return; + + const topRow = Math.max(0, Math.floor(topY / wallpaperGrid.cellHeight)); + const bottomRow = Math.floor(bottomY / wallpaperGrid.cellHeight); + + // Update visible count with 1 row buffer ahead + const bufferRows = 1; + const neededBottomRow = bottomRow + bufferRows; + const neededCount = Math.min((neededBottomRow + 1) * wallpaperGrid.columnsCount, lazyModel.loadedCount); + lazyModel.updateVisibleCount(neededCount); + + // Load more when we're within 1 row of running out of loaded items + const loadedRows = Math.ceil(lazyModel.loadedCount / wallpaperGrid.columnsCount); + const rowsRemaining = loadedRows - (bottomRow + 1); + + if (rowsRemaining <= 1 && lazyModel.loadedCount < lazyModel.totalCount) { + if (!wallpaperGrid._expansionInProgress) { + wallpaperGrid._expansionInProgress = true; + lazyModel.loadOneRow(); + Qt.callLater(function() { + wallpaperGrid._expansionInProgress = false; + }); + } + } + } + } + + // Fallback timer to check scroll position periodically + Timer { + id: scrollCheckTimer + interval: 100 + running: lazyModel && lazyModel.sourceList && lazyModel.loadedCount < lazyModel.totalCount + repeat: true + onTriggered: { + const flickable = wallpaperGridContainer.parentFlickable; + if (!flickable || !lazyModel || !lazyModel.sourceList) return; + + const gridY = wallpaperGridContainer.y; + const scrollY = flickable.contentY; + const viewportHeight = flickable.height; + + const topY = scrollY - gridY; + const bottomY = scrollY + viewportHeight - gridY; + if (bottomY < 0) return; + + const topRow = Math.max(0, Math.floor(topY / wallpaperGrid.cellHeight)); + const bottomRow = Math.floor(bottomY / wallpaperGrid.cellHeight); + + const bufferRows = 1; + const neededBottomRow = bottomRow + bufferRows; + const neededCount = Math.min((neededBottomRow + 1) * wallpaperGrid.columnsCount, lazyModel.loadedCount); + lazyModel.updateVisibleCount(neededCount); + + // Load more when we're within 1 row of running out of loaded items + const loadedRows = Math.ceil(lazyModel.loadedCount / wallpaperGrid.columnsCount); + const rowsRemaining = loadedRows - (bottomRow + 1); + + if (rowsRemaining <= 1 && lazyModel.loadedCount < lazyModel.totalCount) { + if (!wallpaperGrid._expansionInProgress) { + wallpaperGrid._expansionInProgress = true; + lazyModel.loadOneRow(); + Qt.callLater(function() { + wallpaperGrid._expansionInProgress = false; + }); + } + } + } + } + + + // Parent Flickable handles scrolling interactive: false - // Enable caching for better performance - cacheBuffer: cellHeight * 2 delegate: Item { required property var modelData @@ -1928,6 +2159,10 @@ RowLayout { readonly property bool isCurrent: modelData.path === Wallpapers.actualCurrent readonly property real itemMargin: Appearance.spacing.normal / 2 readonly property real itemRadius: Appearance.rounding.normal + + Component.onCompleted: { + wallpaperGrid._delegateCount++; + } StateLayer { anchors.fill: parent @@ -1967,7 +2202,6 @@ RowLayout { antialiasing: true smooth: true - // Show when ready opacity: status === Image.Ready ? 1 : 0 Behavior on opacity { @@ -1978,7 +2212,7 @@ RowLayout { } } - // Fallback image for when caching fails + // Fallback if CachingImage fails to load Image { id: fallbackImage @@ -2001,7 +2235,6 @@ RowLayout { } } - // Timer to trigger fallback only if caching hasn't loaded Timer { id: fallbackTimer @@ -2011,7 +2244,7 @@ RowLayout { onTriggered: triggered = true } - // Gradient overlay for filename - positioned inside image container for perfect alignment + // Gradient overlay for filename Rectangle { id: filenameOverlay @@ -2020,8 +2253,6 @@ RowLayout { anchors.bottom: parent.bottom implicitHeight: filenameText.implicitHeight + Appearance.padding.normal * 1.5 - - // No rounded corners - clipped by parent's rounded corners radius: 0 gradient: Gradient { @@ -2066,7 +2297,6 @@ RowLayout { } } - // Border overlay that doesn't affect image size Rectangle { anchors.fill: parent anchors.leftMargin: itemMargin -- cgit v1.2.3-freya From 9bd3d57ff5cbeb883a71ebd9f4d9301afffbf4d9 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sun, 16 Nov 2025 23:33:54 -0500 Subject: controlcenter: lazyload cleanup confirmed --- .../controlcenter/appearance/AppearancePane.qml | 53 +++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) (limited to 'modules/controlcenter/appearance') diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index 51e4cdf..b4e93ae 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -1891,6 +1891,20 @@ RowLayout { console.error("[AppearancePane] Wallpaper loader error!"); } } + + // Stop lazy loading when loader becomes inactive + onActiveChanged: { + if (!active && wallpaperLoader.item) { + const container = wallpaperLoader.item; + // Access timer through wallpaperGrid + if (container && container.wallpaperGrid) { + if (container.wallpaperGrid.scrollCheckTimer) { + container.wallpaperGrid.scrollCheckTimer.stop(); + } + container.wallpaperGrid._expansionInProgress = false; + } + } + } sourceComponent: Item { id: wallpaperGridContainer @@ -1908,6 +1922,16 @@ RowLayout { return null; } + // Cleanup when component is destroyed + Component.onDestruction: { + if (wallpaperGrid) { + if (wallpaperGrid.scrollCheckTimer) { + wallpaperGrid.scrollCheckTimer.stop(); + } + wallpaperGrid._expansionInProgress = false; + } + } + // Lazy loading model: loads one image at a time, only when touching bottom // This prevents GridView from creating all delegates at once QtObject { @@ -2030,6 +2054,19 @@ RowLayout { target: root.session function onActiveIndexChanged(): void { const isActive = root.session.activeIndex === 3; + + // Stop lazy loading when switching away from appearance pane + if (!isActive) { + if (scrollCheckTimer) { + scrollCheckTimer.stop(); + } + if (wallpaperGrid) { + wallpaperGrid._expansionInProgress = false; + } + return; + } + + // Initialize if needed when switching to appearance pane if (isActive && width > 0 && !lazyModel.sourceList && parent && parent.visible && Wallpapers.list) { lazyModel.initialize(Wallpapers.list); wallpaperListModel.clear(); @@ -2062,6 +2099,10 @@ RowLayout { Connections { target: wallpaperGridContainer.parentFlickable function onContentYChanged(): void { + // Don't process scroll events if appearance pane is not active + const isActive = root.session.activeIndex === 3; + if (!isActive) return; + if (!lazyModel || !lazyModel.sourceList || lazyModel.loadedCount >= lazyModel.totalCount || wallpaperGrid._expansionInProgress) { return; } @@ -2107,9 +2148,19 @@ RowLayout { Timer { id: scrollCheckTimer interval: 100 - running: lazyModel && lazyModel.sourceList && lazyModel.loadedCount < lazyModel.totalCount + running: { + const isActive = root.session.activeIndex === 3; + return isActive && lazyModel && lazyModel.sourceList && lazyModel.loadedCount < lazyModel.totalCount; + } repeat: true onTriggered: { + // Double-check that appearance pane is still active + const isActive = root.session.activeIndex === 3; + if (!isActive) { + stop(); + return; + } + const flickable = wallpaperGridContainer.parentFlickable; if (!flickable || !lazyModel || !lazyModel.sourceList) return; -- cgit v1.2.3-freya From b1cc6418499c6a1af9906043c0b60610bb7b2174 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Mon, 17 Nov 2025 13:34:29 -0500 Subject: controlcenter: correcting padding/margins on containers --- modules/controlcenter/appearance/AppearancePane.qml | 1 + modules/controlcenter/launcher/LauncherPane.qml | 2 +- modules/controlcenter/taskbar/TaskbarPane.qml | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) (limited to 'modules/controlcenter/appearance') diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index b4e93ae..71b4061 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -80,6 +80,7 @@ RowLayout { } Item { + id: leftAppearanceItem Layout.preferredWidth: Math.floor(parent.width * 0.4) Layout.minimumWidth: 420 Layout.fillHeight: true diff --git a/modules/controlcenter/launcher/LauncherPane.qml b/modules/controlcenter/launcher/LauncherPane.qml index 71d1d6f..bf4e85f 100644 --- a/modules/controlcenter/launcher/LauncherPane.qml +++ b/modules/controlcenter/launcher/LauncherPane.qml @@ -169,7 +169,6 @@ RowLayout { anchors.margins: Appearance.padding.large + Appearance.padding.normal anchors.leftMargin: Appearance.padding.large anchors.rightMargin: Appearance.padding.large + Appearance.padding.normal / 2 - anchors.bottomMargin: 0 asynchronous: true sourceComponent: leftContentComponent @@ -186,6 +185,7 @@ RowLayout { id: leftContentComponent ColumnLayout { + id: leftLauncherLayout anchors.fill: parent spacing: Appearance.spacing.small diff --git a/modules/controlcenter/taskbar/TaskbarPane.qml b/modules/controlcenter/taskbar/TaskbarPane.qml index 5112b4a..c944154 100644 --- a/modules/controlcenter/taskbar/TaskbarPane.qml +++ b/modules/controlcenter/taskbar/TaskbarPane.qml @@ -143,6 +143,8 @@ Item { InnerBorder { id: taskbarBorder + leftThickness: 0 + rightThickness: Appearance.padding.normal / 2 } Component { -- 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 'modules/controlcenter/appearance') 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 'modules/controlcenter/appearance') 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 23e5d7d13ce0432e17a7e2077b12b44278f919b6 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Mon, 17 Nov 2025 15:46:40 -0500 Subject: controlcenter: renamed panel titles from settings to panel name --- modules/controlcenter/appearance/AppearancePane.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 ++-- modules/controlcenter/taskbar/TaskbarPane.qml | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) (limited to 'modules/controlcenter/appearance') diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index 177e7b9..891f64b 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -147,7 +147,7 @@ RowLayout { spacing: Appearance.spacing.smaller StyledText { - text: qsTr("Settings") + text: qsTr("Appearance") font.pointSize: Appearance.font.size.large font.weight: 500 } diff --git a/modules/controlcenter/audio/AudioPane.qml b/modules/controlcenter/audio/AudioPane.qml index 3440a2f..c2d60d8 100644 --- a/modules/controlcenter/audio/AudioPane.qml +++ b/modules/controlcenter/audio/AudioPane.qml @@ -74,13 +74,13 @@ RowLayout { anchors.right: parent.right spacing: Appearance.spacing.normal - // Settings header above the collapsible sections + // Audio header above the collapsible sections RowLayout { Layout.fillWidth: true spacing: Appearance.spacing.smaller StyledText { - text: qsTr("Settings") + text: qsTr("Audio") font.pointSize: Appearance.font.size.large font.weight: 500 } diff --git a/modules/controlcenter/bluetooth/DeviceList.qml b/modules/controlcenter/bluetooth/DeviceList.qml index 8e79e72..06700e8 100644 --- a/modules/controlcenter/bluetooth/DeviceList.qml +++ b/modules/controlcenter/bluetooth/DeviceList.qml @@ -25,7 +25,7 @@ ColumnLayout { spacing: Appearance.spacing.smaller StyledText { - text: qsTr("Settings") + text: qsTr("Bluetooth") font.pointSize: Appearance.font.size.large font.weight: 500 } diff --git a/modules/controlcenter/launcher/LauncherPane.qml b/modules/controlcenter/launcher/LauncherPane.qml index bf4e85f..300117a 100644 --- a/modules/controlcenter/launcher/LauncherPane.qml +++ b/modules/controlcenter/launcher/LauncherPane.qml @@ -194,7 +194,7 @@ RowLayout { spacing: Appearance.spacing.smaller StyledText { - text: qsTr("Settings") + text: qsTr("Launcher") font.pointSize: Appearance.font.size.large font.weight: 500 } diff --git a/modules/controlcenter/network/NetworkingPane.qml b/modules/controlcenter/network/NetworkingPane.qml index a632c2b..52499d8 100644 --- a/modules/controlcenter/network/NetworkingPane.qml +++ b/modules/controlcenter/network/NetworkingPane.qml @@ -82,13 +82,13 @@ Item { anchors.right: parent.right spacing: Appearance.spacing.normal - // Settings header above the collapsible sections + // Network header above the collapsible sections RowLayout { Layout.fillWidth: true spacing: Appearance.spacing.smaller StyledText { - text: qsTr("Settings") + text: qsTr("Network") font.pointSize: Appearance.font.size.large font.weight: 500 } diff --git a/modules/controlcenter/taskbar/TaskbarPane.qml b/modules/controlcenter/taskbar/TaskbarPane.qml index b798da8..507a239 100644 --- a/modules/controlcenter/taskbar/TaskbarPane.qml +++ b/modules/controlcenter/taskbar/TaskbarPane.qml @@ -171,7 +171,7 @@ Item { spacing: Appearance.spacing.smaller StyledText { - text: qsTr("Settings") + text: qsTr("Taskbar") font.pointSize: Appearance.font.size.large font.weight: 500 } -- 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 'modules/controlcenter/appearance') 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 ad4213d45ccf227e3528dd2bcb992ec75ab8d0c1 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Wed, 19 Nov 2025 12:39:45 -0500 Subject: refactor: SplitPaneLayout now component --- .../controlcenter/appearance/AppearancePane.qml | 3205 ++++++++++---------- modules/controlcenter/audio/AudioPane.qml | 570 ++-- modules/controlcenter/bluetooth/BtPane.qml | 138 +- .../controlcenter/components/SplitPaneLayout.qml | 120 + modules/controlcenter/launcher/LauncherPane.qml | 288 +- modules/controlcenter/network/NetworkingPane.qml | 296 +- 6 files changed, 2249 insertions(+), 2368 deletions(-) create mode 100644 modules/controlcenter/components/SplitPaneLayout.qml (limited to 'modules/controlcenter/appearance') diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index 61cdcaa..2041bf8 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -1,6 +1,7 @@ pragma ComponentBehavior: Bound import ".." +import "../components" import "../../launcher/services" import qs.components import qs.components.controls @@ -16,7 +17,7 @@ import Quickshell.Widgets import QtQuick import QtQuick.Layouts -RowLayout { +Item { id: root required property Session session @@ -46,9 +47,6 @@ RowLayout { anchors.fill: parent - spacing: 0 - - function saveConfig() { Config.appearance.anim.durations.scale = root.animDurationsScale; @@ -79,794 +77,920 @@ RowLayout { Config.save(); } - Item { - id: leftAppearanceItem - Layout.preferredWidth: Math.floor(parent.width * 0.4) - Layout.minimumWidth: 420 - Layout.fillHeight: true - - ClippingRectangle { - id: leftAppearanceClippingRect - anchors.fill: parent - anchors.margins: Appearance.padding.normal - anchors.leftMargin: 0 - anchors.rightMargin: Appearance.padding.normal / 2 - radius: leftAppearanceBorder.innerRadius - color: "transparent" - - Loader { - id: leftAppearanceLoader - anchors.fill: parent - anchors.margins: Appearance.padding.large + Appearance.padding.normal - anchors.leftMargin: Appearance.padding.large - anchors.rightMargin: Appearance.padding.large + Appearance.padding.normal / 2 - asynchronous: true - sourceComponent: appearanceLeftContentComponent - property var rootPane: root - } - } + Component { + id: appearanceRightContentComponent - InnerBorder { - id: leftAppearanceBorder - leftThickness: 0 - rightThickness: Appearance.padding.normal / 2 - } + StyledFlickable { + id: rightAppearanceFlickable + flickableDirection: Flickable.VerticalFlick + contentHeight: contentLayout.height - Component { - id: appearanceLeftContentComponent + StyledScrollBar.vertical: StyledScrollBar { + flickable: rightAppearanceFlickable + } - StyledFlickable { - id: sidebarFlickable - readonly property var rootPane: leftAppearanceLoader.rootPane - flickableDirection: Flickable.VerticalFlick - contentHeight: sidebarLayout.height + ColumnLayout { + id: contentLayout + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + spacing: Appearance.spacing.normal - StyledScrollBar.vertical: StyledScrollBar { - flickable: sidebarFlickable + MaterialIcon { + Layout.alignment: Qt.AlignHCenter | Qt.AlignTop + Layout.topMargin: 0 + text: "palette" + font.pointSize: Appearance.font.size.extraLarge * 3 + font.bold: true } - ColumnLayout { - id: sidebarLayout - anchors.left: parent.left - anchors.right: parent.right - spacing: Appearance.spacing.small - - readonly property bool allSectionsExpanded: - themeModeSection.expanded && - colorVariantSection.expanded && - colorSchemeSection.expanded && - animationsSection.expanded && - fontsSection.expanded && - scalesSection.expanded && - transparencySection.expanded && - borderSection.expanded && - backgroundSection.expanded - - RowLayout { - spacing: Appearance.spacing.smaller - - StyledText { - text: qsTr("Appearance") - font.pointSize: Appearance.font.size.large - font.weight: 500 - } - - Item { - Layout.fillWidth: true - } - - IconButton { - icon: sidebarLayout.allSectionsExpanded ? "unfold_less" : "unfold_more" - type: IconButton.Text - label.animate: true - onClicked: { - const shouldExpand = !sidebarLayout.allSectionsExpanded; - themeModeSection.expanded = shouldExpand; - colorVariantSection.expanded = shouldExpand; - colorSchemeSection.expanded = shouldExpand; - animationsSection.expanded = shouldExpand; - fontsSection.expanded = shouldExpand; - scalesSection.expanded = shouldExpand; - transparencySection.expanded = shouldExpand; - borderSection.expanded = shouldExpand; - backgroundSection.expanded = shouldExpand; - } - } + StyledText { + Layout.alignment: Qt.AlignHCenter + text: qsTr("Appearance Settings") + font.pointSize: Appearance.font.size.large + font.bold: true } - CollapsibleSection { - id: themeModeSection - title: qsTr("Theme mode") - description: qsTr("Light or dark theme") - showBackground: true - - SwitchRow { - label: qsTr("Dark mode") - checked: !Colours.currentLight - onToggled: checked => { - Colours.setMode(checked ? "dark" : "light"); - } - } + StyledText { + Layout.topMargin: Appearance.spacing.large + Layout.alignment: Qt.AlignHCenter + text: qsTr("Wallpaper") + font.pointSize: Appearance.font.size.extraLarge + font.weight: 600 } - CollapsibleSection { - id: colorVariantSection - title: qsTr("Color variant") - description: qsTr("Material theme variant") - showBackground: true - - ColumnLayout { - Layout.fillWidth: true - spacing: Appearance.spacing.small / 2 - - Repeater { - model: M3Variants.list - - delegate: StyledRect { - required property var modelData - - 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 - - StateLayer { - function onClicked(): void { - const variant = modelData.variant; - - // Optimistic update - set immediately for responsive UI - Schemes.currentVariant = variant; - Quickshell.execDetached(["caelestia", "scheme", "set", "-v", variant]); + StyledText { + Layout.alignment: Qt.AlignHCenter + text: qsTr("Select a wallpaper") + font.pointSize: Appearance.font.size.normal + color: Colours.palette.m3onSurfaceVariant + } - // Reload after a delay to confirm changes - Qt.callLater(() => { - reloadTimer.restart(); - }); + Item { + Layout.fillWidth: true + Layout.topMargin: Appearance.spacing.large + Layout.preferredHeight: wallpaperLoader.item ? wallpaperLoader.item.layoutPreferredHeight : 0 + + Loader { + id: wallpaperLoader + anchors.fill: parent + asynchronous: true + active: { + // Lazy load: only activate when: + // 1. Right pane is loaded AND + // 2. Appearance pane is active (index 3) or adjacent (for smooth transitions) + // This prevents loading all wallpapers when control center opens but appearance pane isn't visible + const isActive = root.session.activeIndex === 3; + const isAdjacent = Math.abs(root.session.activeIndex - 3) === 1; + // Access loader through SplitPaneLayout's rightLoader + const splitLayout = root.children[0]; + const loader = splitLayout && splitLayout.rightLoader ? splitLayout.rightLoader : null; + const shouldActivate = loader && loader.item !== null && (isActive || isAdjacent); + return shouldActivate; + } + + onStatusChanged: { + if (status === Loader.Error) { + console.error("[AppearancePane] Wallpaper loader error!"); + } + } + + // Stop lazy loading when loader becomes inactive + onActiveChanged: { + if (!active && wallpaperLoader.item) { + const container = wallpaperLoader.item; + // Access timer through wallpaperGrid + if (container && container.wallpaperGrid) { + const grid = container.wallpaperGrid; + if (grid.imageUpdateTimer) { + grid.imageUpdateTimer.stop(); } } - - Timer { - id: reloadTimer - interval: 300 - onTriggered: { - Schemes.reload(); + } + } + + sourceComponent: Item { + id: wallpaperGridContainer + property alias layoutPreferredHeight: wallpaperGrid.layoutPreferredHeight + + // Find and store reference to parent Flickable for scroll monitoring + property var parentFlickable: { + let item = parent; + while (item) { + if (item.flickableDirection !== undefined) { + return item; } + item = item.parent; } - - RowLayout { - id: variantRow - - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.normal - - spacing: Appearance.spacing.normal - - MaterialIcon { - text: modelData.icon - font.pointSize: Appearance.font.size.large - fill: modelData.variant === Schemes.currentVariant ? 1 : 0 + return null; + } + + // Cleanup when component is destroyed + Component.onDestruction: { + if (wallpaperGrid) { + if (wallpaperGrid.scrollCheckTimer) { + wallpaperGrid.scrollCheckTimer.stop(); } - - StyledText { - Layout.fillWidth: true - text: modelData.name - font.weight: modelData.variant === Schemes.currentVariant ? 500 : 400 + wallpaperGrid._expansionInProgress = false; + } + } + + // Lazy loading model: loads one image at a time, only when touching bottom + // This prevents GridView from creating all delegates at once + QtObject { + id: lazyModel + + property var sourceList: null + property int loadedCount: 0 // Total items available to load + property int visibleCount: 0 // Items actually exposed to GridView (only visible + buffer) + property int totalCount: 0 + + function initialize(list) { + sourceList = list; + totalCount = list ? list.length : 0; + // Start with enough items to fill the initial viewport (~3 rows) + const initialRows = 3; + const cols = wallpaperGrid.columnsCount > 0 ? wallpaperGrid.columnsCount : 3; + const initialCount = Math.min(initialRows * cols, totalCount); + loadedCount = initialCount; + visibleCount = initialCount; + } + + function loadOneRow() { + if (loadedCount < totalCount) { + const cols = wallpaperGrid.columnsCount > 0 ? wallpaperGrid.columnsCount : 1; + const itemsToLoad = Math.min(cols, totalCount - loadedCount); + loadedCount += itemsToLoad; } - - MaterialIcon { - visible: modelData.variant === Schemes.currentVariant - text: "check" - color: Colours.palette.m3primary - font.pointSize: Appearance.font.size.large + } + + function updateVisibleCount(neededCount) { + // Always round up to complete rows to avoid incomplete rows in the grid + const cols = wallpaperGrid.columnsCount > 0 ? wallpaperGrid.columnsCount : 1; + const maxVisible = Math.min(neededCount, loadedCount); + const rows = Math.ceil(maxVisible / cols); + const newVisibleCount = Math.min(rows * cols, loadedCount); + + if (newVisibleCount > visibleCount) { + visibleCount = newVisibleCount; } } - - implicitHeight: variantRow.implicitHeight + Appearance.padding.normal * 2 } - } - } - } - - CollapsibleSection { - id: colorSchemeSection - title: qsTr("Color scheme") - description: qsTr("Available color schemes") - showBackground: true - - ColumnLayout { - Layout.fillWidth: true - spacing: Appearance.spacing.small / 2 - - Repeater { - model: Schemes.list - - delegate: StyledRect { - required property var modelData - - Layout.fillWidth: true - - readonly property string schemeKey: `${modelData.name} ${modelData.flavour}` - readonly property bool isCurrent: schemeKey === Schemes.currentScheme - - color: Qt.alpha(Colours.tPalette.m3surfaceContainer, isCurrent ? Colours.tPalette.m3surfaceContainer.a : 0) - radius: Appearance.rounding.normal - border.width: isCurrent ? 1 : 0 - border.color: Colours.palette.m3primary - - StateLayer { - function onClicked(): void { - const name = modelData.name; - const flavour = modelData.flavour; - const schemeKey = `${name} ${flavour}`; - - // Optimistic update - set immediately for responsive UI - Schemes.currentScheme = schemeKey; - Quickshell.execDetached(["caelestia", "scheme", "set", "-n", name, "-f", flavour]); - - // Reload after a delay to confirm changes - Qt.callLater(() => { - reloadTimer.restart(); - }); + + GridView { + id: wallpaperGrid + anchors.fill: parent + + property int _delegateCount: 0 + + readonly property int minCellWidth: 200 + Appearance.spacing.normal + readonly property int columnsCount: Math.max(1, Math.floor(parent.width / minCellWidth)) + + // Height based on visible items only - prevents GridView from creating all delegates + readonly property int layoutPreferredHeight: { + if (!lazyModel || lazyModel.visibleCount === 0 || columnsCount === 0) { + return 0; } + const calculated = Math.ceil(lazyModel.visibleCount / columnsCount) * cellHeight; + return calculated; } + + height: layoutPreferredHeight + cellWidth: width / columnsCount + cellHeight: 140 + Appearance.spacing.normal + + leftMargin: 0 + rightMargin: 0 + topMargin: 0 + bottomMargin: 0 + // Use ListModel for incremental updates to prevent flashing when new items are added + ListModel { + id: wallpaperListModel + } + + model: wallpaperListModel + + Connections { + target: lazyModel + function onVisibleCountChanged(): void { + if (!lazyModel || !lazyModel.sourceList) return; + + const newCount = lazyModel.visibleCount; + const currentCount = wallpaperListModel.count; + + // Only append new items - never remove or replace existing ones + if (newCount > currentCount) { + const flickable = wallpaperGridContainer.parentFlickable; + const oldScrollY = flickable ? flickable.contentY : 0; + + for (let i = currentCount; i < newCount; i++) { + wallpaperListModel.append({modelData: lazyModel.sourceList[i]}); + } + + // Preserve scroll position after model update + if (flickable) { + Qt.callLater(function() { + if (Math.abs(flickable.contentY - oldScrollY) < 1) { + flickable.contentY = oldScrollY; + } + }); + } + } + } + } + + Component.onCompleted: { + Qt.callLater(function() { + const isActive = root.session.activeIndex === 3; + if (width > 0 && parent && parent.visible && isActive && Wallpapers.list) { + lazyModel.initialize(Wallpapers.list); + wallpaperListModel.clear(); + for (let i = 0; i < lazyModel.visibleCount; i++) { + wallpaperListModel.append({modelData: lazyModel.sourceList[i]}); + } + } + }); + } + + Connections { + target: root.session + function onActiveIndexChanged(): void { + const isActive = root.session.activeIndex === 3; + + // Stop lazy loading when switching away from appearance pane + if (!isActive) { + if (scrollCheckTimer) { + scrollCheckTimer.stop(); + } + if (wallpaperGrid) { + wallpaperGrid._expansionInProgress = false; + } + return; + } + + // Initialize if needed when switching to appearance pane + if (isActive && width > 0 && !lazyModel.sourceList && parent && parent.visible && Wallpapers.list) { + lazyModel.initialize(Wallpapers.list); + wallpaperListModel.clear(); + for (let i = 0; i < lazyModel.visibleCount; i++) { + wallpaperListModel.append({modelData: lazyModel.sourceList[i]}); + } + } + } + } + + onWidthChanged: { + const isActive = root.session.activeIndex === 3; + if (width > 0 && !lazyModel.sourceList && parent && parent.visible && isActive && Wallpapers.list) { + lazyModel.initialize(Wallpapers.list); + wallpaperListModel.clear(); + for (let i = 0; i < lazyModel.visibleCount; i++) { + wallpaperListModel.append({modelData: lazyModel.sourceList[i]}); + } + } + } + + // Force true lazy loading: only create delegates for visible items + displayMarginBeginning: 0 + displayMarginEnd: 0 + cacheBuffer: 0 + + // Debounce expansion to avoid too frequent checks + property bool _expansionInProgress: false + + Connections { + target: wallpaperGridContainer.parentFlickable + function onContentYChanged(): void { + // Don't process scroll events if appearance pane is not active + const isActive = root.session.activeIndex === 3; + if (!isActive) return; + + if (!lazyModel || !lazyModel.sourceList || lazyModel.loadedCount >= lazyModel.totalCount || wallpaperGrid._expansionInProgress) { + return; + } + + const flickable = wallpaperGridContainer.parentFlickable; + if (!flickable) return; + + const gridY = wallpaperGridContainer.y; + const scrollY = flickable.contentY; + const viewportHeight = flickable.height; + + const topY = scrollY - gridY; + const bottomY = scrollY + viewportHeight - gridY; + + if (bottomY < 0) return; + + const topRow = Math.max(0, Math.floor(topY / wallpaperGrid.cellHeight)); + const bottomRow = Math.floor(bottomY / wallpaperGrid.cellHeight); + + // Update visible count with 1 row buffer ahead + const bufferRows = 1; + const neededBottomRow = bottomRow + bufferRows; + const neededCount = Math.min((neededBottomRow + 1) * wallpaperGrid.columnsCount, lazyModel.loadedCount); + lazyModel.updateVisibleCount(neededCount); + + // Load more when we're within 1 row of running out of loaded items + const loadedRows = Math.ceil(lazyModel.loadedCount / wallpaperGrid.columnsCount); + const rowsRemaining = loadedRows - (bottomRow + 1); + + if (rowsRemaining <= 1 && lazyModel.loadedCount < lazyModel.totalCount) { + if (!wallpaperGrid._expansionInProgress) { + wallpaperGrid._expansionInProgress = true; + lazyModel.loadOneRow(); + Qt.callLater(function() { + wallpaperGrid._expansionInProgress = false; + }); + } + } + } + } + + // Fallback timer to check scroll position periodically Timer { - id: reloadTimer - interval: 300 + id: scrollCheckTimer + interval: 100 + running: { + const isActive = root.session.activeIndex === 3; + return isActive && lazyModel && lazyModel.sourceList && lazyModel.loadedCount < lazyModel.totalCount; + } + repeat: true onTriggered: { - Schemes.reload(); + // Double-check that appearance pane is still active + const isActive = root.session.activeIndex === 3; + if (!isActive) { + stop(); + return; + } + + const flickable = wallpaperGridContainer.parentFlickable; + if (!flickable || !lazyModel || !lazyModel.sourceList) return; + + const gridY = wallpaperGridContainer.y; + const scrollY = flickable.contentY; + const viewportHeight = flickable.height; + + const topY = scrollY - gridY; + const bottomY = scrollY + viewportHeight - gridY; + if (bottomY < 0) return; + + const topRow = Math.max(0, Math.floor(topY / wallpaperGrid.cellHeight)); + const bottomRow = Math.floor(bottomY / wallpaperGrid.cellHeight); + + const bufferRows = 1; + const neededBottomRow = bottomRow + bufferRows; + const neededCount = Math.min((neededBottomRow + 1) * wallpaperGrid.columnsCount, lazyModel.loadedCount); + lazyModel.updateVisibleCount(neededCount); + + // Load more when we're within 1 row of running out of loaded items + const loadedRows = Math.ceil(lazyModel.loadedCount / wallpaperGrid.columnsCount); + const rowsRemaining = loadedRows - (bottomRow + 1); + + if (rowsRemaining <= 1 && lazyModel.loadedCount < lazyModel.totalCount) { + if (!wallpaperGrid._expansionInProgress) { + wallpaperGrid._expansionInProgress = true; + lazyModel.loadOneRow(); + Qt.callLater(function() { + wallpaperGrid._expansionInProgress = false; + }); + } + } } } + + + // Parent Flickable handles scrolling + interactive: false - RowLayout { - id: schemeRow - - anchors.fill: parent - anchors.margins: Appearance.padding.normal - - spacing: Appearance.spacing.normal - StyledRect { - id: preview + delegate: Item { + required property var modelData - Layout.alignment: Qt.AlignVCenter + width: wallpaperGrid.cellWidth + height: wallpaperGrid.cellHeight - border.width: 1 - border.color: Qt.alpha(`#${modelData.colours?.outline}`, 0.5) + readonly property bool isCurrent: modelData.path === Wallpapers.actualCurrent + readonly property real itemMargin: Appearance.spacing.normal / 2 + readonly property real itemRadius: Appearance.rounding.normal + + Component.onCompleted: { + wallpaperGrid._delegateCount++; + } - color: `#${modelData.colours?.surface}` - radius: Appearance.rounding.full - implicitWidth: iconPlaceholder.implicitWidth - implicitHeight: iconPlaceholder.implicitWidth + StateLayer { + anchors.fill: parent + anchors.leftMargin: itemMargin + anchors.rightMargin: itemMargin + anchors.topMargin: itemMargin + anchors.bottomMargin: itemMargin + radius: itemRadius - MaterialIcon { - id: iconPlaceholder - visible: false - text: "circle" - font.pointSize: Appearance.font.size.large - } + function onClicked(): void { + Wallpapers.setWallpaper(modelData.path); + } + } - Item { - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.right: parent.right + StyledClippingRect { + id: image - implicitWidth: parent.implicitWidth / 2 - clip: true + anchors.fill: parent + anchors.leftMargin: itemMargin + anchors.rightMargin: itemMargin + anchors.topMargin: itemMargin + anchors.bottomMargin: itemMargin + color: Colours.tPalette.m3surfaceContainer + radius: itemRadius + antialiasing: true + layer.enabled: true + layer.smooth: true - StyledRect { - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.right: parent.right + CachingImage { + id: cachingImage - implicitWidth: preview.implicitWidth - color: `#${modelData.colours?.primary}` - radius: Appearance.rounding.full - } - } - } + path: modelData.path + anchors.fill: parent + fillMode: Image.PreserveAspectCrop + cache: true + visible: opacity > 0 + antialiasing: true + smooth: true - Column { - Layout.fillWidth: true - spacing: 0 + opacity: status === Image.Ready ? 1 : 0 - StyledText { - text: modelData.flavour ?? "" - font.pointSize: Appearance.font.size.normal - } + Behavior on opacity { + NumberAnimation { + duration: 1000 + easing.type: Easing.OutQuad + } + } + } - StyledText { - text: modelData.name ?? "" - font.pointSize: Appearance.font.size.small - color: Colours.palette.m3outline + // Fallback if CachingImage fails to load + Image { + id: fallbackImage - elide: Text.ElideRight - anchors.left: parent.left - anchors.right: parent.right - } - } + anchors.fill: parent + source: fallbackTimer.triggered && cachingImage.status !== Image.Ready ? modelData.path : "" + asynchronous: true + fillMode: Image.PreserveAspectCrop + cache: true + visible: opacity > 0 + antialiasing: true + smooth: true - Loader { - active: isCurrent - asynchronous: true + opacity: status === Image.Ready && cachingImage.status !== Image.Ready ? 1 : 0 - sourceComponent: MaterialIcon { - text: "check" - color: Colours.palette.m3onSurfaceVariant - font.pointSize: Appearance.font.size.large - } + Behavior on opacity { + NumberAnimation { + duration: 1000 + easing.type: Easing.OutQuad } } + } - implicitHeight: schemeRow.implicitHeight + Appearance.padding.normal * 2 + Timer { + id: fallbackTimer + + property bool triggered: false + interval: 800 + running: cachingImage.status === Image.Loading || cachingImage.status === Image.Null + onTriggered: triggered = true } - } - } - } - CollapsibleSection { - id: animationsSection - title: qsTr("Animations") - showBackground: true + // Gradient overlay for filename + Rectangle { + id: filenameOverlay - SectionContainer { - contentSpacing: Appearance.spacing.normal + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom - ColumnLayout { - Layout.fillWidth: true - spacing: Appearance.spacing.small + implicitHeight: filenameText.implicitHeight + Appearance.padding.normal * 1.5 + radius: 0 + + gradient: Gradient { + GradientStop { + position: 0.0 + color: Qt.rgba(Colours.palette.m3surfaceContainer.r, + Colours.palette.m3surfaceContainer.g, + Colours.palette.m3surfaceContainer.b, 0) + } + GradientStop { + position: 0.3 + color: Qt.rgba(Colours.palette.m3surfaceContainer.r, + Colours.palette.m3surfaceContainer.g, + Colours.palette.m3surfaceContainer.b, 0.7) + } + GradientStop { + position: 0.6 + color: Qt.rgba(Colours.palette.m3surfaceContainer.r, + Colours.palette.m3surfaceContainer.g, + Colours.palette.m3surfaceContainer.b, 0.9) + } + GradientStop { + position: 1.0 + color: Qt.rgba(Colours.palette.m3surfaceContainer.r, + Colours.palette.m3surfaceContainer.g, + Colours.palette.m3surfaceContainer.b, 0.95) + } + } - RowLayout { - Layout.fillWidth: true - spacing: Appearance.spacing.normal + opacity: 0 - StyledText { - text: qsTr("Animation duration scale") - font.pointSize: Appearance.font.size.normal + Behavior on opacity { + NumberAnimation { + duration: 1000 + easing.type: Easing.OutCubic + } } - Item { - Layout.fillWidth: true + Component.onCompleted: { + opacity = 1; } + } + } - StyledRect { - Layout.preferredWidth: 70 - implicitHeight: animDurationsInput.implicitHeight + Appearance.padding.small * 2 - color: animDurationsInputHover.containsMouse || animDurationsInput.activeFocus - ? Colours.layer(Colours.palette.m3surfaceContainer, 3) - : Colours.layer(Colours.palette.m3surfaceContainer, 2) - radius: Appearance.rounding.small - border.width: 1 - border.color: animDurationsInput.activeFocus - ? Colours.palette.m3primary - : Qt.alpha(Colours.palette.m3outline, 0.3) + Rectangle { + anchors.fill: parent + anchors.leftMargin: itemMargin + anchors.rightMargin: itemMargin + anchors.topMargin: itemMargin + anchors.bottomMargin: itemMargin + color: "transparent" + radius: itemRadius + border.width + border.width: isCurrent ? 2 : 0 + border.color: Colours.palette.m3primary + antialiasing: true + smooth: true - Behavior on color { CAnim {} } - Behavior on border.color { CAnim {} } + Behavior on border.width { + NumberAnimation { + duration: 150 + easing.type: Easing.OutQuad + } + } - MouseArea { - id: animDurationsInputHover - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.IBeamCursor - acceptedButtons: Qt.NoButton - } + MaterialIcon { + anchors.right: parent.right + anchors.top: parent.top + anchors.margins: Appearance.padding.small - StyledTextField { - id: animDurationsInput - anchors.centerIn: parent - width: parent.width - Appearance.padding.normal - horizontalAlignment: TextInput.AlignHCenter - validator: DoubleValidator { bottom: 0.1; top: 5.0 } - - Component.onCompleted: { - text = (rootPane.animDurationsScale).toFixed(1); - } - - onTextChanged: { - if (activeFocus) { - const val = parseFloat(text); - if (!isNaN(val) && val >= 0.1 && val <= 5.0) { - rootPane.animDurationsScale = val; - rootPane.saveConfig(); - } - } - } - onEditingFinished: { - const val = parseFloat(text); - if (isNaN(val) || val < 0.1 || val > 5.0) { - text = (rootPane.animDurationsScale).toFixed(1); - } - } - } - } + visible: isCurrent + text: "check_circle" + color: Colours.palette.m3primary + font.pointSize: Appearance.font.size.large + } + } - StyledText { - text: "×" - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.normal - } + StyledText { + id: filenameText + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + anchors.leftMargin: Appearance.padding.normal + Appearance.spacing.normal / 2 + anchors.rightMargin: Appearance.padding.normal + Appearance.spacing.normal / 2 + anchors.bottomMargin: Appearance.padding.normal + + readonly property string fileName: { + const path = modelData.relativePath || ""; + const parts = path.split("/"); + return parts.length > 0 ? parts[parts.length - 1] : path; } - StyledSlider { - id: animDurationsSlider + text: fileName + font.pointSize: Appearance.font.size.smaller + font.weight: 500 + color: isCurrent ? Colours.palette.m3primary : Colours.palette.m3onSurface + elide: Text.ElideMiddle + maximumLineCount: 1 + horizontalAlignment: Text.AlignHCenter - Layout.fillWidth: true - implicitHeight: Appearance.padding.normal * 3 + opacity: 0 - from: 0.1 - to: 5.0 - value: rootPane.animDurationsScale - onMoved: { - rootPane.animDurationsScale = animDurationsSlider.value; - if (!animDurationsInput.activeFocus) { - animDurationsInput.text = (animDurationsSlider.value).toFixed(1); - } - rootPane.saveConfig(); + Behavior on opacity { + NumberAnimation { + duration: 1000 + easing.type: Easing.OutCubic } } + + Component.onCompleted: { + opacity = 1; + } + } + } + } } } } + } + } + } - CollapsibleSection { - id: fontsSection - title: qsTr("Fonts") - showBackground: true - - CollapsibleSection { - id: materialFontSection - title: qsTr("Material font family") - expanded: true - showBackground: true - nested: true - - Loader { - id: materialFontLoader - Layout.fillWidth: true - Layout.preferredHeight: item ? Math.min(item.contentHeight, 300) : 0 - asynchronous: true - active: materialFontSection.expanded - - sourceComponent: StyledListView { - id: materialFontList - property alias contentHeight: materialFontList.contentHeight - - clip: true - spacing: Appearance.spacing.small / 2 - model: Qt.fontFamilies() - - StyledScrollBar.vertical: StyledScrollBar { - flickable: materialFontList - } - - delegate: StyledRect { - required property string modelData - required property int index + SplitPaneLayout { + anchors.fill: parent - width: ListView.view.width + leftContent: Component { - 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 + StyledFlickable { + id: sidebarFlickable + readonly property var rootPane: root + flickableDirection: Flickable.VerticalFlick + contentHeight: sidebarLayout.height - StateLayer { - function onClicked(): void { - rootPane.fontFamilyMaterial = modelData; - rootPane.saveConfig(); - } - } - RowLayout { - id: fontFamilyMaterialRow + StyledScrollBar.vertical: StyledScrollBar { + flickable: sidebarFlickable + } - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.normal + ColumnLayout { + id: sidebarLayout + anchors.left: parent.left + anchors.right: parent.right + spacing: Appearance.spacing.small - spacing: Appearance.spacing.normal + readonly property bool allSectionsExpanded: + themeModeSection.expanded && + colorVariantSection.expanded && + colorSchemeSection.expanded && + animationsSection.expanded && + fontsSection.expanded && + scalesSection.expanded && + transparencySection.expanded && + borderSection.expanded && + backgroundSection.expanded - StyledText { - text: modelData - font.pointSize: Appearance.font.size.normal - } + RowLayout { + spacing: Appearance.spacing.smaller - Item { - Layout.fillWidth: true - } + StyledText { + text: qsTr("Appearance") + font.pointSize: Appearance.font.size.large + font.weight: 500 + } - Loader { - active: isCurrent - asynchronous: true + Item { + Layout.fillWidth: true + } - sourceComponent: MaterialIcon { - text: "check" - color: Colours.palette.m3onSurfaceVariant - font.pointSize: Appearance.font.size.large - } - } - } + IconButton { + icon: sidebarLayout.allSectionsExpanded ? "unfold_less" : "unfold_more" + type: IconButton.Text + label.animate: true + onClicked: { + const shouldExpand = !sidebarLayout.allSectionsExpanded; + themeModeSection.expanded = shouldExpand; + colorVariantSection.expanded = shouldExpand; + colorSchemeSection.expanded = shouldExpand; + animationsSection.expanded = shouldExpand; + fontsSection.expanded = shouldExpand; + scalesSection.expanded = shouldExpand; + transparencySection.expanded = shouldExpand; + borderSection.expanded = shouldExpand; + backgroundSection.expanded = shouldExpand; + } + } + } - implicitHeight: fontFamilyMaterialRow.implicitHeight + Appearance.padding.normal * 2 - } - } + CollapsibleSection { + id: themeModeSection + title: qsTr("Theme mode") + description: qsTr("Light or dark theme") + showBackground: true + SwitchRow { + label: qsTr("Dark mode") + checked: !Colours.currentLight + onToggled: checked => { + Colours.setMode(checked ? "dark" : "light"); } } + } - CollapsibleSection { - id: monoFontSection - title: qsTr("Monospace font family") - expanded: false - showBackground: true - nested: true + CollapsibleSection { + id: colorVariantSection + title: qsTr("Color variant") + description: qsTr("Material theme variant") + showBackground: true - Loader { - Layout.fillWidth: true - Layout.preferredHeight: item ? Math.min(item.contentHeight, 300) : 0 - asynchronous: true - active: monoFontSection.expanded + ColumnLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.small / 2 - sourceComponent: StyledListView { - id: monoFontList - property alias contentHeight: monoFontList.contentHeight + Repeater { + model: M3Variants.list - clip: true - spacing: Appearance.spacing.small / 2 - model: Qt.fontFamilies() + delegate: StyledRect { + required property var modelData - StyledScrollBar.vertical: StyledScrollBar { - flickable: monoFontList - } + Layout.fillWidth: true - delegate: StyledRect { - required property string modelData - required property int index + 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 - width: ListView.view.width + StateLayer { + function onClicked(): void { + const variant = modelData.variant; - 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 + // Optimistic update - set immediately for responsive UI + Schemes.currentVariant = variant; + Quickshell.execDetached(["caelestia", "scheme", "set", "-v", variant]); - StateLayer { - function onClicked(): void { - rootPane.fontFamilyMono = modelData; - rootPane.saveConfig(); - } + // Reload after a delay to confirm changes + Qt.callLater(() => { + reloadTimer.restart(); + }); + } + } + + Timer { + id: reloadTimer + interval: 300 + onTriggered: { + Schemes.reload(); } + } - RowLayout { - id: fontFamilyMonoRow - - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.normal - - spacing: Appearance.spacing.normal + RowLayout { + id: variantRow - StyledText { - text: modelData - font.pointSize: Appearance.font.size.normal - } + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.normal - Item { - Layout.fillWidth: true - } + spacing: Appearance.spacing.normal - Loader { - active: isCurrent - asynchronous: true + MaterialIcon { + text: modelData.icon + font.pointSize: Appearance.font.size.large + fill: modelData.variant === Schemes.currentVariant ? 1 : 0 + } - sourceComponent: MaterialIcon { - text: "check" - color: Colours.palette.m3onSurfaceVariant - font.pointSize: Appearance.font.size.large - } - } + StyledText { + Layout.fillWidth: true + text: modelData.name + font.weight: modelData.variant === Schemes.currentVariant ? 500 : 400 } - implicitHeight: fontFamilyMonoRow.implicitHeight + Appearance.padding.normal * 2 + 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 } } } + } - CollapsibleSection { - id: sansFontSection - title: qsTr("Sans-serif font family") - expanded: false - showBackground: true - nested: true - - Loader { - Layout.fillWidth: true - Layout.preferredHeight: item ? Math.min(item.contentHeight, 300) : 0 - asynchronous: true - active: sansFontSection.expanded - - sourceComponent: StyledListView { - id: sansFontList - property alias contentHeight: sansFontList.contentHeight - - clip: true - spacing: Appearance.spacing.small / 2 - model: Qt.fontFamilies() + CollapsibleSection { + id: colorSchemeSection + title: qsTr("Color scheme") + description: qsTr("Available color schemes") + showBackground: true - StyledScrollBar.vertical: StyledScrollBar { - flickable: sansFontList - } + ColumnLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.small / 2 - delegate: StyledRect { - required property string modelData - required property int index + Repeater { + model: Schemes.list - width: ListView.view.width + delegate: StyledRect { + required property var modelData - 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 + Layout.fillWidth: true - StateLayer { - function onClicked(): void { - rootPane.fontFamilySans = modelData; - rootPane.saveConfig(); - } - } + readonly property string schemeKey: `${modelData.name} ${modelData.flavour}` + readonly property bool isCurrent: schemeKey === Schemes.currentScheme - RowLayout { - id: fontFamilySansRow + 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 - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.normal + StateLayer { + function onClicked(): void { + const name = modelData.name; + const flavour = modelData.flavour; + const schemeKey = `${name} ${flavour}`; - spacing: Appearance.spacing.normal + // Optimistic update - set immediately for responsive UI + Schemes.currentScheme = schemeKey; + Quickshell.execDetached(["caelestia", "scheme", "set", "-n", name, "-f", flavour]); - StyledText { - text: modelData - font.pointSize: Appearance.font.size.normal - } + // Reload after a delay to confirm changes + Qt.callLater(() => { + reloadTimer.restart(); + }); + } + } - Item { - Layout.fillWidth: true - } + Timer { + id: reloadTimer + interval: 300 + onTriggered: { + Schemes.reload(); + } + } - Loader { - active: isCurrent - asynchronous: true + RowLayout { + id: schemeRow - sourceComponent: MaterialIcon { - text: "check" - color: Colours.palette.m3onSurfaceVariant - font.pointSize: Appearance.font.size.large - } - } - } + anchors.fill: parent + anchors.margins: Appearance.padding.normal - implicitHeight: fontFamilySansRow.implicitHeight + Appearance.padding.normal * 2 - } - } - } - } + spacing: Appearance.spacing.normal - SectionContainer { - contentSpacing: Appearance.spacing.normal + StyledRect { + id: preview - ColumnLayout { - Layout.fillWidth: true - spacing: Appearance.spacing.small + Layout.alignment: Qt.AlignVCenter - RowLayout { - Layout.fillWidth: true - spacing: Appearance.spacing.normal + border.width: 1 + border.color: Qt.alpha(`#${modelData.colours?.outline}`, 0.5) - StyledText { - text: qsTr("Font size scale") - font.pointSize: Appearance.font.size.normal - } + color: `#${modelData.colours?.surface}` + radius: Appearance.rounding.full + implicitWidth: iconPlaceholder.implicitWidth + implicitHeight: iconPlaceholder.implicitWidth - Item { - Layout.fillWidth: true - } + MaterialIcon { + id: iconPlaceholder + visible: false + text: "circle" + font.pointSize: Appearance.font.size.large + } - StyledRect { - Layout.preferredWidth: 70 - implicitHeight: fontSizeInput.implicitHeight + Appearance.padding.small * 2 - color: fontSizeInputHover.containsMouse || fontSizeInput.activeFocus - ? Colours.layer(Colours.palette.m3surfaceContainer, 3) - : Colours.layer(Colours.palette.m3surfaceContainer, 2) - radius: Appearance.rounding.small - border.width: 1 - border.color: fontSizeInput.activeFocus - ? Colours.palette.m3primary - : Qt.alpha(Colours.palette.m3outline, 0.3) + Item { + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.right: parent.right - Behavior on color { CAnim {} } - Behavior on border.color { CAnim {} } + implicitWidth: parent.implicitWidth / 2 + clip: true - MouseArea { - id: fontSizeInputHover - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.IBeamCursor - acceptedButtons: Qt.NoButton - } + StyledRect { + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.right: parent.right - StyledTextField { - id: fontSizeInput - anchors.centerIn: parent - width: parent.width - Appearance.padding.normal - horizontalAlignment: TextInput.AlignHCenter - validator: DoubleValidator { bottom: 0.7; top: 1.5 } - - Component.onCompleted: { - text = (rootPane.fontSizeScale).toFixed(1); - } - - onTextChanged: { - if (activeFocus) { - const val = parseFloat(text); - if (!isNaN(val) && val >= 0.7 && val <= 1.5) { - rootPane.fontSizeScale = val; - rootPane.saveConfig(); - } + implicitWidth: preview.implicitWidth + color: `#${modelData.colours?.primary}` + radius: Appearance.rounding.full } } - onEditingFinished: { - const val = parseFloat(text); - if (isNaN(val) || val < 0.7 || val > 1.5) { - text = (rootPane.fontSizeScale).toFixed(1); - } + } + + Column { + Layout.fillWidth: true + spacing: 0 + + StyledText { + text: modelData.flavour ?? "" + font.pointSize: Appearance.font.size.normal } - } - } - StyledText { - text: "×" - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.normal - } - } + StyledText { + text: modelData.name ?? "" + font.pointSize: Appearance.font.size.small + color: Colours.palette.m3outline - StyledSlider { - id: fontSizeSlider + elide: Text.ElideRight + anchors.left: parent.left + anchors.right: parent.right + } + } - Layout.fillWidth: true - implicitHeight: Appearance.padding.normal * 3 + Loader { + active: isCurrent + asynchronous: true - from: 0.7 - to: 1.5 - value: rootPane.fontSizeScale - onMoved: { - rootPane.fontSizeScale = fontSizeSlider.value; - if (!fontSizeInput.activeFocus) { - fontSizeInput.text = (fontSizeSlider.value).toFixed(1); + sourceComponent: MaterialIcon { + text: "check" + color: Colours.palette.m3onSurfaceVariant + font.pointSize: Appearance.font.size.large + } } - rootPane.saveConfig(); } + + implicitHeight: schemeRow.implicitHeight + Appearance.padding.normal * 2 } } } } CollapsibleSection { - id: scalesSection - title: qsTr("Scales") + id: animationsSection + title: qsTr("Animations") showBackground: true SectionContainer { @@ -881,7 +1005,7 @@ RowLayout { spacing: Appearance.spacing.normal StyledText { - text: qsTr("Padding scale") + text: qsTr("Animation duration scale") font.pointSize: Appearance.font.size.normal } @@ -891,13 +1015,13 @@ RowLayout { StyledRect { Layout.preferredWidth: 70 - implicitHeight: paddingInput.implicitHeight + Appearance.padding.small * 2 - color: paddingInputHover.containsMouse || paddingInput.activeFocus + implicitHeight: animDurationsInput.implicitHeight + Appearance.padding.small * 2 + color: animDurationsInputHover.containsMouse || animDurationsInput.activeFocus ? Colours.layer(Colours.palette.m3surfaceContainer, 3) : Colours.layer(Colours.palette.m3surfaceContainer, 2) radius: Appearance.rounding.small border.width: 1 - border.color: paddingInput.activeFocus + border.color: animDurationsInput.activeFocus ? Colours.palette.m3primary : Qt.alpha(Colours.palette.m3outline, 0.3) @@ -905,7 +1029,7 @@ RowLayout { Behavior on border.color { CAnim {} } MouseArea { - id: paddingInputHover + id: animDurationsInputHover anchors.fill: parent hoverEnabled: true cursorShape: Qt.IBeamCursor @@ -913,29 +1037,29 @@ RowLayout { } StyledTextField { - id: paddingInput + id: animDurationsInput anchors.centerIn: parent width: parent.width - Appearance.padding.normal horizontalAlignment: TextInput.AlignHCenter - validator: DoubleValidator { bottom: 0.5; top: 2.0 } + validator: DoubleValidator { bottom: 0.1; top: 5.0 } Component.onCompleted: { - text = (rootPane.paddingScale).toFixed(1); + text = (rootPane.animDurationsScale).toFixed(1); } onTextChanged: { if (activeFocus) { const val = parseFloat(text); - if (!isNaN(val) && val >= 0.5 && val <= 2.0) { - rootPane.paddingScale = val; + if (!isNaN(val) && val >= 0.1 && val <= 5.0) { + rootPane.animDurationsScale = val; rootPane.saveConfig(); } } } onEditingFinished: { const val = parseFloat(text); - if (isNaN(val) || val < 0.5 || val > 2.0) { - text = (rootPane.paddingScale).toFixed(1); + if (isNaN(val) || val < 0.1 || val > 5.0) { + text = (rootPane.animDurationsScale).toFixed(1); } } } @@ -949,332 +1073,271 @@ RowLayout { } StyledSlider { - id: paddingSlider + id: animDurationsSlider Layout.fillWidth: true implicitHeight: Appearance.padding.normal * 3 - from: 0.5 - to: 2.0 - value: rootPane.paddingScale + from: 0.1 + to: 5.0 + value: rootPane.animDurationsScale onMoved: { - rootPane.paddingScale = paddingSlider.value; - if (!paddingInput.activeFocus) { - paddingInput.text = (paddingSlider.value).toFixed(1); + rootPane.animDurationsScale = animDurationsSlider.value; + if (!animDurationsInput.activeFocus) { + animDurationsInput.text = (animDurationsSlider.value).toFixed(1); } rootPane.saveConfig(); } } } } + } - SectionContainer { - contentSpacing: Appearance.spacing.normal + CollapsibleSection { + id: fontsSection + title: qsTr("Fonts") + showBackground: true - ColumnLayout { + CollapsibleSection { + id: materialFontSection + title: qsTr("Material font family") + expanded: true + showBackground: true + nested: true + + Loader { + id: materialFontLoader Layout.fillWidth: true - spacing: Appearance.spacing.small + Layout.preferredHeight: item ? Math.min(item.contentHeight, 300) : 0 + asynchronous: true + active: materialFontSection.expanded - RowLayout { - Layout.fillWidth: true - spacing: Appearance.spacing.normal + sourceComponent: StyledListView { + id: materialFontList + property alias contentHeight: materialFontList.contentHeight - StyledText { - text: qsTr("Rounding scale") - font.pointSize: Appearance.font.size.normal - } + clip: true + spacing: Appearance.spacing.small / 2 + model: Qt.fontFamilies() - Item { - Layout.fillWidth: true + StyledScrollBar.vertical: StyledScrollBar { + flickable: materialFontList } - StyledRect { - Layout.preferredWidth: 70 - implicitHeight: roundingInput.implicitHeight + Appearance.padding.small * 2 - color: roundingInputHover.containsMouse || roundingInput.activeFocus - ? Colours.layer(Colours.palette.m3surfaceContainer, 3) - : Colours.layer(Colours.palette.m3surfaceContainer, 2) - radius: Appearance.rounding.small - border.width: 1 - border.color: roundingInput.activeFocus - ? Colours.palette.m3primary - : Qt.alpha(Colours.palette.m3outline, 0.3) + delegate: StyledRect { + required property string modelData + required property int index - Behavior on color { CAnim {} } - Behavior on border.color { CAnim {} } + width: ListView.view.width - MouseArea { - id: roundingInputHover - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.IBeamCursor - acceptedButtons: Qt.NoButton + 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(); + } } - StyledTextField { - id: roundingInput - anchors.centerIn: parent - width: parent.width - Appearance.padding.normal - horizontalAlignment: TextInput.AlignHCenter - validator: DoubleValidator { bottom: 0.1; top: 5.0 } - - Component.onCompleted: { - text = (rootPane.roundingScale).toFixed(1); + RowLayout { + id: fontFamilyMaterialRow + + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.normal + + spacing: Appearance.spacing.normal + + StyledText { + text: modelData + font.pointSize: Appearance.font.size.normal } - - onTextChanged: { - if (activeFocus) { - const val = parseFloat(text); - if (!isNaN(val) && val >= 0.1 && val <= 5.0) { - rootPane.roundingScale = val; - rootPane.saveConfig(); - } - } + + Item { + Layout.fillWidth: true } - onEditingFinished: { - const val = parseFloat(text); - if (isNaN(val) || val < 0.1 || val > 5.0) { - text = (rootPane.roundingScale).toFixed(1); + + Loader { + active: isCurrent + asynchronous: true + + sourceComponent: MaterialIcon { + text: "check" + color: Colours.palette.m3onSurfaceVariant + font.pointSize: Appearance.font.size.large } } } - } - StyledText { - text: "×" - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.normal + implicitHeight: fontFamilyMaterialRow.implicitHeight + Appearance.padding.normal * 2 } } - StyledSlider { - id: roundingSlider - - Layout.fillWidth: true - implicitHeight: Appearance.padding.normal * 3 - - from: 0.1 - to: 5.0 - value: rootPane.roundingScale - onMoved: { - rootPane.roundingScale = roundingSlider.value; - if (!roundingInput.activeFocus) { - roundingInput.text = (roundingSlider.value).toFixed(1); - } - rootPane.saveConfig(); - } - } } } - SectionContainer { - contentSpacing: Appearance.spacing.normal + CollapsibleSection { + id: monoFontSection + title: qsTr("Monospace font family") + expanded: false + showBackground: true + nested: true - ColumnLayout { + Loader { Layout.fillWidth: true - spacing: Appearance.spacing.small + Layout.preferredHeight: item ? Math.min(item.contentHeight, 300) : 0 + asynchronous: true + active: monoFontSection.expanded - RowLayout { - Layout.fillWidth: true - spacing: Appearance.spacing.normal + sourceComponent: StyledListView { + id: monoFontList + property alias contentHeight: monoFontList.contentHeight - StyledText { - text: qsTr("Spacing scale") - font.pointSize: Appearance.font.size.normal + clip: true + spacing: Appearance.spacing.small / 2 + model: Qt.fontFamilies() + + StyledScrollBar.vertical: StyledScrollBar { + flickable: monoFontList } - Item { - Layout.fillWidth: true - } + delegate: StyledRect { + required property string modelData + required property int index - StyledRect { - Layout.preferredWidth: 70 - implicitHeight: spacingInput.implicitHeight + Appearance.padding.small * 2 - color: spacingInputHover.containsMouse || spacingInput.activeFocus - ? Colours.layer(Colours.palette.m3surfaceContainer, 3) - : Colours.layer(Colours.palette.m3surfaceContainer, 2) - radius: Appearance.rounding.small - border.width: 1 - border.color: spacingInput.activeFocus - ? Colours.palette.m3primary - : Qt.alpha(Colours.palette.m3outline, 0.3) + width: ListView.view.width - Behavior on color { CAnim {} } - Behavior on border.color { CAnim {} } + 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 - MouseArea { - id: spacingInputHover - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.IBeamCursor - acceptedButtons: Qt.NoButton + StateLayer { + function onClicked(): void { + rootPane.fontFamilyMono = modelData; + rootPane.saveConfig(); + } } - StyledTextField { - id: spacingInput - anchors.centerIn: parent - width: parent.width - Appearance.padding.normal - horizontalAlignment: TextInput.AlignHCenter - validator: DoubleValidator { bottom: 0.1; top: 2.0 } - - Component.onCompleted: { - text = (rootPane.spacingScale).toFixed(1); + RowLayout { + id: fontFamilyMonoRow + + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.normal + + spacing: Appearance.spacing.normal + + StyledText { + text: modelData + font.pointSize: Appearance.font.size.normal } - - onTextChanged: { - if (activeFocus) { - const val = parseFloat(text); - if (!isNaN(val) && val >= 0.1 && val <= 2.0) { - rootPane.spacingScale = val; - rootPane.saveConfig(); - } - } + + Item { + Layout.fillWidth: true } - onEditingFinished: { - const val = parseFloat(text); - if (isNaN(val) || val < 0.1 || val > 2.0) { - text = (rootPane.spacingScale).toFixed(1); + + Loader { + active: isCurrent + asynchronous: true + + sourceComponent: MaterialIcon { + text: "check" + color: Colours.palette.m3onSurfaceVariant + font.pointSize: Appearance.font.size.large } } } - } - - StyledText { - text: "×" - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.normal - } - } - - StyledSlider { - id: spacingSlider - - Layout.fillWidth: true - implicitHeight: Appearance.padding.normal * 3 - from: 0.1 - to: 2.0 - value: rootPane.spacingScale - onMoved: { - rootPane.spacingScale = spacingSlider.value; - if (!spacingInput.activeFocus) { - spacingInput.text = (spacingSlider.value).toFixed(1); - } - rootPane.saveConfig(); + implicitHeight: fontFamilyMonoRow.implicitHeight + Appearance.padding.normal * 2 } } } } - } - - CollapsibleSection { - id: transparencySection - title: qsTr("Transparency") - showBackground: true - - SwitchRow { - label: qsTr("Transparency enabled") - checked: rootPane.transparencyEnabled - onToggled: checked => { - rootPane.transparencyEnabled = checked; - rootPane.saveConfig(); - } - } - SectionContainer { - contentSpacing: Appearance.spacing.normal + CollapsibleSection { + id: sansFontSection + title: qsTr("Sans-serif font family") + expanded: false + showBackground: true + nested: true - ColumnLayout { + Loader { Layout.fillWidth: true - spacing: Appearance.spacing.small + Layout.preferredHeight: item ? Math.min(item.contentHeight, 300) : 0 + asynchronous: true + active: sansFontSection.expanded - RowLayout { - Layout.fillWidth: true - spacing: Appearance.spacing.normal + sourceComponent: StyledListView { + id: sansFontList + property alias contentHeight: sansFontList.contentHeight - StyledText { - text: qsTr("Transparency base") - font.pointSize: Appearance.font.size.normal - } + clip: true + spacing: Appearance.spacing.small / 2 + model: Qt.fontFamilies() - Item { - Layout.fillWidth: true + StyledScrollBar.vertical: StyledScrollBar { + flickable: sansFontList } - StyledRect { - Layout.preferredWidth: 70 - implicitHeight: transparencyBaseInput.implicitHeight + Appearance.padding.small * 2 - color: transparencyBaseInputHover.containsMouse || transparencyBaseInput.activeFocus - ? Colours.layer(Colours.palette.m3surfaceContainer, 3) - : Colours.layer(Colours.palette.m3surfaceContainer, 2) - radius: Appearance.rounding.small - border.width: 1 - border.color: transparencyBaseInput.activeFocus - ? Colours.palette.m3primary - : Qt.alpha(Colours.palette.m3outline, 0.3) + delegate: StyledRect { + required property string modelData + required property int index - Behavior on color { CAnim {} } - Behavior on border.color { CAnim {} } + width: ListView.view.width - MouseArea { - id: transparencyBaseInputHover - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.IBeamCursor - acceptedButtons: Qt.NoButton - } + 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 - StyledTextField { - id: transparencyBaseInput - anchors.centerIn: parent - width: parent.width - Appearance.padding.normal - horizontalAlignment: TextInput.AlignHCenter - validator: IntValidator { bottom: 0; top: 100 } - - Component.onCompleted: { - text = Math.round(rootPane.transparencyBase * 100).toString(); - } - - onTextChanged: { - if (activeFocus) { - const val = parseInt(text); - if (!isNaN(val) && val >= 0 && val <= 100) { - rootPane.transparencyBase = val / 100; - rootPane.saveConfig(); - } - } - } - onEditingFinished: { - const val = parseInt(text); - if (isNaN(val) || val < 0 || val > 100) { - text = Math.round(rootPane.transparencyBase * 100).toString(); - } + StateLayer { + function onClicked(): void { + rootPane.fontFamilySans = modelData; + rootPane.saveConfig(); } } - } - StyledText { - text: "%" - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.normal - } - } + RowLayout { + id: fontFamilySansRow - StyledSlider { - id: baseSlider + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.normal - Layout.fillWidth: true - implicitHeight: Appearance.padding.normal * 3 + spacing: Appearance.spacing.normal - from: 0 - to: 100 - value: rootPane.transparencyBase * 100 - onMoved: { - rootPane.transparencyBase = baseSlider.value / 100; - if (!transparencyBaseInput.activeFocus) { - transparencyBaseInput.text = Math.round(baseSlider.value).toString(); + StyledText { + text: modelData + font.pointSize: Appearance.font.size.normal + } + + Item { + Layout.fillWidth: true + } + + Loader { + active: isCurrent + asynchronous: true + + sourceComponent: MaterialIcon { + text: "check" + color: Colours.palette.m3onSurfaceVariant + font.pointSize: Appearance.font.size.large + } + } } - rootPane.saveConfig(); + + implicitHeight: fontFamilySansRow.implicitHeight + Appearance.padding.normal * 2 } } } @@ -1292,7 +1355,7 @@ RowLayout { spacing: Appearance.spacing.normal StyledText { - text: qsTr("Transparency layers") + text: qsTr("Font size scale") font.pointSize: Appearance.font.size.normal } @@ -1302,13 +1365,13 @@ RowLayout { StyledRect { Layout.preferredWidth: 70 - implicitHeight: transparencyLayersInput.implicitHeight + Appearance.padding.small * 2 - color: transparencyLayersInputHover.containsMouse || transparencyLayersInput.activeFocus + implicitHeight: fontSizeInput.implicitHeight + Appearance.padding.small * 2 + color: fontSizeInputHover.containsMouse || fontSizeInput.activeFocus ? Colours.layer(Colours.palette.m3surfaceContainer, 3) : Colours.layer(Colours.palette.m3surfaceContainer, 2) radius: Appearance.rounding.small border.width: 1 - border.color: transparencyLayersInput.activeFocus + border.color: fontSizeInput.activeFocus ? Colours.palette.m3primary : Qt.alpha(Colours.palette.m3outline, 0.3) @@ -1316,7 +1379,7 @@ RowLayout { Behavior on border.color { CAnim {} } MouseArea { - id: transparencyLayersInputHover + id: fontSizeInputHover anchors.fill: parent hoverEnabled: true cursorShape: Qt.IBeamCursor @@ -1324,56 +1387,56 @@ RowLayout { } StyledTextField { - id: transparencyLayersInput + id: fontSizeInput anchors.centerIn: parent width: parent.width - Appearance.padding.normal horizontalAlignment: TextInput.AlignHCenter - validator: IntValidator { bottom: 0; top: 100 } + validator: DoubleValidator { bottom: 0.7; top: 1.5 } Component.onCompleted: { - text = Math.round(rootPane.transparencyLayers * 100).toString(); + text = (rootPane.fontSizeScale).toFixed(1); } onTextChanged: { if (activeFocus) { - const val = parseInt(text); - if (!isNaN(val) && val >= 0 && val <= 100) { - rootPane.transparencyLayers = val / 100; + const val = parseFloat(text); + if (!isNaN(val) && val >= 0.7 && val <= 1.5) { + rootPane.fontSizeScale = val; rootPane.saveConfig(); } } } onEditingFinished: { - const val = parseInt(text); - if (isNaN(val) || val < 0 || val > 100) { - text = Math.round(rootPane.transparencyLayers * 100).toString(); + const val = parseFloat(text); + if (isNaN(val) || val < 0.7 || val > 1.5) { + text = (rootPane.fontSizeScale).toFixed(1); } } } } StyledText { - text: "%" + text: "×" color: Colours.palette.m3outline font.pointSize: Appearance.font.size.normal } } StyledSlider { - id: layersSlider + id: fontSizeSlider Layout.fillWidth: true implicitHeight: Appearance.padding.normal * 3 - from: 0 - to: 100 - value: rootPane.transparencyLayers * 100 + from: 0.7 + to: 1.5 + value: rootPane.fontSizeScale onMoved: { - rootPane.transparencyLayers = layersSlider.value / 100; - if (!transparencyLayersInput.activeFocus) { - transparencyLayersInput.text = Math.round(layersSlider.value).toString(); + rootPane.fontSizeScale = fontSizeSlider.value; + if (!fontSizeInput.activeFocus) { + fontSizeInput.text = (fontSizeSlider.value).toFixed(1); } - rootPane.saveConfig(); + rootPane.saveConfig(); } } } @@ -1381,8 +1444,8 @@ RowLayout { } CollapsibleSection { - id: borderSection - title: qsTr("Border") + id: scalesSection + title: qsTr("Scales") showBackground: true SectionContainer { @@ -1397,7 +1460,7 @@ RowLayout { spacing: Appearance.spacing.normal StyledText { - text: qsTr("Border rounding") + text: qsTr("Padding scale") font.pointSize: Appearance.font.size.normal } @@ -1407,13 +1470,13 @@ RowLayout { StyledRect { Layout.preferredWidth: 70 - implicitHeight: borderRoundingInput.implicitHeight + Appearance.padding.small * 2 - color: borderRoundingInputHover.containsMouse || borderRoundingInput.activeFocus + implicitHeight: paddingInput.implicitHeight + Appearance.padding.small * 2 + color: paddingInputHover.containsMouse || paddingInput.activeFocus ? Colours.layer(Colours.palette.m3surfaceContainer, 3) : Colours.layer(Colours.palette.m3surfaceContainer, 2) radius: Appearance.rounding.small border.width: 1 - border.color: borderRoundingInput.activeFocus + border.color: paddingInput.activeFocus ? Colours.palette.m3primary : Qt.alpha(Colours.palette.m3outline, 0.3) @@ -1421,7 +1484,7 @@ RowLayout { Behavior on border.color { CAnim {} } MouseArea { - id: borderRoundingInputHover + id: paddingInputHover anchors.fill: parent hoverEnabled: true cursorShape: Qt.IBeamCursor @@ -1429,48 +1492,54 @@ RowLayout { } StyledTextField { - id: borderRoundingInput + id: paddingInput anchors.centerIn: parent width: parent.width - Appearance.padding.normal horizontalAlignment: TextInput.AlignHCenter - validator: DoubleValidator { bottom: 0.1; top: 100 } + validator: DoubleValidator { bottom: 0.5; top: 2.0 } Component.onCompleted: { - text = (rootPane.borderRounding).toFixed(1); + text = (rootPane.paddingScale).toFixed(1); } onTextChanged: { if (activeFocus) { const val = parseFloat(text); - if (!isNaN(val) && val >= 0.1 && val <= 100) { - rootPane.borderRounding = val; + if (!isNaN(val) && val >= 0.5 && val <= 2.0) { + rootPane.paddingScale = val; rootPane.saveConfig(); } } } onEditingFinished: { const val = parseFloat(text); - if (isNaN(val) || val < 0.1 || val > 100) { - text = (rootPane.borderRounding).toFixed(1); + if (isNaN(val) || val < 0.5 || val > 2.0) { + text = (rootPane.paddingScale).toFixed(1); } } } } + + StyledText { + text: "×" + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.normal + } } StyledSlider { - id: borderRoundingSlider + id: paddingSlider Layout.fillWidth: true implicitHeight: Appearance.padding.normal * 3 - from: 0.1 - to: 100 - value: rootPane.borderRounding + from: 0.5 + to: 2.0 + value: rootPane.paddingScale onMoved: { - rootPane.borderRounding = borderRoundingSlider.value; - if (!borderRoundingInput.activeFocus) { - borderRoundingInput.text = (borderRoundingSlider.value).toFixed(1); + rootPane.paddingScale = paddingSlider.value; + if (!paddingInput.activeFocus) { + paddingInput.text = (paddingSlider.value).toFixed(1); } rootPane.saveConfig(); } @@ -1490,7 +1559,7 @@ RowLayout { spacing: Appearance.spacing.normal StyledText { - text: qsTr("Border thickness") + text: qsTr("Rounding scale") font.pointSize: Appearance.font.size.normal } @@ -1500,13 +1569,13 @@ RowLayout { StyledRect { Layout.preferredWidth: 70 - implicitHeight: borderThicknessInput.implicitHeight + Appearance.padding.small * 2 - color: borderThicknessInputHover.containsMouse || borderThicknessInput.activeFocus + implicitHeight: roundingInput.implicitHeight + Appearance.padding.small * 2 + color: roundingInputHover.containsMouse || roundingInput.activeFocus ? Colours.layer(Colours.palette.m3surfaceContainer, 3) : Colours.layer(Colours.palette.m3surfaceContainer, 2) radius: Appearance.rounding.small border.width: 1 - border.color: borderThicknessInput.activeFocus + border.color: roundingInput.activeFocus ? Colours.palette.m3primary : Qt.alpha(Colours.palette.m3outline, 0.3) @@ -1514,7 +1583,7 @@ RowLayout { Behavior on border.color { CAnim {} } MouseArea { - id: borderThicknessInputHover + id: roundingInputHover anchors.fill: parent hoverEnabled: true cursorShape: Qt.IBeamCursor @@ -1522,103 +1591,60 @@ RowLayout { } StyledTextField { - id: borderThicknessInput + id: roundingInput anchors.centerIn: parent width: parent.width - Appearance.padding.normal horizontalAlignment: TextInput.AlignHCenter - validator: DoubleValidator { bottom: 0.1; top: 100 } + validator: DoubleValidator { bottom: 0.1; top: 5.0 } Component.onCompleted: { - text = (rootPane.borderThickness).toFixed(1); + text = (rootPane.roundingScale).toFixed(1); } onTextChanged: { if (activeFocus) { const val = parseFloat(text); - if (!isNaN(val) && val >= 0.1 && val <= 100) { - rootPane.borderThickness = val; + if (!isNaN(val) && val >= 0.1 && val <= 5.0) { + rootPane.roundingScale = val; rootPane.saveConfig(); } } } onEditingFinished: { const val = parseFloat(text); - if (isNaN(val) || val < 0.1 || val > 100) { - text = (rootPane.borderThickness).toFixed(1); + if (isNaN(val) || val < 0.1 || val > 5.0) { + text = (rootPane.roundingScale).toFixed(1); } } } } + + StyledText { + text: "×" + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.normal + } } StyledSlider { - id: borderThicknessSlider + id: roundingSlider Layout.fillWidth: true implicitHeight: Appearance.padding.normal * 3 from: 0.1 - to: 100 - value: rootPane.borderThickness + to: 5.0 + value: rootPane.roundingScale onMoved: { - rootPane.borderThickness = borderThicknessSlider.value; - if (!borderThicknessInput.activeFocus) { - borderThicknessInput.text = (borderThicknessSlider.value).toFixed(1); + rootPane.roundingScale = roundingSlider.value; + if (!roundingInput.activeFocus) { + roundingInput.text = (roundingSlider.value).toFixed(1); } rootPane.saveConfig(); } } } } - } - - CollapsibleSection { - id: backgroundSection - title: qsTr("Background") - showBackground: true - - SwitchRow { - label: qsTr("Desktop clock") - checked: rootPane.desktopClockEnabled - onToggled: checked => { - rootPane.desktopClockEnabled = checked; - rootPane.saveConfig(); - } - } - - SwitchRow { - label: qsTr("Background enabled") - checked: rootPane.backgroundEnabled - onToggled: checked => { - rootPane.backgroundEnabled = checked; - rootPane.saveConfig(); - } - } - - StyledText { - Layout.topMargin: Appearance.spacing.normal - text: qsTr("Visualiser") - font.pointSize: Appearance.font.size.larger - font.weight: 500 - } - - SwitchRow { - label: qsTr("Visualiser enabled") - checked: rootPane.visualiserEnabled - onToggled: checked => { - rootPane.visualiserEnabled = checked; - rootPane.saveConfig(); - } - } - - SwitchRow { - label: qsTr("Visualiser auto hide") - checked: rootPane.visualiserAutoHide - onToggled: checked => { - rootPane.visualiserAutoHide = checked; - rootPane.saveConfig(); - } - } SectionContainer { contentSpacing: Appearance.spacing.normal @@ -1632,7 +1658,7 @@ RowLayout { spacing: Appearance.spacing.normal StyledText { - text: qsTr("Visualiser rounding") + text: qsTr("Spacing scale") font.pointSize: Appearance.font.size.normal } @@ -1642,13 +1668,13 @@ RowLayout { StyledRect { Layout.preferredWidth: 70 - implicitHeight: visualiserRoundingInput.implicitHeight + Appearance.padding.small * 2 - color: visualiserRoundingInputHover.containsMouse || visualiserRoundingInput.activeFocus + implicitHeight: spacingInput.implicitHeight + Appearance.padding.small * 2 + color: spacingInputHover.containsMouse || spacingInput.activeFocus ? Colours.layer(Colours.palette.m3surfaceContainer, 3) : Colours.layer(Colours.palette.m3surfaceContainer, 2) radius: Appearance.rounding.small border.width: 1 - border.color: visualiserRoundingInput.activeFocus + border.color: spacingInput.activeFocus ? Colours.palette.m3primary : Qt.alpha(Colours.palette.m3outline, 0.3) @@ -1656,7 +1682,7 @@ RowLayout { Behavior on border.color { CAnim {} } MouseArea { - id: visualiserRoundingInputHover + id: spacingInputHover anchors.fill: parent hoverEnabled: true cursorShape: Qt.IBeamCursor @@ -1664,55 +1690,75 @@ RowLayout { } StyledTextField { - id: visualiserRoundingInput + id: spacingInput anchors.centerIn: parent width: parent.width - Appearance.padding.normal horizontalAlignment: TextInput.AlignHCenter - validator: IntValidator { bottom: 0; top: 10 } + validator: DoubleValidator { bottom: 0.1; top: 2.0 } Component.onCompleted: { - text = Math.round(rootPane.visualiserRounding).toString(); + text = (rootPane.spacingScale).toFixed(1); } onTextChanged: { if (activeFocus) { - const val = parseInt(text); - if (!isNaN(val) && val >= 0 && val <= 10) { - rootPane.visualiserRounding = val; - rootPane.saveConfig(); + const val = parseFloat(text); + if (!isNaN(val) && val >= 0.1 && val <= 2.0) { + rootPane.spacingScale = val; + rootPane.saveConfig(); } } } onEditingFinished: { - const val = parseInt(text); - if (isNaN(val) || val < 0 || val > 10) { - text = Math.round(rootPane.visualiserRounding).toString(); + const val = parseFloat(text); + if (isNaN(val) || val < 0.1 || val > 2.0) { + text = (rootPane.spacingScale).toFixed(1); } } } } + + StyledText { + text: "×" + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.normal + } } StyledSlider { - id: visualiserRoundingSlider + id: spacingSlider Layout.fillWidth: true implicitHeight: Appearance.padding.normal * 3 - from: 0 - to: 10 - stepSize: 1 - value: rootPane.visualiserRounding + from: 0.1 + to: 2.0 + value: rootPane.spacingScale onMoved: { - rootPane.visualiserRounding = Math.round(visualiserRoundingSlider.value); - if (!visualiserRoundingInput.activeFocus) { - visualiserRoundingInput.text = Math.round(visualiserRoundingSlider.value).toString(); + rootPane.spacingScale = spacingSlider.value; + if (!spacingInput.activeFocus) { + spacingInput.text = (spacingSlider.value).toFixed(1); } rootPane.saveConfig(); + } + } } } } - } + + CollapsibleSection { + id: transparencySection + title: qsTr("Transparency") + showBackground: true + + SwitchRow { + label: qsTr("Transparency enabled") + checked: rootPane.transparencyEnabled + onToggled: checked => { + rootPane.transparencyEnabled = checked; + rootPane.saveConfig(); + } + } SectionContainer { contentSpacing: Appearance.spacing.normal @@ -1726,7 +1772,7 @@ RowLayout { spacing: Appearance.spacing.normal StyledText { - text: qsTr("Visualiser spacing") + text: qsTr("Transparency base") font.pointSize: Appearance.font.size.normal } @@ -1736,13 +1782,13 @@ RowLayout { StyledRect { Layout.preferredWidth: 70 - implicitHeight: visualiserSpacingInput.implicitHeight + Appearance.padding.small * 2 - color: visualiserSpacingInputHover.containsMouse || visualiserSpacingInput.activeFocus + implicitHeight: transparencyBaseInput.implicitHeight + Appearance.padding.small * 2 + color: transparencyBaseInputHover.containsMouse || transparencyBaseInput.activeFocus ? Colours.layer(Colours.palette.m3surfaceContainer, 3) : Colours.layer(Colours.palette.m3surfaceContainer, 2) radius: Appearance.rounding.small border.width: 1 - border.color: visualiserSpacingInput.activeFocus + border.color: transparencyBaseInput.activeFocus ? Colours.palette.m3primary : Qt.alpha(Colours.palette.m3outline, 0.3) @@ -1750,7 +1796,7 @@ RowLayout { Behavior on border.color { CAnim {} } MouseArea { - id: visualiserSpacingInputHover + id: transparencyBaseInputHover anchors.fill: parent hoverEnabled: true cursorShape: Qt.IBeamCursor @@ -1758,701 +1804,592 @@ RowLayout { } StyledTextField { - id: visualiserSpacingInput + id: transparencyBaseInput anchors.centerIn: parent width: parent.width - Appearance.padding.normal horizontalAlignment: TextInput.AlignHCenter - validator: DoubleValidator { bottom: 0; top: 2 } + validator: IntValidator { bottom: 0; top: 100 } Component.onCompleted: { - text = (rootPane.visualiserSpacing).toFixed(1); + text = Math.round(rootPane.transparencyBase * 100).toString(); } onTextChanged: { if (activeFocus) { - const val = parseFloat(text); - if (!isNaN(val) && val >= 0 && val <= 2) { - rootPane.visualiserSpacing = val; + const val = parseInt(text); + if (!isNaN(val) && val >= 0 && val <= 100) { + rootPane.transparencyBase = val / 100; rootPane.saveConfig(); } } } onEditingFinished: { - const val = parseFloat(text); - if (isNaN(val) || val < 0 || val > 2) { - text = (rootPane.visualiserSpacing).toFixed(1); + const val = parseInt(text); + if (isNaN(val) || val < 0 || val > 100) { + text = Math.round(rootPane.transparencyBase * 100).toString(); } } } } + + StyledText { + text: "%" + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.normal + } } StyledSlider { - id: visualiserSpacingSlider + id: baseSlider Layout.fillWidth: true implicitHeight: Appearance.padding.normal * 3 from: 0 - to: 2 - value: rootPane.visualiserSpacing + to: 100 + value: rootPane.transparencyBase * 100 onMoved: { - rootPane.visualiserSpacing = visualiserSpacingSlider.value; - if (!visualiserSpacingInput.activeFocus) { - visualiserSpacingInput.text = (visualiserSpacingSlider.value).toFixed(1); + rootPane.transparencyBase = baseSlider.value / 100; + if (!transparencyBaseInput.activeFocus) { + transparencyBaseInput.text = Math.round(baseSlider.value).toString(); } rootPane.saveConfig(); } } } } - } - } - } - } - } - - Item { - Layout.fillWidth: true - Layout.fillHeight: true - - ClippingRectangle { - id: rightAppearanceClippingRect - anchors.fill: parent - anchors.margins: Appearance.padding.normal - anchors.leftMargin: 0 - anchors.rightMargin: Appearance.padding.normal / 2 - radius: rightAppearanceBorder.innerRadius - color: "transparent" - - Loader { - id: rightAppearanceLoader - anchors.fill: parent - anchors.margins: Appearance.padding.large * 2 - asynchronous: true - sourceComponent: appearanceRightContentComponent - property var rootPane: root - - onStatusChanged: { - if (status === Loader.Error) { - console.error("[AppearancePane] Right appearance loader error!"); - } - } - } - } - - InnerBorder { - id: rightAppearanceBorder - leftThickness: Appearance.padding.normal / 2 - } - - Component { - id: appearanceRightContentComponent - - StyledFlickable { - id: rightAppearanceFlickable - flickableDirection: Flickable.VerticalFlick - contentHeight: contentLayout.height - - StyledScrollBar.vertical: StyledScrollBar { - flickable: rightAppearanceFlickable - } - ColumnLayout { - id: contentLayout + SectionContainer { + contentSpacing: Appearance.spacing.normal - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - spacing: Appearance.spacing.normal + ColumnLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.small - MaterialIcon { - Layout.alignment: Qt.AlignHCenter | Qt.AlignTop - Layout.topMargin: 0 - text: "palette" - font.pointSize: Appearance.font.size.extraLarge * 3 - font.bold: true - } + RowLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.normal - StyledText { - Layout.alignment: Qt.AlignHCenter - text: qsTr("Appearance Settings") - font.pointSize: Appearance.font.size.large - font.bold: true - } + StyledText { + text: qsTr("Transparency layers") + font.pointSize: Appearance.font.size.normal + } - StyledText { - Layout.topMargin: Appearance.spacing.large - Layout.alignment: Qt.AlignHCenter - text: qsTr("Wallpaper") - font.pointSize: Appearance.font.size.extraLarge - font.weight: 600 - } + Item { + Layout.fillWidth: true + } - StyledText { - Layout.alignment: Qt.AlignHCenter - text: qsTr("Select a wallpaper") - font.pointSize: Appearance.font.size.normal - color: Colours.palette.m3onSurfaceVariant - } + StyledRect { + Layout.preferredWidth: 70 + implicitHeight: transparencyLayersInput.implicitHeight + Appearance.padding.small * 2 + color: transparencyLayersInputHover.containsMouse || transparencyLayersInput.activeFocus + ? Colours.layer(Colours.palette.m3surfaceContainer, 3) + : Colours.layer(Colours.palette.m3surfaceContainer, 2) + radius: Appearance.rounding.small + border.width: 1 + border.color: transparencyLayersInput.activeFocus + ? Colours.palette.m3primary + : Qt.alpha(Colours.palette.m3outline, 0.3) - Item { - Layout.fillWidth: true - Layout.topMargin: Appearance.spacing.large - Layout.preferredHeight: wallpaperLoader.item ? wallpaperLoader.item.layoutPreferredHeight : 0 - - Loader { - id: wallpaperLoader - anchors.fill: parent - asynchronous: true - active: { - // Lazy load: only activate when: - // 1. Right pane is loaded AND - // 2. Appearance pane is active (index 3) or adjacent (for smooth transitions) - // This prevents loading all wallpapers when control center opens but appearance pane isn't visible - const isActive = root.session.activeIndex === 3; - const isAdjacent = Math.abs(root.session.activeIndex - 3) === 1; - const shouldActivate = rightAppearanceLoader.item !== null && (isActive || isAdjacent); - return shouldActivate; - } - - onStatusChanged: { - if (status === Loader.Error) { - console.error("[AppearancePane] Wallpaper loader error!"); - } - } - - // Stop lazy loading when loader becomes inactive - onActiveChanged: { - if (!active && wallpaperLoader.item) { - const container = wallpaperLoader.item; - // Access timer through wallpaperGrid - if (container && container.wallpaperGrid) { - if (container.wallpaperGrid.scrollCheckTimer) { - container.wallpaperGrid.scrollCheckTimer.stop(); - } - container.wallpaperGrid._expansionInProgress = false; - } - } - } + Behavior on color { CAnim {} } + Behavior on border.color { CAnim {} } - sourceComponent: Item { - id: wallpaperGridContainer - property alias layoutPreferredHeight: wallpaperGrid.layoutPreferredHeight - - // Find and store reference to parent Flickable for scroll monitoring - property var parentFlickable: { - let item = parent; - while (item) { - if (item.flickableDirection !== undefined) { - return item; - } - item = item.parent; - } - return null; - } - - // Cleanup when component is destroyed - Component.onDestruction: { - if (wallpaperGrid) { - if (wallpaperGrid.scrollCheckTimer) { - wallpaperGrid.scrollCheckTimer.stop(); - } - wallpaperGrid._expansionInProgress = false; - } - } - - // Lazy loading model: loads one image at a time, only when touching bottom - // This prevents GridView from creating all delegates at once - QtObject { - id: lazyModel - - property var sourceList: null - property int loadedCount: 0 // Total items available to load - property int visibleCount: 0 // Items actually exposed to GridView (only visible + buffer) - property int totalCount: 0 - - function initialize(list) { - sourceList = list; - totalCount = list ? list.length : 0; - // Start with enough items to fill the initial viewport (~3 rows) - const initialRows = 3; - const cols = wallpaperGrid.columnsCount > 0 ? wallpaperGrid.columnsCount : 3; - const initialCount = Math.min(initialRows * cols, totalCount); - loadedCount = initialCount; - visibleCount = initialCount; - } - - function loadOneRow() { - if (loadedCount < totalCount) { - const cols = wallpaperGrid.columnsCount > 0 ? wallpaperGrid.columnsCount : 1; - const itemsToLoad = Math.min(cols, totalCount - loadedCount); - loadedCount += itemsToLoad; - } - } - - function updateVisibleCount(neededCount) { - // Always round up to complete rows to avoid incomplete rows in the grid - const cols = wallpaperGrid.columnsCount > 0 ? wallpaperGrid.columnsCount : 1; - const maxVisible = Math.min(neededCount, loadedCount); - const rows = Math.ceil(maxVisible / cols); - const newVisibleCount = Math.min(rows * cols, loadedCount); - - if (newVisibleCount > visibleCount) { - visibleCount = newVisibleCount; - } - } - } - - GridView { - id: wallpaperGrid - anchors.fill: parent - - property int _delegateCount: 0 - - readonly property int minCellWidth: 200 + Appearance.spacing.normal - readonly property int columnsCount: Math.max(1, Math.floor(parent.width / minCellWidth)) - - // Height based on visible items only - prevents GridView from creating all delegates - readonly property int layoutPreferredHeight: { - if (!lazyModel || lazyModel.visibleCount === 0 || columnsCount === 0) { - return 0; - } - const calculated = Math.ceil(lazyModel.visibleCount / columnsCount) * cellHeight; - return calculated; - } - - height: layoutPreferredHeight - cellWidth: width / columnsCount - cellHeight: 140 + Appearance.spacing.normal - - leftMargin: 0 - rightMargin: 0 - topMargin: 0 - bottomMargin: 0 + MouseArea { + id: transparencyLayersInputHover + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.IBeamCursor + acceptedButtons: Qt.NoButton + } - // Use ListModel for incremental updates to prevent flashing when new items are added - ListModel { - id: wallpaperListModel - } - - model: wallpaperListModel - - Connections { - target: lazyModel - function onVisibleCountChanged(): void { - if (!lazyModel || !lazyModel.sourceList) return; - - const newCount = lazyModel.visibleCount; - const currentCount = wallpaperListModel.count; + StyledTextField { + id: transparencyLayersInput + anchors.centerIn: parent + width: parent.width - Appearance.padding.normal + horizontalAlignment: TextInput.AlignHCenter + validator: IntValidator { bottom: 0; top: 100 } - // Only append new items - never remove or replace existing ones - if (newCount > currentCount) { - const flickable = wallpaperGridContainer.parentFlickable; - const oldScrollY = flickable ? flickable.contentY : 0; - - for (let i = currentCount; i < newCount; i++) { - wallpaperListModel.append({modelData: lazyModel.sourceList[i]}); - } - - // Preserve scroll position after model update - if (flickable) { - Qt.callLater(function() { - if (Math.abs(flickable.contentY - oldScrollY) < 1) { - flickable.contentY = oldScrollY; - } - }); - } - } - } - } - - Component.onCompleted: { - Qt.callLater(function() { - const isActive = root.session.activeIndex === 3; - if (width > 0 && parent && parent.visible && isActive && Wallpapers.list) { - lazyModel.initialize(Wallpapers.list); - wallpaperListModel.clear(); - for (let i = 0; i < lazyModel.visibleCount; i++) { - wallpaperListModel.append({modelData: lazyModel.sourceList[i]}); - } + Component.onCompleted: { + text = Math.round(rootPane.transparencyLayers * 100).toString(); } - }); - } - - Connections { - target: root.session - function onActiveIndexChanged(): void { - const isActive = root.session.activeIndex === 3; - // Stop lazy loading when switching away from appearance pane - if (!isActive) { - if (scrollCheckTimer) { - scrollCheckTimer.stop(); - } - if (wallpaperGrid) { - wallpaperGrid._expansionInProgress = false; + onTextChanged: { + if (activeFocus) { + const val = parseInt(text); + if (!isNaN(val) && val >= 0 && val <= 100) { + rootPane.transparencyLayers = val / 100; + rootPane.saveConfig(); + } } - return; } - - // Initialize if needed when switching to appearance pane - if (isActive && width > 0 && !lazyModel.sourceList && parent && parent.visible && Wallpapers.list) { - lazyModel.initialize(Wallpapers.list); - wallpaperListModel.clear(); - for (let i = 0; i < lazyModel.visibleCount; i++) { - wallpaperListModel.append({modelData: lazyModel.sourceList[i]}); + onEditingFinished: { + const val = parseInt(text); + if (isNaN(val) || val < 0 || val > 100) { + text = Math.round(rootPane.transparencyLayers * 100).toString(); } } } } - - onWidthChanged: { - const isActive = root.session.activeIndex === 3; - if (width > 0 && !lazyModel.sourceList && parent && parent.visible && isActive && Wallpapers.list) { - lazyModel.initialize(Wallpapers.list); - wallpaperListModel.clear(); - for (let i = 0; i < lazyModel.visibleCount; i++) { - wallpaperListModel.append({modelData: lazyModel.sourceList[i]}); - } + + StyledText { + text: "%" + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.normal + } + } + + StyledSlider { + id: layersSlider + + Layout.fillWidth: true + implicitHeight: Appearance.padding.normal * 3 + + from: 0 + to: 100 + value: rootPane.transparencyLayers * 100 + onMoved: { + rootPane.transparencyLayers = layersSlider.value / 100; + if (!transparencyLayersInput.activeFocus) { + transparencyLayersInput.text = Math.round(layersSlider.value).toString(); } + rootPane.saveConfig(); } - - // Force true lazy loading: only create delegates for visible items - displayMarginBeginning: 0 - displayMarginEnd: 0 - cacheBuffer: 0 - - // Debounce expansion to avoid too frequent checks - property bool _expansionInProgress: false - - Connections { - target: wallpaperGridContainer.parentFlickable - function onContentYChanged(): void { - // Don't process scroll events if appearance pane is not active - const isActive = root.session.activeIndex === 3; - if (!isActive) return; + } + } + } + } + + CollapsibleSection { + id: borderSection + title: qsTr("Border") + showBackground: true + + SectionContainer { + contentSpacing: Appearance.spacing.normal + + ColumnLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.small + + RowLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.normal + + StyledText { + text: qsTr("Border rounding") + font.pointSize: Appearance.font.size.normal + } + + Item { + Layout.fillWidth: true + } + + StyledRect { + Layout.preferredWidth: 70 + implicitHeight: borderRoundingInput.implicitHeight + Appearance.padding.small * 2 + color: borderRoundingInputHover.containsMouse || borderRoundingInput.activeFocus + ? Colours.layer(Colours.palette.m3surfaceContainer, 3) + : Colours.layer(Colours.palette.m3surfaceContainer, 2) + radius: Appearance.rounding.small + border.width: 1 + border.color: borderRoundingInput.activeFocus + ? Colours.palette.m3primary + : Qt.alpha(Colours.palette.m3outline, 0.3) + + Behavior on color { CAnim {} } + Behavior on border.color { CAnim {} } + + MouseArea { + id: borderRoundingInputHover + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.IBeamCursor + acceptedButtons: Qt.NoButton + } + + StyledTextField { + id: borderRoundingInput + anchors.centerIn: parent + width: parent.width - Appearance.padding.normal + horizontalAlignment: TextInput.AlignHCenter + validator: DoubleValidator { bottom: 0.1; top: 100 } - if (!lazyModel || !lazyModel.sourceList || lazyModel.loadedCount >= lazyModel.totalCount || wallpaperGrid._expansionInProgress) { - return; + Component.onCompleted: { + text = (rootPane.borderRounding).toFixed(1); } - const flickable = wallpaperGridContainer.parentFlickable; - if (!flickable) return; - - const gridY = wallpaperGridContainer.y; - const scrollY = flickable.contentY; - const viewportHeight = flickable.height; - - const topY = scrollY - gridY; - const bottomY = scrollY + viewportHeight - gridY; - - if (bottomY < 0) return; - - const topRow = Math.max(0, Math.floor(topY / wallpaperGrid.cellHeight)); - const bottomRow = Math.floor(bottomY / wallpaperGrid.cellHeight); - - // Update visible count with 1 row buffer ahead - const bufferRows = 1; - const neededBottomRow = bottomRow + bufferRows; - const neededCount = Math.min((neededBottomRow + 1) * wallpaperGrid.columnsCount, lazyModel.loadedCount); - lazyModel.updateVisibleCount(neededCount); - - // Load more when we're within 1 row of running out of loaded items - const loadedRows = Math.ceil(lazyModel.loadedCount / wallpaperGrid.columnsCount); - const rowsRemaining = loadedRows - (bottomRow + 1); - - if (rowsRemaining <= 1 && lazyModel.loadedCount < lazyModel.totalCount) { - if (!wallpaperGrid._expansionInProgress) { - wallpaperGrid._expansionInProgress = true; - lazyModel.loadOneRow(); - Qt.callLater(function() { - wallpaperGrid._expansionInProgress = false; - }); + onTextChanged: { + if (activeFocus) { + const val = parseFloat(text); + if (!isNaN(val) && val >= 0.1 && val <= 100) { + rootPane.borderRounding = val; + rootPane.saveConfig(); + } + } + } + onEditingFinished: { + const val = parseFloat(text); + if (isNaN(val) || val < 0.1 || val > 100) { + text = (rootPane.borderRounding).toFixed(1); } } } } - - // Fallback timer to check scroll position periodically - Timer { - id: scrollCheckTimer - interval: 100 - running: { - const isActive = root.session.activeIndex === 3; - return isActive && lazyModel && lazyModel.sourceList && lazyModel.loadedCount < lazyModel.totalCount; + } + + StyledSlider { + id: borderRoundingSlider + + Layout.fillWidth: true + implicitHeight: Appearance.padding.normal * 3 + + from: 0.1 + to: 100 + value: rootPane.borderRounding + onMoved: { + rootPane.borderRounding = borderRoundingSlider.value; + if (!borderRoundingInput.activeFocus) { + borderRoundingInput.text = (borderRoundingSlider.value).toFixed(1); + } + rootPane.saveConfig(); + } + } + } + } + + SectionContainer { + contentSpacing: Appearance.spacing.normal + + ColumnLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.small + + RowLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.normal + + StyledText { + text: qsTr("Border thickness") + font.pointSize: Appearance.font.size.normal + } + + Item { + Layout.fillWidth: true + } + + StyledRect { + Layout.preferredWidth: 70 + implicitHeight: borderThicknessInput.implicitHeight + Appearance.padding.small * 2 + color: borderThicknessInputHover.containsMouse || borderThicknessInput.activeFocus + ? Colours.layer(Colours.palette.m3surfaceContainer, 3) + : Colours.layer(Colours.palette.m3surfaceContainer, 2) + radius: Appearance.rounding.small + border.width: 1 + border.color: borderThicknessInput.activeFocus + ? Colours.palette.m3primary + : Qt.alpha(Colours.palette.m3outline, 0.3) + + Behavior on color { CAnim {} } + Behavior on border.color { CAnim {} } + + MouseArea { + id: borderThicknessInputHover + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.IBeamCursor + acceptedButtons: Qt.NoButton } - repeat: true - onTriggered: { - // Double-check that appearance pane is still active - const isActive = root.session.activeIndex === 3; - if (!isActive) { - stop(); - return; - } - - const flickable = wallpaperGridContainer.parentFlickable; - if (!flickable || !lazyModel || !lazyModel.sourceList) return; - - const gridY = wallpaperGridContainer.y; - const scrollY = flickable.contentY; - const viewportHeight = flickable.height; - - const topY = scrollY - gridY; - const bottomY = scrollY + viewportHeight - gridY; - if (bottomY < 0) return; - - const topRow = Math.max(0, Math.floor(topY / wallpaperGrid.cellHeight)); - const bottomRow = Math.floor(bottomY / wallpaperGrid.cellHeight); - - const bufferRows = 1; - const neededBottomRow = bottomRow + bufferRows; - const neededCount = Math.min((neededBottomRow + 1) * wallpaperGrid.columnsCount, lazyModel.loadedCount); - lazyModel.updateVisibleCount(neededCount); + + StyledTextField { + id: borderThicknessInput + anchors.centerIn: parent + width: parent.width - Appearance.padding.normal + horizontalAlignment: TextInput.AlignHCenter + validator: DoubleValidator { bottom: 0.1; top: 100 } - // Load more when we're within 1 row of running out of loaded items - const loadedRows = Math.ceil(lazyModel.loadedCount / wallpaperGrid.columnsCount); - const rowsRemaining = loadedRows - (bottomRow + 1); + Component.onCompleted: { + text = (rootPane.borderThickness).toFixed(1); + } - if (rowsRemaining <= 1 && lazyModel.loadedCount < lazyModel.totalCount) { - if (!wallpaperGrid._expansionInProgress) { - wallpaperGrid._expansionInProgress = true; - lazyModel.loadOneRow(); - Qt.callLater(function() { - wallpaperGrid._expansionInProgress = false; - }); + onTextChanged: { + if (activeFocus) { + const val = parseFloat(text); + if (!isNaN(val) && val >= 0.1 && val <= 100) { + rootPane.borderThickness = val; + rootPane.saveConfig(); + } + } + } + onEditingFinished: { + const val = parseFloat(text); + if (isNaN(val) || val < 0.1 || val > 100) { + text = (rootPane.borderThickness).toFixed(1); } } } } - - - // Parent Flickable handles scrolling - interactive: false + } + StyledSlider { + id: borderThicknessSlider - delegate: Item { - required property var modelData + Layout.fillWidth: true + implicitHeight: Appearance.padding.normal * 3 - width: wallpaperGrid.cellWidth - height: wallpaperGrid.cellHeight + from: 0.1 + to: 100 + value: rootPane.borderThickness + onMoved: { + rootPane.borderThickness = borderThicknessSlider.value; + if (!borderThicknessInput.activeFocus) { + borderThicknessInput.text = (borderThicknessSlider.value).toFixed(1); + } + rootPane.saveConfig(); + } + } + } + } + } - readonly property bool isCurrent: modelData.path === Wallpapers.actualCurrent - readonly property real itemMargin: Appearance.spacing.normal / 2 - readonly property real itemRadius: Appearance.rounding.normal - - Component.onCompleted: { - wallpaperGrid._delegateCount++; + CollapsibleSection { + id: backgroundSection + title: qsTr("Background") + showBackground: true + + SwitchRow { + label: qsTr("Desktop clock") + checked: rootPane.desktopClockEnabled + onToggled: checked => { + rootPane.desktopClockEnabled = checked; + rootPane.saveConfig(); + } + } + + SwitchRow { + label: qsTr("Background enabled") + checked: rootPane.backgroundEnabled + onToggled: checked => { + rootPane.backgroundEnabled = checked; + rootPane.saveConfig(); } + } - StateLayer { - anchors.fill: parent - anchors.leftMargin: itemMargin - anchors.rightMargin: itemMargin - anchors.topMargin: itemMargin - anchors.bottomMargin: itemMargin - radius: itemRadius + StyledText { + Layout.topMargin: Appearance.spacing.normal + text: qsTr("Visualiser") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } - function onClicked(): void { - Wallpapers.setWallpaper(modelData.path); - } + SwitchRow { + label: qsTr("Visualiser enabled") + checked: rootPane.visualiserEnabled + onToggled: checked => { + rootPane.visualiserEnabled = checked; + rootPane.saveConfig(); } + } - StyledClippingRect { - id: image + SwitchRow { + label: qsTr("Visualiser auto hide") + checked: rootPane.visualiserAutoHide + onToggled: checked => { + rootPane.visualiserAutoHide = checked; + rootPane.saveConfig(); + } + } - anchors.fill: parent - anchors.leftMargin: itemMargin - anchors.rightMargin: itemMargin - anchors.topMargin: itemMargin - anchors.bottomMargin: itemMargin - color: Colours.tPalette.m3surfaceContainer - radius: itemRadius - antialiasing: true - layer.enabled: true - layer.smooth: true + SectionContainer { + contentSpacing: Appearance.spacing.normal - CachingImage { - id: cachingImage + ColumnLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.small - path: modelData.path - anchors.fill: parent - fillMode: Image.PreserveAspectCrop - cache: true - visible: opacity > 0 - antialiasing: true - smooth: true + RowLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.normal - opacity: status === Image.Ready ? 1 : 0 + StyledText { + text: qsTr("Visualiser rounding") + font.pointSize: Appearance.font.size.normal + } - Behavior on opacity { - NumberAnimation { - duration: 1000 - easing.type: Easing.OutQuad - } + Item { + Layout.fillWidth: true } - } - // Fallback if CachingImage fails to load - Image { - id: fallbackImage + StyledRect { + Layout.preferredWidth: 70 + implicitHeight: visualiserRoundingInput.implicitHeight + Appearance.padding.small * 2 + color: visualiserRoundingInputHover.containsMouse || visualiserRoundingInput.activeFocus + ? Colours.layer(Colours.palette.m3surfaceContainer, 3) + : Colours.layer(Colours.palette.m3surfaceContainer, 2) + radius: Appearance.rounding.small + border.width: 1 + border.color: visualiserRoundingInput.activeFocus + ? Colours.palette.m3primary + : Qt.alpha(Colours.palette.m3outline, 0.3) - anchors.fill: parent - source: fallbackTimer.triggered && cachingImage.status !== Image.Ready ? modelData.path : "" - asynchronous: true - fillMode: Image.PreserveAspectCrop - cache: true - visible: opacity > 0 - antialiasing: true - smooth: true + Behavior on color { CAnim {} } + Behavior on border.color { CAnim {} } - opacity: status === Image.Ready && cachingImage.status !== Image.Ready ? 1 : 0 + MouseArea { + id: visualiserRoundingInputHover + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.IBeamCursor + acceptedButtons: Qt.NoButton + } - Behavior on opacity { - NumberAnimation { - duration: 1000 - easing.type: Easing.OutQuad + StyledTextField { + id: visualiserRoundingInput + anchors.centerIn: parent + width: parent.width - Appearance.padding.normal + horizontalAlignment: TextInput.AlignHCenter + validator: IntValidator { bottom: 0; top: 10 } + + Component.onCompleted: { + text = Math.round(rootPane.visualiserRounding).toString(); + } + + onTextChanged: { + if (activeFocus) { + const val = parseInt(text); + if (!isNaN(val) && val >= 0 && val <= 10) { + rootPane.visualiserRounding = val; + rootPane.saveConfig(); + } + } + } + onEditingFinished: { + const val = parseInt(text); + if (isNaN(val) || val < 0 || val > 10) { + text = Math.round(rootPane.visualiserRounding).toString(); + } + } } } } - Timer { - id: fallbackTimer - - property bool triggered: false - interval: 800 - running: cachingImage.status === Image.Loading || cachingImage.status === Image.Null - onTriggered: triggered = true - } - - // Gradient overlay for filename - Rectangle { - id: filenameOverlay + StyledSlider { + id: visualiserRoundingSlider - anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: parent.bottom + Layout.fillWidth: true + implicitHeight: Appearance.padding.normal * 3 - implicitHeight: filenameText.implicitHeight + Appearance.padding.normal * 1.5 - radius: 0 - - gradient: Gradient { - GradientStop { - position: 0.0 - color: Qt.rgba(Colours.palette.m3surfaceContainer.r, - Colours.palette.m3surfaceContainer.g, - Colours.palette.m3surfaceContainer.b, 0) - } - GradientStop { - position: 0.3 - color: Qt.rgba(Colours.palette.m3surfaceContainer.r, - Colours.palette.m3surfaceContainer.g, - Colours.palette.m3surfaceContainer.b, 0.7) - } - GradientStop { - position: 0.6 - color: Qt.rgba(Colours.palette.m3surfaceContainer.r, - Colours.palette.m3surfaceContainer.g, - Colours.palette.m3surfaceContainer.b, 0.9) - } - GradientStop { - position: 1.0 - color: Qt.rgba(Colours.palette.m3surfaceContainer.r, - Colours.palette.m3surfaceContainer.g, - Colours.palette.m3surfaceContainer.b, 0.95) + from: 0 + to: 10 + stepSize: 1 + value: rootPane.visualiserRounding + onMoved: { + rootPane.visualiserRounding = Math.round(visualiserRoundingSlider.value); + if (!visualiserRoundingInput.activeFocus) { + visualiserRoundingInput.text = Math.round(visualiserRoundingSlider.value).toString(); } - } + rootPane.saveConfig(); + } + } + } + } - opacity: 0 + SectionContainer { + contentSpacing: Appearance.spacing.normal - Behavior on opacity { - NumberAnimation { - duration: 1000 - easing.type: Easing.OutCubic - } - } + ColumnLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.small - Component.onCompleted: { - opacity = 1; - } - } - } + RowLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.normal - Rectangle { - anchors.fill: parent - anchors.leftMargin: itemMargin - anchors.rightMargin: itemMargin - anchors.topMargin: itemMargin - anchors.bottomMargin: itemMargin - color: "transparent" - radius: itemRadius + border.width - border.width: isCurrent ? 2 : 0 - border.color: Colours.palette.m3primary - antialiasing: true - smooth: true + StyledText { + text: qsTr("Visualiser spacing") + font.pointSize: Appearance.font.size.normal + } - Behavior on border.width { - NumberAnimation { - duration: 150 - easing.type: Easing.OutQuad + Item { + Layout.fillWidth: true } - } - MaterialIcon { - anchors.right: parent.right - anchors.top: parent.top - anchors.margins: Appearance.padding.small + StyledRect { + Layout.preferredWidth: 70 + implicitHeight: visualiserSpacingInput.implicitHeight + Appearance.padding.small * 2 + color: visualiserSpacingInputHover.containsMouse || visualiserSpacingInput.activeFocus + ? Colours.layer(Colours.palette.m3surfaceContainer, 3) + : Colours.layer(Colours.palette.m3surfaceContainer, 2) + radius: Appearance.rounding.small + border.width: 1 + border.color: visualiserSpacingInput.activeFocus + ? Colours.palette.m3primary + : Qt.alpha(Colours.palette.m3outline, 0.3) - visible: isCurrent - text: "check_circle" - color: Colours.palette.m3primary - font.pointSize: Appearance.font.size.large - } - } + Behavior on color { CAnim {} } + Behavior on border.color { CAnim {} } - StyledText { - id: filenameText - anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: parent.bottom - anchors.leftMargin: Appearance.padding.normal + Appearance.spacing.normal / 2 - anchors.rightMargin: Appearance.padding.normal + Appearance.spacing.normal / 2 - anchors.bottomMargin: Appearance.padding.normal + MouseArea { + id: visualiserSpacingInputHover + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.IBeamCursor + acceptedButtons: Qt.NoButton + } - readonly property string fileName: { - const path = modelData.relativePath || ""; - const parts = path.split("/"); - return parts.length > 0 ? parts[parts.length - 1] : path; + StyledTextField { + id: visualiserSpacingInput + anchors.centerIn: parent + width: parent.width - Appearance.padding.normal + horizontalAlignment: TextInput.AlignHCenter + validator: DoubleValidator { bottom: 0; top: 2 } + + Component.onCompleted: { + text = (rootPane.visualiserSpacing).toFixed(1); + } + + onTextChanged: { + if (activeFocus) { + const val = parseFloat(text); + if (!isNaN(val) && val >= 0 && val <= 2) { + rootPane.visualiserSpacing = val; + rootPane.saveConfig(); + } + } + } + onEditingFinished: { + const val = parseFloat(text); + if (isNaN(val) || val < 0 || val > 2) { + text = (rootPane.visualiserSpacing).toFixed(1); + } + } + } + } } - text: fileName - font.pointSize: Appearance.font.size.smaller - font.weight: 500 - color: isCurrent ? Colours.palette.m3primary : Colours.palette.m3onSurface - elide: Text.ElideMiddle - maximumLineCount: 1 - horizontalAlignment: Text.AlignHCenter + StyledSlider { + id: visualiserSpacingSlider - opacity: 0 + Layout.fillWidth: true + implicitHeight: Appearance.padding.normal * 3 - Behavior on opacity { - NumberAnimation { - duration: 1000 - easing.type: Easing.OutCubic + from: 0 + to: 2 + value: rootPane.visualiserSpacing + onMoved: { + rootPane.visualiserSpacing = visualiserSpacingSlider.value; + if (!visualiserSpacingInput.activeFocus) { + visualiserSpacingInput.text = (visualiserSpacingSlider.value).toFixed(1); + } + rootPane.saveConfig(); } } - - Component.onCompleted: { - opacity = 1; - } - } - } - } } } } } } - } + } + + rightContent: appearanceRightContentComponent } } diff --git a/modules/controlcenter/audio/AudioPane.qml b/modules/controlcenter/audio/AudioPane.qml index c2d60d8..dc3ba56 100644 --- a/modules/controlcenter/audio/AudioPane.qml +++ b/modules/controlcenter/audio/AudioPane.qml @@ -1,6 +1,7 @@ pragma ComponentBehavior: Bound import ".." +import "../components" import qs.components import qs.components.controls import qs.components.effects @@ -11,52 +12,17 @@ import Quickshell.Widgets import QtQuick import QtQuick.Layouts -RowLayout { +Item { id: root required property Session session anchors.fill: parent - spacing: 0 + SplitPaneLayout { + anchors.fill: parent - Item { - id: leftAudioItem - Layout.preferredWidth: Math.floor(parent.width * 0.4) - Layout.minimumWidth: 420 - Layout.fillHeight: true - - ClippingRectangle { - id: leftAudioClippingRect - anchors.fill: parent - anchors.margins: Appearance.padding.normal - anchors.leftMargin: 0 - anchors.rightMargin: Appearance.padding.normal / 2 - - radius: leftAudioBorder.innerRadius - color: "transparent" - - Loader { - id: leftAudioLoader - - anchors.fill: parent - anchors.margins: Appearance.padding.large + Appearance.padding.normal - anchors.leftMargin: Appearance.padding.large - anchors.rightMargin: Appearance.padding.large + Appearance.padding.normal / 2 - - asynchronous: true - sourceComponent: audioLeftContentComponent - } - } - - InnerBorder { - id: leftAudioBorder - leftThickness: 0 - rightThickness: Appearance.padding.normal / 2 - } - - Component { - id: audioLeftContentComponent + leftContent: Component { StyledFlickable { id: leftAudioFlickable @@ -246,349 +212,321 @@ RowLayout { } } } - } } - } - - Item { - id: rightAudioItem - Layout.fillWidth: true - Layout.fillHeight: true - - ClippingRectangle { - id: rightAudioClippingRect - anchors.fill: parent - anchors.margins: Appearance.padding.normal - anchors.leftMargin: 0 - anchors.rightMargin: Appearance.padding.normal / 2 - - radius: rightAudioBorder.innerRadius - color: "transparent" - - Loader { - id: rightAudioLoader + } + rightContent: Component { + Item { anchors.fill: parent anchors.topMargin: Appearance.padding.large * 2 anchors.bottomMargin: Appearance.padding.large * 2 anchors.leftMargin: 0 anchors.rightMargin: 0 - asynchronous: true - sourceComponent: audioRightContentComponent - } - } - - InnerBorder { - id: rightAudioBorder - leftThickness: Appearance.padding.normal / 2 - } - - Component { - id: audioRightContentComponent + StyledFlickable { + id: rightAudioFlickable + anchors.fill: parent + flickableDirection: Flickable.VerticalFlick + contentHeight: contentLayout.height - StyledFlickable { - id: rightAudioFlickable - flickableDirection: Flickable.VerticalFlick - contentHeight: contentLayout.height - - StyledScrollBar.vertical: StyledScrollBar { - flickable: rightAudioFlickable - } + StyledScrollBar.vertical: StyledScrollBar { + flickable: rightAudioFlickable + } - ColumnLayout { - id: contentLayout + ColumnLayout { + id: contentLayout - anchors.left: parent.left - anchors.right: parent.right - anchors.leftMargin: Appearance.padding.large * 2 - anchors.rightMargin: Appearance.padding.large * 2 - spacing: Appearance.spacing.normal + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: Appearance.padding.large * 2 + anchors.rightMargin: Appearance.padding.large * 2 + spacing: Appearance.spacing.normal - ConnectionHeader { - icon: "volume_up" - title: qsTr("Audio Settings") - } + ConnectionHeader { + icon: "volume_up" + title: qsTr("Audio Settings") + } - SectionHeader { - title: qsTr("Output volume") - description: qsTr("Control the volume of your output device") - } + SectionHeader { + title: qsTr("Output volume") + description: qsTr("Control the volume of your output device") + } - SectionContainer { - contentSpacing: Appearance.spacing.normal + SectionContainer { + contentSpacing: Appearance.spacing.normal - ColumnLayout { - Layout.fillWidth: true - spacing: Appearance.spacing.small + ColumnLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.small - RowLayout { - Layout.fillWidth: true - spacing: Appearance.spacing.normal + RowLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.normal - StyledText { - text: qsTr("Volume") - font.pointSize: Appearance.font.size.normal - font.weight: 500 - } + StyledText { + text: qsTr("Volume") + font.pointSize: Appearance.font.size.normal + font.weight: 500 + } - Item { - Layout.fillWidth: true - } + Item { + Layout.fillWidth: true + } - StyledRect { - 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) - 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 - } + StyledRect { + 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) + 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 + } - 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(); + 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(); } - } - } - - onTextChanged: { - if (activeFocus) { - const val = parseInt(text); - if (!isNaN(val) && val >= 0 && val <= 100) { - Audio.setVolume(val / 100); + + Connections { + target: Audio + function onVolumeChanged() { + if (!outputVolumeInput.activeFocus) { + 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: { + const val = parseInt(text); + if (isNaN(val) || val < 0 || val > 100) { + text = Math.round(Audio.volume * 100).toString(); + } } } } - onEditingFinished: { - const val = parseInt(text); - if (isNaN(val) || val < 0 || val > 100) { - text = Math.round(Audio.volume * 100).toString(); - } + + StyledText { + text: "%" + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.normal + opacity: Audio.muted ? 0.5 : 1 } - } - } - StyledText { - text: "%" - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.normal - opacity: Audio.muted ? 0.5 : 1 - } + StyledRect { + implicitWidth: implicitHeight + implicitHeight: muteIcon.implicitHeight + Appearance.padding.normal * 2 - StyledRect { - implicitWidth: implicitHeight - implicitHeight: muteIcon.implicitHeight + Appearance.padding.normal * 2 + radius: Appearance.rounding.normal + color: Audio.muted ? Colours.palette.m3secondary : Colours.palette.m3secondaryContainer - radius: Appearance.rounding.normal - color: Audio.muted ? Colours.palette.m3secondary : Colours.palette.m3secondaryContainer + StateLayer { + function onClicked(): void { + if (Audio.sink?.audio) { + Audio.sink.audio.muted = !Audio.sink.audio.muted; + } + } + } - StateLayer { - function onClicked(): void { - if (Audio.sink?.audio) { - Audio.sink.audio.muted = !Audio.sink.audio.muted; + MaterialIcon { + id: muteIcon + + anchors.centerIn: parent + text: Audio.muted ? "volume_off" : "volume_up" + color: Audio.muted ? Colours.palette.m3onSecondary : Colours.palette.m3onSecondaryContainer } } } - MaterialIcon { - id: muteIcon + StyledSlider { + id: outputVolumeSlider + Layout.fillWidth: true + implicitHeight: Appearance.padding.normal * 3 - anchors.centerIn: parent - text: Audio.muted ? "volume_off" : "volume_up" - color: Audio.muted ? Colours.palette.m3onSecondary : Colours.palette.m3onSecondaryContainer + value: Audio.volume + enabled: !Audio.muted + opacity: enabled ? 1 : 0.5 + onMoved: { + Audio.setVolume(value); + if (!outputVolumeInput.activeFocus) { + outputVolumeInput.text = Math.round(value * 100).toString(); + } + } } } } - StyledSlider { - id: outputVolumeSlider - Layout.fillWidth: true - implicitHeight: Appearance.padding.normal * 3 - - value: Audio.volume - enabled: !Audio.muted - opacity: enabled ? 1 : 0.5 - onMoved: { - Audio.setVolume(value); - if (!outputVolumeInput.activeFocus) { - outputVolumeInput.text = Math.round(value * 100).toString(); - } - } + SectionHeader { + title: qsTr("Input volume") + description: qsTr("Control the volume of your input device") } - } - } - SectionHeader { - title: qsTr("Input volume") - description: qsTr("Control the volume of your input device") - } - - SectionContainer { - contentSpacing: Appearance.spacing.normal + SectionContainer { + contentSpacing: Appearance.spacing.normal - ColumnLayout { - Layout.fillWidth: true - spacing: Appearance.spacing.small + ColumnLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.small - RowLayout { - Layout.fillWidth: true - spacing: Appearance.spacing.normal + RowLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.normal - StyledText { - text: qsTr("Volume") - font.pointSize: Appearance.font.size.normal - font.weight: 500 - } + StyledText { + text: qsTr("Volume") + font.pointSize: Appearance.font.size.normal + font.weight: 500 + } - Item { - Layout.fillWidth: true - } + Item { + Layout.fillWidth: true + } - StyledRect { - 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) - 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 - } + StyledRect { + 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) + 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 + } - 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(); + 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(); } - } - } - - onTextChanged: { - if (activeFocus) { - const val = parseInt(text); - if (!isNaN(val) && val >= 0 && val <= 100) { - Audio.setSourceVolume(val / 100); + + Connections { + target: Audio + function onSourceVolumeChanged() { + if (!inputVolumeInput.activeFocus) { + 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: { + const val = parseInt(text); + if (isNaN(val) || val < 0 || val > 100) { + text = Math.round(Audio.sourceVolume * 100).toString(); + } } } } - onEditingFinished: { - const val = parseInt(text); - if (isNaN(val) || val < 0 || val > 100) { - text = Math.round(Audio.sourceVolume * 100).toString(); - } + + StyledText { + text: "%" + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.normal + opacity: Audio.sourceMuted ? 0.5 : 1 } - } - } - StyledText { - text: "%" - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.normal - opacity: Audio.sourceMuted ? 0.5 : 1 - } + StyledRect { + implicitWidth: implicitHeight + implicitHeight: muteInputIcon.implicitHeight + Appearance.padding.normal * 2 - StyledRect { - implicitWidth: implicitHeight - implicitHeight: muteInputIcon.implicitHeight + Appearance.padding.normal * 2 + radius: Appearance.rounding.normal + color: Audio.sourceMuted ? Colours.palette.m3secondary : Colours.palette.m3secondaryContainer - radius: Appearance.rounding.normal - color: Audio.sourceMuted ? Colours.palette.m3secondary : Colours.palette.m3secondaryContainer + StateLayer { + function onClicked(): void { + if (Audio.source?.audio) { + Audio.source.audio.muted = !Audio.source.audio.muted; + } + } + } - StateLayer { - function onClicked(): void { - if (Audio.source?.audio) { - Audio.source.audio.muted = !Audio.source.audio.muted; + MaterialIcon { + id: muteInputIcon + + anchors.centerIn: parent + text: "mic_off" + color: Audio.sourceMuted ? Colours.palette.m3onSecondary : Colours.palette.m3onSecondaryContainer } } } - MaterialIcon { - id: muteInputIcon + StyledSlider { + id: inputVolumeSlider + Layout.fillWidth: true + implicitHeight: Appearance.padding.normal * 3 - anchors.centerIn: parent - text: "mic_off" - color: Audio.sourceMuted ? Colours.palette.m3onSecondary : Colours.palette.m3onSecondaryContainer - } - } - } - - StyledSlider { - id: inputVolumeSlider - Layout.fillWidth: true - implicitHeight: Appearance.padding.normal * 3 - - value: Audio.sourceVolume - enabled: !Audio.sourceMuted - opacity: enabled ? 1 : 0.5 - onMoved: { - Audio.setSourceVolume(value); - if (!inputVolumeInput.activeFocus) { - inputVolumeInput.text = Math.round(value * 100).toString(); + value: Audio.sourceVolume + enabled: !Audio.sourceMuted + opacity: enabled ? 1 : 0.5 + onMoved: { + Audio.setSourceVolume(value); + if (!inputVolumeInput.activeFocus) { + inputVolumeInput.text = Math.round(value * 100).toString(); + } + } } } } } } } - } } } } \ No newline at end of file diff --git a/modules/controlcenter/bluetooth/BtPane.qml b/modules/controlcenter/bluetooth/BtPane.qml index 8ad4b1f..cacb611 100644 --- a/modules/controlcenter/bluetooth/BtPane.qml +++ b/modules/controlcenter/bluetooth/BtPane.qml @@ -1,6 +1,7 @@ pragma ComponentBehavior: Bound import ".." +import "../components" import qs.components.controls import qs.components.effects import qs.components.containers @@ -10,95 +11,50 @@ import Quickshell.Bluetooth import QtQuick import QtQuick.Layouts -RowLayout { +Item { id: root required property Session session anchors.fill: parent - spacing: 0 - - Item { - id: leftBtItem - Layout.preferredWidth: Math.floor(parent.width * 0.4) - Layout.minimumWidth: 420 - Layout.fillHeight: true - - ClippingRectangle { - id: leftBtClippingRect - anchors.fill: parent - anchors.margins: Appearance.padding.normal - anchors.leftMargin: 0 - anchors.rightMargin: Appearance.padding.normal / 2 - - radius: leftBtBorder.innerRadius - color: "transparent" - - Loader { - id: leftBtLoader - - anchors.fill: parent - anchors.margins: Appearance.padding.large + Appearance.padding.normal - anchors.leftMargin: Appearance.padding.large - anchors.rightMargin: Appearance.padding.large + Appearance.padding.normal / 2 - - asynchronous: true - sourceComponent: btDeviceListComponent - } - } - - InnerBorder { - id: leftBtBorder - leftThickness: 0 - rightThickness: Appearance.padding.normal / 2 - } - - Component { - id: btDeviceListComponent + SplitPaneLayout { + anchors.fill: parent + leftContent: Component { DeviceList { anchors.fill: parent session: root.session } } - } - Item { - id: rightBtItem - Layout.fillWidth: true - Layout.fillHeight: true - - ClippingRectangle { - id: btClippingRect - anchors.fill: parent - anchors.margins: Appearance.padding.normal - anchors.leftMargin: 0 - anchors.rightMargin: Appearance.padding.normal / 2 - - radius: rightBorder.innerRadius - color: "transparent" - - Loader { - id: loader + rightContent: Component { + Item { + id: rightBtPane property BluetoothDevice pane: root.session.bt.active - anchors.fill: parent - anchors.margins: Appearance.padding.large * 2 + Loader { + id: rightLoader - asynchronous: true - sourceComponent: pane ? details : settings + anchors.fill: parent + anchors.margins: Appearance.padding.large * 2 + + asynchronous: true + sourceComponent: rightBtPane.pane ? details : settings + } Behavior on pane { SequentialAnimation { ParallelAnimation { Anim { + target: rightLoader property: "opacity" to: 0 easing.bezierCurve: Appearance.anim.curves.standardAccel } Anim { + target: rightLoader property: "scale" to: 0.8 easing.bezierCurve: Appearance.anim.curves.standardAccel @@ -107,11 +63,13 @@ RowLayout { PropertyAction {} ParallelAnimation { Anim { + target: rightLoader property: "opacity" to: 1 easing.bezierCurve: Appearance.anim.curves.standardDecel } Anim { + target: rightLoader property: "scale" to: 1 easing.bezierCurve: Appearance.anim.curves.standardDecel @@ -119,49 +77,49 @@ RowLayout { } } } - } - } - InnerBorder { - id: rightBorder - - leftThickness: Appearance.padding.normal / 2 + Connections { + target: root.session.bt + function onActiveChanged() { + rightBtPane.pane = root.session.bt.active; + } + } + } } + } - Component { - id: settings + Component { + id: settings - StyledFlickable { - id: settingsFlickable - flickableDirection: Flickable.VerticalFlick - contentHeight: settingsInner.height + StyledFlickable { + id: settingsFlickable + flickableDirection: Flickable.VerticalFlick + contentHeight: settingsInner.height - StyledScrollBar.vertical: StyledScrollBar { - flickable: settingsFlickable - } + StyledScrollBar.vertical: StyledScrollBar { + flickable: settingsFlickable + } - Settings { - id: settingsInner + Settings { + id: settingsInner - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - session: root.session - } + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + session: root.session } } + } - Component { - id: details + Component { + id: details - Details { - session: root.session - } + Details { + session: root.session } } component Anim: NumberAnimation { - target: loader duration: Appearance.anim.durations.normal / 2 easing.type: Easing.BezierSpline } diff --git a/modules/controlcenter/components/SplitPaneLayout.qml b/modules/controlcenter/components/SplitPaneLayout.qml new file mode 100644 index 0000000..7bd7db0 --- /dev/null +++ b/modules/controlcenter/components/SplitPaneLayout.qml @@ -0,0 +1,120 @@ +pragma ComponentBehavior: Bound + +import qs.components +import qs.components.effects +import qs.config +import Quickshell.Widgets +import QtQuick +import QtQuick.Layouts + +RowLayout { + id: root + + spacing: 0 + + property Component leftContent: null + property Component rightContent: null + + // Left pane configuration + property real leftWidthRatio: 0.4 + property int leftMinimumWidth: 420 + property var leftLoaderProperties: ({}) + + // Right pane configuration + property var rightLoaderProperties: ({}) + + // Expose loaders for customization (access via splitLayout.leftLoader or splitLayout.rightLoader) + property alias leftLoader: leftLoader + property alias rightLoader: rightLoader + + // Left pane + Item { + id: leftPane + + Layout.preferredWidth: Math.floor(parent.width * root.leftWidthRatio) + Layout.minimumWidth: root.leftMinimumWidth + Layout.fillHeight: true + + ClippingRectangle { + id: leftClippingRect + + anchors.fill: parent + anchors.margins: Appearance.padding.normal + anchors.leftMargin: 0 + anchors.rightMargin: Appearance.padding.normal / 2 + + radius: leftBorder.innerRadius + color: "transparent" + + Loader { + id: leftLoader + + anchors.fill: parent + anchors.margins: Appearance.padding.large + Appearance.padding.normal + anchors.leftMargin: Appearance.padding.large + anchors.rightMargin: Appearance.padding.large + Appearance.padding.normal / 2 + + asynchronous: true + sourceComponent: root.leftContent + + // Apply any additional properties from leftLoaderProperties + Component.onCompleted: { + for (const key in root.leftLoaderProperties) { + leftLoader[key] = root.leftLoaderProperties[key]; + } + } + } + } + + InnerBorder { + id: leftBorder + + leftThickness: 0 + rightThickness: Appearance.padding.normal / 2 + } + } + + // Right pane + Item { + id: rightPane + + Layout.fillWidth: true + Layout.fillHeight: true + + ClippingRectangle { + id: rightClippingRect + + anchors.fill: parent + anchors.margins: Appearance.padding.normal + anchors.leftMargin: 0 + anchors.rightMargin: Appearance.padding.normal / 2 + + radius: rightBorder.innerRadius + color: "transparent" + + Loader { + id: rightLoader + + anchors.fill: parent + anchors.margins: Appearance.padding.large * 2 + + asynchronous: true + sourceComponent: root.rightContent + + // Apply any additional properties from rightLoaderProperties + Component.onCompleted: { + for (const key in root.rightLoaderProperties) { + rightLoader[key] = root.rightLoaderProperties[key]; + } + } + } + } + + InnerBorder { + id: rightBorder + + leftThickness: Appearance.padding.normal / 2 + } + } +} + diff --git a/modules/controlcenter/launcher/LauncherPane.qml b/modules/controlcenter/launcher/LauncherPane.qml index f2247a7..30e2953 100644 --- a/modules/controlcenter/launcher/LauncherPane.qml +++ b/modules/controlcenter/launcher/LauncherPane.qml @@ -1,6 +1,7 @@ pragma ComponentBehavior: Bound import ".." +import "../components" import "../../launcher/services" import qs.components import qs.components.controls @@ -16,7 +17,7 @@ import QtQuick import QtQuick.Layouts import "../../../utils/scripts/fuzzysort.js" as Fuzzy -RowLayout { +Item { id: root required property Session session @@ -26,8 +27,6 @@ RowLayout { anchors.fill: parent - spacing: 0 - onSelectedAppChanged: { root.session.launcher.active = root.selectedApp; updateToggleState(); @@ -156,43 +155,10 @@ RowLayout { } } - Item { - id: leftLauncherItem - Layout.preferredWidth: Math.floor(parent.width * 0.4) - Layout.minimumWidth: 420 - Layout.fillHeight: true - - ClippingRectangle { - id: leftLauncherClippingRect - anchors.fill: parent - anchors.margins: Appearance.padding.normal - anchors.leftMargin: 0 - anchors.rightMargin: Appearance.padding.normal / 2 - - radius: leftLauncherBorder.innerRadius - color: "transparent" - - Loader { - id: leftLauncherLoader - - anchors.fill: parent - anchors.margins: Appearance.padding.large + Appearance.padding.normal - anchors.leftMargin: Appearance.padding.large - anchors.rightMargin: Appearance.padding.large + Appearance.padding.normal / 2 - - asynchronous: true - sourceComponent: leftContentComponent - } - } - - InnerBorder { - id: leftLauncherBorder - leftThickness: 0 - rightThickness: Appearance.padding.normal / 2 - } + SplitPaneLayout { + anchors.fill: parent - Component { - id: leftContentComponent + leftContent: Component { ColumnLayout { id: leftLauncherLayout @@ -336,7 +302,8 @@ RowLayout { // Lazy load: activate when left pane is loaded // The ListView will load asynchronously, and search will work because filteredApps // is updated regardless of whether the ListView is loaded - return leftLauncherLoader.item !== null; + // Access loader through parent - this will be set when component loads + return true; } sourceComponent: StyledListView { @@ -412,25 +379,10 @@ RowLayout { } } } - } - - Item { - id: rightLauncherItem - Layout.fillWidth: true - Layout.fillHeight: true - - ClippingRectangle { - id: rightLauncherClippingRect - anchors.fill: parent - anchors.margins: Appearance.padding.normal - anchors.leftMargin: 0 - anchors.rightMargin: Appearance.padding.normal / 2 - radius: rightLauncherBorder.innerRadius - color: "transparent" - - Loader { - id: rightLauncherLoader + rightContent: Component { + Item { + id: rightLauncherPane property var pane: root.session.launcher.active property string paneId: pane ? (pane.id || pane.entry?.id || "") : "" @@ -442,28 +394,34 @@ RowLayout { return pane ? appDetails : settings; } - anchors.fill: parent - anchors.margins: Appearance.padding.large * 2 - - opacity: 1 - scale: 1 - transformOrigin: Item.Center - clip: false - - asynchronous: true - sourceComponent: rightLauncherLoader.targetComponent - active: true - Component.onCompleted: { displayedApp = pane; targetComponent = getComponentForPane(); nextComponent = targetComponent; } - onItemChanged: { - // Ensure displayedApp is set when item is created (for async loading) - if (item && pane && displayedApp !== pane) { - displayedApp = pane; + Loader { + id: rightLauncherLoader + + anchors.fill: parent + + opacity: 1 + scale: 1 + transformOrigin: Item.Center + clip: false + + asynchronous: true + sourceComponent: rightLauncherPane.targetComponent + active: true + + // Expose displayedApp to loaded components + property var displayedApp: rightLauncherPane.displayedApp + + onItemChanged: { + // Ensure displayedApp is set when item is created (for async loading) + if (item && rightLauncherPane.pane && rightLauncherPane.displayedApp !== rightLauncherPane.pane) { + rightLauncherPane.displayedApp = rightLauncherPane.pane; + } } } @@ -484,9 +442,9 @@ RowLayout { } } PropertyAction { - target: rightLauncherLoader + target: rightLauncherPane property: "displayedApp" - value: rightLauncherLoader.pane + value: rightLauncherPane.pane } PropertyAction { target: rightLauncherLoader @@ -494,9 +452,9 @@ RowLayout { value: false } PropertyAction { - target: rightLauncherLoader + target: rightLauncherPane property: "targetComponent" - value: rightLauncherLoader.nextComponent + value: rightLauncherPane.nextComponent } PropertyAction { target: rightLauncherLoader @@ -539,90 +497,90 @@ RowLayout { } } } + } - InnerBorder { - id: rightLauncherBorder - - leftThickness: Appearance.padding.normal / 2 - } - - Component { - id: settings + Component { + id: settings - StyledFlickable { - id: settingsFlickable - flickableDirection: Flickable.VerticalFlick - contentHeight: settingsInner.height + StyledFlickable { + id: settingsFlickable + flickableDirection: Flickable.VerticalFlick + contentHeight: settingsInner.height - StyledScrollBar.vertical: StyledScrollBar { - flickable: settingsFlickable - } + StyledScrollBar.vertical: StyledScrollBar { + flickable: settingsFlickable + } - Settings { - id: settingsInner + Settings { + id: settingsInner - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - session: root.session - } + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + session: root.session } } + } - Component { - id: appDetails + Component { + id: appDetails - ColumnLayout { - anchors.fill: parent + ColumnLayout { + id: appDetailsLayout + anchors.fill: parent - spacing: Appearance.spacing.normal + // Get displayedApp from parent Loader (the Loader has displayedApp property we set) + readonly property var displayedApp: parent && parent.displayedApp !== undefined ? parent.displayedApp : null - Item { - Layout.alignment: Qt.AlignHCenter - Layout.leftMargin: Appearance.padding.large * 2 - Layout.rightMargin: Appearance.padding.large * 2 - Layout.topMargin: Appearance.padding.large * 2 - implicitWidth: iconLoader.implicitWidth - implicitHeight: iconLoader.implicitHeight + spacing: Appearance.spacing.normal - Loader { - id: iconLoader - sourceComponent: rightLauncherLoader.displayedApp ? appIconComponent : defaultIconComponent - } + Item { + Layout.alignment: Qt.AlignHCenter + Layout.leftMargin: Appearance.padding.large * 2 + Layout.rightMargin: Appearance.padding.large * 2 + Layout.topMargin: Appearance.padding.large * 2 + implicitWidth: iconLoader.implicitWidth + implicitHeight: iconLoader.implicitHeight - Component { - id: appIconComponent - IconImage { - implicitSize: Appearance.font.size.extraLarge * 3 * 2 - source: { - if (!rightLauncherLoader.displayedApp) return "image-missing"; - const entry = rightLauncherLoader.displayedApp.entry; - if (entry && entry.icon) { - return Quickshell.iconPath(entry.icon, "image-missing"); - } - return "image-missing"; + Loader { + id: iconLoader + sourceComponent: parent.parent.displayedApp ? appIconComponent : defaultIconComponent + } + + Component { + id: appIconComponent + IconImage { + implicitSize: Appearance.font.size.extraLarge * 3 * 2 + source: { + const app = iconLoader.parent.parent.displayedApp; + if (!app) return "image-missing"; + const entry = app.entry; + if (entry && entry.icon) { + return Quickshell.iconPath(entry.icon, "image-missing"); } + return "image-missing"; } } + } - Component { - id: defaultIconComponent - MaterialIcon { - text: "apps" - font.pointSize: Appearance.font.size.extraLarge * 3 - font.bold: true - } + Component { + id: defaultIconComponent + MaterialIcon { + text: "apps" + font.pointSize: Appearance.font.size.extraLarge * 3 + font.bold: true } } + } - StyledText { - Layout.alignment: Qt.AlignHCenter - Layout.leftMargin: Appearance.padding.large * 2 - Layout.rightMargin: Appearance.padding.large * 2 - text: rightLauncherLoader.displayedApp ? (rightLauncherLoader.displayedApp.name || rightLauncherLoader.displayedApp.entry?.name || qsTr("Application Details")) : qsTr("Launcher Applications") - font.pointSize: Appearance.font.size.large - font.bold: true - } + StyledText { + Layout.alignment: Qt.AlignHCenter + Layout.leftMargin: Appearance.padding.large * 2 + Layout.rightMargin: Appearance.padding.large * 2 + text: displayedApp ? (displayedApp.name || displayedApp.entry?.name || qsTr("Application Details")) : qsTr("Launcher Applications") + font.pointSize: Appearance.font.size.large + font.bold: true + } Item { Layout.fillWidth: true @@ -648,38 +606,38 @@ RowLayout { anchors.top: parent.top spacing: Appearance.spacing.normal - SwitchRow { - Layout.topMargin: Appearance.spacing.normal - visible: rightLauncherLoader.displayedApp !== null - label: qsTr("Hide from launcher") - checked: root.hideFromLauncherChecked - enabled: rightLauncherLoader.displayedApp !== null - onToggled: checked => { - root.hideFromLauncherChecked = checked; - if (rightLauncherLoader.displayedApp) { - const appId = rightLauncherLoader.displayedApp.id || rightLauncherLoader.displayedApp.entry?.id; - const hiddenApps = Config.launcher.hiddenApps ? [...Config.launcher.hiddenApps] : []; - if (checked) { - if (!hiddenApps.includes(appId)) { - hiddenApps.push(appId); - } - } else { - const index = hiddenApps.indexOf(appId); - if (index !== -1) { - hiddenApps.splice(index, 1); - } + SwitchRow { + Layout.topMargin: Appearance.spacing.normal + visible: appDetailsLayout.displayedApp !== null + label: qsTr("Hide from launcher") + checked: root.hideFromLauncherChecked + enabled: appDetailsLayout.displayedApp !== null + onToggled: checked => { + root.hideFromLauncherChecked = checked; + const app = appDetailsLayout.displayedApp; + if (app) { + const appId = app.id || app.entry?.id; + const hiddenApps = Config.launcher.hiddenApps ? [...Config.launcher.hiddenApps] : []; + if (checked) { + if (!hiddenApps.includes(appId)) { + hiddenApps.push(appId); + } + } else { + const index = hiddenApps.indexOf(appId); + if (index !== -1) { + hiddenApps.splice(index, 1); } - Config.launcher.hiddenApps = hiddenApps; - Config.save(); } + Config.launcher.hiddenApps = hiddenApps; + Config.save(); } } + } } } } } - } } component Anim: NumberAnimation { diff --git a/modules/controlcenter/network/NetworkingPane.qml b/modules/controlcenter/network/NetworkingPane.qml index d0ea852..55c70d2 100644 --- a/modules/controlcenter/network/NetworkingPane.qml +++ b/modules/controlcenter/network/NetworkingPane.qml @@ -1,6 +1,7 @@ pragma ComponentBehavior: Bound import ".." +import "../components" import "." import qs.components import qs.components.controls @@ -21,49 +22,12 @@ Item { anchors.fill: parent - RowLayout { - id: contentLayout + SplitPaneLayout { + id: splitLayout anchors.fill: parent - spacing: 0 - Item { - id: leftNetworkItem - Layout.preferredWidth: Math.floor(parent.width * 0.4) - Layout.minimumWidth: 420 - Layout.fillHeight: true - - ClippingRectangle { - id: leftNetworkClippingRect - anchors.fill: parent - anchors.margins: Appearance.padding.normal - anchors.leftMargin: 0 - anchors.rightMargin: Appearance.padding.normal / 2 - - radius: leftNetworkBorder.innerRadius - color: "transparent" - - Loader { - id: leftNetworkLoader - - anchors.fill: parent - anchors.margins: Appearance.padding.large + Appearance.padding.normal - anchors.leftMargin: Appearance.padding.large - anchors.rightMargin: Appearance.padding.large + Appearance.padding.normal / 2 - - asynchronous: true - sourceComponent: networkListComponent - } - } - - InnerBorder { - id: leftNetworkBorder - leftThickness: 0 - rightThickness: Appearance.padding.normal / 2 - } - - Component { - id: networkListComponent + leftContent: Component { StyledFlickable { id: leftFlickable @@ -473,38 +437,47 @@ Item { } } } - } } - Item { - id: rightNetworkItem - Layout.fillWidth: true - Layout.fillHeight: true - - ClippingRectangle { - id: networkClippingRect - anchors.fill: parent - anchors.margins: Appearance.padding.normal - anchors.leftMargin: 0 - anchors.rightMargin: Appearance.padding.normal / 2 - - radius: rightBorder.innerRadius - color: "transparent" - + rightContent: Component { + Item { + id: rightPaneItem + // Right pane - networking details/settings - Loader { - id: loader + property var ethernetPane: root.session.ethernet.active + property var wirelessPane: root.session.network.active + property var pane: ethernetPane || wirelessPane + property string paneId: ethernetPane ? (ethernetPane.interface || "") : (wirelessPane ? (wirelessPane.ssid || wirelessPane.bssid || "") : "") + property Component targetComponent: settings + property Component nextComponent: settings + + function getComponentForPane() { + return pane ? (ethernetPane ? ethernetDetails : wirelessDetails) : settings; + } - property var ethernetPane: root.session.ethernet.active - property var wirelessPane: root.session.network.active - property var pane: ethernetPane || wirelessPane - property string paneId: ethernetPane ? (ethernetPane.interface || "") : (wirelessPane ? (wirelessPane.ssid || wirelessPane.bssid || "") : "") - property Component targetComponent: settings - property Component nextComponent: settings + Component.onCompleted: { + targetComponent = getComponentForPane(); + nextComponent = targetComponent; + } - function getComponentForPane() { - return pane ? (ethernetPane ? ethernetDetails : wirelessDetails) : settings; + Connections { + target: root.session.ethernet + function onActiveChanged() { + nextComponent = getComponentForPane(); + paneId = ethernetPane ? (ethernetPane.interface || "") : (wirelessPane ? (wirelessPane.ssid || wirelessPane.bssid || "") : ""); + } + } + + Connections { + target: root.session.network + function onActiveChanged() { + nextComponent = getComponentForPane(); + paneId = ethernetPane ? (ethernetPane.interface || "") : (wirelessPane ? (wirelessPane.ssid || wirelessPane.bssid || "") : ""); } + } + + Loader { + id: rightLoader anchors.fill: parent anchors.margins: Appearance.padding.large * 2 @@ -515,131 +488,129 @@ Item { clip: false asynchronous: true - sourceComponent: loader.targetComponent + sourceComponent: rightPaneItem.targetComponent - Component.onCompleted: { - targetComponent = getComponentForPane(); - nextComponent = targetComponent; + Connections { + target: rightPaneItem + function onPaneIdChanged() { + rightPaneItem.targetComponent = rightPaneItem.nextComponent; + } } + } - Behavior on paneId { - SequentialAnimation { - ParallelAnimation { - Anim { - target: loader - property: "opacity" - to: 0 - easing.bezierCurve: Appearance.anim.curves.standardAccel - } - Anim { - target: loader - property: "scale" - to: 0.8 - easing.bezierCurve: Appearance.anim.curves.standardAccel - } + Behavior on paneId { + SequentialAnimation { + ParallelAnimation { + Anim { + target: rightLoader + property: "opacity" + to: 0 + easing.bezierCurve: Appearance.anim.curves.standardAccel } - PropertyAction { - target: loader - property: "targetComponent" - value: loader.nextComponent + Anim { + target: rightLoader + property: "scale" + to: 0.8 + easing.bezierCurve: Appearance.anim.curves.standardAccel } - ParallelAnimation { - Anim { - target: loader - property: "opacity" - to: 1 - easing.bezierCurve: Appearance.anim.curves.standardDecel - } - Anim { - target: loader - property: "scale" - to: 1 - easing.bezierCurve: Appearance.anim.curves.standardDecel - } + } + PropertyAction { + target: rightPaneItem + property: "targetComponent" + value: rightPaneItem.nextComponent + } + ParallelAnimation { + Anim { + target: rightLoader + property: "opacity" + to: 1 + easing.bezierCurve: Appearance.anim.curves.standardDecel + } + Anim { + target: rightLoader + property: "scale" + to: 1 + easing.bezierCurve: Appearance.anim.curves.standardDecel } } } + } - onPaneChanged: { - nextComponent = getComponentForPane(); - paneId = ethernetPane ? (ethernetPane.interface || "") : (wirelessPane ? (wirelessPane.ssid || wirelessPane.bssid || "") : ""); + Connections { + target: rightPaneItem + function onPaneIdChanged() { + rightPaneItem.targetComponent = rightPaneItem.nextComponent; } } } + } + } - InnerBorder { - id: rightBorder - - leftThickness: Appearance.padding.normal / 2 - } - - Component { - id: settings + Component { + id: settings - StyledFlickable { - id: settingsFlickable - flickableDirection: Flickable.VerticalFlick - contentHeight: settingsInner.height + StyledFlickable { + id: settingsFlickable + flickableDirection: Flickable.VerticalFlick + contentHeight: settingsInner.height - StyledScrollBar.vertical: StyledScrollBar { - flickable: settingsFlickable - } + StyledScrollBar.vertical: StyledScrollBar { + flickable: settingsFlickable + } - NetworkSettings { - id: settingsInner + NetworkSettings { + id: settingsInner - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - session: root.session - } - } + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + session: root.session } + } + } - Component { - id: ethernetDetails + Component { + id: ethernetDetails - StyledFlickable { - id: ethernetFlickable - flickableDirection: Flickable.VerticalFlick - contentHeight: ethernetDetailsInner.height + StyledFlickable { + id: ethernetFlickable + flickableDirection: Flickable.VerticalFlick + contentHeight: ethernetDetailsInner.height - StyledScrollBar.vertical: StyledScrollBar { - flickable: ethernetFlickable - } + StyledScrollBar.vertical: StyledScrollBar { + flickable: ethernetFlickable + } - EthernetDetails { - id: ethernetDetailsInner + EthernetDetails { + id: ethernetDetailsInner - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - session: root.session - } - } + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + session: root.session } + } + } - Component { - id: wirelessDetails + Component { + id: wirelessDetails - StyledFlickable { - id: wirelessFlickable - flickableDirection: Flickable.VerticalFlick - contentHeight: wirelessDetailsInner.height + StyledFlickable { + id: wirelessFlickable + flickableDirection: Flickable.VerticalFlick + contentHeight: wirelessDetailsInner.height - StyledScrollBar.vertical: StyledScrollBar { - flickable: wirelessFlickable - } + StyledScrollBar.vertical: StyledScrollBar { + flickable: wirelessFlickable + } - WirelessDetails { - id: wirelessDetailsInner + WirelessDetails { + id: wirelessDetailsInner - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - session: root.session - } - } + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + session: root.session } } } @@ -651,7 +622,6 @@ Item { } component Anim: NumberAnimation { - target: loader duration: Appearance.anim.durations.normal / 2 easing.type: Easing.BezierSpline } -- cgit v1.2.3-freya From e8fc13630c2fb67d75325e72ba66a811d3c1f4c9 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Wed, 19 Nov 2025 17:20:00 -0500 Subject: refactor: SettingsHeader on all panels --- .../controlcenter/appearance/AppearancePane.qml | 16 +---- modules/controlcenter/audio/AudioPane.qml | 2 +- modules/controlcenter/bluetooth/Details.qml | 18 ++--- modules/controlcenter/bluetooth/Settings.qml | 16 ++--- .../controlcenter/components/SettingsHeader.qml | 59 ++++++++++++++++ modules/controlcenter/launcher/LauncherPane.qml | 81 +++++++++++----------- modules/controlcenter/launcher/Settings.qml | 16 ++--- modules/controlcenter/network/EthernetSettings.qml | 16 ++--- modules/controlcenter/network/NetworkSettings.qml | 16 ++--- modules/controlcenter/network/WirelessSettings.qml | 16 ++--- 10 files changed, 128 insertions(+), 128 deletions(-) create mode 100644 modules/controlcenter/components/SettingsHeader.qml (limited to 'modules/controlcenter/appearance') diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index 2041bf8..3ba0549 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -97,19 +97,9 @@ Item { anchors.top: parent.top spacing: Appearance.spacing.normal - MaterialIcon { - Layout.alignment: Qt.AlignHCenter | Qt.AlignTop - Layout.topMargin: 0 - text: "palette" - font.pointSize: Appearance.font.size.extraLarge * 3 - font.bold: true - } - - StyledText { - Layout.alignment: Qt.AlignHCenter - text: qsTr("Appearance Settings") - font.pointSize: Appearance.font.size.large - font.bold: true + SettingsHeader { + icon: "palette" + title: qsTr("Appearance Settings") } StyledText { diff --git a/modules/controlcenter/audio/AudioPane.qml b/modules/controlcenter/audio/AudioPane.qml index 02cda5b..9b0c7d2 100644 --- a/modules/controlcenter/audio/AudioPane.qml +++ b/modules/controlcenter/audio/AudioPane.qml @@ -233,7 +233,7 @@ Item { anchors.top: parent.top spacing: Appearance.spacing.normal - ConnectionHeader { + SettingsHeader { icon: "volume_up" title: qsTr("Audio Settings") } diff --git a/modules/controlcenter/bluetooth/Details.qml b/modules/controlcenter/bluetooth/Details.qml index c9d10cd..5496966 100644 --- a/modules/controlcenter/bluetooth/Details.qml +++ b/modules/controlcenter/bluetooth/Details.qml @@ -1,6 +1,7 @@ pragma ComponentBehavior: Bound import ".." +import "../components" import qs.components import qs.components.controls import qs.components.effects @@ -41,20 +42,9 @@ StyledFlickable { anchors.top: parent.top spacing: Appearance.spacing.normal - MaterialIcon { - Layout.alignment: Qt.AlignHCenter - animate: true - text: Icons.getBluetoothIcon(root.device?.icon ?? "") - font.pointSize: Appearance.font.size.extraLarge * 3 - font.bold: true - } - - StyledText { - Layout.alignment: Qt.AlignHCenter - animate: true - text: root.device?.name ?? "" - font.pointSize: Appearance.font.size.large - font.bold: true + SettingsHeader { + icon: Icons.getBluetoothIcon(root.device?.icon ?? "") + title: root.device?.name ?? "" } StyledText { diff --git a/modules/controlcenter/bluetooth/Settings.qml b/modules/controlcenter/bluetooth/Settings.qml index c8453b6..b3245ab 100644 --- a/modules/controlcenter/bluetooth/Settings.qml +++ b/modules/controlcenter/bluetooth/Settings.qml @@ -1,6 +1,7 @@ pragma ComponentBehavior: Bound import ".." +import "../components" import qs.components import qs.components.controls import qs.components.effects @@ -17,18 +18,9 @@ ColumnLayout { spacing: Appearance.spacing.normal - MaterialIcon { - Layout.alignment: Qt.AlignHCenter - text: "bluetooth" - font.pointSize: Appearance.font.size.extraLarge * 3 - font.bold: true - } - - StyledText { - Layout.alignment: Qt.AlignHCenter - text: qsTr("Bluetooth Settings") - font.pointSize: Appearance.font.size.large - font.bold: true + SettingsHeader { + icon: "bluetooth" + title: qsTr("Bluetooth Settings") } StyledText { diff --git a/modules/controlcenter/components/SettingsHeader.qml b/modules/controlcenter/components/SettingsHeader.qml new file mode 100644 index 0000000..9a77968 --- /dev/null +++ b/modules/controlcenter/components/SettingsHeader.qml @@ -0,0 +1,59 @@ +pragma ComponentBehavior: Bound + +import qs.components +import qs.config +import QtQuick +import QtQuick.Layouts + +/** + * SettingsHeader + * + * Reusable header component for settings panes. Displays a large icon and title + * in a consistent format across all settings screens. + * + * Usage: + * ```qml + * SettingsHeader { + * icon: "router" + * title: qsTr("Network Settings") + * } + * ``` + */ +Item { + id: root + + /** + * Material icon name to display + */ + required property string icon + + /** + * Title text to display + */ + required property string title + + Layout.fillWidth: true + implicitHeight: column.implicitHeight + + ColumnLayout { + id: column + + anchors.centerIn: parent + spacing: Appearance.spacing.normal + + MaterialIcon { + Layout.alignment: Qt.AlignHCenter + text: root.icon + font.pointSize: Appearance.font.size.extraLarge * 3 + font.bold: true + } + + StyledText { + Layout.alignment: Qt.AlignHCenter + text: root.title + font.pointSize: Appearance.font.size.large + font.bold: true + } + } +} + diff --git a/modules/controlcenter/launcher/LauncherPane.qml b/modules/controlcenter/launcher/LauncherPane.qml index a8c5c76..cf965e8 100644 --- a/modules/controlcenter/launcher/LauncherPane.qml +++ b/modules/controlcenter/launcher/LauncherPane.qml @@ -509,53 +509,54 @@ Item { spacing: Appearance.spacing.normal - Item { - Layout.alignment: Qt.AlignHCenter - Layout.leftMargin: Appearance.padding.large * 2 - Layout.rightMargin: Appearance.padding.large * 2 - Layout.topMargin: Appearance.padding.large * 2 - implicitWidth: iconLoader.implicitWidth - implicitHeight: iconLoader.implicitHeight - - Loader { - id: iconLoader - sourceComponent: parent.parent.displayedApp ? appIconComponent : defaultIconComponent + // Show SettingsHeader when no app is selected, or show app icon + title when app is selected + SettingsHeader { + Layout.leftMargin: Appearance.padding.large * 2 + Layout.rightMargin: Appearance.padding.large * 2 + Layout.topMargin: Appearance.padding.large * 2 + visible: displayedApp === null + icon: "apps" + title: qsTr("Launcher Applications") } - Component { - id: appIconComponent - IconImage { - implicitSize: Appearance.font.size.extraLarge * 3 * 2 - source: { - const app = iconLoader.parent.parent.displayedApp; - if (!app) return "image-missing"; - const entry = app.entry; - if (entry && entry.icon) { - return Quickshell.iconPath(entry.icon, "image-missing"); + // App icon and title display (shown when app is selected) + Item { + Layout.alignment: Qt.AlignHCenter + Layout.leftMargin: Appearance.padding.large * 2 + Layout.rightMargin: Appearance.padding.large * 2 + Layout.topMargin: Appearance.padding.large * 2 + visible: displayedApp !== null + implicitWidth: Math.max(appIconImage.implicitWidth, appTitleText.implicitWidth) + implicitHeight: appIconImage.implicitHeight + Appearance.spacing.normal + appTitleText.implicitHeight + + ColumnLayout { + anchors.centerIn: parent + spacing: Appearance.spacing.normal + + IconImage { + id: appIconImage + Layout.alignment: Qt.AlignHCenter + implicitSize: Appearance.font.size.extraLarge * 3 * 2 + source: { + const app = appDetailsLayout.displayedApp; + if (!app) return "image-missing"; + const entry = app.entry; + if (entry && entry.icon) { + return Quickshell.iconPath(entry.icon, "image-missing"); + } + return "image-missing"; } - return "image-missing"; } - } - } - Component { - id: defaultIconComponent - MaterialIcon { - text: "apps" - font.pointSize: Appearance.font.size.extraLarge * 3 - font.bold: true + StyledText { + id: appTitleText + Layout.alignment: Qt.AlignHCenter + text: displayedApp ? (displayedApp.name || displayedApp.entry?.name || qsTr("Application Details")) : "" + font.pointSize: Appearance.font.size.large + font.bold: true + } } } - } - - StyledText { - Layout.alignment: Qt.AlignHCenter - Layout.leftMargin: Appearance.padding.large * 2 - Layout.rightMargin: Appearance.padding.large * 2 - text: displayedApp ? (displayedApp.name || displayedApp.entry?.name || qsTr("Application Details")) : qsTr("Launcher Applications") - font.pointSize: Appearance.font.size.large - font.bold: true - } Item { Layout.fillWidth: true diff --git a/modules/controlcenter/launcher/Settings.qml b/modules/controlcenter/launcher/Settings.qml index 1fef7f5..161221e 100644 --- a/modules/controlcenter/launcher/Settings.qml +++ b/modules/controlcenter/launcher/Settings.qml @@ -1,6 +1,7 @@ pragma ComponentBehavior: Bound import ".." +import "../components" import qs.components import qs.components.controls import qs.components.effects @@ -16,18 +17,9 @@ ColumnLayout { spacing: Appearance.spacing.normal - MaterialIcon { - Layout.alignment: Qt.AlignHCenter - text: "apps" - font.pointSize: Appearance.font.size.extraLarge * 3 - font.bold: true - } - - StyledText { - Layout.alignment: Qt.AlignHCenter - text: qsTr("Launcher Settings") - font.pointSize: Appearance.font.size.large - font.bold: true + SettingsHeader { + icon: "apps" + title: qsTr("Launcher Settings") } SectionHeader { diff --git a/modules/controlcenter/network/EthernetSettings.qml b/modules/controlcenter/network/EthernetSettings.qml index 161492c..f0f66b4 100644 --- a/modules/controlcenter/network/EthernetSettings.qml +++ b/modules/controlcenter/network/EthernetSettings.qml @@ -1,6 +1,7 @@ pragma ComponentBehavior: Bound import ".." +import "../components" import qs.components import qs.components.controls import qs.components.effects @@ -16,18 +17,9 @@ ColumnLayout { spacing: Appearance.spacing.normal - MaterialIcon { - Layout.alignment: Qt.AlignHCenter - text: "cable" - font.pointSize: Appearance.font.size.extraLarge * 3 - font.bold: true - } - - StyledText { - Layout.alignment: Qt.AlignHCenter - text: qsTr("Ethernet settings") - font.pointSize: Appearance.font.size.large - font.bold: true + SettingsHeader { + icon: "cable" + title: qsTr("Ethernet settings") } StyledText { diff --git a/modules/controlcenter/network/NetworkSettings.qml b/modules/controlcenter/network/NetworkSettings.qml index 75a7660..22e07cb 100644 --- a/modules/controlcenter/network/NetworkSettings.qml +++ b/modules/controlcenter/network/NetworkSettings.qml @@ -1,6 +1,7 @@ pragma ComponentBehavior: Bound import ".." +import "../components" import qs.components import qs.components.controls import qs.components.effects @@ -16,18 +17,9 @@ ColumnLayout { spacing: Appearance.spacing.normal - MaterialIcon { - Layout.alignment: Qt.AlignHCenter - text: "router" - font.pointSize: Appearance.font.size.extraLarge * 3 - font.bold: true - } - - StyledText { - Layout.alignment: Qt.AlignHCenter - text: qsTr("Network Settings") - font.pointSize: Appearance.font.size.large - font.bold: true + SettingsHeader { + icon: "router" + title: qsTr("Network Settings") } SectionHeader { diff --git a/modules/controlcenter/network/WirelessSettings.qml b/modules/controlcenter/network/WirelessSettings.qml index 0eb1578..f87fe39 100644 --- a/modules/controlcenter/network/WirelessSettings.qml +++ b/modules/controlcenter/network/WirelessSettings.qml @@ -1,6 +1,7 @@ pragma ComponentBehavior: Bound import ".." +import "../components" import qs.components import qs.components.controls import qs.components.effects @@ -16,18 +17,9 @@ ColumnLayout { spacing: Appearance.spacing.normal - MaterialIcon { - Layout.alignment: Qt.AlignHCenter - text: "wifi" - font.pointSize: Appearance.font.size.extraLarge * 3 - font.bold: true - } - - StyledText { - Layout.alignment: Qt.AlignHCenter - text: qsTr("Network settings") - font.pointSize: Appearance.font.size.large - font.bold: true + SettingsHeader { + icon: "wifi" + title: qsTr("Network settings") } SectionHeader { -- cgit v1.2.3-freya From 147410e39bf4e0474deca3980dcaa724464cf5c3 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Wed, 19 Nov 2025 21:15:40 -0500 Subject: cleanup: removed unnecessary comments --- modules/controlcenter/ControlCenter.qml | 1 - modules/controlcenter/Panes.qml | 18 ------------ modules/controlcenter/Session.qml | 1 - .../controlcenter/appearance/AppearancePane.qml | 28 ++----------------- modules/controlcenter/audio/AudioPane.qml | 1 - modules/controlcenter/bluetooth/Details.qml | 1 - modules/controlcenter/bluetooth/Settings.qml | 4 +-- modules/controlcenter/components/DeviceDetails.qml | 7 ----- modules/controlcenter/components/DeviceList.qml | 10 ++----- .../controlcenter/components/PaneTransition.qml | 12 -------- .../controlcenter/components/SplitPaneLayout.qml | 8 ------ .../components/SplitPaneWithDetails.qml | 2 -- modules/controlcenter/launcher/LauncherPane.qml | 27 ++---------------- modules/controlcenter/network/NetworkingPane.qml | 2 -- modules/controlcenter/network/WirelessDetails.qml | 9 ------ modules/controlcenter/network/WirelessList.qml | 3 -- .../network/WirelessPasswordDialog.qml | 27 ++---------------- modules/controlcenter/state/BluetoothState.qml | 5 ---- modules/controlcenter/state/EthernetState.qml | 1 - modules/controlcenter/state/LauncherState.qml | 1 - modules/controlcenter/state/NetworkState.qml | 3 -- modules/controlcenter/taskbar/TaskbarPane.qml | 32 ---------------------- 22 files changed, 9 insertions(+), 194 deletions(-) (limited to 'modules/controlcenter/appearance') diff --git a/modules/controlcenter/ControlCenter.qml b/modules/controlcenter/ControlCenter.qml index 3642a33..fdb824e 100644 --- a/modules/controlcenter/ControlCenter.qml +++ b/modules/controlcenter/ControlCenter.qml @@ -97,6 +97,5 @@ Item { } } - // Expose initialOpeningComplete for NavRail to prevent tab switching during opening animation readonly property bool initialOpeningComplete: panes.initialOpeningComplete } diff --git a/modules/controlcenter/Panes.qml b/modules/controlcenter/Panes.qml index b9256a9..833a411 100644 --- a/modules/controlcenter/Panes.qml +++ b/modules/controlcenter/Panes.qml @@ -19,7 +19,6 @@ ClippingRectangle { required property Session session - // Expose initialOpeningComplete so parent can check if opening animation is done readonly property bool initialOpeningComplete: layout.initialOpeningComplete color: "transparent" @@ -27,7 +26,6 @@ ClippingRectangle { focus: false activeFocusOnTab: false - // Clear focus when clicking anywhere in the panes area MouseArea { anchors.fill: parent z: -1 @@ -37,7 +35,6 @@ ClippingRectangle { } } - // Clear focus when switching panes Connections { target: root.session @@ -54,8 +51,6 @@ ClippingRectangle { clip: true property bool animationComplete: true - // Track if initial opening animation has completed - // During initial opening, only the active pane loads to avoid hiccups property bool initialOpeningComplete: false Timer { @@ -66,8 +61,6 @@ ClippingRectangle { } } - // Timer to detect when initial opening animation completes - // Uses large duration to cover both normal and detached opening cases Timer { id: initialOpeningTimer interval: Appearance.anim.durations.large @@ -94,7 +87,6 @@ ClippingRectangle { Connections { target: root.session function onActiveIndexChanged(): void { - // Mark animation as incomplete and start delay timer layout.animationComplete = false; animationDelayTimer.restart(); } @@ -110,28 +102,21 @@ ClippingRectangle { implicitWidth: root.width implicitHeight: root.height - // Track if this pane has ever been loaded to enable caching property bool hasBeenLoaded: false - // Function to compute if this pane should be active function updateActive(): void { const diff = Math.abs(root.session.activeIndex - pane.paneIndex); const isActivePane = diff === 0; let shouldBeActive = false; - // During initial opening animation, only load the active pane - // This prevents hiccups from multiple panes loading simultaneously if (!layout.initialOpeningComplete) { shouldBeActive = isActivePane; } else { - // After initial opening, allow current and adjacent panes for smooth transitions if (diff <= 1) { shouldBeActive = true; } else if (pane.hasBeenLoaded) { - // For distant panes that have been loaded before, keep them active to preserve cached data shouldBeActive = true; } else { - // For new distant panes, wait until animation completes to avoid heavy loading during transition shouldBeActive = layout.animationComplete; } } @@ -152,12 +137,10 @@ ClippingRectangle { } onActiveChanged: { - // Mark pane as loaded when it becomes active if (active && !pane.hasBeenLoaded) { pane.hasBeenLoaded = true; } - // Load the component with initial properties when activated if (active && !item) { loader.setSource(pane.componentPath, { "session": root.session @@ -166,7 +149,6 @@ ClippingRectangle { } onItemChanged: { - // Mark pane as loaded when item is created if (item) { pane.hasBeenLoaded = true; } diff --git a/modules/controlcenter/Session.qml b/modules/controlcenter/Session.qml index 9c6a754..0408a1a 100644 --- a/modules/controlcenter/Session.qml +++ b/modules/controlcenter/Session.qml @@ -11,7 +11,6 @@ QtObject { property int activeIndex: 0 property bool navExpanded: false - // Pane-specific state objects readonly property BluetoothState bt: BluetoothState {} readonly property NetworkState network: NetworkState {} readonly property EthernetState ethernet: EthernetState {} diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index 3ba0549..5b7e859 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -22,7 +22,6 @@ Item { required property Session session - // Appearance settings property real animDurationsScale: Config.appearance.anim.durations.scale ?? 1 property string fontFamilyMaterial: Config.appearance.font.family.material ?? "Material Symbols Rounded" property string fontFamilyMono: Config.appearance.font.family.mono ?? "CaskaydiaCove NF" @@ -37,7 +36,6 @@ Item { property real borderRounding: Config.border.rounding ?? 1 property real borderThickness: Config.border.thickness ?? 1 - // Background settings property bool desktopClockEnabled: Config.background.desktopClock.enabled ?? false property bool backgroundEnabled: Config.background.enabled ?? true property bool visualiserEnabled: Config.background.visualiser.enabled ?? false @@ -127,13 +125,8 @@ Item { anchors.fill: parent asynchronous: true active: { - // Lazy load: only activate when: - // 1. Right pane is loaded AND - // 2. Appearance pane is active (index 3) or adjacent (for smooth transitions) - // This prevents loading all wallpapers when control center opens but appearance pane isn't visible const isActive = root.session.activeIndex === 3; const isAdjacent = Math.abs(root.session.activeIndex - 3) === 1; - // Access loader through SplitPaneLayout's rightLoader const splitLayout = root.children[0]; const loader = splitLayout && splitLayout.rightLoader ? splitLayout.rightLoader : null; const shouldActivate = loader && loader.item !== null && (isActive || isAdjacent); @@ -150,7 +143,6 @@ Item { onActiveChanged: { if (!active && wallpaperLoader.item) { const container = wallpaperLoader.item; - // Access timer through wallpaperGrid if (container && container.wallpaperGrid) { const grid = container.wallpaperGrid; if (grid.imageUpdateTimer) { @@ -186,20 +178,17 @@ Item { } } - // Lazy loading model: loads one image at a time, only when touching bottom - // This prevents GridView from creating all delegates at once QtObject { id: lazyModel property var sourceList: null - property int loadedCount: 0 // Total items available to load - property int visibleCount: 0 // Items actually exposed to GridView (only visible + buffer) + property int loadedCount: 0 + property int visibleCount: 0 property int totalCount: 0 function initialize(list) { sourceList = list; totalCount = list ? list.length : 0; - // Start with enough items to fill the initial viewport (~3 rows) const initialRows = 3; const cols = wallpaperGrid.columnsCount > 0 ? wallpaperGrid.columnsCount : 3; const initialCount = Math.min(initialRows * cols, totalCount); @@ -216,7 +205,6 @@ Item { } function updateVisibleCount(neededCount) { - // Always round up to complete rows to avoid incomplete rows in the grid const cols = wallpaperGrid.columnsCount > 0 ? wallpaperGrid.columnsCount : 1; const maxVisible = Math.min(neededCount, loadedCount); const rows = Math.ceil(maxVisible / cols); @@ -237,7 +225,6 @@ Item { readonly property int minCellWidth: 200 + Appearance.spacing.normal readonly property int columnsCount: Math.max(1, Math.floor(parent.width / minCellWidth)) - // Height based on visible items only - prevents GridView from creating all delegates readonly property int layoutPreferredHeight: { if (!lazyModel || lazyModel.visibleCount === 0 || columnsCount === 0) { return 0; @@ -255,7 +242,6 @@ Item { topMargin: 0 bottomMargin: 0 - // Use ListModel for incremental updates to prevent flashing when new items are added ListModel { id: wallpaperListModel } @@ -270,7 +256,6 @@ Item { const newCount = lazyModel.visibleCount; const currentCount = wallpaperListModel.count; - // Only append new items - never remove or replace existing ones if (newCount > currentCount) { const flickable = wallpaperGridContainer.parentFlickable; const oldScrollY = flickable ? flickable.contentY : 0; @@ -279,7 +264,6 @@ Item { wallpaperListModel.append({modelData: lazyModel.sourceList[i]}); } - // Preserve scroll position after model update if (flickable) { Qt.callLater(function() { if (Math.abs(flickable.contentY - oldScrollY) < 1) { @@ -382,7 +366,6 @@ Item { const neededCount = Math.min((neededBottomRow + 1) * wallpaperGrid.columnsCount, lazyModel.loadedCount); lazyModel.updateVisibleCount(neededCount); - // Load more when we're within 1 row of running out of loaded items const loadedRows = Math.ceil(lazyModel.loadedCount / wallpaperGrid.columnsCount); const rowsRemaining = loadedRows - (bottomRow + 1); @@ -434,7 +417,6 @@ Item { const neededCount = Math.min((neededBottomRow + 1) * wallpaperGrid.columnsCount, lazyModel.loadedCount); lazyModel.updateVisibleCount(neededCount); - // Load more when we're within 1 row of running out of loaded items const loadedRows = Math.ceil(lazyModel.loadedCount / wallpaperGrid.columnsCount); const rowsRemaining = loadedRows - (bottomRow + 1); @@ -450,8 +432,6 @@ Item { } } - - // Parent Flickable handles scrolling interactive: false @@ -786,11 +766,9 @@ Item { function onClicked(): void { const variant = modelData.variant; - // Optimistic update - set immediately for responsive UI Schemes.currentVariant = variant; Quickshell.execDetached(["caelestia", "scheme", "set", "-v", variant]); - // Reload after a delay to confirm changes Qt.callLater(() => { reloadTimer.restart(); }); @@ -873,11 +851,9 @@ Item { const flavour = modelData.flavour; const schemeKey = `${name} ${flavour}`; - // Optimistic update - set immediately for responsive UI Schemes.currentScheme = schemeKey; Quickshell.execDetached(["caelestia", "scheme", "set", "-n", name, "-f", flavour]); - // Reload after a delay to confirm changes Qt.callLater(() => { reloadTimer.restart(); }); diff --git a/modules/controlcenter/audio/AudioPane.qml b/modules/controlcenter/audio/AudioPane.qml index 9b0c7d2..694e178 100644 --- a/modules/controlcenter/audio/AudioPane.qml +++ b/modules/controlcenter/audio/AudioPane.qml @@ -40,7 +40,6 @@ Item { anchors.right: parent.right spacing: Appearance.spacing.normal - // Audio header above the collapsible sections RowLayout { Layout.fillWidth: true spacing: Appearance.spacing.smaller diff --git a/modules/controlcenter/bluetooth/Details.qml b/modules/controlcenter/bluetooth/Details.qml index b260458..5299045 100644 --- a/modules/controlcenter/bluetooth/Details.qml +++ b/modules/controlcenter/bluetooth/Details.qml @@ -440,7 +440,6 @@ StyledFlickable { } } - // FAB Menu (positioned absolutely relative to flickable) ColumnLayout { anchors.right: fabRoot.right anchors.bottom: fabRoot.top diff --git a/modules/controlcenter/bluetooth/Settings.qml b/modules/controlcenter/bluetooth/Settings.qml index b3245ab..c547240 100644 --- a/modules/controlcenter/bluetooth/Settings.qml +++ b/modules/controlcenter/bluetooth/Settings.qml @@ -328,7 +328,7 @@ ColumnLayout { anchors.left: parent.left - text: qsTr("Rename adapter (currently does not work)") // FIXME: remove disclaimer when fixed + text: qsTr("Rename adapter (currently does not work)") color: Colours.palette.m3outline font.pointSize: Appearance.font.size.small } @@ -345,8 +345,6 @@ ColumnLayout { readOnly: !root.session.bt.editingAdapterName onAccepted: { root.session.bt.editingAdapterName = false; - // Doesn't work for now, will be added to QS later - // root.session.bt.currentAdapter.name = text; } leftPadding: Appearance.padding.normal diff --git a/modules/controlcenter/components/DeviceDetails.qml b/modules/controlcenter/components/DeviceDetails.qml index 768e77a..d2e8835 100644 --- a/modules/controlcenter/components/DeviceDetails.qml +++ b/modules/controlcenter/components/DeviceDetails.qml @@ -18,10 +18,7 @@ Item { property Component headerComponent: null property list sections: [] - // Optional: Custom content to insert after header but before sections property Component topContent: null - - // Optional: Custom content to insert after all sections property Component bottomContent: null implicitWidth: layout.implicitWidth @@ -35,7 +32,6 @@ Item { anchors.top: parent.top spacing: Appearance.spacing.normal - // Header component (e.g., ConnectionHeader or SettingsHeader) Loader { id: headerLoader @@ -44,7 +40,6 @@ Item { visible: root.headerComponent !== null } - // Top content (optional) Loader { id: topContentLoader @@ -53,7 +48,6 @@ Item { visible: root.topContent !== null } - // Sections Repeater { model: root.sections @@ -65,7 +59,6 @@ Item { } } - // Bottom content (optional) Loader { id: bottomContentLoader diff --git a/modules/controlcenter/components/DeviceList.qml b/modules/controlcenter/components/DeviceList.qml index a6821d8..75dd913 100644 --- a/modules/controlcenter/components/DeviceList.qml +++ b/modules/controlcenter/components/DeviceList.qml @@ -28,7 +28,6 @@ ColumnLayout { spacing: Appearance.spacing.small - // Header with action buttons (optional) Loader { id: headerLoader @@ -37,7 +36,6 @@ ColumnLayout { visible: root.headerComponent !== null && root.showHeader } - // Title and description row RowLayout { Layout.fillWidth: true Layout.topMargin: root.headerComponent ? 0 : 0 @@ -61,10 +59,8 @@ ColumnLayout { } } - // Expose view for access from parent components property alias view: view - // Description text StyledText { visible: root.description !== "" Layout.fillWidth: true @@ -72,20 +68,18 @@ ColumnLayout { color: Colours.palette.m3outline } - // List view StyledListView { id: view Layout.fillWidth: true - // Use contentHeight to show all items without estimation implicitHeight: contentHeight model: root.model delegate: root.delegate spacing: Appearance.spacing.small / 2 - interactive: false // Disable individual scrolling - parent pane handles it - clip: false // Don't clip - let parent handle scrolling + interactive: false + clip: false } } diff --git a/modules/controlcenter/components/PaneTransition.qml b/modules/controlcenter/components/PaneTransition.qml index 1da4afb..d1814b5 100644 --- a/modules/controlcenter/components/PaneTransition.qml +++ b/modules/controlcenter/components/PaneTransition.qml @@ -3,26 +3,17 @@ pragma ComponentBehavior: Bound import qs.config import QtQuick -// Reusable pane transition animation component -// Provides standard fade-out/scale-down → update → fade-in/scale-up animation -// Used when switching between detail/settings views in panes SequentialAnimation { id: root - // The Loader element to animate required property Item target - - // Optional list of PropertyActions to execute during the transition - // These typically update the component being displayed property list propertyActions - // Animation parameters (with sensible defaults) property real scaleFrom: 1.0 property real scaleTo: 0.8 property real opacityFrom: 1.0 property real opacityTo: 0.0 - // Fade out and scale down ParallelAnimation { NumberAnimation { target: root.target @@ -45,8 +36,6 @@ SequentialAnimation { } } - // Execute property actions (component switching, state updates, etc.) - // This is where the component change happens while invisible ScriptAction { script: { for (let i = 0; i < root.propertyActions.length; i++) { @@ -58,7 +47,6 @@ SequentialAnimation { } } - // Fade in and scale up ParallelAnimation { NumberAnimation { target: root.target diff --git a/modules/controlcenter/components/SplitPaneLayout.qml b/modules/controlcenter/components/SplitPaneLayout.qml index 7bd7db0..8b4f0d9 100644 --- a/modules/controlcenter/components/SplitPaneLayout.qml +++ b/modules/controlcenter/components/SplitPaneLayout.qml @@ -15,19 +15,14 @@ RowLayout { property Component leftContent: null property Component rightContent: null - // Left pane configuration property real leftWidthRatio: 0.4 property int leftMinimumWidth: 420 property var leftLoaderProperties: ({}) - - // Right pane configuration property var rightLoaderProperties: ({}) - // Expose loaders for customization (access via splitLayout.leftLoader or splitLayout.rightLoader) property alias leftLoader: leftLoader property alias rightLoader: rightLoader - // Left pane Item { id: leftPane @@ -57,7 +52,6 @@ RowLayout { asynchronous: true sourceComponent: root.leftContent - // Apply any additional properties from leftLoaderProperties Component.onCompleted: { for (const key in root.leftLoaderProperties) { leftLoader[key] = root.leftLoaderProperties[key]; @@ -74,7 +68,6 @@ RowLayout { } } - // Right pane Item { id: rightPane @@ -101,7 +94,6 @@ RowLayout { asynchronous: true sourceComponent: root.rightContent - // Apply any additional properties from rightLoaderProperties Component.onCompleted: { for (const key in root.rightLoaderProperties) { rightLoader[key] = root.rightLoaderProperties[key]; diff --git a/modules/controlcenter/components/SplitPaneWithDetails.qml b/modules/controlcenter/components/SplitPaneWithDetails.qml index 6af8c1a..e873923 100644 --- a/modules/controlcenter/components/SplitPaneWithDetails.qml +++ b/modules/controlcenter/components/SplitPaneWithDetails.qml @@ -19,7 +19,6 @@ Item { property var activeItem: null property var paneIdGenerator: function(item) { return item ? String(item) : ""; } - // Optional: Additional component to overlay on top (e.g., password dialogs) property Component overlayComponent: null SplitPaneLayout { @@ -82,7 +81,6 @@ Item { } } - // Overlay component (e.g., password dialogs) Loader { id: overlayLoader diff --git a/modules/controlcenter/launcher/LauncherPane.qml b/modules/controlcenter/launcher/LauncherPane.qml index 803d7e0..47f87cc 100644 --- a/modules/controlcenter/launcher/LauncherPane.qml +++ b/modules/controlcenter/launcher/LauncherPane.qml @@ -62,26 +62,20 @@ Item { const appId = root.selectedApp.id || root.selectedApp.entry?.id; - // Create a new array to ensure change detection const hiddenApps = Config.launcher.hiddenApps ? [...Config.launcher.hiddenApps] : []; if (isHidden) { - // Add to hiddenApps if not already there if (!hiddenApps.includes(appId)) { hiddenApps.push(appId); } } else { - // Remove from hiddenApps const index = hiddenApps.indexOf(appId); if (index !== -1) { hiddenApps.splice(index, 1); } } - // Update Config Config.launcher.hiddenApps = hiddenApps; - - // Persist changes to disk Config.save(); } @@ -90,15 +84,13 @@ Item { id: allAppsDb path: `${Paths.state}/apps.sqlite` - entries: DesktopEntries.applications.values // No filter - show all apps + entries: DesktopEntries.applications.values } property string searchText: "" function filterApps(search: string): list { - // If search is empty, return all apps directly if (!search || search.trim() === "") { - // Convert QQmlListProperty to array const apps = []; for (let i = 0; i < allAppsDb.apps.length; i++) { apps.push(allAppsDb.apps[i]); @@ -110,7 +102,6 @@ Item { return []; } - // Prepare apps for fuzzy search const preparedApps = []; for (let i = 0; i < allAppsDb.apps.length; i++) { const app = allAppsDb.apps[i]; @@ -121,14 +112,12 @@ Item { }); } - // Perform fuzzy search const results = Fuzzy.go(search, preparedApps, { all: true, keys: ["name"], scoreFn: r => r[0].score }); - // Return sorted by score (highest first) return results .sort((a, b) => b._score - a._score) .map(r => r.obj._item); @@ -192,7 +181,6 @@ Item { if (root.session.launcher.active) { root.session.launcher.active = null; } else { - // Toggle to show settings - if there are apps, select the first one, otherwise show settings if (root.filteredApps.length > 0) { root.session.launcher.active = root.filteredApps[0]; } @@ -302,13 +290,7 @@ Item { Layout.fillWidth: true Layout.fillHeight: true asynchronous: true - active: { - // Lazy load: activate when left pane is loaded - // The ListView will load asynchronously, and search will work because filteredApps - // is updated regardless of whether the ListView is loaded - // Access loader through parent - this will be set when component loads - return true; - } + active: true sourceComponent: StyledListView { id: appsListView @@ -418,11 +400,9 @@ Item { sourceComponent: rightLauncherPane.targetComponent active: true - // Expose displayedApp to loaded components property var displayedApp: rightLauncherPane.displayedApp onItemChanged: { - // Ensure displayedApp is set when item is created (for async loading) if (item && rightLauncherPane.pane && rightLauncherPane.displayedApp !== rightLauncherPane.pane) { rightLauncherPane.displayedApp = rightLauncherPane.pane; } @@ -508,12 +488,10 @@ Item { id: appDetailsLayout anchors.fill: parent - // Get displayedApp from parent Loader (the Loader has displayedApp property we set) readonly property var displayedApp: parent && parent.displayedApp !== undefined ? parent.displayedApp : null spacing: Appearance.spacing.normal - // Show SettingsHeader when no app is selected, or show app icon + title when app is selected SettingsHeader { Layout.leftMargin: Appearance.padding.large * 2 Layout.rightMargin: Appearance.padding.large * 2 @@ -523,7 +501,6 @@ Item { title: qsTr("Launcher Applications") } - // App icon and title display (shown when app is selected) Item { Layout.alignment: Qt.AlignHCenter Layout.leftMargin: Appearance.padding.large * 2 diff --git a/modules/controlcenter/network/NetworkingPane.qml b/modules/controlcenter/network/NetworkingPane.qml index e28d35c..4446428 100644 --- a/modules/controlcenter/network/NetworkingPane.qml +++ b/modules/controlcenter/network/NetworkingPane.qml @@ -45,7 +45,6 @@ Item { anchors.right: parent.right spacing: Appearance.spacing.normal - // Network header above the collapsible sections RowLayout { Layout.fillWidth: true spacing: Appearance.spacing.smaller @@ -102,7 +101,6 @@ Item { root.session.ethernet.active = null; root.session.network.active = null; } else { - // Toggle to show settings - prefer ethernet if available, otherwise wireless if (Nmcli.ethernetDevices.length > 0) { root.session.ethernet.active = Nmcli.ethernetDevices[0]; } else if (Nmcli.networks.length > 0) { diff --git a/modules/controlcenter/network/WirelessDetails.qml b/modules/controlcenter/network/WirelessDetails.qml index 7f6a4aa..cf16400 100644 --- a/modules/controlcenter/network/WirelessDetails.qml +++ b/modules/controlcenter/network/WirelessDetails.qml @@ -27,7 +27,6 @@ DeviceDetails { } onNetworkChanged: { - // Restart timer when network changes connectionUpdateTimer.stop(); if (network && network.ssid) { connectionUpdateTimer.start(); @@ -48,11 +47,9 @@ DeviceDetails { updateDeviceDetails(); } function onWirelessDeviceDetailsChanged() { - // When details are updated, check if we should stop the timer if (network && network.ssid) { const isActive = network.active || (Nmcli.active && Nmcli.active.ssid === network.ssid); if (isActive && Nmcli.wirelessDeviceDetails && Nmcli.wirelessDeviceDetails !== null) { - // We have details for the active network, stop the timer connectionUpdateTimer.stop(); } } @@ -65,22 +62,16 @@ DeviceDetails { repeat: true running: network && network.ssid onTriggered: { - // Periodically check if network becomes active and update details if (network) { const isActive = network.active || (Nmcli.active && Nmcli.active.ssid === network.ssid); if (isActive) { - // Network is active - check if we have details if (!Nmcli.wirelessDeviceDetails || Nmcli.wirelessDeviceDetails === null) { - // Network is active but we don't have details yet, fetch them Nmcli.getWirelessDeviceDetails("", () => { - // After fetching, check if we got details - if not, timer will try again }); } else { - // We have details, can stop the timer connectionUpdateTimer.stop(); } } else { - // Network is not active, clear details if (Nmcli.wirelessDeviceDetails !== null) { Nmcli.wirelessDeviceDetails = null; } diff --git a/modules/controlcenter/network/WirelessList.qml b/modules/controlcenter/network/WirelessList.qml index 4726712..9dabe9d 100644 --- a/modules/controlcenter/network/WirelessList.qml +++ b/modules/controlcenter/network/WirelessList.qml @@ -33,10 +33,8 @@ DeviceList { model: ScriptModel { values: [...Nmcli.networks].sort((a, b) => { - // Put active/connected network first if (a.active !== b.active) return b.active - a.active; - // Then sort by signal strength return b.strength - a.strength; }) } @@ -114,7 +112,6 @@ DeviceList { StateLayer { function onClicked(): void { root.session.network.active = modelData; - // Check if we need to refresh saved connections when selecting a network if (modelData && modelData.ssid) { root.checkSavedProfileForNetwork(modelData.ssid); } diff --git a/modules/controlcenter/network/WirelessPasswordDialog.qml b/modules/controlcenter/network/WirelessPasswordDialog.qml index 0f1a5cd..7c046af 100644 --- a/modules/controlcenter/network/WirelessPasswordDialog.qml +++ b/modules/controlcenter/network/WirelessPasswordDialog.qml @@ -19,7 +19,6 @@ Item { required property Session session readonly property var network: { - // Prefer pendingNetwork, then active network if (session.network.pendingNetwork) { return session.network.pendingNetwork; } @@ -157,12 +156,10 @@ Item { focus: true Keys.onPressed: event => { - // Ensure we have focus when receiving keyboard input if (!activeFocus) { forceActiveFocus(); } - // Clear error when user starts typing if (connectButton.hasError && event.text && event.text.length > 0) { connectButton.hasError = false; } @@ -191,7 +188,6 @@ Item { target: root.session.network function onShowPasswordDialogChanged(): void { if (root.session.network.showPasswordDialog) { - // Use callLater to ensure focus happens after dialog is fully rendered Qt.callLater(() => { passwordContainer.forceActiveFocus(); passwordContainer.passwordBuffer = ""; @@ -205,7 +201,6 @@ Item { target: root function onVisibleChanged(): void { if (root.visible) { - // Use callLater to ensure focus happens after dialog is fully rendered Qt.callLater(() => { passwordContainer.forceActiveFocus(); }); @@ -383,46 +378,36 @@ Item { return; } - // Clear any previous error hasError = false; - - // Set connecting state connecting = true; enabled = false; text = qsTr("Connecting..."); - // Connect to network NetworkConnection.connectWithPassword(root.network, password, result => { - if (result && result.success) - // Connection successful, monitor will handle the rest - {} else if (result && result.needsPassword) { - // Shouldn't happen since we provided password + if (result && result.success) { + } else if (result && result.needsPassword) { connectionMonitor.stop(); connecting = false; hasError = true; enabled = true; text = qsTr("Connect"); passwordContainer.passwordBuffer = ""; - // Delete the failed connection if (root.network && root.network.ssid) { Nmcli.forgetNetwork(root.network.ssid); } } else { - // Connection failed immediately - show error connectionMonitor.stop(); connecting = false; hasError = true; enabled = true; text = qsTr("Connect"); passwordContainer.passwordBuffer = ""; - // Delete the failed connection if (root.network && root.network.ssid) { Nmcli.forgetNetwork(root.network.ssid); } } }); - // Start monitoring connection connectionMonitor.start(); } } @@ -435,19 +420,14 @@ Item { return; } - // Check if we're connected to the target network (case-insensitive SSID comparison) const isConnected = root.network && Nmcli.active && Nmcli.active.ssid && Nmcli.active.ssid.toLowerCase().trim() === root.network.ssid.toLowerCase().trim(); if (isConnected) { - // Successfully connected - give it a moment for network list to update - // Use Timer for actual delay connectionSuccessTimer.start(); return; } - // Check for connection failures - if pending connection was cleared but we're not connected if (Nmcli.pendingConnection === null && connectButton.connecting) { - // Wait a bit more before giving up (allow time for connection to establish) if (connectionMonitor.repeatCount > 10) { connectionMonitor.stop(); connectButton.connecting = false; @@ -455,7 +435,6 @@ Item { connectButton.enabled = true; connectButton.text = qsTr("Connect"); passwordContainer.passwordBuffer = ""; - // Delete the failed connection if (root.network && root.network.ssid) { Nmcli.forgetNetwork(root.network.ssid); } @@ -486,7 +465,6 @@ Item { id: connectionSuccessTimer interval: 500 onTriggered: { - // Double-check connection is still active if (root.visible && Nmcli.active && Nmcli.active.ssid) { const stillConnected = Nmcli.active.ssid.toLowerCase().trim() === root.network.ssid.toLowerCase().trim(); if (stillConnected) { @@ -514,7 +492,6 @@ Item { connectButton.enabled = true; connectButton.text = qsTr("Connect"); passwordContainer.passwordBuffer = ""; - // Delete the failed connection Nmcli.forgetNetwork(ssid); } } diff --git a/modules/controlcenter/state/BluetoothState.qml b/modules/controlcenter/state/BluetoothState.qml index db8c7e1..00497ce 100644 --- a/modules/controlcenter/state/BluetoothState.qml +++ b/modules/controlcenter/state/BluetoothState.qml @@ -4,13 +4,8 @@ import QtQuick QtObject { id: root - // Active selected device property BluetoothDevice active: null - - // Current adapter being used property BluetoothAdapter currentAdapter: Bluetooth.defaultAdapter - - // UI state flags property bool editingAdapterName: false property bool fabMenuOpen: false property bool editingDeviceName: false diff --git a/modules/controlcenter/state/EthernetState.qml b/modules/controlcenter/state/EthernetState.qml index 25b243a..c5da7aa 100644 --- a/modules/controlcenter/state/EthernetState.qml +++ b/modules/controlcenter/state/EthernetState.qml @@ -3,7 +3,6 @@ import QtQuick QtObject { id: root - // Active selected ethernet interface property var active: null } diff --git a/modules/controlcenter/state/LauncherState.qml b/modules/controlcenter/state/LauncherState.qml index cd9eeb6..c5da7aa 100644 --- a/modules/controlcenter/state/LauncherState.qml +++ b/modules/controlcenter/state/LauncherState.qml @@ -3,7 +3,6 @@ import QtQuick QtObject { id: root - // Active selected application property var active: null } diff --git a/modules/controlcenter/state/NetworkState.qml b/modules/controlcenter/state/NetworkState.qml index 651a35c..da13e65 100644 --- a/modules/controlcenter/state/NetworkState.qml +++ b/modules/controlcenter/state/NetworkState.qml @@ -3,10 +3,7 @@ import QtQuick QtObject { id: root - // Active selected wireless network property var active: null - - // Password dialog state property bool showPasswordDialog: false property var pendingNetwork: null } diff --git a/modules/controlcenter/taskbar/TaskbarPane.qml b/modules/controlcenter/taskbar/TaskbarPane.qml index 1c3adbc..f452b07 100644 --- a/modules/controlcenter/taskbar/TaskbarPane.qml +++ b/modules/controlcenter/taskbar/TaskbarPane.qml @@ -18,15 +18,10 @@ Item { required property Session session - // Clock property bool clockShowIcon: Config.bar.clock.showIcon ?? true - - // Bar Behavior property bool persistent: Config.bar.persistent ?? true property bool showOnHover: Config.bar.showOnHover ?? true property int dragThreshold: Config.bar.dragThreshold ?? 20 - - // Status Icons property bool showAudio: Config.bar.status.showAudio ?? true property bool showMicrophone: Config.bar.status.showMicrophone ?? true property bool showKbLayout: Config.bar.status.showKbLayout ?? false @@ -34,25 +29,17 @@ Item { property bool showBluetooth: Config.bar.status.showBluetooth ?? true property bool showBattery: Config.bar.status.showBattery ?? true property bool showLockStatus: Config.bar.status.showLockStatus ?? true - - // Tray Settings property bool trayBackground: Config.bar.tray.background ?? false property bool trayCompact: Config.bar.tray.compact ?? false property bool trayRecolour: Config.bar.tray.recolour ?? false - - // Workspaces property int workspacesShown: Config.bar.workspaces.shown ?? 5 property bool workspacesActiveIndicator: Config.bar.workspaces.activeIndicator ?? true property bool workspacesOccupiedBg: Config.bar.workspaces.occupiedBg ?? false property bool workspacesShowWindows: Config.bar.workspaces.showWindows ?? false property bool workspacesPerMonitor: Config.bar.workspaces.perMonitorWorkspaces ?? true - - // Scroll Actions property bool scrollWorkspaces: Config.bar.scrollActions.workspaces ?? true property bool scrollVolume: Config.bar.scrollActions.volume ?? true property bool scrollBrightness: Config.bar.scrollActions.brightness ?? true - - // Popouts property bool popoutActiveWindow: Config.bar.popouts.activeWindow ?? true property bool popoutTray: Config.bar.popouts.tray ?? true property bool popoutStatusIcons: Config.bar.popouts.statusIcons ?? true @@ -60,7 +47,6 @@ Item { anchors.fill: parent Component.onCompleted: { - // Update entries if (Config.bar.entries) { entriesModel.clear(); for (let i = 0; i < Config.bar.entries.length; i++) { @@ -74,15 +60,10 @@ Item { } function saveConfig(entryIndex, entryEnabled) { - // Update clock setting Config.bar.clock.showIcon = root.clockShowIcon; - - // Update bar behavior Config.bar.persistent = root.persistent; Config.bar.showOnHover = root.showOnHover; Config.bar.dragThreshold = root.dragThreshold; - - // Update status icons Config.bar.status.showAudio = root.showAudio; Config.bar.status.showMicrophone = root.showMicrophone; Config.bar.status.showKbLayout = root.showKbLayout; @@ -90,35 +71,24 @@ Item { Config.bar.status.showBluetooth = root.showBluetooth; Config.bar.status.showBattery = root.showBattery; Config.bar.status.showLockStatus = root.showLockStatus; - - // Update tray settings Config.bar.tray.background = root.trayBackground; Config.bar.tray.compact = root.trayCompact; Config.bar.tray.recolour = root.trayRecolour; - - // Update workspaces Config.bar.workspaces.shown = root.workspacesShown; Config.bar.workspaces.activeIndicator = root.workspacesActiveIndicator; Config.bar.workspaces.occupiedBg = root.workspacesOccupiedBg; Config.bar.workspaces.showWindows = root.workspacesShowWindows; Config.bar.workspaces.perMonitorWorkspaces = root.workspacesPerMonitor; - - // Update scroll actions Config.bar.scrollActions.workspaces = root.scrollWorkspaces; Config.bar.scrollActions.volume = root.scrollVolume; Config.bar.scrollActions.brightness = root.scrollBrightness; - - // Update popouts Config.bar.popouts.activeWindow = root.popoutActiveWindow; Config.bar.popouts.tray = root.popoutTray; Config.bar.popouts.statusIcons = root.popoutStatusIcons; - // Update entries from the model (same approach as clock - use provided value if available) const entries = []; for (let i = 0; i < entriesModel.count; i++) { const entry = entriesModel.get(i); - // If this is the entry being updated, use the provided value (same as clock toggle reads from switch) - // Otherwise use the value from the model let enabled = entry.enabled; if (entryIndex !== undefined && i === entryIndex) { enabled = entryEnabled; @@ -129,8 +99,6 @@ Item { }); } Config.bar.entries = entries; - - // Persist changes to disk Config.save(); } -- cgit v1.2.3-freya From a243b6148b03d3effb7b86993f8ce89911e49b80 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Wed, 19 Nov 2025 21:57:55 -0500 Subject: refactor: replaced input fields with SliderInput components --- .../controlcenter/appearance/AppearancePane.qml | 1122 +++----------------- modules/controlcenter/components/DeviceDetails.qml | 2 +- modules/controlcenter/components/SliderInput.qml | 207 ++++ .../components/controls/SliderInput.qml | 179 ++++ modules/controlcenter/network/EthernetDetails.qml | 4 +- modules/controlcenter/network/WirelessDetails.qml | 4 +- modules/controlcenter/taskbar/TaskbarPane.qml | 106 +- 7 files changed, 550 insertions(+), 1074 deletions(-) create mode 100644 modules/controlcenter/components/SliderInput.qml create mode 100644 modules/controlcenter/components/controls/SliderInput.qml (limited to 'modules/controlcenter/appearance') diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index 5b7e859..dec260d 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -962,98 +962,20 @@ Item { SectionContainer { contentSpacing: Appearance.spacing.normal - ColumnLayout { + SliderInput { Layout.fillWidth: true - spacing: Appearance.spacing.small - - RowLayout { - Layout.fillWidth: true - spacing: Appearance.spacing.normal - - StyledText { - text: qsTr("Animation duration scale") - font.pointSize: Appearance.font.size.normal - } - - Item { - Layout.fillWidth: true - } - - StyledRect { - Layout.preferredWidth: 70 - implicitHeight: animDurationsInput.implicitHeight + Appearance.padding.small * 2 - color: animDurationsInputHover.containsMouse || animDurationsInput.activeFocus - ? Colours.layer(Colours.palette.m3surfaceContainer, 3) - : Colours.layer(Colours.palette.m3surfaceContainer, 2) - radius: Appearance.rounding.small - border.width: 1 - border.color: animDurationsInput.activeFocus - ? Colours.palette.m3primary - : Qt.alpha(Colours.palette.m3outline, 0.3) - - Behavior on color { CAnim {} } - Behavior on border.color { CAnim {} } - - MouseArea { - id: animDurationsInputHover - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.IBeamCursor - acceptedButtons: Qt.NoButton - } - - StyledTextField { - id: animDurationsInput - anchors.centerIn: parent - width: parent.width - Appearance.padding.normal - horizontalAlignment: TextInput.AlignHCenter - validator: DoubleValidator { bottom: 0.1; top: 5.0 } - - Component.onCompleted: { - text = (rootPane.animDurationsScale).toFixed(1); - } - - onTextChanged: { - if (activeFocus) { - const val = parseFloat(text); - if (!isNaN(val) && val >= 0.1 && val <= 5.0) { - rootPane.animDurationsScale = val; - rootPane.saveConfig(); - } - } - } - onEditingFinished: { - const val = parseFloat(text); - if (isNaN(val) || val < 0.1 || val > 5.0) { - text = (rootPane.animDurationsScale).toFixed(1); - } - } - } - } - - StyledText { - text: "×" - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.normal - } - } - - StyledSlider { - id: animDurationsSlider - - Layout.fillWidth: true - implicitHeight: Appearance.padding.normal * 3 - - from: 0.1 - to: 5.0 - value: rootPane.animDurationsScale - onMoved: { - rootPane.animDurationsScale = animDurationsSlider.value; - if (!animDurationsInput.activeFocus) { - animDurationsInput.text = (animDurationsSlider.value).toFixed(1); - } - rootPane.saveConfig(); - } + + label: qsTr("Animation duration scale") + value: rootPane.animDurationsScale + from: 0.1 + to: 5.0 + decimals: 1 + suffix: "×" + validator: DoubleValidator { bottom: 0.1; top: 5.0 } + + onValueModified: (newValue) => { + rootPane.animDurationsScale = newValue; + rootPane.saveConfig(); } } } @@ -1312,98 +1234,20 @@ Item { SectionContainer { contentSpacing: Appearance.spacing.normal - ColumnLayout { + SliderInput { Layout.fillWidth: true - spacing: Appearance.spacing.small - - RowLayout { - Layout.fillWidth: true - spacing: Appearance.spacing.normal - - StyledText { - text: qsTr("Font size scale") - font.pointSize: Appearance.font.size.normal - } - - Item { - Layout.fillWidth: true - } - - StyledRect { - Layout.preferredWidth: 70 - implicitHeight: fontSizeInput.implicitHeight + Appearance.padding.small * 2 - color: fontSizeInputHover.containsMouse || fontSizeInput.activeFocus - ? Colours.layer(Colours.palette.m3surfaceContainer, 3) - : Colours.layer(Colours.palette.m3surfaceContainer, 2) - radius: Appearance.rounding.small - border.width: 1 - border.color: fontSizeInput.activeFocus - ? Colours.palette.m3primary - : Qt.alpha(Colours.palette.m3outline, 0.3) - - Behavior on color { CAnim {} } - Behavior on border.color { CAnim {} } - - MouseArea { - id: fontSizeInputHover - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.IBeamCursor - acceptedButtons: Qt.NoButton - } - - StyledTextField { - id: fontSizeInput - anchors.centerIn: parent - width: parent.width - Appearance.padding.normal - horizontalAlignment: TextInput.AlignHCenter - validator: DoubleValidator { bottom: 0.7; top: 1.5 } - - Component.onCompleted: { - text = (rootPane.fontSizeScale).toFixed(1); - } - - onTextChanged: { - if (activeFocus) { - const val = parseFloat(text); - if (!isNaN(val) && val >= 0.7 && val <= 1.5) { - rootPane.fontSizeScale = val; - rootPane.saveConfig(); - } - } - } - onEditingFinished: { - const val = parseFloat(text); - if (isNaN(val) || val < 0.7 || val > 1.5) { - text = (rootPane.fontSizeScale).toFixed(1); - } - } - } - } - - StyledText { - text: "×" - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.normal - } - } - - StyledSlider { - id: fontSizeSlider - - Layout.fillWidth: true - implicitHeight: Appearance.padding.normal * 3 - - from: 0.7 - to: 1.5 - value: rootPane.fontSizeScale - onMoved: { - rootPane.fontSizeScale = fontSizeSlider.value; - if (!fontSizeInput.activeFocus) { - fontSizeInput.text = (fontSizeSlider.value).toFixed(1); - } - rootPane.saveConfig(); - } + + label: qsTr("Font size scale") + value: rootPane.fontSizeScale + from: 0.7 + to: 1.5 + decimals: 2 + suffix: "×" + validator: DoubleValidator { bottom: 0.7; top: 1.5 } + + onValueModified: (newValue) => { + rootPane.fontSizeScale = newValue; + rootPane.saveConfig(); } } } @@ -1417,98 +1261,20 @@ Item { SectionContainer { contentSpacing: Appearance.spacing.normal - ColumnLayout { + SliderInput { Layout.fillWidth: true - spacing: Appearance.spacing.small - - RowLayout { - Layout.fillWidth: true - spacing: Appearance.spacing.normal - - StyledText { - text: qsTr("Padding scale") - font.pointSize: Appearance.font.size.normal - } - - Item { - Layout.fillWidth: true - } - - StyledRect { - Layout.preferredWidth: 70 - implicitHeight: paddingInput.implicitHeight + Appearance.padding.small * 2 - color: paddingInputHover.containsMouse || paddingInput.activeFocus - ? Colours.layer(Colours.palette.m3surfaceContainer, 3) - : Colours.layer(Colours.palette.m3surfaceContainer, 2) - radius: Appearance.rounding.small - border.width: 1 - border.color: paddingInput.activeFocus - ? Colours.palette.m3primary - : Qt.alpha(Colours.palette.m3outline, 0.3) - - Behavior on color { CAnim {} } - Behavior on border.color { CAnim {} } - - MouseArea { - id: paddingInputHover - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.IBeamCursor - acceptedButtons: Qt.NoButton - } - - StyledTextField { - id: paddingInput - anchors.centerIn: parent - width: parent.width - Appearance.padding.normal - horizontalAlignment: TextInput.AlignHCenter - validator: DoubleValidator { bottom: 0.5; top: 2.0 } - - Component.onCompleted: { - text = (rootPane.paddingScale).toFixed(1); - } - - onTextChanged: { - if (activeFocus) { - const val = parseFloat(text); - if (!isNaN(val) && val >= 0.5 && val <= 2.0) { - rootPane.paddingScale = val; - rootPane.saveConfig(); - } - } - } - onEditingFinished: { - const val = parseFloat(text); - if (isNaN(val) || val < 0.5 || val > 2.0) { - text = (rootPane.paddingScale).toFixed(1); - } - } - } - } - - StyledText { - text: "×" - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.normal - } - } - - StyledSlider { - id: paddingSlider - - Layout.fillWidth: true - implicitHeight: Appearance.padding.normal * 3 - - from: 0.5 - to: 2.0 - value: rootPane.paddingScale - onMoved: { - rootPane.paddingScale = paddingSlider.value; - if (!paddingInput.activeFocus) { - paddingInput.text = (paddingSlider.value).toFixed(1); - } - rootPane.saveConfig(); - } + + label: qsTr("Padding scale") + value: rootPane.paddingScale + from: 0.5 + to: 2.0 + decimals: 1 + suffix: "×" + validator: DoubleValidator { bottom: 0.5; top: 2.0 } + + onValueModified: (newValue) => { + rootPane.paddingScale = newValue; + rootPane.saveConfig(); } } } @@ -1516,98 +1282,20 @@ Item { SectionContainer { contentSpacing: Appearance.spacing.normal - ColumnLayout { + SliderInput { Layout.fillWidth: true - spacing: Appearance.spacing.small - - RowLayout { - Layout.fillWidth: true - spacing: Appearance.spacing.normal - - StyledText { - text: qsTr("Rounding scale") - font.pointSize: Appearance.font.size.normal - } - - Item { - Layout.fillWidth: true - } - - StyledRect { - Layout.preferredWidth: 70 - implicitHeight: roundingInput.implicitHeight + Appearance.padding.small * 2 - color: roundingInputHover.containsMouse || roundingInput.activeFocus - ? Colours.layer(Colours.palette.m3surfaceContainer, 3) - : Colours.layer(Colours.palette.m3surfaceContainer, 2) - radius: Appearance.rounding.small - border.width: 1 - border.color: roundingInput.activeFocus - ? Colours.palette.m3primary - : Qt.alpha(Colours.palette.m3outline, 0.3) - - Behavior on color { CAnim {} } - Behavior on border.color { CAnim {} } - - MouseArea { - id: roundingInputHover - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.IBeamCursor - acceptedButtons: Qt.NoButton - } - - StyledTextField { - id: roundingInput - anchors.centerIn: parent - width: parent.width - Appearance.padding.normal - horizontalAlignment: TextInput.AlignHCenter - validator: DoubleValidator { bottom: 0.1; top: 5.0 } - - Component.onCompleted: { - text = (rootPane.roundingScale).toFixed(1); - } - - onTextChanged: { - if (activeFocus) { - const val = parseFloat(text); - if (!isNaN(val) && val >= 0.1 && val <= 5.0) { - rootPane.roundingScale = val; - rootPane.saveConfig(); - } - } - } - onEditingFinished: { - const val = parseFloat(text); - if (isNaN(val) || val < 0.1 || val > 5.0) { - text = (rootPane.roundingScale).toFixed(1); - } - } - } - } - - StyledText { - text: "×" - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.normal - } - } - - StyledSlider { - id: roundingSlider - - Layout.fillWidth: true - implicitHeight: Appearance.padding.normal * 3 - - from: 0.1 - to: 5.0 - value: rootPane.roundingScale - onMoved: { - rootPane.roundingScale = roundingSlider.value; - if (!roundingInput.activeFocus) { - roundingInput.text = (roundingSlider.value).toFixed(1); - } - rootPane.saveConfig(); - } + + label: qsTr("Rounding scale") + value: rootPane.roundingScale + from: 0.1 + to: 5.0 + decimals: 1 + suffix: "×" + validator: DoubleValidator { bottom: 0.1; top: 5.0 } + + onValueModified: (newValue) => { + rootPane.roundingScale = newValue; + rootPane.saveConfig(); } } } @@ -1615,98 +1303,20 @@ Item { SectionContainer { contentSpacing: Appearance.spacing.normal - ColumnLayout { + SliderInput { Layout.fillWidth: true - spacing: Appearance.spacing.small - - RowLayout { - Layout.fillWidth: true - spacing: Appearance.spacing.normal - - StyledText { - text: qsTr("Spacing scale") - font.pointSize: Appearance.font.size.normal - } - - Item { - Layout.fillWidth: true - } - - StyledRect { - Layout.preferredWidth: 70 - implicitHeight: spacingInput.implicitHeight + Appearance.padding.small * 2 - color: spacingInputHover.containsMouse || spacingInput.activeFocus - ? Colours.layer(Colours.palette.m3surfaceContainer, 3) - : Colours.layer(Colours.palette.m3surfaceContainer, 2) - radius: Appearance.rounding.small - border.width: 1 - border.color: spacingInput.activeFocus - ? Colours.palette.m3primary - : Qt.alpha(Colours.palette.m3outline, 0.3) - - Behavior on color { CAnim {} } - Behavior on border.color { CAnim {} } - - MouseArea { - id: spacingInputHover - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.IBeamCursor - acceptedButtons: Qt.NoButton - } - - StyledTextField { - id: spacingInput - anchors.centerIn: parent - width: parent.width - Appearance.padding.normal - horizontalAlignment: TextInput.AlignHCenter - validator: DoubleValidator { bottom: 0.1; top: 2.0 } - - Component.onCompleted: { - text = (rootPane.spacingScale).toFixed(1); - } - - onTextChanged: { - if (activeFocus) { - const val = parseFloat(text); - if (!isNaN(val) && val >= 0.1 && val <= 2.0) { - rootPane.spacingScale = val; - rootPane.saveConfig(); - } - } - } - onEditingFinished: { - const val = parseFloat(text); - if (isNaN(val) || val < 0.1 || val > 2.0) { - text = (rootPane.spacingScale).toFixed(1); - } - } - } - } - - StyledText { - text: "×" - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.normal - } - } - - StyledSlider { - id: spacingSlider - - Layout.fillWidth: true - implicitHeight: Appearance.padding.normal * 3 - - from: 0.1 - to: 2.0 - value: rootPane.spacingScale - onMoved: { - rootPane.spacingScale = spacingSlider.value; - if (!spacingInput.activeFocus) { - spacingInput.text = (spacingSlider.value).toFixed(1); - } - rootPane.saveConfig(); - } + + label: qsTr("Spacing scale") + value: rootPane.spacingScale + from: 0.1 + to: 2.0 + decimals: 1 + suffix: "×" + validator: DoubleValidator { bottom: 0.1; top: 2.0 } + + onValueModified: (newValue) => { + rootPane.spacingScale = newValue; + rootPane.saveConfig(); } } } @@ -1729,98 +1339,21 @@ Item { SectionContainer { contentSpacing: Appearance.spacing.normal - ColumnLayout { + SliderInput { Layout.fillWidth: true - spacing: Appearance.spacing.small - - RowLayout { - Layout.fillWidth: true - spacing: Appearance.spacing.normal - - StyledText { - text: qsTr("Transparency base") - font.pointSize: Appearance.font.size.normal - } - - Item { - Layout.fillWidth: true - } - - StyledRect { - Layout.preferredWidth: 70 - implicitHeight: transparencyBaseInput.implicitHeight + Appearance.padding.small * 2 - color: transparencyBaseInputHover.containsMouse || transparencyBaseInput.activeFocus - ? Colours.layer(Colours.palette.m3surfaceContainer, 3) - : Colours.layer(Colours.palette.m3surfaceContainer, 2) - radius: Appearance.rounding.small - border.width: 1 - border.color: transparencyBaseInput.activeFocus - ? Colours.palette.m3primary - : Qt.alpha(Colours.palette.m3outline, 0.3) - - Behavior on color { CAnim {} } - Behavior on border.color { CAnim {} } - - MouseArea { - id: transparencyBaseInputHover - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.IBeamCursor - acceptedButtons: Qt.NoButton - } - - StyledTextField { - id: transparencyBaseInput - anchors.centerIn: parent - width: parent.width - Appearance.padding.normal - horizontalAlignment: TextInput.AlignHCenter - validator: IntValidator { bottom: 0; top: 100 } - - Component.onCompleted: { - text = Math.round(rootPane.transparencyBase * 100).toString(); - } - - onTextChanged: { - if (activeFocus) { - const val = parseInt(text); - if (!isNaN(val) && val >= 0 && val <= 100) { - rootPane.transparencyBase = val / 100; - rootPane.saveConfig(); - } - } - } - onEditingFinished: { - const val = parseInt(text); - if (isNaN(val) || val < 0 || val > 100) { - text = Math.round(rootPane.transparencyBase * 100).toString(); - } - } - } - } - - StyledText { - text: "%" - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.normal - } - } - - StyledSlider { - id: baseSlider - - Layout.fillWidth: true - implicitHeight: Appearance.padding.normal * 3 - - from: 0 - to: 100 - value: rootPane.transparencyBase * 100 - onMoved: { - rootPane.transparencyBase = baseSlider.value / 100; - if (!transparencyBaseInput.activeFocus) { - transparencyBaseInput.text = Math.round(baseSlider.value).toString(); - } - rootPane.saveConfig(); - } + + label: qsTr("Transparency base") + value: rootPane.transparencyBase * 100 + from: 0 + to: 100 + suffix: "%" + validator: IntValidator { bottom: 0; top: 100 } + formatValueFunction: (val) => Math.round(val).toString() + parseValueFunction: (text) => parseInt(text) + + onValueModified: (newValue) => { + rootPane.transparencyBase = newValue / 100; + rootPane.saveConfig(); } } } @@ -1828,98 +1361,21 @@ Item { SectionContainer { contentSpacing: Appearance.spacing.normal - ColumnLayout { + SliderInput { Layout.fillWidth: true - spacing: Appearance.spacing.small - - RowLayout { - Layout.fillWidth: true - spacing: Appearance.spacing.normal - - StyledText { - text: qsTr("Transparency layers") - font.pointSize: Appearance.font.size.normal - } - - Item { - Layout.fillWidth: true - } - - StyledRect { - Layout.preferredWidth: 70 - implicitHeight: transparencyLayersInput.implicitHeight + Appearance.padding.small * 2 - color: transparencyLayersInputHover.containsMouse || transparencyLayersInput.activeFocus - ? Colours.layer(Colours.palette.m3surfaceContainer, 3) - : Colours.layer(Colours.palette.m3surfaceContainer, 2) - radius: Appearance.rounding.small - border.width: 1 - border.color: transparencyLayersInput.activeFocus - ? Colours.palette.m3primary - : Qt.alpha(Colours.palette.m3outline, 0.3) - - Behavior on color { CAnim {} } - Behavior on border.color { CAnim {} } - - MouseArea { - id: transparencyLayersInputHover - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.IBeamCursor - acceptedButtons: Qt.NoButton - } - - StyledTextField { - id: transparencyLayersInput - anchors.centerIn: parent - width: parent.width - Appearance.padding.normal - horizontalAlignment: TextInput.AlignHCenter - validator: IntValidator { bottom: 0; top: 100 } - - Component.onCompleted: { - text = Math.round(rootPane.transparencyLayers * 100).toString(); - } - - onTextChanged: { - if (activeFocus) { - const val = parseInt(text); - if (!isNaN(val) && val >= 0 && val <= 100) { - rootPane.transparencyLayers = val / 100; - rootPane.saveConfig(); - } - } - } - onEditingFinished: { - const val = parseInt(text); - if (isNaN(val) || val < 0 || val > 100) { - text = Math.round(rootPane.transparencyLayers * 100).toString(); - } - } - } - } - - StyledText { - text: "%" - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.normal - } - } - - StyledSlider { - id: layersSlider - - Layout.fillWidth: true - implicitHeight: Appearance.padding.normal * 3 - - from: 0 - to: 100 - value: rootPane.transparencyLayers * 100 - onMoved: { - rootPane.transparencyLayers = layersSlider.value / 100; - if (!transparencyLayersInput.activeFocus) { - transparencyLayersInput.text = Math.round(layersSlider.value).toString(); - } - rootPane.saveConfig(); - } + + label: qsTr("Transparency layers") + value: rootPane.transparencyLayers * 100 + from: 0 + to: 100 + suffix: "%" + validator: IntValidator { bottom: 0; top: 100 } + formatValueFunction: (val) => Math.round(val).toString() + parseValueFunction: (text) => parseInt(text) + + onValueModified: (newValue) => { + rootPane.transparencyLayers = newValue / 100; + rootPane.saveConfig(); } } } @@ -1933,92 +1389,20 @@ Item { SectionContainer { contentSpacing: Appearance.spacing.normal - ColumnLayout { + SliderInput { Layout.fillWidth: true - spacing: Appearance.spacing.small - - RowLayout { - Layout.fillWidth: true - spacing: Appearance.spacing.normal - - StyledText { - text: qsTr("Border rounding") - font.pointSize: Appearance.font.size.normal - } - - Item { - Layout.fillWidth: true - } - - StyledRect { - Layout.preferredWidth: 70 - implicitHeight: borderRoundingInput.implicitHeight + Appearance.padding.small * 2 - color: borderRoundingInputHover.containsMouse || borderRoundingInput.activeFocus - ? Colours.layer(Colours.palette.m3surfaceContainer, 3) - : Colours.layer(Colours.palette.m3surfaceContainer, 2) - radius: Appearance.rounding.small - border.width: 1 - border.color: borderRoundingInput.activeFocus - ? Colours.palette.m3primary - : Qt.alpha(Colours.palette.m3outline, 0.3) - - Behavior on color { CAnim {} } - Behavior on border.color { CAnim {} } - - MouseArea { - id: borderRoundingInputHover - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.IBeamCursor - acceptedButtons: Qt.NoButton - } - - StyledTextField { - id: borderRoundingInput - anchors.centerIn: parent - width: parent.width - Appearance.padding.normal - horizontalAlignment: TextInput.AlignHCenter - validator: DoubleValidator { bottom: 0.1; top: 100 } - - Component.onCompleted: { - text = (rootPane.borderRounding).toFixed(1); - } - - onTextChanged: { - if (activeFocus) { - const val = parseFloat(text); - if (!isNaN(val) && val >= 0.1 && val <= 100) { - rootPane.borderRounding = val; - rootPane.saveConfig(); - } - } - } - onEditingFinished: { - const val = parseFloat(text); - if (isNaN(val) || val < 0.1 || val > 100) { - text = (rootPane.borderRounding).toFixed(1); - } - } - } - } - } - - StyledSlider { - id: borderRoundingSlider - - Layout.fillWidth: true - implicitHeight: Appearance.padding.normal * 3 - - from: 0.1 - to: 100 - value: rootPane.borderRounding - onMoved: { - rootPane.borderRounding = borderRoundingSlider.value; - if (!borderRoundingInput.activeFocus) { - borderRoundingInput.text = (borderRoundingSlider.value).toFixed(1); - } - rootPane.saveConfig(); - } + + label: qsTr("Border rounding") + value: rootPane.borderRounding + from: 0.1 + to: 100 + decimals: 1 + suffix: "px" + validator: DoubleValidator { bottom: 0.1; top: 100 } + + onValueModified: (newValue) => { + rootPane.borderRounding = newValue; + rootPane.saveConfig(); } } } @@ -2026,92 +1410,20 @@ Item { SectionContainer { contentSpacing: Appearance.spacing.normal - ColumnLayout { + SliderInput { Layout.fillWidth: true - spacing: Appearance.spacing.small - - RowLayout { - Layout.fillWidth: true - spacing: Appearance.spacing.normal - - StyledText { - text: qsTr("Border thickness") - font.pointSize: Appearance.font.size.normal - } - - Item { - Layout.fillWidth: true - } - - StyledRect { - Layout.preferredWidth: 70 - implicitHeight: borderThicknessInput.implicitHeight + Appearance.padding.small * 2 - color: borderThicknessInputHover.containsMouse || borderThicknessInput.activeFocus - ? Colours.layer(Colours.palette.m3surfaceContainer, 3) - : Colours.layer(Colours.palette.m3surfaceContainer, 2) - radius: Appearance.rounding.small - border.width: 1 - border.color: borderThicknessInput.activeFocus - ? Colours.palette.m3primary - : Qt.alpha(Colours.palette.m3outline, 0.3) - - Behavior on color { CAnim {} } - Behavior on border.color { CAnim {} } - - MouseArea { - id: borderThicknessInputHover - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.IBeamCursor - acceptedButtons: Qt.NoButton - } - - StyledTextField { - id: borderThicknessInput - anchors.centerIn: parent - width: parent.width - Appearance.padding.normal - horizontalAlignment: TextInput.AlignHCenter - validator: DoubleValidator { bottom: 0.1; top: 100 } - - Component.onCompleted: { - text = (rootPane.borderThickness).toFixed(1); - } - - onTextChanged: { - if (activeFocus) { - const val = parseFloat(text); - if (!isNaN(val) && val >= 0.1 && val <= 100) { - rootPane.borderThickness = val; - rootPane.saveConfig(); - } - } - } - onEditingFinished: { - const val = parseFloat(text); - if (isNaN(val) || val < 0.1 || val > 100) { - text = (rootPane.borderThickness).toFixed(1); - } - } - } - } - } - - StyledSlider { - id: borderThicknessSlider - - Layout.fillWidth: true - implicitHeight: Appearance.padding.normal * 3 - - from: 0.1 - to: 100 - value: rootPane.borderThickness - onMoved: { - rootPane.borderThickness = borderThicknessSlider.value; - if (!borderThicknessInput.activeFocus) { - borderThicknessInput.text = (borderThicknessSlider.value).toFixed(1); - } - rootPane.saveConfig(); - } + + label: qsTr("Border thickness") + value: rootPane.borderThickness + from: 0.1 + to: 100 + decimals: 1 + suffix: "px" + validator: DoubleValidator { bottom: 0.1; top: 100 } + + onValueModified: (newValue) => { + rootPane.borderThickness = newValue; + rootPane.saveConfig(); } } } @@ -2168,186 +1480,40 @@ Item { SectionContainer { contentSpacing: Appearance.spacing.normal - ColumnLayout { + SliderInput { Layout.fillWidth: true - spacing: Appearance.spacing.small - - RowLayout { - Layout.fillWidth: true - spacing: Appearance.spacing.normal - - StyledText { - text: qsTr("Visualiser rounding") - font.pointSize: Appearance.font.size.normal - } - - Item { - Layout.fillWidth: true - } - - StyledRect { - Layout.preferredWidth: 70 - implicitHeight: visualiserRoundingInput.implicitHeight + Appearance.padding.small * 2 - color: visualiserRoundingInputHover.containsMouse || visualiserRoundingInput.activeFocus - ? Colours.layer(Colours.palette.m3surfaceContainer, 3) - : Colours.layer(Colours.palette.m3surfaceContainer, 2) - radius: Appearance.rounding.small - border.width: 1 - border.color: visualiserRoundingInput.activeFocus - ? Colours.palette.m3primary - : Qt.alpha(Colours.palette.m3outline, 0.3) - - Behavior on color { CAnim {} } - Behavior on border.color { CAnim {} } - - MouseArea { - id: visualiserRoundingInputHover - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.IBeamCursor - acceptedButtons: Qt.NoButton - } - - StyledTextField { - id: visualiserRoundingInput - anchors.centerIn: parent - width: parent.width - Appearance.padding.normal - horizontalAlignment: TextInput.AlignHCenter - validator: IntValidator { bottom: 0; top: 10 } - - Component.onCompleted: { - text = Math.round(rootPane.visualiserRounding).toString(); - } - - onTextChanged: { - if (activeFocus) { - const val = parseInt(text); - if (!isNaN(val) && val >= 0 && val <= 10) { - rootPane.visualiserRounding = val; - rootPane.saveConfig(); - } - } - } - onEditingFinished: { - const val = parseInt(text); - if (isNaN(val) || val < 0 || val > 10) { - text = Math.round(rootPane.visualiserRounding).toString(); - } - } - } - } + + label: qsTr("Visualiser rounding") + value: rootPane.visualiserRounding + from: 0 + to: 10 + stepSize: 1 + validator: IntValidator { bottom: 0; top: 10 } + formatValueFunction: (val) => Math.round(val).toString() + parseValueFunction: (text) => parseInt(text) + + onValueModified: (newValue) => { + rootPane.visualiserRounding = Math.round(newValue); + rootPane.saveConfig(); } - - StyledSlider { - id: visualiserRoundingSlider - - Layout.fillWidth: true - implicitHeight: Appearance.padding.normal * 3 - - from: 0 - to: 10 - stepSize: 1 - value: rootPane.visualiserRounding - onMoved: { - rootPane.visualiserRounding = Math.round(visualiserRoundingSlider.value); - if (!visualiserRoundingInput.activeFocus) { - visualiserRoundingInput.text = Math.round(visualiserRoundingSlider.value).toString(); - } - rootPane.saveConfig(); } } - } - } SectionContainer { contentSpacing: Appearance.spacing.normal - ColumnLayout { + SliderInput { Layout.fillWidth: true - spacing: Appearance.spacing.small - - RowLayout { - Layout.fillWidth: true - spacing: Appearance.spacing.normal - - StyledText { - text: qsTr("Visualiser spacing") - font.pointSize: Appearance.font.size.normal - } - - Item { - Layout.fillWidth: true - } - - StyledRect { - Layout.preferredWidth: 70 - implicitHeight: visualiserSpacingInput.implicitHeight + Appearance.padding.small * 2 - color: visualiserSpacingInputHover.containsMouse || visualiserSpacingInput.activeFocus - ? Colours.layer(Colours.palette.m3surfaceContainer, 3) - : Colours.layer(Colours.palette.m3surfaceContainer, 2) - radius: Appearance.rounding.small - border.width: 1 - border.color: visualiserSpacingInput.activeFocus - ? Colours.palette.m3primary - : Qt.alpha(Colours.palette.m3outline, 0.3) - - Behavior on color { CAnim {} } - Behavior on border.color { CAnim {} } - - MouseArea { - id: visualiserSpacingInputHover - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.IBeamCursor - acceptedButtons: Qt.NoButton - } - - StyledTextField { - id: visualiserSpacingInput - anchors.centerIn: parent - width: parent.width - Appearance.padding.normal - horizontalAlignment: TextInput.AlignHCenter - validator: DoubleValidator { bottom: 0; top: 2 } - - Component.onCompleted: { - text = (rootPane.visualiserSpacing).toFixed(1); - } - - onTextChanged: { - if (activeFocus) { - const val = parseFloat(text); - if (!isNaN(val) && val >= 0 && val <= 2) { - rootPane.visualiserSpacing = val; - rootPane.saveConfig(); - } - } - } - onEditingFinished: { - const val = parseFloat(text); - if (isNaN(val) || val < 0 || val > 2) { - text = (rootPane.visualiserSpacing).toFixed(1); - } - } - } - } - } - - StyledSlider { - id: visualiserSpacingSlider - - Layout.fillWidth: true - implicitHeight: Appearance.padding.normal * 3 - - from: 0 - to: 2 - value: rootPane.visualiserSpacing - onMoved: { - rootPane.visualiserSpacing = visualiserSpacingSlider.value; - if (!visualiserSpacingInput.activeFocus) { - visualiserSpacingInput.text = (visualiserSpacingSlider.value).toFixed(1); - } - rootPane.saveConfig(); - } + + label: qsTr("Visualiser spacing") + value: rootPane.visualiserSpacing + from: 0 + to: 2 + validator: DoubleValidator { bottom: 0; top: 2 } + + onValueModified: (newValue) => { + rootPane.visualiserSpacing = newValue; + rootPane.saveConfig(); } } } diff --git a/modules/controlcenter/components/DeviceDetails.qml b/modules/controlcenter/components/DeviceDetails.qml index d2e8835..8cc9177 100644 --- a/modules/controlcenter/components/DeviceDetails.qml +++ b/modules/controlcenter/components/DeviceDetails.qml @@ -12,7 +12,7 @@ import QtQuick.Layouts Item { id: root - required property Session session + property Session session property var device: null property Component headerComponent: null diff --git a/modules/controlcenter/components/SliderInput.qml b/modules/controlcenter/components/SliderInput.qml new file mode 100644 index 0000000..3d7cd4d --- /dev/null +++ b/modules/controlcenter/components/SliderInput.qml @@ -0,0 +1,207 @@ +pragma ComponentBehavior: Bound + +import qs.components +import qs.components.controls +import qs.components.effects +import qs.services +import qs.config +import QtQuick +import QtQuick.Layouts + +ColumnLayout { + id: root + + 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 int decimals: 1 // Number of decimal places to show (default: 1) + 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 + // Check if it's an IntValidator (IntValidator doesn't have a 'decimals' property) + if (validator && validator.bottom !== undefined && validator.decimals === undefined) { + return Math.round(val).toString(); + } + // For DoubleValidator or no validator, use the decimals property + return val.toFixed(root.decimals); + } + + 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 valueModified(real newValue) + + property bool _initialized: false + + spacing: Appearance.spacing.small + + Component.onCompleted: { + // Set initialized flag after a brief delay to allow component to fully load + Qt.callLater(() => { + _initialized = true; + }); + } + + 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: { + // 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; + } + } + + 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; + } + } + + 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 + + // Use Binding to allow slider to move freely during dragging + Binding { + target: slider + property: "value" + value: root.value + when: !slider.pressed + } + + 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) { + const newValue = root.stepSize > 0 ? Math.round(value / root.stepSize) * root.stepSize : value; + inputField.text = root.formatValue(newValue); + } + } + + onMoved: { + const newValue = root.stepSize > 0 ? Math.round(value / root.stepSize) * root.stepSize : value; + root.valueModified(newValue); + if (!inputField.activeFocus) { + inputField.text = root.formatValue(newValue); + } + } + } + + // 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) { + inputField.text = root.formatValue(root.value); + } + } +} + diff --git a/modules/controlcenter/components/controls/SliderInput.qml b/modules/controlcenter/components/controls/SliderInput.qml new file mode 100644 index 0000000..a114f7f --- /dev/null +++ b/modules/controlcenter/components/controls/SliderInput.qml @@ -0,0 +1,179 @@ +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); + } + } +} + diff --git a/modules/controlcenter/network/EthernetDetails.qml b/modules/controlcenter/network/EthernetDetails.qml index ad078ec..1cd6c0a 100644 --- a/modules/controlcenter/network/EthernetDetails.qml +++ b/modules/controlcenter/network/EthernetDetails.qml @@ -15,8 +15,8 @@ DeviceDetails { id: root required property Session session - readonly property var ethernetDevice: session.ethernet.active - + readonly property var ethernetDevice: root.session.ethernet.active + device: ethernetDevice Component.onCompleted: { diff --git a/modules/controlcenter/network/WirelessDetails.qml b/modules/controlcenter/network/WirelessDetails.qml index cf16400..47d42c2 100644 --- a/modules/controlcenter/network/WirelessDetails.qml +++ b/modules/controlcenter/network/WirelessDetails.qml @@ -17,8 +17,8 @@ DeviceDetails { id: root required property Session session - readonly property var network: session.network.active - + readonly property var network: root.session.network.active + device: network Component.onCompleted: { diff --git a/modules/controlcenter/taskbar/TaskbarPane.qml b/modules/controlcenter/taskbar/TaskbarPane.qml index f452b07..38c1179 100644 --- a/modules/controlcenter/taskbar/TaskbarPane.qml +++ b/modules/controlcenter/taskbar/TaskbarPane.qml @@ -1,6 +1,7 @@ pragma ComponentBehavior: Bound import ".." +import "../components" import qs.components import qs.components.controls import qs.components.effects @@ -520,98 +521,21 @@ Item { SectionContainer { contentSpacing: Appearance.spacing.normal - ColumnLayout { + SliderInput { Layout.fillWidth: true - spacing: Appearance.spacing.small - - RowLayout { - Layout.fillWidth: true - spacing: Appearance.spacing.normal - - 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(); - } + + label: qsTr("Drag threshold") + value: root.dragThreshold + from: 0 + to: 100 + suffix: "px" + validator: IntValidator { bottom: 0; top: 100 } + formatValueFunction: (val) => Math.round(val).toString() + parseValueFunction: (text) => parseInt(text) + + onValueModified: (newValue) => { + root.dragThreshold = Math.round(newValue); + root.saveConfig(); } } } -- cgit v1.2.3-freya From bf3a1781d12271a7edb9ead26eb59740f87877fd Mon Sep 17 00:00:00 2001 From: ATMDA Date: Wed, 19 Nov 2025 22:18:10 -0500 Subject: refactor: reorganized AppearancePane sections into dedicated components --- .../controlcenter/appearance/AppearancePane.qml | 515 +-------------------- .../appearance/sections/AnimationsSection.qml | 42 ++ .../appearance/sections/BackgroundSection.qml | 105 +++++ .../appearance/sections/BorderSection.qml | 63 +++ .../appearance/sections/ColorSchemeSection.qml | 147 ++++++ .../appearance/sections/ColorVariantSection.qml | 92 ++++ .../appearance/sections/ScalesSection.qml | 84 ++++ .../appearance/sections/ThemeModeSection.qml | 24 + .../appearance/sections/TransparencySection.qml | 74 +++ modules/controlcenter/network/NetworkingPane.qml | 14 +- 10 files changed, 659 insertions(+), 501 deletions(-) create mode 100644 modules/controlcenter/appearance/sections/AnimationsSection.qml create mode 100644 modules/controlcenter/appearance/sections/BackgroundSection.qml create mode 100644 modules/controlcenter/appearance/sections/BorderSection.qml create mode 100644 modules/controlcenter/appearance/sections/ColorSchemeSection.qml create mode 100644 modules/controlcenter/appearance/sections/ColorVariantSection.qml create mode 100644 modules/controlcenter/appearance/sections/ScalesSection.qml create mode 100644 modules/controlcenter/appearance/sections/ThemeModeSection.qml create mode 100644 modules/controlcenter/appearance/sections/TransparencySection.qml (limited to 'modules/controlcenter/appearance') diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index dec260d..d72700f 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -2,6 +2,7 @@ pragma ComponentBehavior: Bound import ".." import "../components" +import "./sections" import "../../launcher/services" import qs.components import qs.components.controls @@ -680,6 +681,8 @@ Item { anchors.left: parent.left anchors.right: parent.right spacing: Appearance.spacing.small + + readonly property var rootPane: sidebarFlickable.rootPane readonly property bool allSectionsExpanded: themeModeSection.expanded && @@ -724,261 +727,21 @@ Item { } } - CollapsibleSection { + ThemeModeSection { id: themeModeSection - title: qsTr("Theme mode") - description: qsTr("Light or dark theme") - showBackground: true - - SwitchRow { - label: qsTr("Dark mode") - checked: !Colours.currentLight - onToggled: checked => { - Colours.setMode(checked ? "dark" : "light"); - } - } } - CollapsibleSection { + ColorVariantSection { id: colorVariantSection - title: qsTr("Color variant") - description: qsTr("Material theme variant") - showBackground: true - - ColumnLayout { - Layout.fillWidth: true - spacing: Appearance.spacing.small / 2 - - Repeater { - model: M3Variants.list - - delegate: StyledRect { - required property var modelData - - 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 - - StateLayer { - function onClicked(): void { - const variant = modelData.variant; - - Schemes.currentVariant = variant; - Quickshell.execDetached(["caelestia", "scheme", "set", "-v", variant]); - - Qt.callLater(() => { - reloadTimer.restart(); - }); - } - } - - Timer { - id: reloadTimer - interval: 300 - onTriggered: { - Schemes.reload(); - } - } - - RowLayout { - id: variantRow - - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.normal - - spacing: Appearance.spacing.normal - - MaterialIcon { - text: modelData.icon - font.pointSize: Appearance.font.size.large - fill: modelData.variant === Schemes.currentVariant ? 1 : 0 - } - - StyledText { - Layout.fillWidth: true - text: modelData.name - font.weight: modelData.variant === Schemes.currentVariant ? 500 : 400 - } - - MaterialIcon { - visible: modelData.variant === Schemes.currentVariant - text: "check" - color: Colours.palette.m3primary - font.pointSize: Appearance.font.size.large - } - } - - implicitHeight: variantRow.implicitHeight + Appearance.padding.normal * 2 - } - } - } } - CollapsibleSection { + ColorSchemeSection { id: colorSchemeSection - title: qsTr("Color scheme") - description: qsTr("Available color schemes") - showBackground: true - - ColumnLayout { - Layout.fillWidth: true - spacing: Appearance.spacing.small / 2 - - Repeater { - model: Schemes.list - - delegate: StyledRect { - required property var modelData - - Layout.fillWidth: true - - readonly property string schemeKey: `${modelData.name} ${modelData.flavour}` - readonly property bool isCurrent: schemeKey === Schemes.currentScheme - - color: Qt.alpha(Colours.tPalette.m3surfaceContainer, isCurrent ? Colours.tPalette.m3surfaceContainer.a : 0) - radius: Appearance.rounding.normal - border.width: isCurrent ? 1 : 0 - border.color: Colours.palette.m3primary - - StateLayer { - function onClicked(): void { - const name = modelData.name; - const flavour = modelData.flavour; - const schemeKey = `${name} ${flavour}`; - - Schemes.currentScheme = schemeKey; - Quickshell.execDetached(["caelestia", "scheme", "set", "-n", name, "-f", flavour]); - - Qt.callLater(() => { - reloadTimer.restart(); - }); - } - } - - Timer { - id: reloadTimer - interval: 300 - onTriggered: { - Schemes.reload(); - } - } - - RowLayout { - id: schemeRow - - anchors.fill: parent - anchors.margins: Appearance.padding.normal - - spacing: Appearance.spacing.normal - - StyledRect { - id: preview - - Layout.alignment: Qt.AlignVCenter - - border.width: 1 - border.color: Qt.alpha(`#${modelData.colours?.outline}`, 0.5) - - color: `#${modelData.colours?.surface}` - radius: Appearance.rounding.full - implicitWidth: iconPlaceholder.implicitWidth - implicitHeight: iconPlaceholder.implicitWidth - - MaterialIcon { - id: iconPlaceholder - visible: false - text: "circle" - font.pointSize: Appearance.font.size.large - } - - Item { - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.right: parent.right - - implicitWidth: parent.implicitWidth / 2 - clip: true - - StyledRect { - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.right: parent.right - - implicitWidth: preview.implicitWidth - color: `#${modelData.colours?.primary}` - radius: Appearance.rounding.full - } - } - } - - Column { - Layout.fillWidth: true - spacing: 0 - - StyledText { - text: modelData.flavour ?? "" - font.pointSize: Appearance.font.size.normal - } - - 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 - } - } - - Loader { - active: isCurrent - asynchronous: true - - sourceComponent: MaterialIcon { - text: "check" - color: Colours.palette.m3onSurfaceVariant - font.pointSize: Appearance.font.size.large - } - } - } - - implicitHeight: schemeRow.implicitHeight + Appearance.padding.normal * 2 - } - } - } } - CollapsibleSection { + AnimationsSection { id: animationsSection - title: qsTr("Animations") - showBackground: true - - SectionContainer { - contentSpacing: Appearance.spacing.normal - - SliderInput { - Layout.fillWidth: true - - label: qsTr("Animation duration scale") - value: rootPane.animDurationsScale - from: 0.1 - to: 5.0 - decimals: 1 - suffix: "×" - validator: DoubleValidator { bottom: 0.1; top: 5.0 } - - onValueModified: (newValue) => { - rootPane.animDurationsScale = newValue; - rootPane.saveConfig(); - } - } - } + rootPane: sidebarFlickable.rootPane } CollapsibleSection { @@ -1253,270 +1016,24 @@ Item { } } - CollapsibleSection { + ScalesSection { id: scalesSection - title: qsTr("Scales") - showBackground: true - - SectionContainer { - contentSpacing: Appearance.spacing.normal - - SliderInput { - Layout.fillWidth: true - - label: qsTr("Padding scale") - value: rootPane.paddingScale - from: 0.5 - to: 2.0 - decimals: 1 - suffix: "×" - validator: DoubleValidator { bottom: 0.5; top: 2.0 } - - onValueModified: (newValue) => { - rootPane.paddingScale = newValue; - rootPane.saveConfig(); - } - } - } - - SectionContainer { - contentSpacing: Appearance.spacing.normal - - SliderInput { - Layout.fillWidth: true - - label: qsTr("Rounding scale") - value: rootPane.roundingScale - from: 0.1 - to: 5.0 - decimals: 1 - suffix: "×" - validator: DoubleValidator { bottom: 0.1; top: 5.0 } - - onValueModified: (newValue) => { - rootPane.roundingScale = newValue; - rootPane.saveConfig(); - } - } - } - - SectionContainer { - contentSpacing: Appearance.spacing.normal - - SliderInput { - Layout.fillWidth: true - - label: qsTr("Spacing scale") - value: rootPane.spacingScale - from: 0.1 - to: 2.0 - decimals: 1 - suffix: "×" - validator: DoubleValidator { bottom: 0.1; top: 2.0 } - - onValueModified: (newValue) => { - rootPane.spacingScale = newValue; - rootPane.saveConfig(); - } - } - } + rootPane: sidebarFlickable.rootPane } - CollapsibleSection { + TransparencySection { id: transparencySection - title: qsTr("Transparency") - showBackground: true - - SwitchRow { - label: qsTr("Transparency enabled") - checked: rootPane.transparencyEnabled - onToggled: checked => { - rootPane.transparencyEnabled = checked; - rootPane.saveConfig(); - } - } - - SectionContainer { - contentSpacing: Appearance.spacing.normal - - SliderInput { - Layout.fillWidth: true - - label: qsTr("Transparency base") - value: rootPane.transparencyBase * 100 - from: 0 - to: 100 - suffix: "%" - validator: IntValidator { bottom: 0; top: 100 } - formatValueFunction: (val) => Math.round(val).toString() - parseValueFunction: (text) => parseInt(text) - - onValueModified: (newValue) => { - rootPane.transparencyBase = newValue / 100; - rootPane.saveConfig(); - } - } - } - - SectionContainer { - contentSpacing: Appearance.spacing.normal - - SliderInput { - Layout.fillWidth: true - - label: qsTr("Transparency layers") - value: rootPane.transparencyLayers * 100 - from: 0 - to: 100 - suffix: "%" - validator: IntValidator { bottom: 0; top: 100 } - formatValueFunction: (val) => Math.round(val).toString() - parseValueFunction: (text) => parseInt(text) - - onValueModified: (newValue) => { - rootPane.transparencyLayers = newValue / 100; - rootPane.saveConfig(); - } - } - } + rootPane: sidebarFlickable.rootPane } - CollapsibleSection { + BorderSection { id: borderSection - title: qsTr("Border") - showBackground: true - - SectionContainer { - contentSpacing: Appearance.spacing.normal - - SliderInput { - Layout.fillWidth: true - - label: qsTr("Border rounding") - value: rootPane.borderRounding - from: 0.1 - to: 100 - decimals: 1 - suffix: "px" - validator: DoubleValidator { bottom: 0.1; top: 100 } - - onValueModified: (newValue) => { - rootPane.borderRounding = newValue; - rootPane.saveConfig(); - } - } - } - - SectionContainer { - contentSpacing: Appearance.spacing.normal - - SliderInput { - Layout.fillWidth: true - - label: qsTr("Border thickness") - value: rootPane.borderThickness - from: 0.1 - to: 100 - decimals: 1 - suffix: "px" - validator: DoubleValidator { bottom: 0.1; top: 100 } - - onValueModified: (newValue) => { - rootPane.borderThickness = newValue; - rootPane.saveConfig(); - } - } - } + rootPane: sidebarFlickable.rootPane } - CollapsibleSection { + BackgroundSection { id: backgroundSection - title: qsTr("Background") - showBackground: true - - SwitchRow { - label: qsTr("Desktop clock") - checked: rootPane.desktopClockEnabled - onToggled: checked => { - rootPane.desktopClockEnabled = checked; - rootPane.saveConfig(); - } - } - - SwitchRow { - label: qsTr("Background enabled") - checked: rootPane.backgroundEnabled - onToggled: checked => { - rootPane.backgroundEnabled = checked; - rootPane.saveConfig(); - } - } - - StyledText { - Layout.topMargin: Appearance.spacing.normal - text: qsTr("Visualiser") - font.pointSize: Appearance.font.size.larger - font.weight: 500 - } - - SwitchRow { - label: qsTr("Visualiser enabled") - checked: rootPane.visualiserEnabled - onToggled: checked => { - rootPane.visualiserEnabled = checked; - rootPane.saveConfig(); - } - } - - SwitchRow { - label: qsTr("Visualiser auto hide") - checked: rootPane.visualiserAutoHide - onToggled: checked => { - rootPane.visualiserAutoHide = checked; - rootPane.saveConfig(); - } - } - - SectionContainer { - contentSpacing: Appearance.spacing.normal - - SliderInput { - Layout.fillWidth: true - - label: qsTr("Visualiser rounding") - value: rootPane.visualiserRounding - from: 0 - to: 10 - stepSize: 1 - validator: IntValidator { bottom: 0; top: 10 } - formatValueFunction: (val) => Math.round(val).toString() - parseValueFunction: (text) => parseInt(text) - - onValueModified: (newValue) => { - rootPane.visualiserRounding = Math.round(newValue); - rootPane.saveConfig(); - } - } - } - - SectionContainer { - contentSpacing: Appearance.spacing.normal - - SliderInput { - Layout.fillWidth: true - - label: qsTr("Visualiser spacing") - value: rootPane.visualiserSpacing - from: 0 - to: 2 - validator: DoubleValidator { bottom: 0; top: 2 } - - onValueModified: (newValue) => { - rootPane.visualiserSpacing = newValue; - rootPane.saveConfig(); - } - } - } + rootPane: sidebarFlickable.rootPane } } } diff --git a/modules/controlcenter/appearance/sections/AnimationsSection.qml b/modules/controlcenter/appearance/sections/AnimationsSection.qml new file mode 100644 index 0000000..03fc2b1 --- /dev/null +++ b/modules/controlcenter/appearance/sections/AnimationsSection.qml @@ -0,0 +1,42 @@ +pragma ComponentBehavior: Bound + +import ".." +import "../../components" +import qs.components +import qs.components.controls +import qs.components.containers +import qs.services +import qs.config +import QtQuick +import QtQuick.Layouts + +CollapsibleSection { + id: root + + required property var rootPane + + title: qsTr("Animations") + showBackground: true + + SectionContainer { + contentSpacing: Appearance.spacing.normal + + SliderInput { + Layout.fillWidth: true + + label: qsTr("Animation duration scale") + value: rootPane.animDurationsScale + from: 0.1 + to: 5.0 + decimals: 1 + suffix: "×" + validator: DoubleValidator { bottom: 0.1; top: 5.0 } + + onValueModified: (newValue) => { + rootPane.animDurationsScale = newValue; + rootPane.saveConfig(); + } + } + } +} + diff --git a/modules/controlcenter/appearance/sections/BackgroundSection.qml b/modules/controlcenter/appearance/sections/BackgroundSection.qml new file mode 100644 index 0000000..8754e73 --- /dev/null +++ b/modules/controlcenter/appearance/sections/BackgroundSection.qml @@ -0,0 +1,105 @@ +pragma ComponentBehavior: Bound + +import ".." +import "../../components" +import qs.components +import qs.components.controls +import qs.components.containers +import qs.services +import qs.config +import QtQuick +import QtQuick.Layouts + +CollapsibleSection { + id: root + + required property var rootPane + + title: qsTr("Background") + showBackground: true + + SwitchRow { + label: qsTr("Desktop clock") + checked: rootPane.desktopClockEnabled + onToggled: checked => { + rootPane.desktopClockEnabled = checked; + rootPane.saveConfig(); + } + } + + SwitchRow { + label: qsTr("Background enabled") + checked: rootPane.backgroundEnabled + onToggled: checked => { + rootPane.backgroundEnabled = checked; + rootPane.saveConfig(); + } + } + + StyledText { + Layout.topMargin: Appearance.spacing.normal + text: qsTr("Visualiser") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } + + SwitchRow { + label: qsTr("Visualiser enabled") + checked: rootPane.visualiserEnabled + onToggled: checked => { + rootPane.visualiserEnabled = checked; + rootPane.saveConfig(); + } + } + + SwitchRow { + label: qsTr("Visualiser auto hide") + checked: rootPane.visualiserAutoHide + onToggled: checked => { + rootPane.visualiserAutoHide = checked; + rootPane.saveConfig(); + } + } + + SectionContainer { + contentSpacing: Appearance.spacing.normal + + SliderInput { + Layout.fillWidth: true + + label: qsTr("Visualiser rounding") + value: rootPane.visualiserRounding + from: 0 + to: 10 + stepSize: 1 + validator: IntValidator { bottom: 0; top: 10 } + formatValueFunction: (val) => Math.round(val).toString() + parseValueFunction: (text) => parseInt(text) + + onValueModified: (newValue) => { + rootPane.visualiserRounding = Math.round(newValue); + rootPane.saveConfig(); + } + } + } + + SectionContainer { + contentSpacing: Appearance.spacing.normal + + SliderInput { + Layout.fillWidth: true + + label: qsTr("Visualiser spacing") + value: rootPane.visualiserSpacing + from: 0 + to: 2 + validator: DoubleValidator { bottom: 0; top: 2 } + + onValueModified: (newValue) => { + rootPane.visualiserSpacing = newValue; + rootPane.saveConfig(); + } + } + } +} + diff --git a/modules/controlcenter/appearance/sections/BorderSection.qml b/modules/controlcenter/appearance/sections/BorderSection.qml new file mode 100644 index 0000000..dae26c3 --- /dev/null +++ b/modules/controlcenter/appearance/sections/BorderSection.qml @@ -0,0 +1,63 @@ +pragma ComponentBehavior: Bound + +import ".." +import "../../components" +import qs.components +import qs.components.controls +import qs.components.containers +import qs.services +import qs.config +import QtQuick +import QtQuick.Layouts + +CollapsibleSection { + id: root + + required property var rootPane + + title: qsTr("Border") + showBackground: true + + SectionContainer { + contentSpacing: Appearance.spacing.normal + + SliderInput { + Layout.fillWidth: true + + label: qsTr("Border rounding") + value: rootPane.borderRounding + from: 0.1 + to: 100 + decimals: 1 + suffix: "px" + validator: DoubleValidator { bottom: 0.1; top: 100 } + + onValueModified: (newValue) => { + rootPane.borderRounding = newValue; + rootPane.saveConfig(); + } + } + } + + SectionContainer { + contentSpacing: Appearance.spacing.normal + + SliderInput { + Layout.fillWidth: true + + label: qsTr("Border thickness") + value: rootPane.borderThickness + from: 0.1 + to: 100 + decimals: 1 + suffix: "px" + validator: DoubleValidator { bottom: 0.1; top: 100 } + + onValueModified: (newValue) => { + rootPane.borderThickness = newValue; + rootPane.saveConfig(); + } + } + } +} + diff --git a/modules/controlcenter/appearance/sections/ColorSchemeSection.qml b/modules/controlcenter/appearance/sections/ColorSchemeSection.qml new file mode 100644 index 0000000..c0e5eb5 --- /dev/null +++ b/modules/controlcenter/appearance/sections/ColorSchemeSection.qml @@ -0,0 +1,147 @@ +pragma ComponentBehavior: Bound + +import ".." +import "../../../launcher/services" +import qs.components +import qs.components.controls +import qs.components.containers +import qs.services +import qs.config +import Quickshell +import QtQuick +import QtQuick.Layouts + +CollapsibleSection { + title: qsTr("Color scheme") + description: qsTr("Available color schemes") + showBackground: true + + ColumnLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.small / 2 + + Repeater { + model: Schemes.list + + delegate: StyledRect { + required property var modelData + + Layout.fillWidth: true + + readonly property string schemeKey: `${modelData.name} ${modelData.flavour}` + readonly property bool isCurrent: schemeKey === Schemes.currentScheme + + color: Qt.alpha(Colours.tPalette.m3surfaceContainer, isCurrent ? Colours.tPalette.m3surfaceContainer.a : 0) + radius: Appearance.rounding.normal + border.width: isCurrent ? 1 : 0 + border.color: Colours.palette.m3primary + + StateLayer { + function onClicked(): void { + const name = modelData.name; + const flavour = modelData.flavour; + const schemeKey = `${name} ${flavour}`; + + Schemes.currentScheme = schemeKey; + Quickshell.execDetached(["caelestia", "scheme", "set", "-n", name, "-f", flavour]); + + Qt.callLater(() => { + reloadTimer.restart(); + }); + } + } + + Timer { + id: reloadTimer + interval: 300 + onTriggered: { + Schemes.reload(); + } + } + + RowLayout { + id: schemeRow + + anchors.fill: parent + anchors.margins: Appearance.padding.normal + + spacing: Appearance.spacing.normal + + StyledRect { + id: preview + + Layout.alignment: Qt.AlignVCenter + + border.width: 1 + border.color: Qt.alpha(`#${modelData.colours?.outline}`, 0.5) + + color: `#${modelData.colours?.surface}` + radius: Appearance.rounding.full + implicitWidth: iconPlaceholder.implicitWidth + implicitHeight: iconPlaceholder.implicitWidth + + MaterialIcon { + id: iconPlaceholder + visible: false + text: "circle" + font.pointSize: Appearance.font.size.large + } + + Item { + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.right: parent.right + + implicitWidth: parent.implicitWidth / 2 + clip: true + + StyledRect { + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.right: parent.right + + implicitWidth: preview.implicitWidth + color: `#${modelData.colours?.primary}` + radius: Appearance.rounding.full + } + } + } + + Column { + Layout.fillWidth: true + spacing: 0 + + StyledText { + text: modelData.flavour ?? "" + font.pointSize: Appearance.font.size.normal + } + + 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 + } + } + + Loader { + active: isCurrent + asynchronous: true + + sourceComponent: MaterialIcon { + text: "check" + color: Colours.palette.m3onSurfaceVariant + font.pointSize: Appearance.font.size.large + } + } + } + + implicitHeight: schemeRow.implicitHeight + Appearance.padding.normal * 2 + } + } + } +} + diff --git a/modules/controlcenter/appearance/sections/ColorVariantSection.qml b/modules/controlcenter/appearance/sections/ColorVariantSection.qml new file mode 100644 index 0000000..98c3d7c --- /dev/null +++ b/modules/controlcenter/appearance/sections/ColorVariantSection.qml @@ -0,0 +1,92 @@ +pragma ComponentBehavior: Bound + +import ".." +import "../../../launcher/services" +import qs.components +import qs.components.controls +import qs.components.containers +import qs.services +import qs.config +import Quickshell +import QtQuick +import QtQuick.Layouts + +CollapsibleSection { + title: qsTr("Color variant") + description: qsTr("Material theme variant") + showBackground: true + + ColumnLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.small / 2 + + Repeater { + model: M3Variants.list + + delegate: StyledRect { + required property var modelData + + 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 + + StateLayer { + function onClicked(): void { + const variant = modelData.variant; + + Schemes.currentVariant = variant; + Quickshell.execDetached(["caelestia", "scheme", "set", "-v", variant]); + + Qt.callLater(() => { + reloadTimer.restart(); + }); + } + } + + Timer { + id: reloadTimer + interval: 300 + onTriggered: { + Schemes.reload(); + } + } + + RowLayout { + id: variantRow + + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.normal + + spacing: Appearance.spacing.normal + + MaterialIcon { + text: modelData.icon + font.pointSize: Appearance.font.size.large + fill: modelData.variant === Schemes.currentVariant ? 1 : 0 + } + + StyledText { + Layout.fillWidth: true + text: modelData.name + font.weight: modelData.variant === Schemes.currentVariant ? 500 : 400 + } + + MaterialIcon { + visible: modelData.variant === Schemes.currentVariant + text: "check" + color: Colours.palette.m3primary + font.pointSize: Appearance.font.size.large + } + } + + implicitHeight: variantRow.implicitHeight + Appearance.padding.normal * 2 + } + } + } +} + diff --git a/modules/controlcenter/appearance/sections/ScalesSection.qml b/modules/controlcenter/appearance/sections/ScalesSection.qml new file mode 100644 index 0000000..f74923b --- /dev/null +++ b/modules/controlcenter/appearance/sections/ScalesSection.qml @@ -0,0 +1,84 @@ +pragma ComponentBehavior: Bound + +import ".." +import "../../components" +import qs.components +import qs.components.controls +import qs.components.containers +import qs.services +import qs.config +import QtQuick +import QtQuick.Layouts + +CollapsibleSection { + id: root + + required property var rootPane + + title: qsTr("Scales") + showBackground: true + + SectionContainer { + contentSpacing: Appearance.spacing.normal + + SliderInput { + Layout.fillWidth: true + + label: qsTr("Padding scale") + value: rootPane.paddingScale + from: 0.5 + to: 2.0 + decimals: 1 + suffix: "×" + validator: DoubleValidator { bottom: 0.5; top: 2.0 } + + onValueModified: (newValue) => { + rootPane.paddingScale = newValue; + rootPane.saveConfig(); + } + } + } + + SectionContainer { + contentSpacing: Appearance.spacing.normal + + SliderInput { + Layout.fillWidth: true + + label: qsTr("Rounding scale") + value: rootPane.roundingScale + from: 0.1 + to: 5.0 + decimals: 1 + suffix: "×" + validator: DoubleValidator { bottom: 0.1; top: 5.0 } + + onValueModified: (newValue) => { + rootPane.roundingScale = newValue; + rootPane.saveConfig(); + } + } + } + + SectionContainer { + contentSpacing: Appearance.spacing.normal + + SliderInput { + Layout.fillWidth: true + + label: qsTr("Spacing scale") + value: rootPane.spacingScale + from: 0.1 + to: 2.0 + decimals: 1 + suffix: "×" + validator: DoubleValidator { bottom: 0.1; top: 2.0 } + + onValueModified: (newValue) => { + rootPane.spacingScale = newValue; + rootPane.saveConfig(); + } + } + } +} + diff --git a/modules/controlcenter/appearance/sections/ThemeModeSection.qml b/modules/controlcenter/appearance/sections/ThemeModeSection.qml new file mode 100644 index 0000000..c136437 --- /dev/null +++ b/modules/controlcenter/appearance/sections/ThemeModeSection.qml @@ -0,0 +1,24 @@ +pragma ComponentBehavior: Bound + +import ".." +import qs.components +import qs.components.controls +import qs.components.containers +import qs.services +import qs.config +import QtQuick + +CollapsibleSection { + title: qsTr("Theme mode") + description: qsTr("Light or dark theme") + showBackground: true + + SwitchRow { + label: qsTr("Dark mode") + checked: !Colours.currentLight + onToggled: checked => { + Colours.setMode(checked ? "dark" : "light"); + } + } +} + diff --git a/modules/controlcenter/appearance/sections/TransparencySection.qml b/modules/controlcenter/appearance/sections/TransparencySection.qml new file mode 100644 index 0000000..c9dbfb8 --- /dev/null +++ b/modules/controlcenter/appearance/sections/TransparencySection.qml @@ -0,0 +1,74 @@ +pragma ComponentBehavior: Bound + +import ".." +import "../../components" +import qs.components +import qs.components.controls +import qs.components.containers +import qs.services +import qs.config +import QtQuick +import QtQuick.Layouts + +CollapsibleSection { + id: root + + required property var rootPane + + title: qsTr("Transparency") + showBackground: true + + SwitchRow { + label: qsTr("Transparency enabled") + checked: rootPane.transparencyEnabled + onToggled: checked => { + rootPane.transparencyEnabled = checked; + rootPane.saveConfig(); + } + } + + SectionContainer { + contentSpacing: Appearance.spacing.normal + + SliderInput { + Layout.fillWidth: true + + label: qsTr("Transparency base") + value: rootPane.transparencyBase * 100 + from: 0 + to: 100 + suffix: "%" + validator: IntValidator { bottom: 0; top: 100 } + formatValueFunction: (val) => Math.round(val).toString() + parseValueFunction: (text) => parseInt(text) + + onValueModified: (newValue) => { + rootPane.transparencyBase = newValue / 100; + rootPane.saveConfig(); + } + } + } + + SectionContainer { + contentSpacing: Appearance.spacing.normal + + SliderInput { + Layout.fillWidth: true + + label: qsTr("Transparency layers") + value: rootPane.transparencyLayers * 100 + from: 0 + to: 100 + suffix: "%" + validator: IntValidator { bottom: 0; top: 100 } + formatValueFunction: (val) => Math.round(val).toString() + parseValueFunction: (text) => parseInt(text) + + onValueModified: (newValue) => { + rootPane.transparencyLayers = newValue / 100; + rootPane.saveConfig(); + } + } + } +} + diff --git a/modules/controlcenter/network/NetworkingPane.qml b/modules/controlcenter/network/NetworkingPane.qml index 4446428..b430cce 100644 --- a/modules/controlcenter/network/NetworkingPane.qml +++ b/modules/controlcenter/network/NetworkingPane.qml @@ -175,16 +175,26 @@ Item { Connections { target: root.session.ethernet function onActiveChanged() { + // Clear wireless when ethernet is selected + if (root.session.ethernet.active && root.session.network.active) { + root.session.network.active = null; + return; // Let the network.onActiveChanged handle the update + } rightPaneItem.nextComponent = rightPaneItem.getComponentForPane(); - rightPaneItem.paneId = rightPaneItem.ethernetPane ? ("eth:" + (rightPaneItem.ethernetPane.interface || "")) : (rightPaneItem.wirelessPane ? ("wifi:" + (rightPaneItem.wirelessPane.ssid || rightPaneItem.wirelessPane.bssid || "")) : "settings"); + // paneId will automatically update via property binding } } Connections { target: root.session.network function onActiveChanged() { + // Clear ethernet when wireless is selected + if (root.session.network.active && root.session.ethernet.active) { + root.session.ethernet.active = null; + return; // Let the ethernet.onActiveChanged handle the update + } rightPaneItem.nextComponent = rightPaneItem.getComponentForPane(); - rightPaneItem.paneId = rightPaneItem.ethernetPane ? ("eth:" + (rightPaneItem.ethernetPane.interface || "")) : (rightPaneItem.wirelessPane ? ("wifi:" + (rightPaneItem.wirelessPane.ssid || rightPaneItem.wirelessPane.bssid || "")) : "settings"); + // paneId will automatically update via property binding } } -- cgit v1.2.3-freya From ecc1226a0ec470faed9ab90f5725dc99858bcb73 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Wed, 19 Nov 2025 22:25:12 -0500 Subject: refactor: replaced CollapsibleSection with FontsSection --- .../controlcenter/appearance/AppearancePane.qml | 271 +------------------ .../appearance/sections/FontsSection.qml | 286 +++++++++++++++++++++ 2 files changed, 288 insertions(+), 269 deletions(-) create mode 100644 modules/controlcenter/appearance/sections/FontsSection.qml (limited to 'modules/controlcenter/appearance') diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index d72700f..36028d2 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -744,276 +744,9 @@ Item { rootPane: sidebarFlickable.rootPane } - CollapsibleSection { + FontsSection { id: fontsSection - title: qsTr("Fonts") - showBackground: true - - CollapsibleSection { - id: materialFontSection - title: qsTr("Material font family") - expanded: true - showBackground: true - nested: true - - Loader { - id: materialFontLoader - Layout.fillWidth: true - Layout.preferredHeight: item ? Math.min(item.contentHeight, 300) : 0 - asynchronous: true - active: materialFontSection.expanded - - sourceComponent: StyledListView { - id: materialFontList - property alias contentHeight: materialFontList.contentHeight - - clip: true - spacing: Appearance.spacing.small / 2 - model: Qt.fontFamilies() - - StyledScrollBar.vertical: StyledScrollBar { - flickable: materialFontList - } - - delegate: StyledRect { - required property string modelData - required property int index - - width: ListView.view.width - - 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(); - } - } - - RowLayout { - id: fontFamilyMaterialRow - - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.normal - - spacing: Appearance.spacing.normal - - StyledText { - text: modelData - font.pointSize: Appearance.font.size.normal - } - - Item { - Layout.fillWidth: true - } - - Loader { - active: isCurrent - asynchronous: true - - sourceComponent: MaterialIcon { - text: "check" - color: Colours.palette.m3onSurfaceVariant - font.pointSize: Appearance.font.size.large - } - } - } - - implicitHeight: fontFamilyMaterialRow.implicitHeight + Appearance.padding.normal * 2 - } - } - - } - } - - CollapsibleSection { - id: monoFontSection - title: qsTr("Monospace font family") - expanded: false - showBackground: true - nested: true - - Loader { - Layout.fillWidth: true - Layout.preferredHeight: item ? Math.min(item.contentHeight, 300) : 0 - asynchronous: true - active: monoFontSection.expanded - - sourceComponent: StyledListView { - id: monoFontList - property alias contentHeight: monoFontList.contentHeight - - clip: true - spacing: Appearance.spacing.small / 2 - model: Qt.fontFamilies() - - StyledScrollBar.vertical: StyledScrollBar { - flickable: monoFontList - } - - delegate: StyledRect { - required property string modelData - required property int index - - width: ListView.view.width - - 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(); - } - } - - RowLayout { - id: fontFamilyMonoRow - - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.normal - - spacing: Appearance.spacing.normal - - StyledText { - text: modelData - font.pointSize: Appearance.font.size.normal - } - - Item { - Layout.fillWidth: true - } - - Loader { - active: isCurrent - asynchronous: true - - sourceComponent: MaterialIcon { - text: "check" - color: Colours.palette.m3onSurfaceVariant - font.pointSize: Appearance.font.size.large - } - } - } - - implicitHeight: fontFamilyMonoRow.implicitHeight + Appearance.padding.normal * 2 - } - } - } - } - - CollapsibleSection { - id: sansFontSection - title: qsTr("Sans-serif font family") - expanded: false - showBackground: true - nested: true - - Loader { - Layout.fillWidth: true - Layout.preferredHeight: item ? Math.min(item.contentHeight, 300) : 0 - asynchronous: true - active: sansFontSection.expanded - - sourceComponent: StyledListView { - id: sansFontList - property alias contentHeight: sansFontList.contentHeight - - clip: true - spacing: Appearance.spacing.small / 2 - model: Qt.fontFamilies() - - StyledScrollBar.vertical: StyledScrollBar { - flickable: sansFontList - } - - delegate: StyledRect { - required property string modelData - required property int index - - width: ListView.view.width - - 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(); - } - } - - RowLayout { - id: fontFamilySansRow - - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.normal - - spacing: Appearance.spacing.normal - - StyledText { - text: modelData - font.pointSize: Appearance.font.size.normal - } - - Item { - Layout.fillWidth: true - } - - Loader { - active: isCurrent - asynchronous: true - - sourceComponent: MaterialIcon { - text: "check" - color: Colours.palette.m3onSurfaceVariant - font.pointSize: Appearance.font.size.large - } - } - } - - implicitHeight: fontFamilySansRow.implicitHeight + Appearance.padding.normal * 2 - } - } - } - } - - SectionContainer { - contentSpacing: Appearance.spacing.normal - - SliderInput { - Layout.fillWidth: true - - label: qsTr("Font size scale") - value: rootPane.fontSizeScale - from: 0.7 - to: 1.5 - decimals: 2 - suffix: "×" - validator: DoubleValidator { bottom: 0.7; top: 1.5 } - - onValueModified: (newValue) => { - rootPane.fontSizeScale = newValue; - rootPane.saveConfig(); - } - } - } + rootPane: sidebarFlickable.rootPane } ScalesSection { diff --git a/modules/controlcenter/appearance/sections/FontsSection.qml b/modules/controlcenter/appearance/sections/FontsSection.qml new file mode 100644 index 0000000..57b10ff --- /dev/null +++ b/modules/controlcenter/appearance/sections/FontsSection.qml @@ -0,0 +1,286 @@ +pragma ComponentBehavior: Bound + +import ".." +import "../../components" +import qs.components +import qs.components.controls +import qs.components.containers +import qs.services +import qs.config +import QtQuick +import QtQuick.Layouts + +CollapsibleSection { + id: root + + required property var rootPane + + title: qsTr("Fonts") + showBackground: true + + CollapsibleSection { + id: materialFontSection + title: qsTr("Material font family") + expanded: true + showBackground: true + nested: true + + Loader { + id: materialFontLoader + Layout.fillWidth: true + Layout.preferredHeight: item ? Math.min(item.contentHeight, 300) : 0 + asynchronous: true + active: materialFontSection.expanded + + sourceComponent: StyledListView { + id: materialFontList + property alias contentHeight: materialFontList.contentHeight + + clip: true + spacing: Appearance.spacing.small / 2 + model: Qt.fontFamilies() + + StyledScrollBar.vertical: StyledScrollBar { + flickable: materialFontList + } + + delegate: StyledRect { + required property string modelData + required property int index + + width: ListView.view.width + + 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(); + } + } + + RowLayout { + id: fontFamilyMaterialRow + + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.normal + + spacing: Appearance.spacing.normal + + StyledText { + text: modelData + font.pointSize: Appearance.font.size.normal + } + + Item { + Layout.fillWidth: true + } + + Loader { + active: isCurrent + asynchronous: true + + sourceComponent: MaterialIcon { + text: "check" + color: Colours.palette.m3onSurfaceVariant + font.pointSize: Appearance.font.size.large + } + } + } + + implicitHeight: fontFamilyMaterialRow.implicitHeight + Appearance.padding.normal * 2 + } + } + } + } + + CollapsibleSection { + id: monoFontSection + title: qsTr("Monospace font family") + expanded: false + showBackground: true + nested: true + + Loader { + Layout.fillWidth: true + Layout.preferredHeight: item ? Math.min(item.contentHeight, 300) : 0 + asynchronous: true + active: monoFontSection.expanded + + sourceComponent: StyledListView { + id: monoFontList + property alias contentHeight: monoFontList.contentHeight + + clip: true + spacing: Appearance.spacing.small / 2 + model: Qt.fontFamilies() + + StyledScrollBar.vertical: StyledScrollBar { + flickable: monoFontList + } + + delegate: StyledRect { + required property string modelData + required property int index + + width: ListView.view.width + + 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(); + } + } + + RowLayout { + id: fontFamilyMonoRow + + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.normal + + spacing: Appearance.spacing.normal + + StyledText { + text: modelData + font.pointSize: Appearance.font.size.normal + } + + Item { + Layout.fillWidth: true + } + + Loader { + active: isCurrent + asynchronous: true + + sourceComponent: MaterialIcon { + text: "check" + color: Colours.palette.m3onSurfaceVariant + font.pointSize: Appearance.font.size.large + } + } + } + + implicitHeight: fontFamilyMonoRow.implicitHeight + Appearance.padding.normal * 2 + } + } + } + } + + CollapsibleSection { + id: sansFontSection + title: qsTr("Sans-serif font family") + expanded: false + showBackground: true + nested: true + + Loader { + Layout.fillWidth: true + Layout.preferredHeight: item ? Math.min(item.contentHeight, 300) : 0 + asynchronous: true + active: sansFontSection.expanded + + sourceComponent: StyledListView { + id: sansFontList + property alias contentHeight: sansFontList.contentHeight + + clip: true + spacing: Appearance.spacing.small / 2 + model: Qt.fontFamilies() + + StyledScrollBar.vertical: StyledScrollBar { + flickable: sansFontList + } + + delegate: StyledRect { + required property string modelData + required property int index + + width: ListView.view.width + + 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(); + } + } + + RowLayout { + id: fontFamilySansRow + + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.normal + + spacing: Appearance.spacing.normal + + StyledText { + text: modelData + font.pointSize: Appearance.font.size.normal + } + + Item { + Layout.fillWidth: true + } + + Loader { + active: isCurrent + asynchronous: true + + sourceComponent: MaterialIcon { + text: "check" + color: Colours.palette.m3onSurfaceVariant + font.pointSize: Appearance.font.size.large + } + } + } + + implicitHeight: fontFamilySansRow.implicitHeight + Appearance.padding.normal * 2 + } + } + } + } + + SectionContainer { + contentSpacing: Appearance.spacing.normal + + SliderInput { + Layout.fillWidth: true + + label: qsTr("Font size scale") + value: rootPane.fontSizeScale + from: 0.7 + to: 1.5 + decimals: 2 + suffix: "×" + validator: DoubleValidator { bottom: 0.7; top: 1.5 } + + onValueModified: (newValue) => { + rootPane.fontSizeScale = newValue; + rootPane.saveConfig(); + } + } + } +} + -- cgit v1.2.3-freya From e781326da40f919e736702e18081ab3418c92741 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Wed, 19 Nov 2025 22:41:07 -0500 Subject: refactor: replaced wallpaper logic with WallpaperGrid component --- .../controlcenter/appearance/AppearancePane.qml | 515 +------------------- modules/controlcenter/components/WallpaperGrid.qml | 516 +++++++++++++++++++++ 2 files changed, 518 insertions(+), 513 deletions(-) create mode 100644 modules/controlcenter/components/WallpaperGrid.qml (limited to 'modules/controlcenter/appearance') diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index 36028d2..aea0085 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -140,519 +140,8 @@ Item { } } - // Stop lazy loading when loader becomes inactive - onActiveChanged: { - if (!active && wallpaperLoader.item) { - const container = wallpaperLoader.item; - if (container && container.wallpaperGrid) { - const grid = container.wallpaperGrid; - if (grid.imageUpdateTimer) { - grid.imageUpdateTimer.stop(); - } - } - } - } - - sourceComponent: Item { - id: wallpaperGridContainer - property alias layoutPreferredHeight: wallpaperGrid.layoutPreferredHeight - - // Find and store reference to parent Flickable for scroll monitoring - property var parentFlickable: { - let item = parent; - while (item) { - if (item.flickableDirection !== undefined) { - return item; - } - item = item.parent; - } - return null; - } - - // Cleanup when component is destroyed - Component.onDestruction: { - if (wallpaperGrid) { - if (wallpaperGrid.scrollCheckTimer) { - wallpaperGrid.scrollCheckTimer.stop(); - } - wallpaperGrid._expansionInProgress = false; - } - } - - QtObject { - id: lazyModel - - property var sourceList: null - property int loadedCount: 0 - property int visibleCount: 0 - property int totalCount: 0 - - function initialize(list) { - sourceList = list; - totalCount = list ? list.length : 0; - const initialRows = 3; - const cols = wallpaperGrid.columnsCount > 0 ? wallpaperGrid.columnsCount : 3; - const initialCount = Math.min(initialRows * cols, totalCount); - loadedCount = initialCount; - visibleCount = initialCount; - } - - function loadOneRow() { - if (loadedCount < totalCount) { - const cols = wallpaperGrid.columnsCount > 0 ? wallpaperGrid.columnsCount : 1; - const itemsToLoad = Math.min(cols, totalCount - loadedCount); - loadedCount += itemsToLoad; - } - } - - function updateVisibleCount(neededCount) { - const cols = wallpaperGrid.columnsCount > 0 ? wallpaperGrid.columnsCount : 1; - const maxVisible = Math.min(neededCount, loadedCount); - const rows = Math.ceil(maxVisible / cols); - const newVisibleCount = Math.min(rows * cols, loadedCount); - - if (newVisibleCount > visibleCount) { - visibleCount = newVisibleCount; - } - } - } - - GridView { - id: wallpaperGrid - anchors.fill: parent - - property int _delegateCount: 0 - - readonly property int minCellWidth: 200 + Appearance.spacing.normal - readonly property int columnsCount: Math.max(1, Math.floor(parent.width / minCellWidth)) - - readonly property int layoutPreferredHeight: { - if (!lazyModel || lazyModel.visibleCount === 0 || columnsCount === 0) { - return 0; - } - const calculated = Math.ceil(lazyModel.visibleCount / columnsCount) * cellHeight; - return calculated; - } - - height: layoutPreferredHeight - cellWidth: width / columnsCount - cellHeight: 140 + Appearance.spacing.normal - - leftMargin: 0 - rightMargin: 0 - topMargin: 0 - bottomMargin: 0 - - ListModel { - id: wallpaperListModel - } - - model: wallpaperListModel - - Connections { - target: lazyModel - function onVisibleCountChanged(): void { - if (!lazyModel || !lazyModel.sourceList) return; - - const newCount = lazyModel.visibleCount; - const currentCount = wallpaperListModel.count; - - if (newCount > currentCount) { - const flickable = wallpaperGridContainer.parentFlickable; - const oldScrollY = flickable ? flickable.contentY : 0; - - for (let i = currentCount; i < newCount; i++) { - wallpaperListModel.append({modelData: lazyModel.sourceList[i]}); - } - - if (flickable) { - Qt.callLater(function() { - if (Math.abs(flickable.contentY - oldScrollY) < 1) { - flickable.contentY = oldScrollY; - } - }); - } - } - } - } - - Component.onCompleted: { - Qt.callLater(function() { - const isActive = root.session.activeIndex === 3; - if (width > 0 && parent && parent.visible && isActive && Wallpapers.list) { - lazyModel.initialize(Wallpapers.list); - wallpaperListModel.clear(); - for (let i = 0; i < lazyModel.visibleCount; i++) { - wallpaperListModel.append({modelData: lazyModel.sourceList[i]}); - } - } - }); - } - - Connections { - target: root.session - function onActiveIndexChanged(): void { - const isActive = root.session.activeIndex === 3; - - // Stop lazy loading when switching away from appearance pane - if (!isActive) { - if (scrollCheckTimer) { - scrollCheckTimer.stop(); - } - if (wallpaperGrid) { - wallpaperGrid._expansionInProgress = false; - } - return; - } - - // Initialize if needed when switching to appearance pane - if (isActive && width > 0 && !lazyModel.sourceList && parent && parent.visible && Wallpapers.list) { - lazyModel.initialize(Wallpapers.list); - wallpaperListModel.clear(); - for (let i = 0; i < lazyModel.visibleCount; i++) { - wallpaperListModel.append({modelData: lazyModel.sourceList[i]}); - } - } - } - } - - onWidthChanged: { - const isActive = root.session.activeIndex === 3; - if (width > 0 && !lazyModel.sourceList && parent && parent.visible && isActive && Wallpapers.list) { - lazyModel.initialize(Wallpapers.list); - wallpaperListModel.clear(); - for (let i = 0; i < lazyModel.visibleCount; i++) { - wallpaperListModel.append({modelData: lazyModel.sourceList[i]}); - } - } - } - - // Force true lazy loading: only create delegates for visible items - displayMarginBeginning: 0 - displayMarginEnd: 0 - cacheBuffer: 0 - - // Debounce expansion to avoid too frequent checks - property bool _expansionInProgress: false - - Connections { - target: wallpaperGridContainer.parentFlickable - function onContentYChanged(): void { - // Don't process scroll events if appearance pane is not active - const isActive = root.session.activeIndex === 3; - if (!isActive) return; - - if (!lazyModel || !lazyModel.sourceList || lazyModel.loadedCount >= lazyModel.totalCount || wallpaperGrid._expansionInProgress) { - return; - } - - const flickable = wallpaperGridContainer.parentFlickable; - if (!flickable) return; - - const gridY = wallpaperGridContainer.y; - const scrollY = flickable.contentY; - const viewportHeight = flickable.height; - - const topY = scrollY - gridY; - const bottomY = scrollY + viewportHeight - gridY; - - if (bottomY < 0) return; - - const topRow = Math.max(0, Math.floor(topY / wallpaperGrid.cellHeight)); - const bottomRow = Math.floor(bottomY / wallpaperGrid.cellHeight); - - // Update visible count with 1 row buffer ahead - const bufferRows = 1; - const neededBottomRow = bottomRow + bufferRows; - const neededCount = Math.min((neededBottomRow + 1) * wallpaperGrid.columnsCount, lazyModel.loadedCount); - lazyModel.updateVisibleCount(neededCount); - - const loadedRows = Math.ceil(lazyModel.loadedCount / wallpaperGrid.columnsCount); - const rowsRemaining = loadedRows - (bottomRow + 1); - - if (rowsRemaining <= 1 && lazyModel.loadedCount < lazyModel.totalCount) { - if (!wallpaperGrid._expansionInProgress) { - wallpaperGrid._expansionInProgress = true; - lazyModel.loadOneRow(); - Qt.callLater(function() { - wallpaperGrid._expansionInProgress = false; - }); - } - } - } - } - - // Fallback timer to check scroll position periodically - Timer { - id: scrollCheckTimer - interval: 100 - running: { - const isActive = root.session.activeIndex === 3; - return isActive && lazyModel && lazyModel.sourceList && lazyModel.loadedCount < lazyModel.totalCount; - } - repeat: true - onTriggered: { - // Double-check that appearance pane is still active - const isActive = root.session.activeIndex === 3; - if (!isActive) { - stop(); - return; - } - - const flickable = wallpaperGridContainer.parentFlickable; - if (!flickable || !lazyModel || !lazyModel.sourceList) return; - - const gridY = wallpaperGridContainer.y; - const scrollY = flickable.contentY; - const viewportHeight = flickable.height; - - const topY = scrollY - gridY; - const bottomY = scrollY + viewportHeight - gridY; - if (bottomY < 0) return; - - const topRow = Math.max(0, Math.floor(topY / wallpaperGrid.cellHeight)); - const bottomRow = Math.floor(bottomY / wallpaperGrid.cellHeight); - - const bufferRows = 1; - const neededBottomRow = bottomRow + bufferRows; - const neededCount = Math.min((neededBottomRow + 1) * wallpaperGrid.columnsCount, lazyModel.loadedCount); - lazyModel.updateVisibleCount(neededCount); - - const loadedRows = Math.ceil(lazyModel.loadedCount / wallpaperGrid.columnsCount); - const rowsRemaining = loadedRows - (bottomRow + 1); - - if (rowsRemaining <= 1 && lazyModel.loadedCount < lazyModel.totalCount) { - if (!wallpaperGrid._expansionInProgress) { - wallpaperGrid._expansionInProgress = true; - lazyModel.loadOneRow(); - Qt.callLater(function() { - wallpaperGrid._expansionInProgress = false; - }); - } - } - } - } - - interactive: false - - - delegate: Item { - required property var modelData - - width: wallpaperGrid.cellWidth - height: wallpaperGrid.cellHeight - - readonly property bool isCurrent: modelData.path === Wallpapers.actualCurrent - readonly property real itemMargin: Appearance.spacing.normal / 2 - readonly property real itemRadius: Appearance.rounding.normal - - Component.onCompleted: { - wallpaperGrid._delegateCount++; - } - - StateLayer { - anchors.fill: parent - anchors.leftMargin: itemMargin - anchors.rightMargin: itemMargin - anchors.topMargin: itemMargin - anchors.bottomMargin: itemMargin - radius: itemRadius - - function onClicked(): void { - Wallpapers.setWallpaper(modelData.path); - } - } - - StyledClippingRect { - id: image - - anchors.fill: parent - anchors.leftMargin: itemMargin - anchors.rightMargin: itemMargin - anchors.topMargin: itemMargin - anchors.bottomMargin: itemMargin - color: Colours.tPalette.m3surfaceContainer - radius: itemRadius - antialiasing: true - layer.enabled: true - layer.smooth: true - - CachingImage { - id: cachingImage - - path: modelData.path - anchors.fill: parent - fillMode: Image.PreserveAspectCrop - cache: true - visible: opacity > 0 - antialiasing: true - smooth: true - - opacity: status === Image.Ready ? 1 : 0 - - Behavior on opacity { - NumberAnimation { - duration: 1000 - easing.type: Easing.OutQuad - } - } - } - - // Fallback if CachingImage fails to load - Image { - id: fallbackImage - - anchors.fill: parent - source: fallbackTimer.triggered && cachingImage.status !== Image.Ready ? modelData.path : "" - asynchronous: true - fillMode: Image.PreserveAspectCrop - cache: true - visible: opacity > 0 - antialiasing: true - smooth: true - - opacity: status === Image.Ready && cachingImage.status !== Image.Ready ? 1 : 0 - - Behavior on opacity { - NumberAnimation { - duration: 1000 - easing.type: Easing.OutQuad - } - } - } - - Timer { - id: fallbackTimer - - property bool triggered: false - interval: 800 - running: cachingImage.status === Image.Loading || cachingImage.status === Image.Null - onTriggered: triggered = true - } - - // Gradient overlay for filename - Rectangle { - id: filenameOverlay - - anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: parent.bottom - - implicitHeight: filenameText.implicitHeight + Appearance.padding.normal * 1.5 - radius: 0 - - gradient: Gradient { - GradientStop { - position: 0.0 - color: Qt.rgba(Colours.palette.m3surfaceContainer.r, - Colours.palette.m3surfaceContainer.g, - Colours.palette.m3surfaceContainer.b, 0) - } - GradientStop { - position: 0.3 - color: Qt.rgba(Colours.palette.m3surfaceContainer.r, - Colours.palette.m3surfaceContainer.g, - Colours.palette.m3surfaceContainer.b, 0.7) - } - GradientStop { - position: 0.6 - color: Qt.rgba(Colours.palette.m3surfaceContainer.r, - Colours.palette.m3surfaceContainer.g, - Colours.palette.m3surfaceContainer.b, 0.9) - } - GradientStop { - position: 1.0 - color: Qt.rgba(Colours.palette.m3surfaceContainer.r, - Colours.palette.m3surfaceContainer.g, - Colours.palette.m3surfaceContainer.b, 0.95) - } - } - - opacity: 0 - - Behavior on opacity { - NumberAnimation { - duration: 1000 - easing.type: Easing.OutCubic - } - } - - Component.onCompleted: { - opacity = 1; - } - } - } - - Rectangle { - anchors.fill: parent - anchors.leftMargin: itemMargin - anchors.rightMargin: itemMargin - anchors.topMargin: itemMargin - anchors.bottomMargin: itemMargin - color: "transparent" - radius: itemRadius + border.width - border.width: isCurrent ? 2 : 0 - border.color: Colours.palette.m3primary - antialiasing: true - smooth: true - - Behavior on border.width { - NumberAnimation { - duration: 150 - easing.type: Easing.OutQuad - } - } - - MaterialIcon { - anchors.right: parent.right - anchors.top: parent.top - anchors.margins: Appearance.padding.small - - visible: isCurrent - text: "check_circle" - color: Colours.palette.m3primary - font.pointSize: Appearance.font.size.large - } - } - - StyledText { - id: filenameText - anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: parent.bottom - anchors.leftMargin: Appearance.padding.normal + Appearance.spacing.normal / 2 - anchors.rightMargin: Appearance.padding.normal + Appearance.spacing.normal / 2 - anchors.bottomMargin: Appearance.padding.normal - - readonly property string fileName: { - const path = modelData.relativePath || ""; - const parts = path.split("/"); - return parts.length > 0 ? parts[parts.length - 1] : path; - } - - text: fileName - font.pointSize: Appearance.font.size.smaller - font.weight: 500 - color: isCurrent ? Colours.palette.m3primary : Colours.palette.m3onSurface - elide: Text.ElideMiddle - maximumLineCount: 1 - horizontalAlignment: Text.AlignHCenter - - opacity: 0 - - Behavior on opacity { - NumberAnimation { - duration: 1000 - easing.type: Easing.OutCubic - } - } - - Component.onCompleted: { - opacity = 1; - } - } - } - } + sourceComponent: WallpaperGrid { + session: root.session } } } diff --git a/modules/controlcenter/components/WallpaperGrid.qml b/modules/controlcenter/components/WallpaperGrid.qml new file mode 100644 index 0000000..2fa4242 --- /dev/null +++ b/modules/controlcenter/components/WallpaperGrid.qml @@ -0,0 +1,516 @@ +pragma ComponentBehavior: Bound + +import ".." +import qs.components +import qs.components.controls +import qs.components.effects +import qs.components.images +import qs.services +import qs.config +import Caelestia.Models +import QtQuick + +Item { + id: root + + required property Session session + + property alias layoutPreferredHeight: wallpaperGrid.layoutPreferredHeight + + // Find and store reference to parent Flickable for scroll monitoring + property var parentFlickable: { + let item = parent; + while (item) { + if (item.flickableDirection !== undefined) { + return item; + } + item = item.parent; + } + return null; + } + + // Cleanup when component is destroyed + Component.onDestruction: { + if (wallpaperGrid) { + if (wallpaperGrid.scrollCheckTimer) { + wallpaperGrid.scrollCheckTimer.stop(); + } + wallpaperGrid._expansionInProgress = false; + } + } + + QtObject { + id: lazyModel + + property var sourceList: null + property int loadedCount: 0 + property int visibleCount: 0 + property int totalCount: 0 + + function initialize(list) { + sourceList = list; + totalCount = list ? list.length : 0; + const initialRows = 3; + const cols = wallpaperGrid.columnsCount > 0 ? wallpaperGrid.columnsCount : 3; + const initialCount = Math.min(initialRows * cols, totalCount); + loadedCount = initialCount; + visibleCount = initialCount; + } + + function loadOneRow() { + if (loadedCount < totalCount) { + const cols = wallpaperGrid.columnsCount > 0 ? wallpaperGrid.columnsCount : 1; + const itemsToLoad = Math.min(cols, totalCount - loadedCount); + loadedCount += itemsToLoad; + } + } + + function updateVisibleCount(neededCount) { + const cols = wallpaperGrid.columnsCount > 0 ? wallpaperGrid.columnsCount : 1; + const maxVisible = Math.min(neededCount, loadedCount); + const rows = Math.ceil(maxVisible / cols); + const newVisibleCount = Math.min(rows * cols, loadedCount); + + if (newVisibleCount > visibleCount) { + visibleCount = newVisibleCount; + } + } + } + + GridView { + id: wallpaperGrid + anchors.fill: parent + + property int _delegateCount: 0 + + readonly property int minCellWidth: 200 + Appearance.spacing.normal + readonly property int columnsCount: Math.max(1, Math.floor(parent.width / minCellWidth)) + + readonly property int layoutPreferredHeight: { + if (!lazyModel || lazyModel.visibleCount === 0 || columnsCount === 0) { + return 0; + } + const calculated = Math.ceil(lazyModel.visibleCount / columnsCount) * cellHeight; + return calculated; + } + + height: layoutPreferredHeight + cellWidth: width / columnsCount + cellHeight: 140 + Appearance.spacing.normal + + leftMargin: 0 + rightMargin: 0 + topMargin: 0 + bottomMargin: 0 + + ListModel { + id: wallpaperListModel + } + + model: wallpaperListModel + + Connections { + target: lazyModel + function onVisibleCountChanged(): void { + if (!lazyModel || !lazyModel.sourceList) return; + + const newCount = lazyModel.visibleCount; + const currentCount = wallpaperListModel.count; + + if (newCount > currentCount) { + const flickable = root.parentFlickable; + const oldScrollY = flickable ? flickable.contentY : 0; + + for (let i = currentCount; i < newCount; i++) { + wallpaperListModel.append({modelData: lazyModel.sourceList[i]}); + } + + if (flickable) { + Qt.callLater(function() { + if (Math.abs(flickable.contentY - oldScrollY) < 1) { + flickable.contentY = oldScrollY; + } + }); + } + } + } + } + + Component.onCompleted: { + Qt.callLater(function() { + const isActive = root.session.activeIndex === 3; + if (width > 0 && parent && parent.visible && isActive && Wallpapers.list) { + lazyModel.initialize(Wallpapers.list); + wallpaperListModel.clear(); + for (let i = 0; i < lazyModel.visibleCount; i++) { + wallpaperListModel.append({modelData: lazyModel.sourceList[i]}); + } + } + }); + } + + Connections { + target: root.session + function onActiveIndexChanged(): void { + const isActive = root.session.activeIndex === 3; + + // Stop lazy loading when switching away from appearance pane + if (!isActive) { + if (scrollCheckTimer) { + scrollCheckTimer.stop(); + } + if (wallpaperGrid) { + wallpaperGrid._expansionInProgress = false; + } + return; + } + + // Initialize if needed when switching to appearance pane + if (isActive && width > 0 && !lazyModel.sourceList && parent && parent.visible && Wallpapers.list) { + lazyModel.initialize(Wallpapers.list); + wallpaperListModel.clear(); + for (let i = 0; i < lazyModel.visibleCount; i++) { + wallpaperListModel.append({modelData: lazyModel.sourceList[i]}); + } + } + } + } + + onWidthChanged: { + const isActive = root.session.activeIndex === 3; + if (width > 0 && !lazyModel.sourceList && parent && parent.visible && isActive && Wallpapers.list) { + lazyModel.initialize(Wallpapers.list); + wallpaperListModel.clear(); + for (let i = 0; i < lazyModel.visibleCount; i++) { + wallpaperListModel.append({modelData: lazyModel.sourceList[i]}); + } + } + } + + // Force true lazy loading: only create delegates for visible items + displayMarginBeginning: 0 + displayMarginEnd: 0 + cacheBuffer: 0 + + // Debounce expansion to avoid too frequent checks + property bool _expansionInProgress: false + + Connections { + target: root.parentFlickable + function onContentYChanged(): void { + // Don't process scroll events if appearance pane is not active + const isActive = root.session.activeIndex === 3; + if (!isActive) return; + + if (!lazyModel || !lazyModel.sourceList || lazyModel.loadedCount >= lazyModel.totalCount || wallpaperGrid._expansionInProgress) { + return; + } + + const flickable = root.parentFlickable; + if (!flickable) return; + + const gridY = root.y; + const scrollY = flickable.contentY; + const viewportHeight = flickable.height; + + const topY = scrollY - gridY; + const bottomY = scrollY + viewportHeight - gridY; + + if (bottomY < 0) return; + + const topRow = Math.max(0, Math.floor(topY / wallpaperGrid.cellHeight)); + const bottomRow = Math.floor(bottomY / wallpaperGrid.cellHeight); + + // Update visible count with 1 row buffer ahead + const bufferRows = 1; + const neededBottomRow = bottomRow + bufferRows; + const neededCount = Math.min((neededBottomRow + 1) * wallpaperGrid.columnsCount, lazyModel.loadedCount); + lazyModel.updateVisibleCount(neededCount); + + const loadedRows = Math.ceil(lazyModel.loadedCount / wallpaperGrid.columnsCount); + const rowsRemaining = loadedRows - (bottomRow + 1); + + if (rowsRemaining <= 1 && lazyModel.loadedCount < lazyModel.totalCount) { + if (!wallpaperGrid._expansionInProgress) { + wallpaperGrid._expansionInProgress = true; + lazyModel.loadOneRow(); + Qt.callLater(function() { + wallpaperGrid._expansionInProgress = false; + }); + } + } + } + } + + // Fallback timer to check scroll position periodically + Timer { + id: scrollCheckTimer + interval: 100 + running: { + const isActive = root.session.activeIndex === 3; + return isActive && lazyModel && lazyModel.sourceList && lazyModel.loadedCount < lazyModel.totalCount; + } + repeat: true + onTriggered: { + // Double-check that appearance pane is still active + const isActive = root.session.activeIndex === 3; + if (!isActive) { + stop(); + return; + } + + const flickable = root.parentFlickable; + if (!flickable || !lazyModel || !lazyModel.sourceList) return; + + const gridY = root.y; + const scrollY = flickable.contentY; + const viewportHeight = flickable.height; + + const topY = scrollY - gridY; + const bottomY = scrollY + viewportHeight - gridY; + if (bottomY < 0) return; + + const topRow = Math.max(0, Math.floor(topY / wallpaperGrid.cellHeight)); + const bottomRow = Math.floor(bottomY / wallpaperGrid.cellHeight); + + const bufferRows = 1; + const neededBottomRow = bottomRow + bufferRows; + const neededCount = Math.min((neededBottomRow + 1) * wallpaperGrid.columnsCount, lazyModel.loadedCount); + lazyModel.updateVisibleCount(neededCount); + + const loadedRows = Math.ceil(lazyModel.loadedCount / wallpaperGrid.columnsCount); + const rowsRemaining = loadedRows - (bottomRow + 1); + + if (rowsRemaining <= 1 && lazyModel.loadedCount < lazyModel.totalCount) { + if (!wallpaperGrid._expansionInProgress) { + wallpaperGrid._expansionInProgress = true; + lazyModel.loadOneRow(); + Qt.callLater(function() { + wallpaperGrid._expansionInProgress = false; + }); + } + } + } + } + + interactive: false + + delegate: Item { + required property var modelData + + width: wallpaperGrid.cellWidth + height: wallpaperGrid.cellHeight + + readonly property bool isCurrent: modelData.path === Wallpapers.actualCurrent + readonly property real itemMargin: Appearance.spacing.normal / 2 + readonly property real itemRadius: Appearance.rounding.normal + + Component.onCompleted: { + wallpaperGrid._delegateCount++; + } + + StateLayer { + anchors.fill: parent + anchors.leftMargin: itemMargin + anchors.rightMargin: itemMargin + anchors.topMargin: itemMargin + anchors.bottomMargin: itemMargin + radius: itemRadius + + function onClicked(): void { + Wallpapers.setWallpaper(modelData.path); + } + } + + StyledClippingRect { + id: image + + anchors.fill: parent + anchors.leftMargin: itemMargin + anchors.rightMargin: itemMargin + anchors.topMargin: itemMargin + anchors.bottomMargin: itemMargin + color: Colours.tPalette.m3surfaceContainer + radius: itemRadius + antialiasing: true + layer.enabled: true + layer.smooth: true + + CachingImage { + id: cachingImage + + path: modelData.path + anchors.fill: parent + fillMode: Image.PreserveAspectCrop + cache: true + visible: opacity > 0 + antialiasing: true + smooth: true + + opacity: status === Image.Ready ? 1 : 0 + + Behavior on opacity { + NumberAnimation { + duration: 1000 + easing.type: Easing.OutQuad + } + } + } + + // Fallback if CachingImage fails to load + Image { + id: fallbackImage + + anchors.fill: parent + source: fallbackTimer.triggered && cachingImage.status !== Image.Ready ? modelData.path : "" + asynchronous: true + fillMode: Image.PreserveAspectCrop + cache: true + visible: opacity > 0 + antialiasing: true + smooth: true + + opacity: status === Image.Ready && cachingImage.status !== Image.Ready ? 1 : 0 + + Behavior on opacity { + NumberAnimation { + duration: 1000 + easing.type: Easing.OutQuad + } + } + } + + Timer { + id: fallbackTimer + + property bool triggered: false + interval: 800 + running: cachingImage.status === Image.Loading || cachingImage.status === Image.Null + onTriggered: triggered = true + } + + // Gradient overlay for filename + Rectangle { + id: filenameOverlay + + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + + implicitHeight: filenameText.implicitHeight + Appearance.padding.normal * 1.5 + radius: 0 + + gradient: Gradient { + GradientStop { + position: 0.0 + color: Qt.rgba(Colours.palette.m3surfaceContainer.r, + Colours.palette.m3surfaceContainer.g, + Colours.palette.m3surfaceContainer.b, 0) + } + GradientStop { + position: 0.3 + color: Qt.rgba(Colours.palette.m3surfaceContainer.r, + Colours.palette.m3surfaceContainer.g, + Colours.palette.m3surfaceContainer.b, 0.7) + } + GradientStop { + position: 0.6 + color: Qt.rgba(Colours.palette.m3surfaceContainer.r, + Colours.palette.m3surfaceContainer.g, + Colours.palette.m3surfaceContainer.b, 0.9) + } + GradientStop { + position: 1.0 + color: Qt.rgba(Colours.palette.m3surfaceContainer.r, + Colours.palette.m3surfaceContainer.g, + Colours.palette.m3surfaceContainer.b, 0.95) + } + } + + opacity: 0 + + Behavior on opacity { + NumberAnimation { + duration: 1000 + easing.type: Easing.OutCubic + } + } + + Component.onCompleted: { + opacity = 1; + } + } + } + + Rectangle { + anchors.fill: parent + anchors.leftMargin: itemMargin + anchors.rightMargin: itemMargin + anchors.topMargin: itemMargin + anchors.bottomMargin: itemMargin + color: "transparent" + radius: itemRadius + border.width + border.width: isCurrent ? 2 : 0 + border.color: Colours.palette.m3primary + antialiasing: true + smooth: true + + Behavior on border.width { + NumberAnimation { + duration: 150 + easing.type: Easing.OutQuad + } + } + + MaterialIcon { + anchors.right: parent.right + anchors.top: parent.top + anchors.margins: Appearance.padding.small + + visible: isCurrent + text: "check_circle" + color: Colours.palette.m3primary + font.pointSize: Appearance.font.size.large + } + } + + StyledText { + id: filenameText + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + anchors.leftMargin: Appearance.padding.normal + Appearance.spacing.normal / 2 + anchors.rightMargin: Appearance.padding.normal + Appearance.spacing.normal / 2 + anchors.bottomMargin: Appearance.padding.normal + + readonly property string fileName: { + const path = modelData.relativePath || ""; + const parts = path.split("/"); + return parts.length > 0 ? parts[parts.length - 1] : path; + } + + text: fileName + font.pointSize: Appearance.font.size.smaller + font.weight: 500 + color: isCurrent ? Colours.palette.m3primary : Colours.palette.m3onSurface + elide: Text.ElideMiddle + maximumLineCount: 1 + horizontalAlignment: Text.AlignHCenter + + opacity: 0 + + Behavior on opacity { + NumberAnimation { + duration: 1000 + easing.type: Easing.OutCubic + } + } + + Component.onCompleted: { + opacity = 1; + } + } + } + } +} + -- cgit v1.2.3-freya From 0029f2d48a67f838f1ca77055625a6dbfd88686e Mon Sep 17 00:00:00 2001 From: ATMDA Date: Thu, 20 Nov 2025 10:42:48 -0500 Subject: controlcenter: wallpaper now uses gridview, removed custom lazy loading --- .../controlcenter/appearance/AppearancePane.qml | 74 ++--- modules/controlcenter/components/WallpaperGrid.qml | 307 ++------------------- 2 files changed, 44 insertions(+), 337 deletions(-) (limited to 'modules/controlcenter/appearance') diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index aea0085..b6acbe5 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -79,71 +79,49 @@ Item { Component { id: appearanceRightContentComponent - StyledFlickable { + Item { id: rightAppearanceFlickable - flickableDirection: Flickable.VerticalFlick - contentHeight: contentLayout.height - - StyledScrollBar.vertical: StyledScrollBar { - flickable: rightAppearanceFlickable - } ColumnLayout { id: contentLayout - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - spacing: Appearance.spacing.normal - - SettingsHeader { - icon: "palette" - title: qsTr("Appearance Settings") - } + anchors.fill: parent + spacing: 0 StyledText { - Layout.topMargin: Appearance.spacing.large Layout.alignment: Qt.AlignHCenter + Layout.bottomMargin: Appearance.spacing.normal text: qsTr("Wallpaper") font.pointSize: Appearance.font.size.extraLarge font.weight: 600 } - StyledText { - Layout.alignment: Qt.AlignHCenter - text: qsTr("Select a wallpaper") - font.pointSize: Appearance.font.size.normal - color: Colours.palette.m3onSurfaceVariant - } - - Item { + Loader { + id: wallpaperLoader + Layout.fillWidth: true - Layout.topMargin: Appearance.spacing.large - Layout.preferredHeight: wallpaperLoader.item ? wallpaperLoader.item.layoutPreferredHeight : 0 + Layout.fillHeight: true + Layout.bottomMargin: -Appearance.padding.large * 2 - Loader { - id: wallpaperLoader - anchors.fill: parent - asynchronous: true - active: { - const isActive = root.session.activeIndex === 3; - const isAdjacent = Math.abs(root.session.activeIndex - 3) === 1; - const splitLayout = root.children[0]; - const loader = splitLayout && splitLayout.rightLoader ? splitLayout.rightLoader : null; - const shouldActivate = loader && loader.item !== null && (isActive || isAdjacent); - return shouldActivate; - } - - onStatusChanged: { - if (status === Loader.Error) { - console.error("[AppearancePane] Wallpaper loader error!"); - } - } - - sourceComponent: WallpaperGrid { - session: root.session + asynchronous: true + active: { + const isActive = root.session.activeIndex === 3; + const isAdjacent = Math.abs(root.session.activeIndex - 3) === 1; + const splitLayout = root.children[0]; + const loader = splitLayout && splitLayout.rightLoader ? splitLayout.rightLoader : null; + const shouldActivate = loader && loader.item !== null && (isActive || isAdjacent); + return shouldActivate; + } + + onStatusChanged: { + if (status === Loader.Error) { + console.error("[AppearancePane] Wallpaper loader error!"); } } + + sourceComponent: WallpaperGrid { + session: root.session + } } } } diff --git a/modules/controlcenter/components/WallpaperGrid.qml b/modules/controlcenter/components/WallpaperGrid.qml index d1858bc..fa72291 100644 --- a/modules/controlcenter/components/WallpaperGrid.qml +++ b/modules/controlcenter/components/WallpaperGrid.qml @@ -10,304 +10,35 @@ import qs.config import Caelestia.Models import QtQuick -Item { +GridView { id: root required property Session session - property alias layoutPreferredHeight: wallpaperGrid.layoutPreferredHeight + readonly property int minCellWidth: 200 + Appearance.spacing.normal + readonly property int columnsCount: Math.max(1, Math.floor(width / minCellWidth)) - // Find and store reference to parent Flickable for scroll monitoring - property var parentFlickable: { - let item = parent; - while (item) { - if (item.flickableDirection !== undefined) { - return item; - } - item = item.parent; - } - return null; - } - - // Cleanup when component is destroyed - Component.onDestruction: { - if (wallpaperGrid) { - if (wallpaperGrid.scrollCheckTimer) { - wallpaperGrid.scrollCheckTimer.stop(); - } - wallpaperGrid._expansionInProgress = false; - } - } - - QtObject { - id: lazyModel - - property var sourceList: null - property int loadedCount: 0 - property int visibleCount: 0 - property int totalCount: 0 - - function initialize(list) { - sourceList = list; - totalCount = list ? list.length : 0; - const initialRows = 3; - const cols = wallpaperGrid.columnsCount > 0 ? wallpaperGrid.columnsCount : 3; - const initialCount = Math.min(initialRows * cols, totalCount); - loadedCount = initialCount; - visibleCount = initialCount; - } - - function loadOneRow() { - if (loadedCount < totalCount) { - const cols = wallpaperGrid.columnsCount > 0 ? wallpaperGrid.columnsCount : 1; - const itemsToLoad = Math.min(cols, totalCount - loadedCount); - loadedCount += itemsToLoad; - } - } + cellWidth: width / columnsCount + cellHeight: 140 + Appearance.spacing.normal - function updateVisibleCount(neededCount) { - const cols = wallpaperGrid.columnsCount > 0 ? wallpaperGrid.columnsCount : 1; - const maxVisible = Math.min(neededCount, loadedCount); - const rows = Math.ceil(maxVisible / cols); - const newVisibleCount = Math.min(rows * cols, loadedCount); + model: Wallpapers.list - if (newVisibleCount > visibleCount) { - visibleCount = newVisibleCount; - } - } + clip: true + + StyledScrollBar.vertical: StyledScrollBar { + flickable: root } - GridView { - id: wallpaperGrid - anchors.fill: parent - - property int _delegateCount: 0 - - readonly property int minCellWidth: 200 + Appearance.spacing.normal - readonly property int columnsCount: Math.max(1, Math.floor(parent.width / minCellWidth)) - - readonly property int layoutPreferredHeight: { - if (!lazyModel || lazyModel.visibleCount === 0 || columnsCount === 0) { - return 0; - } - const calculated = Math.ceil(lazyModel.visibleCount / columnsCount) * cellHeight; - return calculated; - } - - height: layoutPreferredHeight - cellWidth: width / columnsCount - cellHeight: 140 + Appearance.spacing.normal - - leftMargin: 0 - rightMargin: 0 - topMargin: 0 - bottomMargin: 0 - - ListModel { - id: wallpaperListModel - } - - model: wallpaperListModel - - Connections { - target: lazyModel - function onVisibleCountChanged(): void { - if (!lazyModel || !lazyModel.sourceList) return; - - const newCount = lazyModel.visibleCount; - const currentCount = wallpaperListModel.count; - - if (newCount > currentCount) { - const flickable = root.parentFlickable; - const oldScrollY = flickable ? flickable.contentY : 0; - - for (let i = currentCount; i < newCount; i++) { - wallpaperListModel.append({modelData: lazyModel.sourceList[i]}); - } - - if (flickable) { - Qt.callLater(function() { - if (Math.abs(flickable.contentY - oldScrollY) < 1) { - flickable.contentY = oldScrollY; - } - }); - } - } - } - } - - Component.onCompleted: { - Qt.callLater(function() { - const isActive = root.session.activeIndex === 3; - if (width > 0 && parent && parent.visible && isActive && Wallpapers.list) { - lazyModel.initialize(Wallpapers.list); - wallpaperListModel.clear(); - for (let i = 0; i < lazyModel.visibleCount; i++) { - wallpaperListModel.append({modelData: lazyModel.sourceList[i]}); - } - } - }); - } - - Connections { - target: root.session - function onActiveIndexChanged(): void { - const isActive = root.session.activeIndex === 3; - - // Stop lazy loading when switching away from appearance pane - if (!isActive) { - if (scrollCheckTimer) { - scrollCheckTimer.stop(); - } - if (wallpaperGrid) { - wallpaperGrid._expansionInProgress = false; - } - return; - } - - // Initialize if needed when switching to appearance pane - if (isActive && width > 0 && !lazyModel.sourceList && parent && parent.visible && Wallpapers.list) { - lazyModel.initialize(Wallpapers.list); - wallpaperListModel.clear(); - for (let i = 0; i < lazyModel.visibleCount; i++) { - wallpaperListModel.append({modelData: lazyModel.sourceList[i]}); - } - } - } - } - - onWidthChanged: { - const isActive = root.session.activeIndex === 3; - if (width > 0 && !lazyModel.sourceList && parent && parent.visible && isActive && Wallpapers.list) { - lazyModel.initialize(Wallpapers.list); - wallpaperListModel.clear(); - for (let i = 0; i < lazyModel.visibleCount; i++) { - wallpaperListModel.append({modelData: lazyModel.sourceList[i]}); - } - } - } - - // Force true lazy loading: only create delegates for visible items - displayMarginBeginning: 0 - displayMarginEnd: 0 - cacheBuffer: 0 - - // Debounce expansion to avoid too frequent checks - property bool _expansionInProgress: false - - Connections { - target: root.parentFlickable - function onContentYChanged(): void { - // Don't process scroll events if appearance pane is not active - const isActive = root.session.activeIndex === 3; - if (!isActive) return; - - if (!lazyModel || !lazyModel.sourceList || lazyModel.loadedCount >= lazyModel.totalCount || wallpaperGrid._expansionInProgress) { - return; - } - - const flickable = root.parentFlickable; - if (!flickable) return; - - const gridY = root.y; - const scrollY = flickable.contentY; - const viewportHeight = flickable.height; - - const topY = scrollY - gridY; - const bottomY = scrollY + viewportHeight - gridY; - - if (bottomY < 0) return; - - const topRow = Math.max(0, Math.floor(topY / wallpaperGrid.cellHeight)); - const bottomRow = Math.floor(bottomY / wallpaperGrid.cellHeight); - - // Update visible count with 1 row buffer ahead - const bufferRows = 1; - const neededBottomRow = bottomRow + bufferRows; - const neededCount = Math.min((neededBottomRow + 1) * wallpaperGrid.columnsCount, lazyModel.loadedCount); - lazyModel.updateVisibleCount(neededCount); - - const loadedRows = Math.ceil(lazyModel.loadedCount / wallpaperGrid.columnsCount); - const rowsRemaining = loadedRows - (bottomRow + 1); - - if (rowsRemaining <= 1 && lazyModel.loadedCount < lazyModel.totalCount) { - if (!wallpaperGrid._expansionInProgress) { - wallpaperGrid._expansionInProgress = true; - lazyModel.loadOneRow(); - Qt.callLater(function() { - wallpaperGrid._expansionInProgress = false; - }); - } - } - } - } - - // Fallback timer to check scroll position periodically - Timer { - id: scrollCheckTimer - interval: 100 - running: { - const isActive = root.session.activeIndex === 3; - return isActive && lazyModel && lazyModel.sourceList && lazyModel.loadedCount < lazyModel.totalCount; - } - repeat: true - onTriggered: { - // Double-check that appearance pane is still active - const isActive = root.session.activeIndex === 3; - if (!isActive) { - stop(); - return; - } - - const flickable = root.parentFlickable; - if (!flickable || !lazyModel || !lazyModel.sourceList) return; - - const gridY = root.y; - const scrollY = flickable.contentY; - const viewportHeight = flickable.height; - - const topY = scrollY - gridY; - const bottomY = scrollY + viewportHeight - gridY; - if (bottomY < 0) return; - - const topRow = Math.max(0, Math.floor(topY / wallpaperGrid.cellHeight)); - const bottomRow = Math.floor(bottomY / wallpaperGrid.cellHeight); - - const bufferRows = 1; - const neededBottomRow = bottomRow + bufferRows; - const neededCount = Math.min((neededBottomRow + 1) * wallpaperGrid.columnsCount, lazyModel.loadedCount); - lazyModel.updateVisibleCount(neededCount); + delegate: Item { + required property var modelData + required property int index - const loadedRows = Math.ceil(lazyModel.loadedCount / wallpaperGrid.columnsCount); - const rowsRemaining = loadedRows - (bottomRow + 1); + width: root.cellWidth + height: root.cellHeight - if (rowsRemaining <= 1 && lazyModel.loadedCount < lazyModel.totalCount) { - if (!wallpaperGrid._expansionInProgress) { - wallpaperGrid._expansionInProgress = true; - lazyModel.loadOneRow(); - Qt.callLater(function() { - wallpaperGrid._expansionInProgress = false; - }); - } - } - } - } - - interactive: false - - delegate: Item { - required property var modelData - - width: wallpaperGrid.cellWidth - height: wallpaperGrid.cellHeight - - readonly property bool isCurrent: modelData.path === Wallpapers.actualCurrent - readonly property real itemMargin: Appearance.spacing.normal / 2 - readonly property real itemRadius: Appearance.rounding.normal - - Component.onCompleted: { - wallpaperGrid._delegateCount++; - } + readonly property bool isCurrent: modelData && modelData.path === Wallpapers.actualCurrent + readonly property real itemMargin: Appearance.spacing.normal / 2 + readonly property real itemRadius: Appearance.rounding.normal StateLayer { anchors.fill: parent @@ -514,5 +245,3 @@ Item { } } } -} - -- cgit v1.2.3-freya