/* * Komplex Wallpaper Engine * Copyright (C) 2025 @DigitalArtifex | github.com/DigitalArtifex * * config.qml * * This component provides a configuration interface for the Komplex Wallpaper Engine, * allowing users to customize shader settings, channel configurations, and other * parameters related to the wallpaper engine. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see */ // pragma ComponentBehavior: Bound import QtQuick import QtQuick.Controls import QtQuick.Dialogs import QtQuick.Layouts //import org.kde.kirigami as Kirigami //import org.kde.plasma.core as PlasmaCore import QtCore import Qt.labs.folderlistmodel 2.15 import KomplexHub import KomplexHub.Controls import KomplexHubPlugin //import com.github.digitalartifex.komplex 1.0 as Komplex Item { SettingsManager { id: manager onError: (title, message) => { console.log(title + ": " + message) } } id: root property alias cfg_pauseMode: pauseModeCombo.currentIndex property alias cfg_isPaused: runningCombo.checked property alias cfg_shaderSpeed: speedSlider.value property alias cfg_mouseSpeedBias: mouseBiasSlider.value property alias cfg_mouseAllowed: mouseEnableButton.checked property bool cfg_infoPlasma6Preview_dismissed property bool cfg_warningResources_dismissed property bool cfg_emergencyHelp_dismissed property bool cfg_infoiChannelSettings_dismissed property alias cfg_checkActiveScreen: activeScreenOnlyCheckbox.checked property alias cfg_excludeWindows: excludeWindows.windows property alias cfg_running: runningCombo.checked property alias cfg_resolution_x: resolutionXField.value property alias cfg_resolution_y: resolutionYField.value property alias cfg_framerate_limit: frameRateField.value property bool cfg_shader_updated property string shaderPackage: "" property int shaderPackageIndex: 0 property real shaderSpeed: 1.0 property int resolutionX: 1080 property int resolution: 1920 property bool enableMouseTracking: true property real mouseTrackingBias: 0.5 Rectangle { anchors.fill: parent color: palette.base clip: true ColumnLayout { anchors.fill: parent Text { text: qsTr("Wallpaper") font: Constants.h2Font color: palette.text } Text { text: manager.shaderPack } ComboBox { property string shader Layout.fillWidth: true id: selectedShaderPack model: manager.availableShaderPacks delegate: Component { id: packListDelegate ItemDelegate { width: parent ? parent.width : 0 text: modelData } } textRole: "modelData" currentIndex: manager.shaderPackIndex displayText: currentIndex === -1 ? "Custom File" : currentText.replace("_", " ").charAt(0).toUpperCase() + currentText.replace("_", " ").slice(1) // onCurrentTextChanged: // { // root.cfg_shader_package_index = currentIndex; // if (root.cfg_shader_package_index === -1) // return; // var source = currentText // shaderPackModel.loadMetadata(source) // root.cfg_shader_package = "file://" + shaderPackModel.path(source) // } } RowLayout { Layout.fillWidth: true visible: true //Kirigami.FormData.label: i18nd("com.github.digitalartifex.komplex", "Shader Pack:") // ComboBox // { // property string shader // Layout.fillWidth: true // id: selectedShaderPack // //model: shaderPackModel.availableShaderPacks // delegate: Component // { // id: packListDelegate // ItemDelegate // { // width: parent ? parent.width : 0 // text: modelData // } // } // textRole: "modelData" // currentIndex: root.cfg_shader_package_index // displayText: currentIndex === -1 ? "Custom File" : currentText.replace("_", " ").charAt(0).toUpperCase() + currentText.replace("_", " ").slice(1) // onCurrentTextChanged: // { // root.cfg_shader_package_index = currentIndex; // if (root.cfg_shader_package_index === -1) // return; // var source = currentText // shaderPackModel.loadMetadata(source) // root.cfg_shader_package = "file://" + shaderPackModel.path(source) // } // } FileDialog { id: packDialog fileMode: FileDialog.OpenFile title: i18nd("@dialog_title:choose_shader", "Choose a shader") // will accept and auto convert .frag in the near future nameFilters: ["Shader Package files (*.json)", "All files (*)"] visible: false currentFolder: "file://" + shaderPackModel.shaderPackInstallPath onAccepted: { root.cfg_shader_package_index = -1; root.cfg_shader_package = selectedFile; shaderPackModel.loadMetadataFromFile(selectedFile) } } } // Kirigami.Separator // { // Kirigami.FormData.isSection: false // } // TabBar // { // Layout.topMargin: 6 // Layout.fillWidth: true // id: navBar // width: parent.width // TabButton { // height: 36 // text: root.cfg_komplex_mode === 0 ? qsTr("Channels") : qsTr("Information") // } // TabButton { // height: 36 // text: qsTr("Settings") // } // TabButton { // height: 36 // text: qsTr("Extra Settings") // } // } // RowLayout // { // Layout.fillWidth: true // Kirigami.InlineMessage // { // id: warningResources // Layout.fillWidth: true // type: Kirigami.MessageType.Warning // text: qsTr("Some shaders might consume more power and resources than others, beware!") // showCloseButton: true // visible: !root.cfg_warningResources_dismissed // onVisibleChanged: () => // { // root.cfg_warningResources_dismissed = true; // } // } // } Text { text: qsTr("Render Resolution") font: Constants.h2Font color: palette.text } RowLayout { visible: navBar.currentIndex === 1 //Kirigami.FormData.label: i18nd("com.github.digitalartifex.komplex", "Resolution:") Text { color: palette.text text: "X" verticalAlignment: Text.AlignVCenter } TextField { property int value id: resolutionXField inputMethodHints: Qt.ImhFormattedNumbersOnly horizontalAlignment: Text.AlignRight Layout.preferredWidth: Kirigami.Units.gridUnit * 4 text: value onEditingFinished: () => { value = parseInt(text) } Keys.onPressed: (event) => { if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { resolutionXField.focus = false; // Unfocus the TextField event.accepted = true; // Prevent further propagation of the key event } } background: Rectangle { color: resolutionXField.activeFocus ? palette.base : "transparent" border.color: resolutionXField.activeFocus ? palette.highlight : "transparent" border.width: 1 radius: 4 anchors.fill: resolutionXField anchors.margins: -2 } } Text { color: palette.text text: "Y" verticalAlignment: Text.AlignVCenter } TextField { property int value id: resolutionYField inputMethodHints: Qt.ImhFormattedNumbersOnly horizontalAlignment: Text.AlignRight Layout.preferredWidth: Kirigami.Units.gridUnit * 4 text: value onEditingFinished: () => { value = parseInt(text) } Keys.onPressed: (event) => { if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { resolutionYField.focus = false; // Unfocus the TextField event.accepted = true; // Prevent further propagation of the key event } } background: Rectangle { color: resolutionYField.activeFocus ? palette.base : "transparent" border.color: resolutionYField.activeFocus ? palette.highlight : "transparent" border.width: 1 radius: 4 anchors.fill: resolutionYField anchors.margins: -2 } } } RowLayout { visible: navBar.currentIndex === 1 //Kirigami.FormData.label: i18nd("com.github.digitalartifex.komplex", "Frame Rate:") TextField { property int value id: frameRateField inputMethodHints: Qt.ImhFormattedNumbersOnly Layout.preferredWidth: Kirigami.Units.gridUnit * 4 onEditingFinished: () => { value = parseInt(text) } Keys.onPressed: (event) => { if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { frameRateField.focus = false; // Unfocus the TextField event.accepted = true; // Prevent further propagation of the key event } } background: Rectangle { color: frameRateField.activeFocus ? palette.base : "transparent" border.color: frameRateField.activeFocus ? palette.highlight : "transparent" border.width: 1 radius: 4 anchors.fill: frameRateField anchors.margins: -2 } Component.onCompleted: () => { text = value } } } ColumnLayout { visible: navBar.currentIndex === 1 id: speedLayout Layout.fillWidth: true //Kirigami.FormData.label: i18nd("com.github.digitalartifex.komplex", "Shader speed:") Slider { id: speedSlider Layout.fillWidth: true from: -4.0 to: 4.0 stepSize: 0.01 onValueChanged: shaderSpeedField.text = String(value.toFixed(2)); } LineEdit { id: shaderSpeedField //inputMethodHints: Qt.ImhFormattedNumbersOnly //horizontalAlignment: Text.AlignRight Layout.preferredWidth: 128 // onEditingFinished: () => // { // let inputValue = parseFloat(text); // if (isNaN(inputValue) || inputValue < speedSlider.from) // inputValue = speedSlider.from; // else if (inputValue > speedSlider.to) // inputValue = speedSlider.to; // text = inputValue.toFixed(2); // speedSlider.value = inputValue; // } Keys.onPressed: (event) => { if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { shaderSpeedField.focus = false; // Unfocus the TextField event.accepted = true; // Prevent further propagation of the key event } } // background: Rectangle // { // color: shaderSpeedField.activeFocus ? palette.base : "transparent" // border.color: shaderSpeedField.activeFocus ? palette.highlight : "transparent" // border.width: 1 // radius: 4 // anchors.fill: shaderSpeedField // anchors.margins: -2 // } } } ComboBox { visible: navBar.currentIndex === 1 id: pauseModeCombo //Kirigami.FormData.label: i18nd("@buttonGroup:pause_mode", "Pause:") model: [ { 'label': i18nd("@option:pause_mode", "Maximized or full-screen windows") }, { 'label': i18nd("@option:pause_mode", "Active window is present") }, { 'label': i18nd("@option:pause_mode", "At least one window is shown") }, { 'label': i18nd("@option:pause_mode", "Never") } ] textRole: "label" onCurrentIndexChanged: root.cfg_pauseMode = currentIndex currentIndex: root.cfg_pauseMode } CheckBox { visible: navBar.currentIndex === 1 id: activeScreenOnlyCheckbox //Kirigami.FormData.label: i18nd("@checkbox:screen_filter", "Filter:") text: i18n("Only check for windows in active screen") } TextField { visible: navBar.currentIndex === 1 id: excludeWindows property var windows: [] width: Kirigami.Units.gridUnit * 11 //Kirigami.FormData.label: i18nd("com.github.digitalartifex.komplex", "Exclude windows:") text: windows.join(",") onEditingFinished: () => { windows = excludeWindows.text.trim().replace(/\s+/g, "").split(","); } ToolTip.visible: hovered ToolTip.text: qsTr("A comma-separated list of fully-qualified App-IDs to exclude their windows from triggering pause mode.") } CheckBox { visible: navBar.currentIndex === 1 //Kirigami.FormData.label: i18nd("com.github.digitalartifex.komplex", cfg_isPaused ? "Playing" : "Paused") id: runningCombo checked: root.cfg_running text: i18n("Play/Pause the shader") onCheckedChanged: () => { // wallpaper.configuration.running = checked; root.cfg_running = checked; } } RowLayout { visible: navBar.currentIndex === 2 id: mouseLayout //Kirigami.FormData.label: i18nd("com.github.digitalartifex.komplex", "Mouse allowed:") Button { id: mouseEnableButton icon.name: checked ? "followmouse-symbolic" : "hidemouse-symbolic" text: i18nd("@button:toggle_use_mouse", checked ? "Enabled" : "Disabled") checkable: true ToolTip.visible: hovered ToolTip.text: qsTr("Enabling this will allow the shader to interact with the cursor but will prevent interaction with desktop elements") } } RowLayout { id: mouseBiasLayout visible: root.cfg_mouseAllowed && navBar.currentIndex === 2 //Kirigami.FormData.label: i18nd("com.github.digitalartifex.komplex", "Mouse bias:") ColumnLayout { Slider { id: mouseBiasSlider Layout.preferredWidth: Kirigami.Units.gridUnit * 16 from: 0.0 to: 4.0 stepSize: 0.01 value: root.cfg_mouseSpeedBias ? root.cfg_mouseSpeedBias : 1.0 onValueChanged: () => { mouseBiasField.text = String(value.toFixed(2)); // wallpaper.configuration.mouseBias = mouseBiasField.text; root.cfg_mouseSpeedBias = mouseBiasField.text; } } } ColumnLayout { TextField { id: mouseBiasField text: root.cfg_mouseSpeedBias ? String(root.cfg_mouseSpeedBias.toFixed(2)) : "1.00" inputMethodHints: Qt.ImhFormattedNumbersOnly Layout.preferredWidth: Kirigami.Units.gridUnit * 3 onEditingFinished: () => { let inputValue = parseFloat(text); if (isNaN(inputValue) || inputValue < mouseBiasSlider.from) inputValue = mouseBiasSlider.from; else if (inputValue > mouseBiasSlider.to) inputValue = mouseBiasSlider.to; text = inputValue.toFixed(2); mouseBiasSlider.value = inputValue; // wallpaper.configuration.mouseBias = inputValue; root.cfg_mouseSpeedBias = inputValue; } Keys.onPressed: (event) => { if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { mouseBiasField.focus = false; // Unfocus the TextField event.accepted = true; // Prevent further propagation of the key event } } background: Rectangle { color: mouseBiasField.activeFocus ? palette.base : "transparent" border.color: mouseBiasField.activeFocus ? palette.highlight : "transparent" border.width: 1 radius: 4 anchors.fill: mouseBiasField anchors.margins: -2 } } } } Button { visible: navBar.currentIndex === 2 id: kofiButton Layout.preferredWidth: Kirigami.Units.gridUnit * 5 Layout.preferredHeight: Kirigami.Units.gridUnit * 3 contentItem: RowLayout { AnimatedImage { source: "icons/kofi.gif" sourceSize.width: 36 sourceSize.height: 36 fillMode: Image.Pad horizontalAlignment: Image.AlignLeft transform: Translate { x: 8 } } Text { text: i18nd("@button:kofi", "Kofi") horizontalAlignment: Text.AlignHCenter color: palette.text transform: Translate { x: -8 } } } onClicked: () => { Qt.openUrlExternally("https://ko-fi.com/digitalartifex"); } } RowLayout { visible: root.cfg_komplex_mode === 1 && navBar.currentIndex === 0 //Kirigami.FormData.label: i18nd("com.github.digitalartifex.komplex", "Author:") Text { Layout.preferredWidth: Kirigami.Units.gridUnit * 11 text: shaderPackModel.metadata.author horizontalAlignment: Text.AlignLeft color: palette.text } } RowLayout { visible: root.cfg_komplex_mode === 1 && navBar.currentIndex === 0 //Kirigami.FormData.label: i18nd("com.github.digitalartifex.komplex", "Description:") Text { Layout.preferredWidth: Kirigami.Units.gridUnit * 16 text: shaderPackModel.metadata.description horizontalAlignment: Text.AlignLeft color: palette.text wrapMode: Text.WordWrap maximumLineCount: 2 elide: Text.ElideRight } } RowLayout { visible: root.cfg_komplex_mode === 1 && navBar.currentIndex === 0 //Kirigami.FormData.label: i18nd("com.github.digitalartifex.komplex", "License:") Text { Layout.preferredWidth: Kirigami.Units.gridUnit * 11 text: shaderPackModel.metadata.license horizontalAlignment: Text.AlignLeft color: palette.text } } RowLayout { visible: root.cfg_komplex_mode === 1 && navBar.currentIndex === 0 //Kirigami.FormData.label: i18nd("com.github.digitalartifex.komplex", "Version:") Text { Layout.preferredWidth: Kirigami.Units.gridUnit * 11 text: shaderPackModel.metadata.version horizontalAlignment: Text.AlignLeft color: palette.text } } } } }