From 97d461871a5fbfedb3aff080303145efb8a6fffc Mon Sep 17 00:00:00 2001 From: ATMDA Date: Tue, 11 Nov 2025 11:41:30 -0500 Subject: controlcenter: launcher panel, can hide/show applications --- modules/controlcenter/launcher/LauncherPane.qml | 446 ++++++++++++++++++++++++ 1 file changed, 446 insertions(+) create mode 100644 modules/controlcenter/launcher/LauncherPane.qml (limited to 'modules/controlcenter/launcher') diff --git a/modules/controlcenter/launcher/LauncherPane.qml b/modules/controlcenter/launcher/LauncherPane.qml new file mode 100644 index 0000000..7377f42 --- /dev/null +++ b/modules/controlcenter/launcher/LauncherPane.qml @@ -0,0 +1,446 @@ +pragma ComponentBehavior: Bound + +import ".." +import "../../launcher/services" +import qs.components +import qs.components.controls +import qs.components.effects +import qs.components.containers +import qs.services +import qs.config +import qs.utils +import Caelestia +import Quickshell +import Quickshell.Io +import Quickshell.Widgets +import QtQuick +import QtQuick.Layouts + +RowLayout { + id: root + + required property Session session + + property var selectedApp: null + property bool showDebugInfo: false + + anchors.fill: parent + + spacing: 0 + + FileView { + id: configFile + + path: `${Paths.config}/shell.json` + watchChanges: true + + onLoaded: { + try { + const config = JSON.parse(text()); + updateToggleState(); + } catch (e) { + console.error("Failed to parse config:", e); + } + } + } + + function updateToggleState() { + if (!root.selectedApp || !configFile.loaded) { + hideFromLauncherSwitch.checked = false; + return; + } + + try { + const config = JSON.parse(configFile.text()); + const appId = root.selectedApp.id || root.selectedApp.entry?.id; + + if (config.launcher && config.launcher.hiddenApps) { + hideFromLauncherSwitch.checked = config.launcher.hiddenApps.includes(appId); + } else { + hideFromLauncherSwitch.checked = false; + } + } catch (e) { + console.error("Failed to update toggle state:", e); + } + } + + function saveHiddenApps() { + if (!configFile.loaded || !root.selectedApp) { + return; + } + + try { + const config = JSON.parse(configFile.text()); + const appId = root.selectedApp.id || root.selectedApp.entry?.id; + + if (!config.launcher) config.launcher = {}; + if (!config.launcher.hiddenApps) config.launcher.hiddenApps = []; + + const hiddenApps = config.launcher.hiddenApps; + const isHidden = hideFromLauncherSwitch.checked; + + if (isHidden) { + // Add to hiddenApps if not already there + if (!hiddenApps.includes(appId)) { + hiddenApps.push(appId); + } + } else { + // Remove from hiddenApps + const index = hiddenApps.indexOf(appId); + if (index !== -1) { + hiddenApps.splice(index, 1); + } + } + + const jsonString = JSON.stringify(config, null, 4); + configFile.setText(jsonString); + } catch (e) { + console.error("Failed to save config:", e); + } + } + + onSelectedAppChanged: { + updateToggleState(); + } + + AppDb { + id: allAppsDb + + path: `${Paths.state}/apps.sqlite` + entries: DesktopEntries.applications.values // No filter - show all apps + } + + Item { + Layout.preferredWidth: Math.floor(parent.width * 0.4) + Layout.minimumWidth: 420 + Layout.fillHeight: true + + ColumnLayout { + anchors.fill: parent + anchors.margins: Appearance.padding.large + Appearance.padding.normal + anchors.leftMargin: Appearance.padding.large + anchors.rightMargin: Appearance.padding.large + Appearance.padding.normal / 2 + + spacing: Appearance.spacing.small + + RowLayout { + spacing: Appearance.spacing.smaller + + StyledText { + text: qsTr("Settings") + font.pointSize: Appearance.font.size.large + font.weight: 500 + } + + Item { + Layout.fillWidth: true + } + } + + StyledText { + Layout.topMargin: Appearance.spacing.large + text: qsTr("Applications (%1)").arg(allAppsDb.apps.length) + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } + + StyledText { + text: qsTr("All applications available in the launcher") + color: Colours.palette.m3outline + } + + StyledListView { + Layout.fillWidth: true + Layout.fillHeight: true + Layout.topMargin: Appearance.spacing.normal + + model: allAppsDb.apps + spacing: Appearance.spacing.small / 2 + clip: true + + StyledScrollBar.vertical: StyledScrollBar { + flickable: parent + } + + delegate: StyledRect { + required property var modelData + + anchors.left: parent.left + anchors.right: parent.right + + readonly property bool isSelected: root.selectedApp === modelData + + color: Qt.alpha(Colours.tPalette.m3surfaceContainer, isSelected ? Colours.tPalette.m3surfaceContainer.a : 0) + radius: Appearance.rounding.normal + border.width: isSelected ? 1 : 0 + border.color: Colours.palette.m3primary + + StateLayer { + function onClicked(): void { + root.selectedApp = modelData; + } + } + + RowLayout { + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.normal + + spacing: Appearance.spacing.normal + + IconImage { + Layout.alignment: Qt.AlignVCenter + implicitSize: 32 + source: { + const entry = modelData.entry; + return entry ? Quickshell.iconPath(entry.icon, "image-missing") : "image-missing"; + } + } + + StyledText { + Layout.fillWidth: true + text: modelData.name || modelData.entry?.name || qsTr("Unknown") + font.pointSize: Appearance.font.size.normal + } + } + + implicitHeight: 40 + } + } + } + + InnerBorder { + leftThickness: 0 + rightThickness: Appearance.padding.normal / 2 + } + } + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + + ColumnLayout { + anchors.fill: parent + anchors.margins: Appearance.padding.large * 2 + + spacing: Appearance.spacing.normal + + Item { + Layout.alignment: Qt.AlignHCenter + implicitWidth: iconLoader.implicitWidth + implicitHeight: iconLoader.implicitHeight + + Loader { + id: iconLoader + sourceComponent: root.selectedApp ? appIconComponent : defaultIconComponent + } + + Component { + id: appIconComponent + IconImage { + implicitSize: Appearance.font.size.extraLarge * 3 * 2 + source: { + if (!root.selectedApp) return "image-missing"; + const entry = root.selectedApp.entry; + if (entry && entry.icon) { + return Quickshell.iconPath(entry.icon, "image-missing"); + } + return "image-missing"; + } + } + } + + Component { + id: defaultIconComponent + MaterialIcon { + text: "apps" + font.pointSize: Appearance.font.size.extraLarge * 3 + font.bold: true + } + } + } + + StyledText { + Layout.alignment: Qt.AlignHCenter + text: root.selectedApp ? (root.selectedApp.name || root.selectedApp.entry?.name || qsTr("Application Details")) : qsTr("Launcher Applications") + font.pointSize: Appearance.font.size.large + font.bold: true + } + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + Layout.topMargin: Appearance.spacing.large + + StyledFlickable { + anchors.fill: parent + flickableDirection: Flickable.VerticalFlick + contentHeight: debugLayout.implicitHeight + + StyledScrollBar.vertical: StyledScrollBar { + flickable: parent + } + + ColumnLayout { + id: debugLayout + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + spacing: Appearance.spacing.normal + + StyledRect { + Layout.fillWidth: true + Layout.topMargin: Appearance.spacing.normal + implicitHeight: hideToggleRow.implicitHeight + Appearance.padding.large * 2 + color: Colours.tPalette.m3surfaceContainer + radius: Appearance.rounding.normal + + RowLayout { + id: hideToggleRow + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.large + spacing: Appearance.spacing.normal + + StyledText { + Layout.fillWidth: true + text: qsTr("Hide from launcher") + font.pointSize: Appearance.font.size.normal + } + + StyledSwitch { + id: hideFromLauncherSwitch + checked: false + enabled: root.selectedApp !== null && configFile.loaded + onToggled: { + root.saveHiddenApps(); + } + } + } + } + + StyledRect { + Layout.fillWidth: true + Layout.topMargin: Appearance.spacing.normal + implicitHeight: debugToggleRow.implicitHeight + Appearance.padding.large * 2 + color: Colours.tPalette.m3surfaceContainer + radius: Appearance.rounding.normal + + RowLayout { + id: debugToggleRow + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.large + spacing: Appearance.spacing.normal + + StyledText { + Layout.fillWidth: true + text: qsTr("Show debug information") + font.pointSize: Appearance.font.size.normal + } + + StyledSwitch { + id: showDebugInfoSwitch + checked: root.showDebugInfo + onToggled: { + root.showDebugInfo = checked; + } + } + } + } + + StyledRect { + Layout.fillWidth: true + Layout.topMargin: Appearance.spacing.normal + Layout.preferredHeight: 300 + visible: root.showDebugInfo + color: Colours.tPalette.m3surfaceContainer + radius: Appearance.rounding.normal + clip: true + + ColumnLayout { + anchors.fill: parent + anchors.margins: Appearance.padding.normal + spacing: Appearance.spacing.small + + StyledText { + Layout.alignment: Qt.AlignHCenter + text: qsTr("Debug Info - All Available Properties") + font.pointSize: Appearance.font.size.normal + font.weight: 500 + } + + StyledFlickable { + Layout.fillWidth: true + Layout.fillHeight: true + flickableDirection: Flickable.VerticalFlick + contentHeight: debugText.implicitHeight + clip: true + + StyledScrollBar.vertical: StyledScrollBar { + flickable: parent + } + + TextEdit { + id: debugText + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + width: parent.width + + text: { + if (!root.selectedApp) return "No app selected"; + + let debug = ""; + const app = root.selectedApp; + const entry = app.entry; + + debug += "=== App Properties ===\n"; + for (let prop in app) { + try { + const value = app[prop]; + debug += prop + ": " + (value !== null && value !== undefined ? String(value) : "null/undefined") + "\n"; + } catch (e) { + debug += prop + ": [error accessing]\n"; + } + } + + debug += "\n=== Entry Properties ===\n"; + if (entry) { + for (let prop in entry) { + try { + const value = entry[prop]; + debug += prop + ": " + (value !== null && value !== undefined ? String(value) : "null/undefined") + "\n"; + } catch (e) { + debug += prop + ": [error accessing]\n"; + } + } + } else { + debug += "entry is null\n"; + } + + return debug; + } + font.pointSize: Appearance.font.size.small + color: Colours.palette.m3onSurfaceVariant + wrapMode: TextEdit.Wrap + readOnly: true + selectByMouse: true + selectByKeyboard: true + } + } + } + } + } + } + } + } + + InnerBorder { + leftThickness: Appearance.padding.normal / 2 + } + } +} -- cgit v1.2.3-freya From 90bb8b1d006eaa4223c27754d621e6e26162003f Mon Sep 17 00:00:00 2001 From: ATMDA Date: Tue, 11 Nov 2025 16:28:59 -0500 Subject: controlcenter: removed debug info toggles --- modules/controlcenter/launcher/LauncherPane.qml | 114 ------------------------ modules/controlcenter/taskbar/TaskbarPane.qml | 109 +--------------------- 2 files changed, 1 insertion(+), 222 deletions(-) (limited to 'modules/controlcenter/launcher') diff --git a/modules/controlcenter/launcher/LauncherPane.qml b/modules/controlcenter/launcher/LauncherPane.qml index 7377f42..82e145a 100644 --- a/modules/controlcenter/launcher/LauncherPane.qml +++ b/modules/controlcenter/launcher/LauncherPane.qml @@ -22,7 +22,6 @@ RowLayout { required property Session session property var selectedApp: null - property bool showDebugInfo: false anchors.fill: parent @@ -321,119 +320,6 @@ RowLayout { } } - StyledRect { - Layout.fillWidth: true - Layout.topMargin: Appearance.spacing.normal - implicitHeight: debugToggleRow.implicitHeight + Appearance.padding.large * 2 - color: Colours.tPalette.m3surfaceContainer - radius: Appearance.rounding.normal - - RowLayout { - id: debugToggleRow - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.large - spacing: Appearance.spacing.normal - - StyledText { - Layout.fillWidth: true - text: qsTr("Show debug information") - font.pointSize: Appearance.font.size.normal - } - - StyledSwitch { - id: showDebugInfoSwitch - checked: root.showDebugInfo - onToggled: { - root.showDebugInfo = checked; - } - } - } - } - - StyledRect { - Layout.fillWidth: true - Layout.topMargin: Appearance.spacing.normal - Layout.preferredHeight: 300 - visible: root.showDebugInfo - color: Colours.tPalette.m3surfaceContainer - radius: Appearance.rounding.normal - clip: true - - ColumnLayout { - anchors.fill: parent - anchors.margins: Appearance.padding.normal - spacing: Appearance.spacing.small - - StyledText { - Layout.alignment: Qt.AlignHCenter - text: qsTr("Debug Info - All Available Properties") - font.pointSize: Appearance.font.size.normal - font.weight: 500 - } - - StyledFlickable { - Layout.fillWidth: true - Layout.fillHeight: true - flickableDirection: Flickable.VerticalFlick - contentHeight: debugText.implicitHeight - clip: true - - StyledScrollBar.vertical: StyledScrollBar { - flickable: parent - } - - TextEdit { - id: debugText - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - width: parent.width - - text: { - if (!root.selectedApp) return "No app selected"; - - let debug = ""; - const app = root.selectedApp; - const entry = app.entry; - - debug += "=== App Properties ===\n"; - for (let prop in app) { - try { - const value = app[prop]; - debug += prop + ": " + (value !== null && value !== undefined ? String(value) : "null/undefined") + "\n"; - } catch (e) { - debug += prop + ": [error accessing]\n"; - } - } - - debug += "\n=== Entry Properties ===\n"; - if (entry) { - for (let prop in entry) { - try { - const value = entry[prop]; - debug += prop + ": " + (value !== null && value !== undefined ? String(value) : "null/undefined") + "\n"; - } catch (e) { - debug += prop + ": [error accessing]\n"; - } - } - } else { - debug += "entry is null\n"; - } - - return debug; - } - font.pointSize: Appearance.font.size.small - color: Colours.palette.m3onSurfaceVariant - wrapMode: TextEdit.Wrap - readOnly: true - selectByMouse: true - selectByKeyboard: true - } - } - } - } } } } diff --git a/modules/controlcenter/taskbar/TaskbarPane.qml b/modules/controlcenter/taskbar/TaskbarPane.qml index 105bbe1..22414c7 100644 --- a/modules/controlcenter/taskbar/TaskbarPane.qml +++ b/modules/controlcenter/taskbar/TaskbarPane.qml @@ -18,8 +18,6 @@ RowLayout { required property Session session - property bool showDebugInfo: false - // Bar Behavior property bool persistent: true property bool showOnHover: true @@ -120,8 +118,6 @@ RowLayout { function saveConfig(entryIndex, entryEnabled) { if (!configFile.loaded) { - root.lastSaveStatus = "Error: Config file not loaded yet"; - root.debugInfo = "Config file not loaded yet, cannot save"; return; } @@ -168,11 +164,6 @@ RowLayout { if (!config.bar.entries) config.bar.entries = []; config.bar.entries = []; - let debugInfo = `saveConfig called\n`; - debugInfo += `entryIndex: ${entryIndex}\n`; - debugInfo += `entryEnabled: ${entryEnabled}\n`; - debugInfo += `entriesModel.count: ${entriesModel.count}\n\n`; - for (let i = 0; i < entriesModel.count; i++) { const entry = entriesModel.get(i); // If this is the entry being updated, use the provided value (same as clock toggle reads from switch) @@ -180,9 +171,6 @@ RowLayout { let enabled = entry.enabled; if (entryIndex !== undefined && i === entryIndex) { enabled = entryEnabled; - debugInfo += `Entry ${i} (${entry.id}): Using provided value = ${entryEnabled}\n`; - } else { - debugInfo += `Entry ${i} (${entry.id}): Using model value = ${entry.enabled}\n`; } config.bar.entries.push({ id: entry.id, @@ -190,16 +178,11 @@ RowLayout { }); } - debugInfo += `\nFinal entries array:\n${JSON.stringify(config.bar.entries, null, 2)}\n`; - root.debugInfo = debugInfo; - // Write back to file using setText (same simple approach that worked for clock) const jsonString = JSON.stringify(config, null, 4); configFile.setText(jsonString); - root.lastSaveStatus = `Saved! Entries count: ${config.bar.entries.length}`; } catch (e) { - root.lastSaveStatus = `Error: ${e.message}`; - root.debugInfo = `Failed to save config:\n${e.message}\n${e.stack}`; + console.error("Failed to save config:", e); } } @@ -207,9 +190,6 @@ RowLayout { id: entriesModel } - // Debug info - property string debugInfo: "" - property string lastSaveStatus: "" function collapseAllSections(exceptSection) { if (exceptSection !== clockSection) clockSection.expanded = false; @@ -1261,93 +1241,6 @@ RowLayout { color: Colours.palette.m3outline } - StyledRect { - Layout.fillWidth: true - Layout.topMargin: Appearance.spacing.large - implicitHeight: debugToggleRow.implicitHeight + Appearance.padding.large * 2 - color: Colours.tPalette.m3surfaceContainer - radius: Appearance.rounding.normal - - RowLayout { - id: debugToggleRow - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.large - spacing: Appearance.spacing.normal - - StyledText { - Layout.fillWidth: true - text: qsTr("Show debug information") - font.pointSize: Appearance.font.size.normal - } - - StyledSwitch { - id: showDebugInfoSwitch - checked: root.showDebugInfo - onToggled: { - root.showDebugInfo = checked; - } - } - } - } - - StyledText { - Layout.topMargin: Appearance.spacing.large - Layout.alignment: Qt.AlignHCenter - visible: root.showDebugInfo - text: qsTr("Debug Info") - font.pointSize: Appearance.font.size.larger - font.weight: 500 - } - - StyledRect { - Layout.fillWidth: true - Layout.preferredHeight: 200 - Layout.maximumHeight: 300 - visible: root.showDebugInfo - - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - clip: true - - StyledFlickable { - id: debugFlickable - anchors.fill: parent - anchors.margins: Appearance.padding.normal - contentHeight: debugText.implicitHeight - clip: true - - StyledScrollBar.vertical: StyledScrollBar { - flickable: debugFlickable - } - - TextEdit { - id: debugText - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - width: parent.width - - text: root.debugInfo || "No debug info yet" - font.pointSize: Appearance.font.size.small - color: Colours.palette.m3onSurface - wrapMode: TextEdit.Wrap - readOnly: true - selectByMouse: true - selectByKeyboard: true - } - } - } - - StyledText { - Layout.topMargin: Appearance.spacing.small - Layout.alignment: Qt.AlignHCenter - visible: root.showDebugInfo - text: root.lastSaveStatus || "" - font.pointSize: Appearance.font.size.small - color: root.lastSaveStatus.includes("Error") ? Colours.palette.m3error : Colours.palette.m3primary - } } } -- 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/launcher') 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/launcher') 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 9cdc30058a0d53602f6c065315b3956497a2f68c Mon Sep 17 00:00:00 2001 From: ATMDA Date: Fri, 14 Nov 2025 14:03:10 -0500 Subject: controlcenter: font size adjustments on headings --- components/controls/CollapsibleSection.qml | 2 +- modules/controlcenter/audio/AudioPane.qml | 4 ++-- modules/controlcenter/bluetooth/DeviceList.qml | 2 +- modules/controlcenter/launcher/LauncherPane.qml | 2 +- modules/controlcenter/network/NetworkingPane.qml | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) (limited to 'modules/controlcenter/launcher') diff --git a/components/controls/CollapsibleSection.qml b/components/controls/CollapsibleSection.qml index a22ad99..5bec5f8 100644 --- a/components/controls/CollapsibleSection.qml +++ b/components/controls/CollapsibleSection.qml @@ -34,7 +34,7 @@ ColumnLayout { StyledText { text: root.title - font.pointSize: Appearance.font.size.normal + font.pointSize: Appearance.font.size.larger font.weight: 500 } diff --git a/modules/controlcenter/audio/AudioPane.qml b/modules/controlcenter/audio/AudioPane.qml index 502134a..cf6e85a 100644 --- a/modules/controlcenter/audio/AudioPane.qml +++ b/modules/controlcenter/audio/AudioPane.qml @@ -48,7 +48,7 @@ RowLayout { StyledText { text: qsTr("Output devices (%1)").arg(Audio.sinks.length) - font.pointSize: Appearance.font.size.large + font.pointSize: Appearance.font.size.normal font.weight: 500 } @@ -119,7 +119,7 @@ RowLayout { StyledText { Layout.topMargin: Appearance.spacing.large text: qsTr("Input devices (%1)").arg(Audio.sources.length) - font.pointSize: Appearance.font.size.large + font.pointSize: Appearance.font.size.normal font.weight: 500 } diff --git a/modules/controlcenter/bluetooth/DeviceList.qml b/modules/controlcenter/bluetooth/DeviceList.qml index 3831e4a..8bf5daa 100644 --- a/modules/controlcenter/bluetooth/DeviceList.qml +++ b/modules/controlcenter/bluetooth/DeviceList.qml @@ -97,7 +97,7 @@ ColumnLayout { StyledText { Layout.fillWidth: true text: qsTr("Devices (%1)").arg(Bluetooth.devices.values.length) - font.pointSize: Appearance.font.size.large + font.pointSize: Appearance.font.size.normal font.weight: 500 } diff --git a/modules/controlcenter/launcher/LauncherPane.qml b/modules/controlcenter/launcher/LauncherPane.qml index dd00877..7408101 100644 --- a/modules/controlcenter/launcher/LauncherPane.qml +++ b/modules/controlcenter/launcher/LauncherPane.qml @@ -139,7 +139,7 @@ RowLayout { StyledText { Layout.topMargin: Appearance.spacing.large text: qsTr("Applications (%1)").arg(allAppsDb.apps.length) - font.pointSize: Appearance.font.size.larger + font.pointSize: Appearance.font.size.normal font.weight: 500 } diff --git a/modules/controlcenter/network/NetworkingPane.qml b/modules/controlcenter/network/NetworkingPane.qml index 5271c56..7db6f54 100644 --- a/modules/controlcenter/network/NetworkingPane.qml +++ b/modules/controlcenter/network/NetworkingPane.qml @@ -116,7 +116,7 @@ RowLayout { StyledText { text: qsTr("Devices (%1)").arg(Nmcli.ethernetDevices.length) - font.pointSize: Appearance.font.size.large + font.pointSize: Appearance.font.size.normal font.weight: 500 } } @@ -243,7 +243,7 @@ RowLayout { StyledText { text: qsTr("Networks (%1)").arg(Nmcli.networks.length) - font.pointSize: Appearance.font.size.large + font.pointSize: Appearance.font.size.normal font.weight: 500 } -- cgit v1.2.3-freya From d123dc0b7ef242baa6d9ee96804003a5855899a8 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Fri, 14 Nov 2025 22:50:03 -0500 Subject: controlcenter: search applications in launcher pane --- modules/controlcenter/launcher/LauncherPane.qml | 152 +++++++++++++++++++++++- 1 file changed, 149 insertions(+), 3 deletions(-) (limited to 'modules/controlcenter/launcher') diff --git a/modules/controlcenter/launcher/LauncherPane.qml b/modules/controlcenter/launcher/LauncherPane.qml index 7408101..d0bd406 100644 --- a/modules/controlcenter/launcher/LauncherPane.qml +++ b/modules/controlcenter/launcher/LauncherPane.qml @@ -15,6 +15,7 @@ import Quickshell.Io import Quickshell.Widgets import QtQuick import QtQuick.Layouts +import "../../../utils/scripts/fuzzysort.js" as Fuzzy RowLayout { id: root @@ -109,6 +110,68 @@ RowLayout { entries: DesktopEntries.applications.values // No filter - show all apps } + 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]); + } + return apps; + } + + if (!allAppsDb.apps || allAppsDb.apps.length === 0) { + return []; + } + + // Prepare apps for fuzzy search + const preparedApps = []; + for (let i = 0; i < allAppsDb.apps.length; i++) { + const app = allAppsDb.apps[i]; + const name = app.name || app.entry?.name || ""; + preparedApps.push({ + _item: app, + name: Fuzzy.prepare(name) + }); + } + + // 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); + } + + property list filteredApps: [] + + function updateFilteredApps() { + filteredApps = filterApps(searchText); + } + + onSearchTextChanged: { + updateFilteredApps(); + } + + Component.onCompleted: { + updateFilteredApps(); + } + + Connections { + target: allAppsDb + function onAppsChanged() { + updateFilteredApps(); + } + } + Item { Layout.preferredWidth: Math.floor(parent.width * 0.4) Layout.minimumWidth: 420 @@ -138,7 +201,7 @@ RowLayout { StyledText { Layout.topMargin: Appearance.spacing.large - text: qsTr("Applications (%1)").arg(allAppsDb.apps.length) + text: qsTr("Applications (%1)").arg(root.searchText ? root.filteredApps.length : allAppsDb.apps.length) font.pointSize: Appearance.font.size.normal font.weight: 500 } @@ -148,12 +211,95 @@ RowLayout { color: Colours.palette.m3outline } + StyledRect { + Layout.fillWidth: true + Layout.topMargin: Appearance.spacing.normal + Layout.bottomMargin: Appearance.spacing.small + + color: Colours.tPalette.m3surfaceContainer + radius: Appearance.rounding.full + + implicitHeight: Math.max(searchIcon.implicitHeight, searchField.implicitHeight, clearIcon.implicitHeight) + + MaterialIcon { + id: searchIcon + + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: Appearance.padding.normal + + text: "search" + color: Colours.palette.m3onSurfaceVariant + } + + StyledTextField { + id: searchField + + anchors.left: searchIcon.right + anchors.right: clearIcon.left + anchors.leftMargin: Appearance.spacing.small + anchors.rightMargin: Appearance.spacing.small + + topPadding: Appearance.padding.normal + bottomPadding: Appearance.padding.normal + + placeholderText: qsTr("Search applications...") + + onTextChanged: { + root.searchText = text; + } + } + + MaterialIcon { + id: clearIcon + + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + anchors.rightMargin: Appearance.padding.normal + + width: searchField.text ? implicitWidth : implicitWidth / 2 + opacity: { + if (!searchField.text) + return 0; + if (clearMouse.pressed) + return 0.7; + if (clearMouse.containsMouse) + return 0.8; + return 1; + } + + text: "close" + color: Colours.palette.m3onSurfaceVariant + + MouseArea { + id: clearMouse + + anchors.fill: parent + hoverEnabled: true + cursorShape: searchField.text ? Qt.PointingHandCursor : undefined + + onClicked: searchField.text = "" + } + + Behavior on width { + Anim { + duration: Appearance.anim.durations.small + } + } + + Behavior on opacity { + Anim { + duration: Appearance.anim.durations.small + } + } + } + } + StyledListView { Layout.fillWidth: true Layout.fillHeight: true - Layout.topMargin: Appearance.spacing.normal - model: allAppsDb.apps + model: root.filteredApps spacing: Appearance.spacing.small / 2 clip: 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/launcher') 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/launcher') 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 a40624839bba5cb429c34b9461b817a4a3434d02 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sat, 15 Nov 2025 00:36:26 -0500 Subject: controlcenter: selectables now match bluetooth pane --- modules/controlcenter/audio/AudioPane.qml | 4 ---- modules/controlcenter/launcher/LauncherPane.qml | 2 -- modules/controlcenter/network/NetworkingPane.qml | 4 ---- 3 files changed, 10 deletions(-) (limited to 'modules/controlcenter/launcher') diff --git a/modules/controlcenter/audio/AudioPane.qml b/modules/controlcenter/audio/AudioPane.qml index d6989cd..ca54f77 100644 --- a/modules/controlcenter/audio/AudioPane.qml +++ b/modules/controlcenter/audio/AudioPane.qml @@ -95,8 +95,6 @@ RowLayout { color: Qt.alpha(Colours.tPalette.m3surfaceContainer, Audio.sink?.id === modelData.id ? Colours.tPalette.m3surfaceContainer.a : 0) radius: Appearance.rounding.normal - border.width: Audio.sink?.id === modelData.id ? 1 : 0 - border.color: Colours.palette.m3primary StateLayer { function onClicked(): void { @@ -175,8 +173,6 @@ RowLayout { color: Qt.alpha(Colours.tPalette.m3surfaceContainer, Audio.source?.id === modelData.id ? Colours.tPalette.m3surfaceContainer.a : 0) radius: Appearance.rounding.normal - border.width: Audio.source?.id === modelData.id ? 1 : 0 - border.color: Colours.palette.m3primary StateLayer { function onClicked(): void { diff --git a/modules/controlcenter/launcher/LauncherPane.qml b/modules/controlcenter/launcher/LauncherPane.qml index 01ed72e..92e17ee 100644 --- a/modules/controlcenter/launcher/LauncherPane.qml +++ b/modules/controlcenter/launcher/LauncherPane.qml @@ -316,8 +316,6 @@ RowLayout { color: Qt.alpha(Colours.tPalette.m3surfaceContainer, isSelected ? Colours.tPalette.m3surfaceContainer.a : 0) radius: Appearance.rounding.normal - border.width: isSelected ? 1 : 0 - border.color: Colours.palette.m3primary StateLayer { function onClicked(): void { diff --git a/modules/controlcenter/network/NetworkingPane.qml b/modules/controlcenter/network/NetworkingPane.qml index 5d93ff9..c605a36 100644 --- a/modules/controlcenter/network/NetworkingPane.qml +++ b/modules/controlcenter/network/NetworkingPane.qml @@ -148,8 +148,6 @@ RowLayout { color: Qt.alpha(Colours.tPalette.m3surfaceContainer, root.session.ethernet.active === modelData ? Colours.tPalette.m3surfaceContainer.a : 0) radius: Appearance.rounding.normal - border.width: root.session.ethernet.active === modelData ? 1 : 0 - border.color: Colours.palette.m3primary StateLayer { function onClicked(): void { @@ -290,8 +288,6 @@ RowLayout { color: Qt.alpha(Colours.tPalette.m3surfaceContainer, root.session.network.active === modelData ? Colours.tPalette.m3surfaceContainer.a : 0) radius: Appearance.rounding.normal - border.width: root.session.network.active === modelData ? 1 : 0 - border.color: Colours.palette.m3primary StateLayer { function onClicked(): void { -- cgit v1.2.3-freya From f204e860ed582b05965936bf470cdfc9649d2b3a Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sat, 15 Nov 2025 00:38:04 -0500 Subject: controlcenter: launcher pane only shows hide when app selected --- modules/controlcenter/launcher/LauncherPane.qml | 1 + 1 file changed, 1 insertion(+) (limited to 'modules/controlcenter/launcher') diff --git a/modules/controlcenter/launcher/LauncherPane.qml b/modules/controlcenter/launcher/LauncherPane.qml index 92e17ee..0a1175f 100644 --- a/modules/controlcenter/launcher/LauncherPane.qml +++ b/modules/controlcenter/launcher/LauncherPane.qml @@ -434,6 +434,7 @@ RowLayout { SwitchRow { Layout.topMargin: Appearance.spacing.normal + visible: root.selectedApp !== null label: qsTr("Hide from launcher") checked: root.hideFromLauncherChecked enabled: root.selectedApp !== null && configFile.loaded -- 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/launcher') 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 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/launcher') 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/launcher') 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 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/launcher') 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 4ada7ea608711aa21f8bfc51698e7a3ec261aeeb Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sat, 15 Nov 2025 17:16:52 -0500 Subject: controlcenter: corrected certain containers not respecting transparency layers --- components/SectionContainer.qml | 2 +- components/controls/FilledSlider.qml | 2 +- components/controls/SpinBoxRow.qml | 2 +- components/controls/StyledSlider.qml | 2 +- components/controls/SwitchRow.qml | 2 +- modules/controlcenter/audio/AudioPane.qml | 4 ++-- modules/controlcenter/launcher/LauncherPane.qml | 4 ++-- modules/launcher/Content.qml | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) (limited to 'modules/controlcenter/launcher') diff --git a/components/SectionContainer.qml b/components/SectionContainer.qml index d41254b..f7dfef4 100644 --- a/components/SectionContainer.qml +++ b/components/SectionContainer.qml @@ -15,7 +15,7 @@ StyledRect { implicitHeight: contentColumn.implicitHeight + Appearance.padding.large * 2 radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer + color: Colours.layer(Colours.palette.m3surfaceContainer, 2) ColumnLayout { id: contentColumn diff --git a/components/controls/FilledSlider.qml b/components/controls/FilledSlider.qml index 78b8a5c..80dd44c 100644 --- a/components/controls/FilledSlider.qml +++ b/components/controls/FilledSlider.qml @@ -15,7 +15,7 @@ Slider { orientation: Qt.Vertical background: StyledRect { - color: Colours.tPalette.m3surfaceContainer + color: Colours.layer(Colours.palette.m3surfaceContainer, 2) radius: Appearance.rounding.full StyledRect { diff --git a/components/controls/SpinBoxRow.qml b/components/controls/SpinBoxRow.qml index 2507b80..4902627 100644 --- a/components/controls/SpinBoxRow.qml +++ b/components/controls/SpinBoxRow.qml @@ -19,7 +19,7 @@ StyledRect { Layout.fillWidth: true implicitHeight: row.implicitHeight + Appearance.padding.large * 2 radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer + color: Colours.layer(Colours.palette.m3surfaceContainer, 2) Behavior on implicitHeight { Anim {} diff --git a/components/controls/StyledSlider.qml b/components/controls/StyledSlider.qml index 92c8aa8..55e8c8d 100644 --- a/components/controls/StyledSlider.qml +++ b/components/controls/StyledSlider.qml @@ -32,7 +32,7 @@ Slider { implicitWidth: parent.width - root.handle.x - root.handle.implicitWidth - root.implicitHeight / 6 - color: Colours.tPalette.m3surfaceContainer + color: Colours.layer(Colours.palette.m3surfaceContainer, 2) radius: Appearance.rounding.full topLeftRadius: root.implicitHeight / 15 bottomLeftRadius: root.implicitHeight / 15 diff --git a/components/controls/SwitchRow.qml b/components/controls/SwitchRow.qml index 0ec7aa5..7fa3e1b 100644 --- a/components/controls/SwitchRow.qml +++ b/components/controls/SwitchRow.qml @@ -17,7 +17,7 @@ StyledRect { Layout.fillWidth: true implicitHeight: row.implicitHeight + Appearance.padding.large * 2 radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer + color: Colours.layer(Colours.palette.m3surfaceContainer, 2) Behavior on implicitHeight { Anim {} diff --git a/modules/controlcenter/audio/AudioPane.qml b/modules/controlcenter/audio/AudioPane.qml index 005de3a..1c0c770 100644 --- a/modules/controlcenter/audio/AudioPane.qml +++ b/modules/controlcenter/audio/AudioPane.qml @@ -127,7 +127,7 @@ RowLayout { Layout.fillWidth: true - color: Qt.alpha(Colours.tPalette.m3surfaceContainer, Audio.sink?.id === modelData.id ? Colours.tPalette.m3surfaceContainer.a : 0) + color: Audio.sink?.id === modelData.id ? Colours.layer(Colours.palette.m3surfaceContainer, 2) : "transparent" radius: Appearance.rounding.normal StateLayer { @@ -205,7 +205,7 @@ RowLayout { Layout.fillWidth: true - color: Qt.alpha(Colours.tPalette.m3surfaceContainer, Audio.source?.id === modelData.id ? Colours.tPalette.m3surfaceContainer.a : 0) + color: Audio.source?.id === modelData.id ? Colours.layer(Colours.palette.m3surfaceContainer, 2) : "transparent" radius: Appearance.rounding.normal StateLayer { diff --git a/modules/controlcenter/launcher/LauncherPane.qml b/modules/controlcenter/launcher/LauncherPane.qml index 12abc1e..dc7a7a7 100644 --- a/modules/controlcenter/launcher/LauncherPane.qml +++ b/modules/controlcenter/launcher/LauncherPane.qml @@ -218,7 +218,7 @@ RowLayout { Layout.topMargin: Appearance.spacing.normal Layout.bottomMargin: Appearance.spacing.small - color: Colours.tPalette.m3surfaceContainer + color: Colours.layer(Colours.palette.m3surfaceContainer, 2) radius: Appearance.rounding.full implicitHeight: Math.max(searchIcon.implicitHeight, searchField.implicitHeight, clearIcon.implicitHeight) @@ -317,7 +317,7 @@ RowLayout { readonly property bool isSelected: root.selectedApp === modelData - color: Qt.alpha(Colours.tPalette.m3surfaceContainer, isSelected ? Colours.tPalette.m3surfaceContainer.a : 0) + color: isSelected ? Colours.layer(Colours.palette.m3surfaceContainer, 2) : "transparent" radius: Appearance.rounding.normal StateLayer { diff --git a/modules/launcher/Content.qml b/modules/launcher/Content.qml index f674569..c085976 100644 --- a/modules/launcher/Content.qml +++ b/modules/launcher/Content.qml @@ -47,7 +47,7 @@ Item { StyledRect { id: searchWrapper - color: Colours.tPalette.m3surfaceContainer + color: Colours.layer(Colours.palette.m3surfaceContainer, 2) radius: Appearance.rounding.full anchors.left: parent.left -- cgit v1.2.3-freya From b5a31fbc59d6d2408a0cc6aaa3ea31c348a7241d Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sat, 15 Nov 2025 19:15:34 -0500 Subject: controlcenter: launcher pane now fades in applications --- modules/controlcenter/launcher/LauncherPane.qml | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'modules/controlcenter/launcher') diff --git a/modules/controlcenter/launcher/LauncherPane.qml b/modules/controlcenter/launcher/LauncherPane.qml index dc7a7a7..a6f6bd6 100644 --- a/modules/controlcenter/launcher/LauncherPane.qml +++ b/modules/controlcenter/launcher/LauncherPane.qml @@ -320,6 +320,19 @@ RowLayout { color: isSelected ? Colours.layer(Colours.palette.m3surfaceContainer, 2) : "transparent" radius: Appearance.rounding.normal + opacity: 0 + + Behavior on opacity { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + } + + Component.onCompleted: { + opacity = 1; + } + StateLayer { function onClicked(): void { root.selectedApp = modelData; -- 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/launcher') 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 d751b68bdfcf1113fcab8da0d99748772feaebcd Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sun, 16 Nov 2025 13:15:08 -0500 Subject: controlcenter: corrected missing config.save() on panes --- modules/controlcenter/launcher/LauncherPane.qml | 5 ++++- modules/controlcenter/taskbar/TaskbarPane.qml | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'modules/controlcenter/launcher') diff --git a/modules/controlcenter/launcher/LauncherPane.qml b/modules/controlcenter/launcher/LauncherPane.qml index a7e122e..0cb223b 100644 --- a/modules/controlcenter/launcher/LauncherPane.qml +++ b/modules/controlcenter/launcher/LauncherPane.qml @@ -66,8 +66,11 @@ RowLayout { } } - // Update Config to trigger save + // Update Config Config.launcher.hiddenApps = hiddenApps; + + // Persist changes to disk + Config.save(); } onSelectedAppChanged: { diff --git a/modules/controlcenter/taskbar/TaskbarPane.qml b/modules/controlcenter/taskbar/TaskbarPane.qml index 6d9761b..8d0e5a0 100644 --- a/modules/controlcenter/taskbar/TaskbarPane.qml +++ b/modules/controlcenter/taskbar/TaskbarPane.qml @@ -111,6 +111,9 @@ RowLayout { }); } Config.bar.entries = entries; + + // Persist changes to disk + Config.save(); } ListModel { -- cgit v1.2.3-freya From d9034ede8efafc6334f7b2c56b6b3d299c2937fa Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sun, 16 Nov 2025 13:26:11 -0500 Subject: controlcenter: lazyloading applicatoins in launcher pane --- modules/controlcenter/launcher/LauncherPane.qml | 108 +++++++++++++----------- 1 file changed, 61 insertions(+), 47 deletions(-) (limited to 'modules/controlcenter/launcher') diff --git a/modules/controlcenter/launcher/LauncherPane.qml b/modules/controlcenter/launcher/LauncherPane.qml index 0cb223b..71d1d6f 100644 --- a/modules/controlcenter/launcher/LauncherPane.qml +++ b/modules/controlcenter/launcher/LauncherPane.qml @@ -300,73 +300,87 @@ RowLayout { } } - StyledListView { - id: appsListView + Loader { + id: appsListLoader 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 + return leftLauncherLoader.item !== null; + } - model: root.filteredApps - spacing: Appearance.spacing.small / 2 - clip: true + sourceComponent: StyledListView { + id: appsListView + + Layout.fillWidth: true + Layout.fillHeight: true - StyledScrollBar.vertical: StyledScrollBar { - flickable: parent - } + model: root.filteredApps + spacing: Appearance.spacing.small / 2 + clip: true - delegate: StyledRect { - required property var modelData + StyledScrollBar.vertical: StyledScrollBar { + flickable: parent + } - width: parent ? parent.width : 0 + delegate: StyledRect { + required property var modelData - readonly property bool isSelected: root.selectedApp === modelData + width: parent ? parent.width : 0 - color: isSelected ? Colours.layer(Colours.palette.m3surfaceContainer, 2) : "transparent" - radius: Appearance.rounding.normal + readonly property bool isSelected: root.selectedApp === modelData - opacity: 0 + color: isSelected ? Colours.layer(Colours.palette.m3surfaceContainer, 2) : "transparent" + radius: Appearance.rounding.normal - Behavior on opacity { - NumberAnimation { - duration: 1000 - easing.type: Easing.OutCubic - } - } + opacity: 0 - Component.onCompleted: { - opacity = 1; - } + Behavior on opacity { + NumberAnimation { + duration: 1000 + easing.type: Easing.OutCubic + } + } - StateLayer { - function onClicked(): void { - root.selectedApp = modelData; + Component.onCompleted: { + opacity = 1; } - } - RowLayout { - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.normal + StateLayer { + function onClicked(): void { + root.selectedApp = modelData; + } + } - spacing: Appearance.spacing.normal + RowLayout { + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.normal + + spacing: Appearance.spacing.normal + + IconImage { + Layout.alignment: Qt.AlignVCenter + implicitSize: 32 + source: { + const entry = modelData.entry; + return entry ? Quickshell.iconPath(entry.icon, "image-missing") : "image-missing"; + } + } - IconImage { - Layout.alignment: Qt.AlignVCenter - implicitSize: 32 - source: { - const entry = modelData.entry; - return entry ? Quickshell.iconPath(entry.icon, "image-missing") : "image-missing"; + StyledText { + Layout.fillWidth: true + text: modelData.name || modelData.entry?.name || qsTr("Unknown") + font.pointSize: Appearance.font.size.normal } } - StyledText { - Layout.fillWidth: true - text: modelData.name || modelData.entry?.name || qsTr("Unknown") - font.pointSize: Appearance.font.size.normal - } + implicitHeight: 40 } - - implicitHeight: 40 } } } -- 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/launcher') 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 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/launcher') 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 4de4a787def43587a230ae3de10febc37b34d0ac Mon Sep 17 00:00:00 2001 From: ATMDA Date: Mon, 17 Nov 2025 22:01:40 -0500 Subject: controlcenter: launcher pane refreshment --- modules/controlcenter/Session.qml | 5 + modules/controlcenter/launcher/LauncherPane.qml | 133 +++++++++++++- modules/controlcenter/launcher/Settings.qml | 226 ++++++++++++++++++++++++ 3 files changed, 357 insertions(+), 7 deletions(-) create mode 100644 modules/controlcenter/launcher/Settings.qml (limited to 'modules/controlcenter/launcher') diff --git a/modules/controlcenter/Session.qml b/modules/controlcenter/Session.qml index efd360d..6f560f5 100644 --- a/modules/controlcenter/Session.qml +++ b/modules/controlcenter/Session.qml @@ -13,6 +13,7 @@ QtObject { readonly property Bt bt: Bt {} readonly property Network network: Network {} readonly property Ethernet ethernet: Ethernet {} + readonly property Launcher launcher: Launcher {} onActiveChanged: activeIndex = panes.indexOf(active) onActiveIndexChanged: active = panes[activeIndex] @@ -34,4 +35,8 @@ QtObject { component Ethernet: QtObject { property var active } + + component Launcher: QtObject { + property var active + } } diff --git a/modules/controlcenter/launcher/LauncherPane.qml b/modules/controlcenter/launcher/LauncherPane.qml index 300117a..85db6df 100644 --- a/modules/controlcenter/launcher/LauncherPane.qml +++ b/modules/controlcenter/launcher/LauncherPane.qml @@ -21,13 +21,26 @@ RowLayout { required property Session session - property var selectedApp: null + property var selectedApp: root.session.launcher.active property bool hideFromLauncherChecked: false anchors.fill: parent spacing: 0 + onSelectedAppChanged: { + root.session.launcher.active = root.selectedApp; + updateToggleState(); + } + + Connections { + target: root.session.launcher + function onActiveChanged() { + root.selectedApp = root.session.launcher.active; + updateToggleState(); + } + } + function updateToggleState() { if (!root.selectedApp) { root.hideFromLauncherChecked = false; @@ -73,9 +86,6 @@ RowLayout { Config.save(); } - onSelectedAppChanged: { - updateToggleState(); - } AppDb { id: allAppsDb @@ -202,6 +212,23 @@ RowLayout { Item { Layout.fillWidth: true } + + ToggleButton { + toggled: !root.session.launcher.active + icon: "settings" + accent: "Primary" + + onClicked: { + if (root.session.launcher.active) { + root.session.launcher.active = null; + } else { + // Toggle to show settings - if there are apps, select the first one, otherwise show settings + if (root.filteredApps.length > 0) { + root.session.launcher.active = root.filteredApps[0]; + } + } + } + } } StyledText { @@ -351,7 +378,7 @@ RowLayout { StateLayer { function onClicked(): void { - root.selectedApp = modelData; + root.session.launcher.active = modelData; } } @@ -405,11 +432,74 @@ RowLayout { Loader { id: rightLauncherLoader + property var pane: root.session.launcher.active + property string paneId: pane ? (pane.id || pane.entry?.id || "") : "" + anchors.fill: parent anchors.margins: Appearance.padding.large * 2 + opacity: 1 + scale: 1 + transformOrigin: Item.Center + clip: false + asynchronous: true - sourceComponent: rightContentComponent + sourceComponent: pane ? appDetails : settings + + Component.onCompleted: { + targetComponent = pane ? appDetails : settings; + nextComponent = targetComponent; + } + + property Component targetComponent: settings + property Component nextComponent: settings + + function getComponentForPane() { + return pane ? appDetails : settings; + } + + Behavior on paneId { + SequentialAnimation { + ParallelAnimation { + Anim { + target: rightLauncherLoader + property: "opacity" + to: 0 + easing.bezierCurve: Appearance.anim.curves.standardAccel + } + Anim { + target: rightLauncherLoader + property: "scale" + to: 0.8 + easing.bezierCurve: Appearance.anim.curves.standardAccel + } + } + PropertyAction { + target: rightLauncherLoader + property: "targetComponent" + value: rightLauncherLoader.nextComponent + } + ParallelAnimation { + Anim { + target: rightLauncherLoader + property: "opacity" + to: 1 + easing.bezierCurve: Appearance.anim.curves.standardDecel + } + Anim { + target: rightLauncherLoader + property: "scale" + to: 1 + easing.bezierCurve: Appearance.anim.curves.standardDecel + } + } + } + } + + onPaneChanged: { + nextComponent = getComponentForPane(); + paneId = pane ? (pane.id || pane.entry?.id || "") : ""; + } } } @@ -420,7 +510,30 @@ RowLayout { } Component { - id: rightContentComponent + id: settings + + StyledFlickable { + id: settingsFlickable + flickableDirection: Flickable.VerticalFlick + contentHeight: settingsInner.height + + StyledScrollBar.vertical: StyledScrollBar { + flickable: settingsFlickable + } + + Settings { + id: settingsInner + + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + session: root.session + } + } + } + + Component { + id: appDetails ColumnLayout { anchors.fill: parent @@ -516,4 +629,10 @@ RowLayout { } } } + + component Anim: NumberAnimation { + target: rightLauncherLoader + duration: Appearance.anim.durations.normal / 2 + easing.type: Easing.BezierSpline + } } diff --git a/modules/controlcenter/launcher/Settings.qml b/modules/controlcenter/launcher/Settings.qml new file mode 100644 index 0000000..1fef7f5 --- /dev/null +++ b/modules/controlcenter/launcher/Settings.qml @@ -0,0 +1,226 @@ +pragma ComponentBehavior: Bound + +import ".." +import qs.components +import qs.components.controls +import qs.components.effects +import qs.services +import qs.config +import QtQuick +import QtQuick.Layouts + +ColumnLayout { + id: root + + required property Session session + + spacing: Appearance.spacing.normal + + MaterialIcon { + Layout.alignment: Qt.AlignHCenter + text: "apps" + font.pointSize: Appearance.font.size.extraLarge * 3 + font.bold: true + } + + StyledText { + Layout.alignment: Qt.AlignHCenter + text: qsTr("Launcher Settings") + font.pointSize: Appearance.font.size.large + font.bold: true + } + + SectionHeader { + Layout.topMargin: Appearance.spacing.large + title: qsTr("General") + description: qsTr("General launcher settings") + } + + SectionContainer { + ToggleRow { + label: qsTr("Enabled") + checked: Config.launcher.enabled + toggle.onToggled: { + Config.launcher.enabled = checked; + Config.save(); + } + } + + ToggleRow { + label: qsTr("Show on hover") + checked: Config.launcher.showOnHover + toggle.onToggled: { + Config.launcher.showOnHover = checked; + Config.save(); + } + } + + ToggleRow { + label: qsTr("Vim keybinds") + checked: Config.launcher.vimKeybinds + toggle.onToggled: { + Config.launcher.vimKeybinds = checked; + Config.save(); + } + } + + ToggleRow { + label: qsTr("Enable dangerous actions") + checked: Config.launcher.enableDangerousActions + toggle.onToggled: { + Config.launcher.enableDangerousActions = checked; + Config.save(); + } + } + } + + SectionHeader { + Layout.topMargin: Appearance.spacing.large + title: qsTr("Display") + description: qsTr("Display and appearance settings") + } + + SectionContainer { + contentSpacing: Appearance.spacing.small / 2 + + PropertyRow { + label: qsTr("Max shown items") + value: qsTr("%1").arg(Config.launcher.maxShown) + } + + PropertyRow { + showTopMargin: true + label: qsTr("Max wallpapers") + value: qsTr("%1").arg(Config.launcher.maxWallpapers) + } + + PropertyRow { + showTopMargin: true + label: qsTr("Drag threshold") + value: qsTr("%1 px").arg(Config.launcher.dragThreshold) + } + } + + SectionHeader { + Layout.topMargin: Appearance.spacing.large + title: qsTr("Prefixes") + description: qsTr("Command prefix settings") + } + + SectionContainer { + contentSpacing: Appearance.spacing.small / 2 + + PropertyRow { + label: qsTr("Special prefix") + value: Config.launcher.specialPrefix || qsTr("None") + } + + PropertyRow { + showTopMargin: true + label: qsTr("Action prefix") + value: Config.launcher.actionPrefix || qsTr("None") + } + } + + SectionHeader { + Layout.topMargin: Appearance.spacing.large + title: qsTr("Fuzzy search") + description: qsTr("Fuzzy search settings") + } + + SectionContainer { + ToggleRow { + label: qsTr("Apps") + checked: Config.launcher.useFuzzy.apps + toggle.onToggled: { + Config.launcher.useFuzzy.apps = checked; + Config.save(); + } + } + + ToggleRow { + label: qsTr("Actions") + checked: Config.launcher.useFuzzy.actions + toggle.onToggled: { + Config.launcher.useFuzzy.actions = checked; + Config.save(); + } + } + + ToggleRow { + label: qsTr("Schemes") + checked: Config.launcher.useFuzzy.schemes + toggle.onToggled: { + Config.launcher.useFuzzy.schemes = checked; + Config.save(); + } + } + + ToggleRow { + label: qsTr("Variants") + checked: Config.launcher.useFuzzy.variants + toggle.onToggled: { + Config.launcher.useFuzzy.variants = checked; + Config.save(); + } + } + + ToggleRow { + label: qsTr("Wallpapers") + checked: Config.launcher.useFuzzy.wallpapers + toggle.onToggled: { + Config.launcher.useFuzzy.wallpapers = checked; + Config.save(); + } + } + } + + SectionHeader { + Layout.topMargin: Appearance.spacing.large + title: qsTr("Sizes") + description: qsTr("Size settings for launcher items") + } + + SectionContainer { + contentSpacing: Appearance.spacing.small / 2 + + PropertyRow { + label: qsTr("Item width") + value: qsTr("%1 px").arg(Config.launcher.sizes.itemWidth) + } + + PropertyRow { + showTopMargin: true + label: qsTr("Item height") + value: qsTr("%1 px").arg(Config.launcher.sizes.itemHeight) + } + + PropertyRow { + showTopMargin: true + label: qsTr("Wallpaper width") + value: qsTr("%1 px").arg(Config.launcher.sizes.wallpaperWidth) + } + + PropertyRow { + showTopMargin: true + label: qsTr("Wallpaper height") + value: qsTr("%1 px").arg(Config.launcher.sizes.wallpaperHeight) + } + } + + SectionHeader { + Layout.topMargin: Appearance.spacing.large + title: qsTr("Hidden apps") + description: qsTr("Applications hidden from launcher") + } + + SectionContainer { + contentSpacing: Appearance.spacing.small / 2 + + PropertyRow { + label: qsTr("Total hidden") + value: qsTr("%1").arg(Config.launcher.hiddenApps ? Config.launcher.hiddenApps.length : 0) + } + } +} + -- cgit v1.2.3-freya From e72fe3f73b4e9eb9dd3e502458090839e86b947f Mon Sep 17 00:00:00 2001 From: ATMDA Date: Mon, 17 Nov 2025 22:37:33 -0500 Subject: controlcenter: corrected launcher animation on data update --- modules/controlcenter/launcher/LauncherPane.qml | 80 ++++++++++++++++++++----- 1 file changed, 66 insertions(+), 14 deletions(-) (limited to 'modules/controlcenter/launcher') diff --git a/modules/controlcenter/launcher/LauncherPane.qml b/modules/controlcenter/launcher/LauncherPane.qml index 85db6df..f2247a7 100644 --- a/modules/controlcenter/launcher/LauncherPane.qml +++ b/modules/controlcenter/launcher/LauncherPane.qml @@ -434,6 +434,13 @@ RowLayout { property var pane: root.session.launcher.active property string paneId: pane ? (pane.id || pane.entry?.id || "") : "" + property Component targetComponent: settings + property Component nextComponent: settings + property var displayedApp: null + + function getComponentForPane() { + return pane ? appDetails : settings; + } anchors.fill: parent anchors.margins: Appearance.padding.large * 2 @@ -444,18 +451,20 @@ RowLayout { clip: false asynchronous: true - sourceComponent: pane ? appDetails : settings + sourceComponent: rightLauncherLoader.targetComponent + active: true Component.onCompleted: { - targetComponent = pane ? appDetails : settings; + displayedApp = pane; + targetComponent = getComponentForPane(); nextComponent = targetComponent; } - property Component targetComponent: settings - property Component nextComponent: settings - - function getComponentForPane() { - return pane ? appDetails : settings; + onItemChanged: { + // Ensure displayedApp is set when item is created (for async loading) + if (item && pane && displayedApp !== pane) { + displayedApp = pane; + } } Behavior on paneId { @@ -474,11 +483,26 @@ RowLayout { easing.bezierCurve: Appearance.anim.curves.standardAccel } } + PropertyAction { + target: rightLauncherLoader + property: "displayedApp" + value: rightLauncherLoader.pane + } + PropertyAction { + target: rightLauncherLoader + property: "active" + value: false + } PropertyAction { target: rightLauncherLoader property: "targetComponent" value: rightLauncherLoader.nextComponent } + PropertyAction { + target: rightLauncherLoader + property: "active" + value: true + } ParallelAnimation { Anim { target: rightLauncherLoader @@ -500,6 +524,19 @@ RowLayout { nextComponent = getComponentForPane(); paneId = pane ? (pane.id || pane.entry?.id || "") : ""; } + + onDisplayedAppChanged: { + if (displayedApp) { + const appId = displayedApp.id || displayedApp.entry?.id; + if (Config.launcher.hiddenApps && Config.launcher.hiddenApps.length > 0) { + root.hideFromLauncherChecked = Config.launcher.hiddenApps.includes(appId); + } else { + root.hideFromLauncherChecked = false; + } + } else { + root.hideFromLauncherChecked = false; + } + } } } @@ -550,7 +587,7 @@ RowLayout { Loader { id: iconLoader - sourceComponent: root.selectedApp ? appIconComponent : defaultIconComponent + sourceComponent: rightLauncherLoader.displayedApp ? appIconComponent : defaultIconComponent } Component { @@ -558,8 +595,8 @@ RowLayout { IconImage { implicitSize: Appearance.font.size.extraLarge * 3 * 2 source: { - if (!root.selectedApp) return "image-missing"; - const entry = root.selectedApp.entry; + if (!rightLauncherLoader.displayedApp) return "image-missing"; + const entry = rightLauncherLoader.displayedApp.entry; if (entry && entry.icon) { return Quickshell.iconPath(entry.icon, "image-missing"); } @@ -582,7 +619,7 @@ RowLayout { 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") + 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 } @@ -613,13 +650,28 @@ RowLayout { SwitchRow { Layout.topMargin: Appearance.spacing.normal - visible: root.selectedApp !== null + visible: rightLauncherLoader.displayedApp !== null label: qsTr("Hide from launcher") checked: root.hideFromLauncherChecked - enabled: root.selectedApp !== null + enabled: rightLauncherLoader.displayedApp !== null onToggled: checked => { root.hideFromLauncherChecked = checked; - root.saveHiddenApps(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); + } + } + Config.launcher.hiddenApps = hiddenApps; + Config.save(); + } } } -- 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/launcher') 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 aff01f01a859f0d959c76d67f346b34a714cb971 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Wed, 19 Nov 2025 13:10:09 -0500 Subject: refactor: PaneTransition now component --- modules/controlcenter/bluetooth/BtPane.qml | 60 +++++++--------- .../controlcenter/components/PaneTransition.qml | 84 ++++++++++++++++++++++ modules/controlcenter/launcher/LauncherPane.qml | 73 ++++++------------- modules/controlcenter/network/EthernetPane.qml | 45 ++---------- modules/controlcenter/network/NetworkingPane.qml | 70 ++++-------------- modules/controlcenter/network/WirelessPane.qml | 45 ++---------- 6 files changed, 156 insertions(+), 221 deletions(-) create mode 100644 modules/controlcenter/components/PaneTransition.qml (limited to 'modules/controlcenter/launcher') diff --git a/modules/controlcenter/bluetooth/BtPane.qml b/modules/controlcenter/bluetooth/BtPane.qml index cacb611..9d0a4c1 100644 --- a/modules/controlcenter/bluetooth/BtPane.qml +++ b/modules/controlcenter/bluetooth/BtPane.qml @@ -33,6 +33,18 @@ Item { id: rightBtPane property BluetoothDevice pane: root.session.bt.active + property string paneId: pane ? (pane.address || "") : "" + property Component targetComponent: settings + property Component nextComponent: settings + + function getComponentForPane() { + return pane ? details : settings; + } + + Component.onCompleted: { + targetComponent = getComponentForPane(); + nextComponent = targetComponent; + } Loader { id: rightLoader @@ -41,40 +53,19 @@ Item { anchors.margins: Appearance.padding.large * 2 asynchronous: true - sourceComponent: rightBtPane.pane ? details : settings + sourceComponent: rightBtPane.targetComponent } - 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 + Behavior on paneId { + PaneTransition { + target: rightLoader + propertyActions: [ + PropertyAction { + target: rightBtPane + property: "targetComponent" + value: rightBtPane.nextComponent } - } - 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 - } - } + ] } } @@ -82,6 +73,8 @@ Item { target: root.session.bt function onActiveChanged() { rightBtPane.pane = root.session.bt.active; + rightBtPane.nextComponent = rightBtPane.getComponentForPane(); + rightBtPane.paneId = pane ? (pane.address || "") : ""; } } } @@ -118,9 +111,4 @@ Item { session: root.session } } - - component Anim: NumberAnimation { - duration: Appearance.anim.durations.normal / 2 - easing.type: Easing.BezierSpline - } } diff --git a/modules/controlcenter/components/PaneTransition.qml b/modules/controlcenter/components/PaneTransition.qml new file mode 100644 index 0000000..1da4afb --- /dev/null +++ b/modules/controlcenter/components/PaneTransition.qml @@ -0,0 +1,84 @@ +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 + property: "opacity" + from: root.opacityFrom + to: root.opacityTo + duration: Appearance.anim.durations.normal / 2 + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.standardAccel + } + + NumberAnimation { + target: root.target + property: "scale" + from: root.scaleFrom + to: root.scaleTo + duration: Appearance.anim.durations.normal / 2 + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.standardAccel + } + } + + // 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++) { + const action = root.propertyActions[i]; + if (action.target && action.property !== undefined) { + action.target[action.property] = action.value; + } + } + } + } + + // Fade in and scale up + ParallelAnimation { + NumberAnimation { + target: root.target + property: "opacity" + from: root.opacityTo + to: root.opacityFrom + duration: Appearance.anim.durations.normal / 2 + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.standardDecel + } + + NumberAnimation { + target: root.target + property: "scale" + from: root.scaleTo + to: root.scaleFrom + duration: Appearance.anim.durations.normal / 2 + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.standardDecel + } + } +} + diff --git a/modules/controlcenter/launcher/LauncherPane.qml b/modules/controlcenter/launcher/LauncherPane.qml index 30e2953..a8c5c76 100644 --- a/modules/controlcenter/launcher/LauncherPane.qml +++ b/modules/controlcenter/launcher/LauncherPane.qml @@ -426,55 +426,30 @@ Item { } Behavior on paneId { - SequentialAnimation { - ParallelAnimation { - Anim { + PaneTransition { + target: rightLauncherLoader + propertyActions: [ + PropertyAction { + target: rightLauncherPane + property: "displayedApp" + value: rightLauncherPane.pane + }, + PropertyAction { target: rightLauncherLoader - property: "opacity" - to: 0 - easing.bezierCurve: Appearance.anim.curves.standardAccel - } - Anim { + property: "active" + value: false + }, + PropertyAction { + target: rightLauncherPane + property: "targetComponent" + value: rightLauncherPane.nextComponent + }, + PropertyAction { target: rightLauncherLoader - property: "scale" - to: 0.8 - easing.bezierCurve: Appearance.anim.curves.standardAccel + property: "active" + value: true } - } - PropertyAction { - target: rightLauncherPane - property: "displayedApp" - value: rightLauncherPane.pane - } - PropertyAction { - target: rightLauncherLoader - property: "active" - value: false - } - PropertyAction { - target: rightLauncherPane - property: "targetComponent" - value: rightLauncherPane.nextComponent - } - PropertyAction { - target: rightLauncherLoader - property: "active" - value: true - } - ParallelAnimation { - Anim { - target: rightLauncherLoader - property: "opacity" - to: 1 - easing.bezierCurve: Appearance.anim.curves.standardDecel - } - Anim { - target: rightLauncherLoader - property: "scale" - to: 1 - easing.bezierCurve: Appearance.anim.curves.standardDecel - } - } + ] } } @@ -639,10 +614,4 @@ Item { } } } - - component Anim: NumberAnimation { - target: rightLauncherLoader - duration: Appearance.anim.durations.normal / 2 - easing.type: Easing.BezierSpline - } } diff --git a/modules/controlcenter/network/EthernetPane.qml b/modules/controlcenter/network/EthernetPane.qml index 6a50cde..fc979c3 100644 --- a/modules/controlcenter/network/EthernetPane.qml +++ b/modules/controlcenter/network/EthernetPane.qml @@ -77,40 +77,15 @@ RowLayout { } Behavior on paneId { - SequentialAnimation { - ParallelAnimation { - Anim { + PaneTransition { + target: loader + propertyActions: [ + PropertyAction { target: loader - property: "opacity" - to: 0 - easing.bezierCurve: Appearance.anim.curves.standardAccel + property: "targetComponent" + value: loader.nextComponent } - Anim { - target: loader - property: "scale" - to: 0.8 - easing.bezierCurve: Appearance.anim.curves.standardAccel - } - } - PropertyAction { - target: loader - property: "targetComponent" - value: loader.nextComponent - } - 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 - } - } + ] } } @@ -153,10 +128,4 @@ RowLayout { } } } - - component Anim: NumberAnimation { - target: loader - duration: Appearance.anim.durations.normal / 2 - easing.type: Easing.BezierSpline - } } \ No newline at end of file diff --git a/modules/controlcenter/network/NetworkingPane.qml b/modules/controlcenter/network/NetworkingPane.qml index 55c70d2..127a42a 100644 --- a/modules/controlcenter/network/NetworkingPane.qml +++ b/modules/controlcenter/network/NetworkingPane.qml @@ -447,7 +447,7 @@ Item { 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 string paneId: ethernetPane ? ("eth:" + (ethernetPane.interface || "")) : (wirelessPane ? ("wifi:" + (wirelessPane.ssid || wirelessPane.bssid || "")) : "settings") property Component targetComponent: settings property Component nextComponent: settings @@ -463,16 +463,16 @@ Item { Connections { target: root.session.ethernet function onActiveChanged() { - nextComponent = getComponentForPane(); - paneId = ethernetPane ? (ethernetPane.interface || "") : (wirelessPane ? (wirelessPane.ssid || wirelessPane.bssid || "") : ""); + rightPaneItem.nextComponent = rightPaneItem.getComponentForPane(); + rightPaneItem.paneId = rightPaneItem.ethernetPane ? ("eth:" + (rightPaneItem.ethernetPane.interface || "")) : (rightPaneItem.wirelessPane ? ("wifi:" + (rightPaneItem.wirelessPane.ssid || rightPaneItem.wirelessPane.bssid || "")) : "settings"); } } Connections { target: root.session.network function onActiveChanged() { - nextComponent = getComponentForPane(); - paneId = ethernetPane ? (ethernetPane.interface || "") : (wirelessPane ? (wirelessPane.ssid || wirelessPane.bssid || "") : ""); + rightPaneItem.nextComponent = rightPaneItem.getComponentForPane(); + rightPaneItem.paneId = rightPaneItem.ethernetPane ? ("eth:" + (rightPaneItem.ethernetPane.interface || "")) : (rightPaneItem.wirelessPane ? ("wifi:" + (rightPaneItem.wirelessPane.ssid || rightPaneItem.wirelessPane.bssid || "")) : "settings"); } } @@ -489,57 +489,18 @@ Item { asynchronous: true sourceComponent: rightPaneItem.targetComponent - - Connections { - target: rightPaneItem - function onPaneIdChanged() { - rightPaneItem.targetComponent = rightPaneItem.nextComponent; - } - } } Behavior on paneId { - 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 - } - } - 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 + PaneTransition { + target: rightLoader + propertyActions: [ + PropertyAction { + target: rightPaneItem + property: "targetComponent" + value: rightPaneItem.nextComponent } - } - } - } - - Connections { - target: rightPaneItem - function onPaneIdChanged() { - rightPaneItem.targetComponent = rightPaneItem.nextComponent; + ] } } } @@ -621,11 +582,6 @@ Item { z: 1000 } - component Anim: NumberAnimation { - duration: Appearance.anim.durations.normal / 2 - easing.type: Easing.BezierSpline - } - function checkSavedProfileForNetwork(ssid: string): void { if (ssid && ssid.length > 0) { Nmcli.loadSavedConnections(() => {}); diff --git a/modules/controlcenter/network/WirelessPane.qml b/modules/controlcenter/network/WirelessPane.qml index 22364a1..0c8f0ae 100644 --- a/modules/controlcenter/network/WirelessPane.qml +++ b/modules/controlcenter/network/WirelessPane.qml @@ -76,40 +76,15 @@ RowLayout { } Behavior on paneId { - SequentialAnimation { - ParallelAnimation { - Anim { + PaneTransition { + target: loader + propertyActions: [ + PropertyAction { target: loader - property: "opacity" - to: 0 - easing.bezierCurve: Appearance.anim.curves.standardAccel + property: "targetComponent" + value: loader.nextComponent } - Anim { - target: loader - property: "scale" - to: 0.8 - easing.bezierCurve: Appearance.anim.curves.standardAccel - } - } - PropertyAction { - target: loader - property: "targetComponent" - value: loader.nextComponent - } - 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 - } - } + ] } } @@ -158,10 +133,4 @@ RowLayout { session: root.session z: 1000 } - - component Anim: NumberAnimation { - target: loader - duration: Appearance.anim.durations.normal / 2 - easing.type: Easing.BezierSpline - } } \ No newline at end of file -- 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/launcher') 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 ef46a02b2f3561574c1ada5afefbbb806bcb6a3b Mon Sep 17 00:00:00 2001 From: ATMDA Date: Wed, 19 Nov 2025 21:04:32 -0500 Subject: refactor: ToggleButton Tooltips and DeviceList --- components/controls/ToggleButton.qml | 51 +- components/controls/Tooltip.qml | 170 +++++ modules/controlcenter/bluetooth/BtPane.qml | 19 +- modules/controlcenter/bluetooth/Details.qml | 681 +++++++++++---------- modules/controlcenter/bluetooth/DeviceList.qml | 84 ++- modules/controlcenter/components/DeviceDetails.qml | 2 + modules/controlcenter/components/DeviceList.qml | 14 +- modules/controlcenter/launcher/LauncherPane.qml | 4 + modules/controlcenter/network/EthernetDetails.qml | 140 +++-- modules/controlcenter/network/EthernetList.qml | 6 +- modules/controlcenter/network/NetworkingPane.qml | 377 ++---------- modules/controlcenter/network/WirelessDetails.qml | 182 +++--- modules/controlcenter/network/WirelessList.qml | 13 +- 13 files changed, 865 insertions(+), 878 deletions(-) create mode 100644 components/controls/Tooltip.qml (limited to 'modules/controlcenter/launcher') diff --git a/components/controls/ToggleButton.qml b/components/controls/ToggleButton.qml index 9d8e094..b2c2afe 100644 --- a/components/controls/ToggleButton.qml +++ b/components/controls/ToggleButton.qml @@ -1,5 +1,6 @@ import ".." import qs.components +import qs.components.controls import qs.components.effects import qs.services import qs.config @@ -13,12 +14,31 @@ StyledRect { property string icon property string label property string accent: "Secondary" + property real iconSize: Appearance.font.size.large + property real horizontalPadding: Appearance.padding.large + property real verticalPadding: Appearance.padding.normal + property string tooltip: "" + property bool hovered: false signal clicked + Component.onCompleted: { + hovered = toggleStateLayer.containsMouse; + } + + Connections { + target: toggleStateLayer + function onContainsMouseChanged() { + const newHovered = toggleStateLayer.containsMouse; + if (hovered !== newHovered) { + hovered = newHovered; + } + } + } + Layout.preferredWidth: implicitWidth + (toggleStateLayer.pressed ? Appearance.padding.normal * 2 : toggled ? Appearance.padding.small * 2 : 0) - implicitWidth: toggleBtnInner.implicitWidth + Appearance.padding.large * 2 - implicitHeight: toggleBtnIcon.implicitHeight + Appearance.padding.normal * 2 + implicitWidth: toggleBtnInner.implicitWidth + horizontalPadding * 2 + implicitHeight: toggleBtnIcon.implicitHeight + verticalPadding * 2 radius: toggled || toggleStateLayer.pressed ? Appearance.rounding.small : Math.min(width, height) / 2 * Math.min(1, Appearance.rounding.scale) color: toggled ? Colours.palette[`m3${accent.toLowerCase()}`] : Colours.palette[`m3${accent.toLowerCase()}Container`] @@ -46,7 +66,7 @@ StyledRect { fill: root.toggled ? 1 : 0 text: root.icon color: root.toggled ? Colours.palette[`m3on${root.accent}`] : Colours.palette[`m3on${root.accent}Container`] - font.pointSize: Appearance.font.size.large + font.pointSize: root.iconSize Behavior on fill { Anim {} @@ -78,5 +98,30 @@ StyledRect { easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial } } + + // Tooltip - positioned absolutely, doesn't affect layout + Loader { + id: tooltipLoader + active: root.tooltip !== "" + asynchronous: true + z: 10000 + width: 0 + height: 0 + sourceComponent: Component { + Tooltip { + target: root + text: root.tooltip + } + } + // Completely remove from layout + Layout.fillWidth: false + Layout.fillHeight: false + Layout.preferredWidth: 0 + Layout.preferredHeight: 0 + Layout.maximumWidth: 0 + Layout.maximumHeight: 0 + Layout.minimumWidth: 0 + Layout.minimumHeight: 0 + } } diff --git a/components/controls/Tooltip.qml b/components/controls/Tooltip.qml new file mode 100644 index 0000000..bab8086 --- /dev/null +++ b/components/controls/Tooltip.qml @@ -0,0 +1,170 @@ +import ".." +import qs.components.effects +import qs.services +import qs.config +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +Popup { + id: root + + required property Item target + required property string text + property int delay: 500 + property int timeout: 0 + + property bool tooltipVisible: false + property Timer showTimer: Timer { + interval: root.delay + onTriggered: root.tooltipVisible = true + } + property Timer hideTimer: Timer { + interval: root.timeout + onTriggered: root.tooltipVisible = false + } + + // Popup properties - doesn't affect layout + parent: { + let p = target; + // Walk up to find the root Item (usually has anchors.fill: parent) + while (p && p.parent) { + const parentItem = p.parent; + // Check if this looks like a root pane Item + if (parentItem && parentItem.anchors && parentItem.anchors.fill !== undefined) { + return parentItem; + } + p = parentItem; + } + // Fallback + return target.parent?.parent?.parent ?? target.parent?.parent ?? target.parent ?? target; + } + + visible: tooltipVisible + modal: false + closePolicy: Popup.NoAutoClose + padding: 0 + margins: 0 + + // Update position when target moves or tooltip becomes visible + onTooltipVisibleChanged: { + if (tooltipVisible) { + Qt.callLater(updatePosition); + } + } + Connections { + target: root.target + function onXChanged() { if (root.tooltipVisible) root.updatePosition(); } + function onYChanged() { if (root.tooltipVisible) root.updatePosition(); } + function onWidthChanged() { if (root.tooltipVisible) root.updatePosition(); } + function onHeightChanged() { if (root.tooltipVisible) root.updatePosition(); } + } + + function updatePosition() { + if (!target || !parent) return; + + // Wait for tooltipRect to have its size calculated + Qt.callLater(() => { + if (!target || !parent || !tooltipRect) return; + + // Get target position in parent's coordinate system + const targetPos = target.mapToItem(parent, 0, 0); + const targetCenterX = targetPos.x + target.width / 2; + + // Get tooltip size (use width/height if available, otherwise implicit) + const tooltipWidth = tooltipRect.width > 0 ? tooltipRect.width : tooltipRect.implicitWidth; + const tooltipHeight = tooltipRect.height > 0 ? tooltipRect.height : tooltipRect.implicitHeight; + + // Center tooltip horizontally on target + let newX = targetCenterX - tooltipWidth / 2; + + // Position tooltip above target + let newY = targetPos.y - tooltipHeight - Appearance.spacing.small; + + // Keep within bounds + const padding = Appearance.padding.normal; + if (newX < padding) { + newX = padding; + } else if (newX + tooltipWidth > (parent.width - padding)) { + newX = parent.width - tooltipWidth - padding; + } + + // Update popup position + x = newX; + y = newY; + }); + } + + enter: Transition { + Anim { + property: "opacity" + from: 0 + to: 1 + duration: Appearance.anim.durations.expressiveFastSpatial + easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial + } + } + + exit: Transition { + Anim { + property: "opacity" + from: 1 + to: 0 + duration: Appearance.anim.durations.expressiveFastSpatial + easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial + } + } + + // Monitor hover state + Connections { + target: root.target + function onHoveredChanged() { + if (target.hovered) { + showTimer.start(); + if (timeout > 0) { + hideTimer.stop(); + hideTimer.start(); + } + } else { + showTimer.stop(); + hideTimer.stop(); + tooltipVisible = false; + } + } + } + + contentItem: StyledRect { + id: tooltipRect + + implicitWidth: tooltipText.implicitWidth + Appearance.padding.normal * 2 + implicitHeight: tooltipText.implicitHeight + Appearance.padding.smaller * 2 + + color: Colours.palette.m3surfaceContainerHighest + radius: Appearance.rounding.small + + // Add elevation for depth + Elevation { + anchors.fill: parent + radius: parent.radius + z: -1 + level: 3 + } + + StyledText { + id: tooltipText + + anchors.centerIn: parent + + text: root.text + color: Colours.palette.m3onSurface + font.pointSize: Appearance.font.size.small + } + } + + Component.onCompleted: { + if (tooltipVisible) { + updatePosition(); + } + } +} + diff --git a/modules/controlcenter/bluetooth/BtPane.qml b/modules/controlcenter/bluetooth/BtPane.qml index 6877801..a987e75 100644 --- a/modules/controlcenter/bluetooth/BtPane.qml +++ b/modules/controlcenter/bluetooth/BtPane.qml @@ -24,8 +24,23 @@ SplitPaneWithDetails { } leftContent: Component { - DeviceList { - session: root.session + StyledFlickable { + id: leftFlickable + + flickableDirection: Flickable.VerticalFlick + contentHeight: deviceList.height + + StyledScrollBar.vertical: StyledScrollBar { + flickable: leftFlickable + } + + DeviceList { + id: deviceList + + anchors.left: parent.left + anchors.right: parent.right + session: root.session + } } } diff --git a/modules/controlcenter/bluetooth/Details.qml b/modules/controlcenter/bluetooth/Details.qml index 5496966..b260458 100644 --- a/modules/controlcenter/bluetooth/Details.qml +++ b/modules/controlcenter/bluetooth/Details.qml @@ -20,404 +20,427 @@ StyledFlickable { readonly property BluetoothDevice device: session.bt.active flickableDirection: Flickable.VerticalFlick - contentHeight: layoutWrapper.height + contentHeight: detailsWrapper.height StyledScrollBar.vertical: StyledScrollBar { flickable: root } - Item { - id: layoutWrapper + Item { + id: detailsWrapper + + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + implicitHeight: details.implicitHeight + + DeviceDetails { + id: details anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top - implicitHeight: layout.height - - ColumnLayout { - id: layout - - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - spacing: Appearance.spacing.normal - - SettingsHeader { - icon: Icons.getBluetoothIcon(root.device?.icon ?? "") - title: root.device?.name ?? "" - } - - StyledText { - Layout.topMargin: Appearance.spacing.large - text: qsTr("Connection status") - font.pointSize: Appearance.font.size.larger - font.weight: 500 - } - StyledText { - text: qsTr("Connection settings for this device") - color: Colours.palette.m3outline - } - - StyledRect { - Layout.fillWidth: true - implicitHeight: deviceStatus.implicitHeight + Appearance.padding.large * 2 - - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - - ColumnLayout { - id: deviceStatus - - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.large + session: root.session + device: root.device - spacing: Appearance.spacing.larger - - Toggle { - label: qsTr("Connected") - checked: root.device?.connected ?? false - toggle.onToggled: root.device.connected = checked - } - - Toggle { - label: qsTr("Paired") - checked: root.device?.paired ?? false - toggle.onToggled: { - if (root.device.paired) - root.device.forget(); - else - root.device.pair(); - } - } - - Toggle { - label: qsTr("Blocked") - checked: root.device?.blocked ?? false - toggle.onToggled: root.device.blocked = checked - } + headerComponent: Component { + SettingsHeader { + icon: Icons.getBluetoothIcon(root.device?.icon ?? "") + title: root.device?.name ?? "" } } - StyledText { - Layout.topMargin: Appearance.spacing.large - text: qsTr("Device properties") - font.pointSize: Appearance.font.size.larger - font.weight: 500 - } - - StyledText { - text: qsTr("Additional settings") - color: Colours.palette.m3outline - } - - StyledRect { - Layout.fillWidth: true - implicitHeight: deviceProps.implicitHeight + Appearance.padding.large * 2 - - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer - - ColumnLayout { - id: deviceProps + sections: [ + Component { + ColumnLayout { + spacing: Appearance.spacing.normal - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.large + StyledText { + Layout.topMargin: Appearance.spacing.large + text: qsTr("Connection status") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } - spacing: Appearance.spacing.larger + StyledText { + text: qsTr("Connection settings for this device") + color: Colours.palette.m3outline + } - RowLayout { - Layout.fillWidth: true - spacing: Appearance.spacing.small + StyledRect { + Layout.fillWidth: true + implicitHeight: deviceStatus.implicitHeight + Appearance.padding.large * 2 - Item { - id: renameDevice + radius: Appearance.rounding.normal + color: Colours.tPalette.m3surfaceContainer - Layout.fillWidth: true - Layout.rightMargin: Appearance.spacing.small + ColumnLayout { + id: deviceStatus - implicitHeight: renameLabel.implicitHeight + deviceNameEdit.implicitHeight + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.large - states: State { - name: "editingDeviceName" - when: root.session.bt.editingDeviceName + spacing: Appearance.spacing.larger - AnchorChanges { - target: deviceNameEdit - anchors.top: renameDevice.top - } - PropertyChanges { - renameDevice.implicitHeight: deviceNameEdit.implicitHeight - renameLabel.opacity: 0 - deviceNameEdit.padding: Appearance.padding.normal + Toggle { + label: qsTr("Connected") + checked: root.device?.connected ?? false + toggle.onToggled: root.device.connected = checked } - } - transitions: Transition { - AnchorAnimation { - duration: Appearance.anim.durations.normal - easing.type: Easing.BezierSpline - easing.bezierCurve: Appearance.anim.curves.standard + Toggle { + label: qsTr("Paired") + checked: root.device?.paired ?? false + toggle.onToggled: { + if (root.device.paired) + root.device.forget(); + else + root.device.pair(); + } } - Anim { - properties: "implicitHeight,opacity,padding" + + Toggle { + label: qsTr("Blocked") + checked: root.device?.blocked ?? false + toggle.onToggled: root.device.blocked = checked } } + } + } + }, + Component { + ColumnLayout { + spacing: Appearance.spacing.normal + + StyledText { + Layout.topMargin: Appearance.spacing.large + text: qsTr("Device properties") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } - StyledText { - id: renameLabel + StyledText { + text: qsTr("Additional settings") + color: Colours.palette.m3outline + } - anchors.left: parent.left + StyledRect { + Layout.fillWidth: true + implicitHeight: deviceProps.implicitHeight + Appearance.padding.large * 2 - text: qsTr("Device name") - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.small - } + radius: Appearance.rounding.normal + color: Colours.tPalette.m3surfaceContainer - StyledTextField { - id: deviceNameEdit + ColumnLayout { + id: deviceProps anchors.left: parent.left anchors.right: parent.right - anchors.top: renameLabel.bottom - anchors.leftMargin: root.session.bt.editingDeviceName ? 0 : -Appearance.padding.normal - - text: root.device?.name ?? "" - readOnly: !root.session.bt.editingDeviceName - onAccepted: { - root.session.bt.editingDeviceName = false; - root.device.name = text; - } - - leftPadding: Appearance.padding.normal - rightPadding: Appearance.padding.normal - - background: StyledRect { - radius: Appearance.rounding.small - border.width: 2 - border.color: Colours.palette.m3primary - opacity: root.session.bt.editingDeviceName ? 1 : 0 + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.large + + spacing: Appearance.spacing.larger + + RowLayout { + Layout.fillWidth: true + spacing: Appearance.spacing.small + + Item { + id: renameDevice + + Layout.fillWidth: true + Layout.rightMargin: Appearance.spacing.small + + implicitHeight: renameLabel.implicitHeight + deviceNameEdit.implicitHeight + + states: State { + name: "editingDeviceName" + when: root.session.bt.editingDeviceName + + AnchorChanges { + target: deviceNameEdit + anchors.top: renameDevice.top + } + PropertyChanges { + renameDevice.implicitHeight: deviceNameEdit.implicitHeight + renameLabel.opacity: 0 + deviceNameEdit.padding: Appearance.padding.normal + } + } + + transitions: Transition { + AnchorAnimation { + duration: Appearance.anim.durations.normal + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.anim.curves.standard + } + Anim { + properties: "implicitHeight,opacity,padding" + } + } + + StyledText { + id: renameLabel + + anchors.left: parent.left + + text: qsTr("Device name") + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.small + } + + StyledTextField { + id: deviceNameEdit + + anchors.left: parent.left + anchors.right: parent.right + anchors.top: renameLabel.bottom + anchors.leftMargin: root.session.bt.editingDeviceName ? 0 : -Appearance.padding.normal + + text: root.device?.name ?? "" + readOnly: !root.session.bt.editingDeviceName + onAccepted: { + root.session.bt.editingDeviceName = false; + root.device.name = text; + } + + leftPadding: Appearance.padding.normal + rightPadding: Appearance.padding.normal + + background: StyledRect { + radius: Appearance.rounding.small + border.width: 2 + border.color: Colours.palette.m3primary + opacity: root.session.bt.editingDeviceName ? 1 : 0 + + Behavior on border.color { + CAnim {} + } + + Behavior on opacity { + Anim {} + } + } + + Behavior on anchors.leftMargin { + Anim {} + } + } + } - Behavior on border.color { - CAnim {} + StyledRect { + implicitWidth: implicitHeight + implicitHeight: cancelEditIcon.implicitHeight + Appearance.padding.smaller * 2 + + radius: Appearance.rounding.small + color: Colours.palette.m3secondaryContainer + opacity: root.session.bt.editingDeviceName ? 1 : 0 + scale: root.session.bt.editingDeviceName ? 1 : 0.5 + + StateLayer { + color: Colours.palette.m3onSecondaryContainer + disabled: !root.session.bt.editingDeviceName + + function onClicked(): void { + root.session.bt.editingDeviceName = false; + deviceNameEdit.text = Qt.binding(() => root.device?.name ?? ""); + } + } + + MaterialIcon { + id: cancelEditIcon + + anchors.centerIn: parent + animate: true + text: "cancel" + color: Colours.palette.m3onSecondaryContainer + } + + Behavior on opacity { + Anim {} + } + + Behavior on scale { + Anim { + duration: Appearance.anim.durations.expressiveFastSpatial + easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial + } + } } - Behavior on opacity { - Anim {} + StyledRect { + implicitWidth: implicitHeight + implicitHeight: editIcon.implicitHeight + Appearance.padding.smaller * 2 + + radius: root.session.bt.editingDeviceName ? Appearance.rounding.small : implicitHeight / 2 * Math.min(1, Appearance.rounding.scale) + color: Qt.alpha(Colours.palette.m3primary, root.session.bt.editingDeviceName ? 1 : 0) + + StateLayer { + color: root.session.bt.editingDeviceName ? Colours.palette.m3onPrimary : Colours.palette.m3onSurface + + function onClicked(): void { + root.session.bt.editingDeviceName = !root.session.bt.editingDeviceName; + if (root.session.bt.editingDeviceName) + deviceNameEdit.forceActiveFocus(); + else + deviceNameEdit.accepted(); + } + } + + MaterialIcon { + id: editIcon + + anchors.centerIn: parent + animate: true + text: root.session.bt.editingDeviceName ? "check_circle" : "edit" + color: root.session.bt.editingDeviceName ? Colours.palette.m3onPrimary : Colours.palette.m3onSurface + } + + Behavior on radius { + Anim {} + } } } - Behavior on anchors.leftMargin { - Anim {} + Toggle { + label: qsTr("Trusted") + checked: root.device?.trusted ?? false + toggle.onToggled: root.device.trusted = checked } - } - } - - StyledRect { - implicitWidth: implicitHeight - implicitHeight: cancelEditIcon.implicitHeight + Appearance.padding.smaller * 2 - - radius: Appearance.rounding.small - color: Colours.palette.m3secondaryContainer - opacity: root.session.bt.editingDeviceName ? 1 : 0 - scale: root.session.bt.editingDeviceName ? 1 : 0.5 - - StateLayer { - color: Colours.palette.m3onSecondaryContainer - disabled: !root.session.bt.editingDeviceName - function onClicked(): void { - root.session.bt.editingDeviceName = false; - deviceNameEdit.text = Qt.binding(() => root.device?.name ?? ""); + Toggle { + label: qsTr("Wake allowed") + checked: root.device?.wakeAllowed ?? false + toggle.onToggled: root.device.wakeAllowed = checked } } + } + } + }, + Component { + ColumnLayout { + spacing: Appearance.spacing.normal + + StyledText { + Layout.topMargin: Appearance.spacing.large + text: qsTr("Device information") + font.pointSize: Appearance.font.size.larger + font.weight: 500 + } - MaterialIcon { - id: cancelEditIcon - - anchors.centerIn: parent - animate: true - text: "cancel" - color: Colours.palette.m3onSecondaryContainer - } - - Behavior on opacity { - Anim {} - } - - Behavior on scale { - Anim { - duration: Appearance.anim.durations.expressiveFastSpatial - easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial - } - } + StyledText { + text: qsTr("Information about this device") + color: Colours.palette.m3outline } StyledRect { - implicitWidth: implicitHeight - implicitHeight: editIcon.implicitHeight + Appearance.padding.smaller * 2 - - radius: root.session.bt.editingDeviceName ? Appearance.rounding.small : implicitHeight / 2 * Math.min(1, Appearance.rounding.scale) - color: Qt.alpha(Colours.palette.m3primary, root.session.bt.editingDeviceName ? 1 : 0) - - StateLayer { - color: root.session.bt.editingDeviceName ? Colours.palette.m3onPrimary : Colours.palette.m3onSurface - - function onClicked(): void { - root.session.bt.editingDeviceName = !root.session.bt.editingDeviceName; - if (root.session.bt.editingDeviceName) - deviceNameEdit.forceActiveFocus(); - else - deviceNameEdit.accepted(); - } - } - - MaterialIcon { - id: editIcon - - anchors.centerIn: parent - animate: true - text: root.session.bt.editingDeviceName ? "check_circle" : "edit" - color: root.session.bt.editingDeviceName ? Colours.palette.m3onPrimary : Colours.palette.m3onSurface - } - - Behavior on radius { - Anim {} - } - } - } + Layout.fillWidth: true + implicitHeight: deviceInfo.implicitHeight + Appearance.padding.large * 2 - Toggle { - label: qsTr("Trusted") - checked: root.device?.trusted ?? false - toggle.onToggled: root.device.trusted = checked - } + radius: Appearance.rounding.normal + color: Colours.tPalette.m3surfaceContainer - Toggle { - label: qsTr("Wake allowed") - checked: root.device?.wakeAllowed ?? false - toggle.onToggled: root.device.wakeAllowed = checked - } - } - } + ColumnLayout { + id: deviceInfo - StyledText { - Layout.topMargin: Appearance.spacing.large - text: qsTr("Device information") - font.pointSize: Appearance.font.size.larger - font.weight: 500 - } + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: Appearance.padding.large - StyledText { - text: qsTr("Information about this device") - color: Colours.palette.m3outline - } + spacing: Appearance.spacing.small / 2 - StyledRect { - Layout.fillWidth: true - implicitHeight: deviceInfo.implicitHeight + Appearance.padding.large * 2 + StyledText { + text: root.device?.batteryAvailable ? qsTr("Device battery (%1%)").arg(root.device.battery * 100) : qsTr("Battery unavailable") + } - radius: Appearance.rounding.normal - color: Colours.tPalette.m3surfaceContainer + RowLayout { + Layout.topMargin: Appearance.spacing.small / 2 + Layout.fillWidth: true + Layout.preferredHeight: Appearance.padding.smaller + spacing: Appearance.spacing.small / 2 + + StyledRect { + Layout.fillHeight: true + implicitWidth: root.device?.batteryAvailable ? parent.width * root.device.battery : 0 + radius: Appearance.rounding.full + color: Colours.palette.m3primary + } - ColumnLayout { - id: deviceInfo + StyledRect { + Layout.fillWidth: true + Layout.fillHeight: true + radius: Appearance.rounding.full + color: Colours.palette.m3secondaryContainer + + StyledRect { + anchors.right: parent.right + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.margins: parent.height * 0.25 + + implicitWidth: height + radius: Appearance.rounding.full + color: Colours.palette.m3primary + } + } + } - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.large + StyledText { + Layout.topMargin: Appearance.spacing.normal + text: qsTr("Dbus path") + } - spacing: Appearance.spacing.small / 2 + StyledText { + text: root.device?.dbusPath ?? "" + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.small + } - StyledText { - text: root.device?.batteryAvailable ? qsTr("Device battery (%1%)").arg(root.device.battery * 100) : qsTr("Battery unavailable") - } + StyledText { + Layout.topMargin: Appearance.spacing.normal + text: qsTr("MAC address") + } - RowLayout { - Layout.topMargin: Appearance.spacing.small / 2 - Layout.fillWidth: true - Layout.preferredHeight: Appearance.padding.smaller - spacing: Appearance.spacing.small / 2 + StyledText { + text: root.device?.address ?? "" + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.small + } - StyledRect { - Layout.fillHeight: true - implicitWidth: root.device?.batteryAvailable ? parent.width * root.device.battery : 0 - radius: Appearance.rounding.full - color: Colours.palette.m3primary - } + StyledText { + Layout.topMargin: Appearance.spacing.normal + text: qsTr("Bonded") + } - StyledRect { - Layout.fillWidth: true - Layout.fillHeight: true - radius: Appearance.rounding.full - color: Colours.palette.m3secondaryContainer + StyledText { + text: root.device?.bonded ? qsTr("Yes") : qsTr("No") + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.small + } - StyledRect { - anchors.right: parent.right - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.margins: parent.height * 0.25 + StyledText { + Layout.topMargin: Appearance.spacing.normal + text: qsTr("System name") + } - implicitWidth: height - radius: Appearance.rounding.full - color: Colours.palette.m3primary + StyledText { + text: root.device?.deviceName ?? "" + color: Colours.palette.m3outline + font.pointSize: Appearance.font.size.small + } } } } - - StyledText { - Layout.topMargin: Appearance.spacing.normal - text: qsTr("Dbus path") - } - - StyledText { - text: root.device?.dbusPath ?? "" - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.small - } - - StyledText { - Layout.topMargin: Appearance.spacing.normal - text: qsTr("MAC address") - } - - StyledText { - text: root.device?.address ?? "" - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.small - } - - StyledText { - Layout.topMargin: Appearance.spacing.normal - text: qsTr("Bonded") - } - - StyledText { - text: root.device?.bonded ? qsTr("Yes") : qsTr("No") - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.small - } - - StyledText { - Layout.topMargin: Appearance.spacing.normal - text: qsTr("System name") - } - - StyledText { - text: root.device?.deviceName ?? "" - color: Colours.palette.m3outline - font.pointSize: Appearance.font.size.small - } } - } - } + ] } + } + // FAB Menu (positioned absolutely relative to flickable) ColumnLayout { anchors.right: fabRoot.right anchors.bottom: fabRoot.top diff --git a/modules/controlcenter/bluetooth/DeviceList.qml b/modules/controlcenter/bluetooth/DeviceList.qml index b3db236..b978a2d 100644 --- a/modules/controlcenter/bluetooth/DeviceList.qml +++ b/modules/controlcenter/bluetooth/DeviceList.qml @@ -48,8 +48,12 @@ DeviceList { toggled: Bluetooth.defaultAdapter?.enabled ?? false icon: "power" accent: "Tertiary" + iconSize: Appearance.font.size.normal + horizontalPadding: Appearance.padding.normal + verticalPadding: Appearance.padding.smaller + tooltip: qsTr("Toggle Bluetooth") - function onClicked(): void { + onClicked: { const adapter = Bluetooth.defaultAdapter; if (adapter) adapter.enabled = !adapter.enabled; @@ -60,8 +64,12 @@ DeviceList { toggled: Bluetooth.defaultAdapter?.discoverable ?? false icon: root.smallDiscoverable ? "group_search" : "" label: root.smallDiscoverable ? "" : qsTr("Discoverable") + iconSize: Appearance.font.size.normal + horizontalPadding: Appearance.padding.normal + verticalPadding: Appearance.padding.smaller + tooltip: qsTr("Make discoverable") - function onClicked(): void { + onClicked: { const adapter = Bluetooth.defaultAdapter; if (adapter) adapter.discoverable = !adapter.discoverable; @@ -72,20 +80,44 @@ DeviceList { toggled: Bluetooth.defaultAdapter?.pairable ?? false icon: "missing_controller" label: root.smallPairable ? "" : qsTr("Pairable") + iconSize: Appearance.font.size.normal + horizontalPadding: Appearance.padding.normal + verticalPadding: Appearance.padding.smaller + tooltip: qsTr("Make pairable") - function onClicked(): void { + onClicked: { const adapter = Bluetooth.defaultAdapter; if (adapter) adapter.pairable = !adapter.pairable; } } + ToggleButton { + toggled: Bluetooth.defaultAdapter?.discovering ?? false + icon: "bluetooth_searching" + accent: "Secondary" + iconSize: Appearance.font.size.normal + horizontalPadding: Appearance.padding.normal + verticalPadding: Appearance.padding.smaller + tooltip: qsTr("Scan for devices") + + onClicked: { + const adapter = Bluetooth.defaultAdapter; + if (adapter) + adapter.discovering = !adapter.discovering; + } + } + ToggleButton { toggled: !root.session.bt.active icon: "settings" accent: "Primary" + iconSize: Appearance.font.size.normal + horizontalPadding: Appearance.padding.normal + verticalPadding: Appearance.padding.smaller + tooltip: qsTr("Bluetooth settings") - function onClicked(): void { + onClicked: { if (root.session.bt.active) root.session.bt.active = null; else { @@ -96,47 +128,6 @@ DeviceList { } } - titleSuffix: Component { - RowLayout { - spacing: Appearance.spacing.normal - - Item { - Layout.fillWidth: true - } - - StyledRect { - implicitWidth: implicitHeight - implicitHeight: scanIcon.implicitHeight + Appearance.padding.normal * 2 - - radius: Bluetooth.defaultAdapter?.discovering ? Appearance.rounding.normal : implicitHeight / 2 * Math.min(1, Appearance.rounding.scale) - color: Bluetooth.defaultAdapter?.discovering ? Colours.palette.m3secondary : Colours.palette.m3secondaryContainer - - StateLayer { - color: Bluetooth.defaultAdapter?.discovering ? Colours.palette.m3onSecondary : Colours.palette.m3onSecondaryContainer - - function onClicked(): void { - const adapter = Bluetooth.defaultAdapter; - if (adapter) - adapter.discovering = !adapter.discovering; - } - } - - MaterialIcon { - id: scanIcon - - anchors.centerIn: parent - animate: true - text: "bluetooth_searching" - color: Bluetooth.defaultAdapter?.discovering ? Colours.palette.m3onSecondary : Colours.palette.m3onSecondaryContainer - fill: Bluetooth.defaultAdapter?.discovering ? 1 : 0 - } - - Behavior on radius { - Anim {} - } - } - } - } delegate: Component { StyledRect { @@ -146,8 +137,7 @@ DeviceList { readonly property bool loading: modelData && (modelData.state === BluetoothDeviceState.Connecting || modelData.state === BluetoothDeviceState.Disconnecting) readonly property bool connected: modelData && modelData.state === BluetoothDeviceState.Connected - anchors.left: parent.left - anchors.right: parent.right + width: ListView.view ? ListView.view.width : undefined implicitHeight: deviceInner.implicitHeight + Appearance.padding.normal * 2 color: Qt.alpha(Colours.tPalette.m3surfaceContainer, root.activeItem === modelData ? Colours.tPalette.m3surfaceContainer.a : 0) diff --git a/modules/controlcenter/components/DeviceDetails.qml b/modules/controlcenter/components/DeviceDetails.qml index 256e689..eef0aae 100644 --- a/modules/controlcenter/components/DeviceDetails.qml +++ b/modules/controlcenter/components/DeviceDetails.qml @@ -90,6 +90,8 @@ Item { model: root.sections Loader { + required property Component modelData + Layout.fillWidth: true sourceComponent: modelData } diff --git a/modules/controlcenter/components/DeviceList.qml b/modules/controlcenter/components/DeviceList.qml index f8473ff..bf7126f 100644 --- a/modules/controlcenter/components/DeviceList.qml +++ b/modules/controlcenter/components/DeviceList.qml @@ -6,6 +6,7 @@ import qs.components.controls import qs.components.containers import qs.services import qs.config +import Quickshell import QtQuick import QtQuick.Layouts @@ -55,6 +56,7 @@ ColumnLayout { property var activeItem: null property Component headerComponent: null property Component titleSuffix: null + property bool showHeader: true signal itemSelected(var item) @@ -66,7 +68,7 @@ ColumnLayout { Layout.fillWidth: true sourceComponent: root.headerComponent - visible: root.headerComponent !== null + visible: root.headerComponent !== null && root.showHeader } // Title and description row @@ -109,17 +111,15 @@ ColumnLayout { id: view Layout.fillWidth: true - Layout.fillHeight: true + // Use contentHeight to show all items without estimation + implicitHeight: contentHeight model: root.model delegate: root.delegate spacing: Appearance.spacing.small / 2 - clip: true - - StyledScrollBar.vertical: StyledScrollBar { - flickable: view - } + interactive: false // Disable individual scrolling - parent pane handles it + clip: false // Don't clip - let parent handle scrolling } } diff --git a/modules/controlcenter/launcher/LauncherPane.qml b/modules/controlcenter/launcher/LauncherPane.qml index cf965e8..803d7e0 100644 --- a/modules/controlcenter/launcher/LauncherPane.qml +++ b/modules/controlcenter/launcher/LauncherPane.qml @@ -183,6 +183,10 @@ Item { toggled: !root.session.launcher.active icon: "settings" accent: "Primary" + iconSize: Appearance.font.size.normal + horizontalPadding: Appearance.padding.normal + verticalPadding: Appearance.padding.smaller + tooltip: qsTr("Launcher settings") onClicked: { if (root.session.launcher.active) { diff --git a/modules/controlcenter/network/EthernetDetails.qml b/modules/controlcenter/network/EthernetDetails.qml index 7c2534a..ad078ec 100644 --- a/modules/controlcenter/network/EthernetDetails.qml +++ b/modules/controlcenter/network/EthernetDetails.qml @@ -1,6 +1,7 @@ pragma ComponentBehavior: Bound import ".." +import "../components" import qs.components import qs.components.controls import qs.components.effects @@ -10,99 +11,108 @@ import qs.config import QtQuick import QtQuick.Layouts -Item { +DeviceDetails { id: root required property Session session - readonly property var device: session.ethernet.active + readonly property var ethernetDevice: session.ethernet.active - implicitWidth: layout.implicitWidth - implicitHeight: layout.implicitHeight + device: ethernetDevice Component.onCompleted: { - if (device && device.interface) { - Nmcli.getEthernetDeviceDetails(device.interface, () => {}); + if (ethernetDevice && ethernetDevice.interface) { + Nmcli.getEthernetDeviceDetails(ethernetDevice.interface, () => {}); } } - onDeviceChanged: { - if (device && device.interface) { - Nmcli.getEthernetDeviceDetails(device.interface, () => {}); + onEthernetDeviceChanged: { + if (ethernetDevice && ethernetDevice.interface) { + Nmcli.getEthernetDeviceDetails(ethernetDevice.interface, () => {}); } else { Nmcli.ethernetDeviceDetails = null; } } - ColumnLayout { - id: layout - - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - spacing: Appearance.spacing.normal + headerComponent: Component { + ConnectionHeader { + icon: "cable" + title: root.ethernetDevice?.interface ?? qsTr("Unknown") + } + } - ConnectionHeader { - icon: "cable" - title: root.device?.interface ?? qsTr("Unknown") - } + sections: [ + Component { + ColumnLayout { + spacing: Appearance.spacing.normal - SectionHeader { - title: qsTr("Connection status") - description: qsTr("Connection settings for this device") - } + SectionHeader { + title: qsTr("Connection status") + description: qsTr("Connection settings for this device") + } - SectionContainer { - ToggleRow { - label: qsTr("Connected") - checked: root.device?.connected ?? false - toggle.onToggled: { - if (checked) { - Nmcli.connectEthernet(root.device?.connection || "", root.device?.interface || "", () => {}); - } else { - if (root.device?.connection) { - Nmcli.disconnectEthernet(root.device.connection, () => {}); + SectionContainer { + ToggleRow { + label: qsTr("Connected") + checked: root.ethernetDevice?.connected ?? false + toggle.onToggled: { + if (checked) { + Nmcli.connectEthernet(root.ethernetDevice?.connection || "", root.ethernetDevice?.interface || "", () => {}); + } else { + if (root.ethernetDevice?.connection) { + Nmcli.disconnectEthernet(root.ethernetDevice.connection, () => {}); + } } } } } } + }, + Component { + ColumnLayout { + spacing: Appearance.spacing.normal + + SectionHeader { + title: qsTr("Device properties") + description: qsTr("Additional information") + } - SectionHeader { - title: qsTr("Device properties") - description: qsTr("Additional information") - } - - SectionContainer { - contentSpacing: Appearance.spacing.small / 2 + SectionContainer { + contentSpacing: Appearance.spacing.small / 2 - PropertyRow { - label: qsTr("Interface") - value: root.device?.interface ?? qsTr("Unknown") - } + PropertyRow { + label: qsTr("Interface") + value: root.ethernetDevice?.interface ?? qsTr("Unknown") + } - PropertyRow { - showTopMargin: true - label: qsTr("Connection") - value: root.device?.connection || qsTr("Not connected") - } + PropertyRow { + showTopMargin: true + label: qsTr("Connection") + value: root.ethernetDevice?.connection || qsTr("Not connected") + } - PropertyRow { - showTopMargin: true - label: qsTr("State") - value: root.device?.state ?? qsTr("Unknown") + PropertyRow { + showTopMargin: true + label: qsTr("State") + value: root.ethernetDevice?.state ?? qsTr("Unknown") + } } } + }, + Component { + ColumnLayout { + spacing: Appearance.spacing.normal + + SectionHeader { + title: qsTr("Connection information") + description: qsTr("Network connection details") + } - SectionHeader { - title: qsTr("Connection information") - description: qsTr("Network connection details") - } - - SectionContainer { - ConnectionInfoSection { - deviceDetails: Nmcli.ethernetDeviceDetails + SectionContainer { + ConnectionInfoSection { + deviceDetails: Nmcli.ethernetDeviceDetails + } } } - } - -} \ No newline at end of file + } + ] +} diff --git a/modules/controlcenter/network/EthernetList.qml b/modules/controlcenter/network/EthernetList.qml index 03bd37e..ea3ece5 100644 --- a/modules/controlcenter/network/EthernetList.qml +++ b/modules/controlcenter/network/EthernetList.qml @@ -39,6 +39,9 @@ DeviceList { toggled: !root.session.ethernet.active icon: "settings" accent: "Primary" + iconSize: Appearance.font.size.normal + horizontalPadding: Appearance.padding.normal + verticalPadding: Appearance.padding.smaller onClicked: { if (root.session.ethernet.active) @@ -55,8 +58,7 @@ DeviceList { StyledRect { required property var modelData - anchors.left: parent.left - anchors.right: parent.right + width: ListView.view ? ListView.view.width : undefined color: Qt.alpha(Colours.tPalette.m3surfaceContainer, root.activeItem === modelData ? Colours.tPalette.m3surfaceContainer.a : 0) radius: Appearance.rounding.normal diff --git a/modules/controlcenter/network/NetworkingPane.qml b/modules/controlcenter/network/NetworkingPane.qml index d76e8f5..e28d35c 100644 --- a/modules/controlcenter/network/NetworkingPane.qml +++ b/modules/controlcenter/network/NetworkingPane.qml @@ -28,23 +28,22 @@ Item { anchors.fill: parent leftContent: Component { + StyledFlickable { + id: leftFlickable - StyledFlickable { - id: leftFlickable + flickableDirection: Flickable.VerticalFlick + contentHeight: leftContent.height - flickableDirection: Flickable.VerticalFlick - contentHeight: leftContent.height - - StyledScrollBar.vertical: StyledScrollBar { - flickable: leftFlickable - } + StyledScrollBar.vertical: StyledScrollBar { + flickable: leftFlickable + } - ColumnLayout { - id: leftContent + ColumnLayout { + id: leftContent - anchors.left: parent.left - anchors.right: parent.right - spacing: Appearance.spacing.normal + anchors.left: parent.left + anchors.right: parent.right + spacing: Appearance.spacing.normal // Network header above the collapsible sections RowLayout { @@ -65,6 +64,10 @@ Item { toggled: Nmcli.wifiEnabled icon: "wifi" accent: "Tertiary" + iconSize: Appearance.font.size.normal + horizontalPadding: Appearance.padding.normal + verticalPadding: Appearance.padding.smaller + tooltip: qsTr("Toggle WiFi") onClicked: { Nmcli.toggleWifi(null); @@ -75,6 +78,10 @@ Item { toggled: Nmcli.scanning icon: "wifi_find" accent: "Secondary" + iconSize: Appearance.font.size.normal + horizontalPadding: Appearance.padding.normal + verticalPadding: Appearance.padding.smaller + tooltip: qsTr("Scan for networks") onClicked: { Nmcli.rescanWifi(); @@ -85,6 +92,10 @@ Item { toggled: !root.session.ethernet.active && !root.session.network.active icon: "settings" accent: "Primary" + iconSize: Appearance.font.size.normal + horizontalPadding: Appearance.padding.normal + verticalPadding: Appearance.padding.smaller + tooltip: qsTr("Network settings") onClicked: { if (root.session.ethernet.active || root.session.network.active) { @@ -109,127 +120,12 @@ Item { title: qsTr("Ethernet") expanded: true - ColumnLayout { + Loader { Layout.fillWidth: true - spacing: Appearance.spacing.small - - RowLayout { - Layout.fillWidth: true - spacing: Appearance.spacing.small - - StyledText { - text: qsTr("Devices (%1)").arg(Nmcli.ethernetDevices.length) - font.pointSize: Appearance.font.size.normal - font.weight: 500 - } - } - - StyledText { - Layout.fillWidth: true - text: qsTr("All available ethernet devices") - color: Colours.palette.m3outline - } - - Repeater { - id: ethernetRepeater - - Layout.fillWidth: true - model: Nmcli.ethernetDevices - - delegate: StyledRect { - required property var modelData - - Layout.fillWidth: true - - color: Qt.alpha(Colours.tPalette.m3surfaceContainer, root.session.ethernet.active === modelData ? Colours.tPalette.m3surfaceContainer.a : 0) - radius: Appearance.rounding.normal - - StateLayer { - function onClicked(): void { - root.session.network.active = null; - root.session.ethernet.active = modelData; - } - } - - RowLayout { - id: rowLayout - - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.normal - - spacing: Appearance.spacing.normal - - StyledRect { - implicitWidth: implicitHeight - implicitHeight: icon.implicitHeight + Appearance.padding.normal * 2 - - radius: Appearance.rounding.normal - color: modelData.connected ? Colours.palette.m3primaryContainer : Colours.tPalette.m3surfaceContainerHigh - - MaterialIcon { - id: icon - - anchors.centerIn: parent - text: "cable" - font.pointSize: Appearance.font.size.large - fill: modelData.connected ? 1 : 0 - color: modelData.connected ? Colours.palette.m3onPrimaryContainer : Colours.palette.m3onSurface - } - } - - ColumnLayout { - Layout.fillWidth: true - - spacing: 0 - - StyledText { - Layout.fillWidth: true - elide: Text.ElideRight - maximumLineCount: 1 - - text: modelData.interface || qsTr("Unknown") - } - - StyledText { - Layout.fillWidth: true - text: modelData.connected ? qsTr("Connected") : qsTr("Disconnected") - color: modelData.connected ? Colours.palette.m3primary : Colours.palette.m3outline - font.pointSize: Appearance.font.size.small - font.weight: modelData.connected ? 500 : 400 - elide: Text.ElideRight - } - } - - StyledRect { - implicitWidth: implicitHeight - implicitHeight: connectIcon.implicitHeight + Appearance.padding.smaller * 2 - - radius: Appearance.rounding.full - color: Qt.alpha(Colours.palette.m3primaryContainer, modelData.connected ? 1 : 0) - - StateLayer { - function onClicked(): void { - if (modelData.connected && modelData.connection) { - Nmcli.disconnectEthernet(modelData.connection, () => {}); - } else { - Nmcli.connectEthernet(modelData.connection || "", modelData.interface || "", () => {}); - } - } - } - - MaterialIcon { - id: connectIcon - - anchors.centerIn: parent - text: modelData.connected ? "link_off" : "link" - color: modelData.connected ? Colours.palette.m3onPrimaryContainer : Colours.palette.m3onSurface - } - } - } - - implicitHeight: rowLayout.implicitHeight + Appearance.padding.normal * 2 + sourceComponent: Component { + EthernetList { + session: root.session + showHeader: false } } } @@ -242,195 +138,12 @@ Item { title: qsTr("Wireless") expanded: true - ColumnLayout { + Loader { Layout.fillWidth: true - spacing: Appearance.spacing.small - - RowLayout { - Layout.fillWidth: true - spacing: Appearance.spacing.small - - StyledText { - text: qsTr("Networks (%1)").arg(Nmcli.networks.length) - font.pointSize: Appearance.font.size.normal - font.weight: 500 - } - - StyledText { - visible: Nmcli.scanning - text: qsTr("Scanning...") - color: Colours.palette.m3primary - font.pointSize: Appearance.font.size.small - } - } - - StyledText { - Layout.fillWidth: true - text: qsTr("All available WiFi networks") - color: Colours.palette.m3outline - } - - Repeater { - id: wirelessRepeater - - Layout.fillWidth: true - model: ScriptModel { - values: [...Nmcli.networks].sort((a, b) => { - // Put active/connected network first - if (a.active !== b.active) - return b.active - a.active; - // Then sort by signal strength - return b.strength - a.strength; - }) - } - - delegate: StyledRect { - required property var modelData - - Layout.fillWidth: true - - color: Qt.alpha(Colours.tPalette.m3surfaceContainer, (modelData && root.session.network.active === modelData) ? Colours.tPalette.m3surfaceContainer.a : 0) - radius: Appearance.rounding.normal - - StateLayer { - function onClicked(): void { - if (!modelData) { - return; - } - root.session.ethernet.active = null; - root.session.network.active = modelData; - // Check if we need to refresh saved connections when selecting a network - if (modelData.ssid) { - checkSavedProfileForNetwork(modelData.ssid); - } - } - } - - RowLayout { - id: wirelessRowLayout - - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.margins: Appearance.padding.normal - - spacing: Appearance.spacing.normal - - StyledRect { - implicitWidth: implicitHeight - implicitHeight: wirelessIcon.implicitHeight + Appearance.padding.normal * 2 - - radius: Appearance.rounding.normal - color: (modelData && modelData.active) ? Colours.palette.m3primaryContainer : Colours.tPalette.m3surfaceContainerHigh - - MaterialIcon { - id: wirelessIcon - - anchors.centerIn: parent - text: Icons.getNetworkIcon(modelData && modelData.strength !== undefined ? modelData.strength : 0) - font.pointSize: Appearance.font.size.large - fill: (modelData && modelData.active) ? 1 : 0 - color: (modelData && modelData.active) ? Colours.palette.m3onPrimaryContainer : Colours.palette.m3onSurface - } - - StyledRect { - id: lockBadge - - visible: modelData && modelData.isSecure - anchors.right: parent.right - anchors.bottom: parent.bottom - anchors.margins: -Appearance.padding.smaller / 2 - - implicitWidth: lockIconSize + Appearance.padding.smaller - implicitHeight: lockIconSize + Appearance.padding.smaller - radius: Appearance.rounding.full - color: Colours.palette.m3secondaryContainer - - readonly property real lockIconSize: lockIcon.implicitWidth - - Elevation { - anchors.fill: parent - radius: parent.radius - z: -1 - level: 2 - } - - MaterialIcon { - id: lockIcon - - anchors.centerIn: parent - text: "lock" - font.pointSize: Appearance.font.size.small - fill: 1 - color: Colours.palette.m3onSurface - } - } - } - - ColumnLayout { - Layout.fillWidth: true - - spacing: 0 - - StyledText { - Layout.fillWidth: true - elide: Text.ElideRight - maximumLineCount: 1 - - text: (modelData && modelData.ssid) ? modelData.ssid : qsTr("Unknown") - } - - RowLayout { - Layout.fillWidth: true - spacing: Appearance.spacing.smaller - - StyledText { - Layout.fillWidth: true - text: { - if (!modelData) return qsTr("Open"); - if (modelData.active) return qsTr("Connected"); - if (modelData.isSecure && modelData.security && modelData.security.length > 0) { - return modelData.security; - } - if (modelData.isSecure) return qsTr("Secured"); - return qsTr("Open"); - } - color: (modelData && modelData.active) ? Colours.palette.m3primary : Colours.palette.m3outline - font.pointSize: Appearance.font.size.small - font.weight: (modelData && modelData.active) ? 500 : 400 - elide: Text.ElideRight - } - } - } - - StyledRect { - implicitWidth: implicitHeight - implicitHeight: wirelessConnectIcon.implicitHeight + Appearance.padding.smaller * 2 - - radius: Appearance.rounding.full - color: Qt.alpha(Colours.palette.m3primaryContainer, (modelData && modelData.active) ? 1 : 0) - - StateLayer { - function onClicked(): void { - if (modelData && modelData.active) { - Nmcli.disconnectFromNetwork(); - } else if (modelData) { - NetworkConnection.handleConnect(modelData, root.session, null); - } - } - } - - MaterialIcon { - id: wirelessConnectIcon - - anchors.centerIn: parent - text: (modelData && modelData.active) ? "link_off" : "link" - color: (modelData && modelData.active) ? Colours.palette.m3onPrimaryContainer : Colours.palette.m3onSurface - } - } - } - - implicitHeight: wirelessRowLayout.implicitHeight + Appearance.padding.normal * 2 + sourceComponent: Component { + WirelessList { + session: root.session + showHeader: false } } } @@ -443,16 +156,17 @@ Item { Item { id: rightPaneItem - // Right pane - networking details/settings property var ethernetPane: root.session.ethernet.active property var wirelessPane: root.session.network.active property var pane: ethernetPane || wirelessPane property string paneId: ethernetPane ? ("eth:" + (ethernetPane.interface || "")) : (wirelessPane ? ("wifi:" + (wirelessPane.ssid || wirelessPane.bssid || "")) : "settings") - property Component targetComponent: settings - property Component nextComponent: settings + property Component targetComponent: settingsComponent + property Component nextComponent: settingsComponent function getComponentForPane() { - return pane ? (ethernetPane ? ethernetDetails : wirelessDetails) : settings; + if (ethernetPane) return ethernetDetailsComponent; + if (wirelessPane) return wirelessDetailsComponent; + return settingsComponent; } Component.onCompleted: { @@ -507,7 +221,7 @@ Item { } Component { - id: settings + id: settingsComponent StyledFlickable { id: settingsFlickable @@ -530,7 +244,7 @@ Item { } Component { - id: ethernetDetails + id: ethernetDetailsComponent StyledFlickable { id: ethernetFlickable @@ -553,7 +267,7 @@ Item { } Component { - id: wirelessDetails + id: wirelessDetailsComponent StyledFlickable { id: wirelessFlickable @@ -580,11 +294,4 @@ Item { session: root.session z: 1000 } - - function checkSavedProfileForNetwork(ssid: string): void { - if (ssid && ssid.length > 0) { - Nmcli.loadSavedConnections(() => {}); - } - } } - diff --git a/modules/controlcenter/network/WirelessDetails.qml b/modules/controlcenter/network/WirelessDetails.qml index 57c06c8..7f6a4aa 100644 --- a/modules/controlcenter/network/WirelessDetails.qml +++ b/modules/controlcenter/network/WirelessDetails.qml @@ -1,6 +1,7 @@ pragma ComponentBehavior: Bound import ".." +import "../components" import "." import qs.components import qs.components.controls @@ -12,14 +13,13 @@ import qs.utils import QtQuick import QtQuick.Layouts -Item { +DeviceDetails { id: root required property Session session readonly property var network: session.network.active - implicitWidth: layout.implicitWidth - implicitHeight: layout.implicitHeight + device: network Component.onCompleted: { updateDeviceDetails(); @@ -102,110 +102,120 @@ Item { } } - ColumnLayout { - id: layout - - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - spacing: Appearance.spacing.normal - - ConnectionHeader { - icon: root.network?.isSecure ? "lock" : "wifi" - title: root.network?.ssid ?? qsTr("Unknown") - } + headerComponent: Component { + ConnectionHeader { + icon: root.network?.isSecure ? "lock" : "wifi" + title: root.network?.ssid ?? qsTr("Unknown") + } + } - SectionHeader { - title: qsTr("Connection status") - description: qsTr("Connection settings for this network") - } + sections: [ + Component { + ColumnLayout { + spacing: Appearance.spacing.normal - SectionContainer { - ToggleRow { - label: qsTr("Connected") - checked: root.network?.active ?? false - toggle.onToggled: { - if (checked) { - NetworkConnection.handleConnect(root.network, root.session, null); - } else { - Nmcli.disconnectFromNetwork(); - } - } + SectionHeader { + title: qsTr("Connection status") + description: qsTr("Connection settings for this network") } - TextButton { - Layout.fillWidth: true - Layout.topMargin: Appearance.spacing.normal - Layout.minimumHeight: Appearance.font.size.normal + Appearance.padding.normal * 2 - visible: { - if (!root.network || !root.network.ssid) { - return false; + SectionContainer { + ToggleRow { + label: qsTr("Connected") + checked: root.network?.active ?? false + toggle.onToggled: { + if (checked) { + NetworkConnection.handleConnect(root.network, root.session, null); + } else { + Nmcli.disconnectFromNetwork(); + } } - return Nmcli.hasSavedProfile(root.network.ssid); } - inactiveColour: Colours.palette.m3secondaryContainer - inactiveOnColour: Colours.palette.m3onSecondaryContainer - text: qsTr("Forget Network") - onClicked: { - if (root.network && root.network.ssid) { - if (root.network.active) { - Nmcli.disconnectFromNetwork(); + TextButton { + Layout.fillWidth: true + Layout.topMargin: Appearance.spacing.normal + Layout.minimumHeight: Appearance.font.size.normal + Appearance.padding.normal * 2 + visible: { + if (!root.network || !root.network.ssid) { + return false; + } + return Nmcli.hasSavedProfile(root.network.ssid); + } + inactiveColour: Colours.palette.m3secondaryContainer + inactiveOnColour: Colours.palette.m3onSecondaryContainer + text: qsTr("Forget Network") + + onClicked: { + if (root.network && root.network.ssid) { + if (root.network.active) { + Nmcli.disconnectFromNetwork(); + } + Nmcli.forgetNetwork(root.network.ssid); } - Nmcli.forgetNetwork(root.network.ssid); } } } } + }, + Component { + ColumnLayout { + spacing: Appearance.spacing.normal + + SectionHeader { + title: qsTr("Network properties") + description: qsTr("Additional information") + } - SectionHeader { - title: qsTr("Network properties") - description: qsTr("Additional information") - } - - SectionContainer { - contentSpacing: Appearance.spacing.small / 2 + SectionContainer { + contentSpacing: Appearance.spacing.small / 2 - PropertyRow { - label: qsTr("SSID") - value: root.network?.ssid ?? qsTr("Unknown") - } + PropertyRow { + label: qsTr("SSID") + value: root.network?.ssid ?? qsTr("Unknown") + } - PropertyRow { - showTopMargin: true - label: qsTr("BSSID") - value: root.network?.bssid ?? qsTr("Unknown") - } + PropertyRow { + showTopMargin: true + label: qsTr("BSSID") + value: root.network?.bssid ?? qsTr("Unknown") + } - PropertyRow { - showTopMargin: true - label: qsTr("Signal strength") - value: root.network ? qsTr("%1%").arg(root.network.strength) : qsTr("N/A") - } + PropertyRow { + showTopMargin: true + label: qsTr("Signal strength") + value: root.network ? qsTr("%1%").arg(root.network.strength) : qsTr("N/A") + } - PropertyRow { - showTopMargin: true - label: qsTr("Frequency") - value: root.network ? qsTr("%1 MHz").arg(root.network.frequency) : qsTr("N/A") - } + PropertyRow { + showTopMargin: true + label: qsTr("Frequency") + value: root.network ? qsTr("%1 MHz").arg(root.network.frequency) : qsTr("N/A") + } - PropertyRow { - showTopMargin: true - label: qsTr("Security") - value: root.network ? (root.network.isSecure ? root.network.security : qsTr("Open")) : qsTr("N/A") + PropertyRow { + showTopMargin: true + label: qsTr("Security") + value: root.network ? (root.network.isSecure ? root.network.security : qsTr("Open")) : qsTr("N/A") + } } } + }, + Component { + ColumnLayout { + spacing: Appearance.spacing.normal + + SectionHeader { + title: qsTr("Connection information") + description: qsTr("Network connection details") + } - SectionHeader { - title: qsTr("Connection information") - description: qsTr("Network connection details") - } - - SectionContainer { - ConnectionInfoSection { - deviceDetails: Nmcli.wirelessDeviceDetails + SectionContainer { + ConnectionInfoSection { + deviceDetails: Nmcli.wirelessDeviceDetails + } } } - } - + } + ] } diff --git a/modules/controlcenter/network/WirelessList.qml b/modules/controlcenter/network/WirelessList.qml index 2f0288f..4726712 100644 --- a/modules/controlcenter/network/WirelessList.qml +++ b/modules/controlcenter/network/WirelessList.qml @@ -9,6 +9,7 @@ import qs.components.containers import qs.services import qs.config import qs.utils +import Quickshell import QtQuick import QtQuick.Layouts @@ -58,6 +59,9 @@ DeviceList { toggled: Nmcli.wifiEnabled icon: "wifi" accent: "Tertiary" + iconSize: Appearance.font.size.normal + horizontalPadding: Appearance.padding.normal + verticalPadding: Appearance.padding.smaller onClicked: { Nmcli.toggleWifi(null); @@ -68,6 +72,9 @@ DeviceList { toggled: Nmcli.scanning icon: "wifi_find" accent: "Secondary" + iconSize: Appearance.font.size.normal + horizontalPadding: Appearance.padding.normal + verticalPadding: Appearance.padding.smaller onClicked: { Nmcli.rescanWifi(); @@ -78,6 +85,9 @@ DeviceList { toggled: !root.session.network.active icon: "settings" accent: "Primary" + iconSize: Appearance.font.size.normal + horizontalPadding: Appearance.padding.normal + verticalPadding: Appearance.padding.smaller onClicked: { if (root.session.network.active) @@ -94,8 +104,7 @@ DeviceList { StyledRect { required property var modelData - anchors.left: parent.left - anchors.right: parent.right + width: ListView.view ? ListView.view.width : undefined color: Qt.alpha(Colours.tPalette.m3surfaceContainer, root.activeItem === modelData ? Colours.tPalette.m3surfaceContainer.a : 0) radius: Appearance.rounding.normal -- cgit v1.2.3-freya From 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/launcher') 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