diff options
Diffstat (limited to 'modules/controlcenter/appearance')
| -rw-r--r-- | modules/controlcenter/appearance/AppearancePane.qml | 617 |
1 files changed, 582 insertions, 35 deletions
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 { |